@mhosaic/feedback 0.31.0 → 0.32.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.
- package/dist/{chunk-AA24TFKX.mjs → chunk-YNDBZR37.mjs} +264 -65
- package/dist/chunk-YNDBZR37.mjs.map +1 -0
- package/dist/embed.min.js +19 -5
- package/dist/embed.min.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/react.mjs +1 -1
- package/dist/widget.min.js +20 -6
- package/dist/widget.min.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-AA24TFKX.mjs.map +0 -1
package/dist/widget.min.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/src/constants.js","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/src/util.js","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/src/options.js","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/src/create-element.js","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/src/component.js","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/src/diff/props.js","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/src/create-context.js","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/src/diff/children.js","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/src/diff/index.js","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/src/render.js","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/src/clone-element.js","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/src/diff/catch-error.js","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/hooks/src/index.js","../src/qa-meter/i18n.ts","../src/qa-meter/resolver.ts","../src/qa-meter/styles.ts","../src/qa-meter/ui.ts","../src/qa-meter/Pastille.tsx","../src/qa-meter/LaneIcons.tsx","../src/qa-meter/TestPanel.tsx","../src/qa-meter/SitemapModal.tsx","../src/qa-meter/mount.tsx","../src/qa-meter/index.ts","../src/widget.ts","../src/api/client.ts","../src/capture/urlSanitizer.ts","../src/capture/scrub.ts","../src/capture/device.ts","../src/capture/console.ts","../src/capture/network.ts","../src/capture/errors.ts","../src/capture/performance.ts","../src/capture/ringBuffer.ts","../src/capture/index.ts","../src/widget/i18n.ts","../src/widget/mount.tsx","../src/widget/currentPage.ts","../src/widget/BoardView.tsx","../src/widget/boardViewStore.ts","../src/widget/poll.ts","../src/widget/rateLimit.ts","../src/widget/ReportDetailView.tsx","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/jsx-runtime/src/utils.js","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/src/constants.js","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/jsx-runtime/src/index.js","../src/widget/linkifySeq.tsx","../src/widget/CommentBubble.tsx","../src/widget/screenshot-utils.ts","../src/widget/ChangelogList.tsx","../src/widget/ReportRow.tsx","../src/widget/Fab.tsx","../src/widget/Form.tsx","../src/widget/Annotator.tsx","../src/widget/MineList.tsx","../src/widget/KpiStrip.tsx","../src/widget/Modal.tsx","../src/widget/PageActivityStrip.tsx","../src/widget/PagePeekPanel.tsx","../src/widget/pageSignal.ts","../src/widget/styles.ts","../src/core.ts","../src/modules/error-tracking.ts"],"sourcesContent":["/** Normal hydration that attaches to a DOM tree but does not diff it. */\nexport const MODE_HYDRATE = 1 << 5;\n/** Signifies this VNode suspended on the previous render */\nexport const MODE_SUSPENDED = 1 << 7;\n/** Indicates that this node needs to be inserted while patching children */\nexport const INSERT_VNODE = 1 << 2;\n/** Indicates a VNode has been matched with another VNode in the diff */\nexport const MATCHED = 1 << 1;\n\n/** Reset all mode flags */\nexport const RESET_MODE = ~(MODE_HYDRATE | MODE_SUSPENDED);\n\nexport const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';\nexport const XHTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';\nexport const MATH_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';\n\nexport const NULL = null;\nexport const UNDEFINED = undefined;\nexport const EMPTY_OBJ = /** @type {any} */ ({});\nexport const EMPTY_ARR = [];\nexport const IS_NON_DIMENSIONAL =\n\t/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;\n","import { EMPTY_ARR } from './constants';\n\nexport const isArray = Array.isArray;\n\n/**\n * Assign properties from `props` to `obj`\n * @template O, P The obj and props types\n * @param {O} obj The object to copy properties to\n * @param {P} props The object to copy properties from\n * @returns {O & P}\n */\nexport function assign(obj, props) {\n\t// @ts-expect-error We change the type of `obj` to be `O & P`\n\tfor (let i in props) obj[i] = props[i];\n\treturn /** @type {O & P} */ (obj);\n}\n\n/**\n * Remove a child node from its parent if attached. This is a workaround for\n * IE11 which doesn't support `Element.prototype.remove()`. Using this function\n * is smaller than including a dedicated polyfill.\n * @param {import('./index').ContainerNode} node The node to remove\n */\nexport function removeNode(node) {\n\tif (node && node.parentNode) node.parentNode.removeChild(node);\n}\n\nexport const slice = EMPTY_ARR.slice;\n","import { _catchError } from './diff/catch-error';\n\n/**\n * The `option` object can potentially contain callback functions\n * that are called during various stages of our renderer. This is the\n * foundation on which all our addons like `preact/debug`, `preact/compat`,\n * and `preact/hooks` are based on. See the `Options` type in `internal.d.ts`\n * for a full list of available option hooks (most editors/IDEs allow you to\n * ctrl+click or cmd+click on mac the type definition below).\n * @type {import('./internal').Options}\n */\nconst options = {\n\t_catchError\n};\n\nexport default options;\n","import { slice } from './util';\nimport options from './options';\nimport { NULL, UNDEFINED } from './constants';\n\nlet vnodeId = 0;\n\n/**\n * Create an virtual node (used for JSX)\n * @param {import('./internal').VNode[\"type\"]} type The node name or Component constructor for this\n * virtual node\n * @param {object | null | undefined} [props] The properties of the virtual node\n * @param {Array<import('.').ComponentChildren>} [children] The children of the\n * virtual node\n * @returns {import('./internal').VNode}\n */\nexport function createElement(type, props, children) {\n\tlet normalizedProps = {},\n\t\tkey,\n\t\tref,\n\t\ti;\n\tfor (i in props) {\n\t\tif (i == 'key') key = props[i];\n\t\telse if (i == 'ref') ref = props[i];\n\t\telse normalizedProps[i] = props[i];\n\t}\n\n\tif (arguments.length > 2) {\n\t\tnormalizedProps.children =\n\t\t\targuments.length > 3 ? slice.call(arguments, 2) : children;\n\t}\n\n\t// If a Component VNode, check for and apply defaultProps\n\t// Note: type may be undefined in development, must never error here.\n\tif (typeof type == 'function' && type.defaultProps != NULL) {\n\t\tfor (i in type.defaultProps) {\n\t\t\tif (normalizedProps[i] === UNDEFINED) {\n\t\t\t\tnormalizedProps[i] = type.defaultProps[i];\n\t\t\t}\n\t\t}\n\t}\n\n\treturn createVNode(type, normalizedProps, key, ref, NULL);\n}\n\n/**\n * Create a VNode (used internally by Preact)\n * @param {import('./internal').VNode[\"type\"]} type The node name or Component\n * Constructor for this virtual node\n * @param {object | string | number | null} props The properties of this virtual node.\n * If this virtual node represents a text node, this is the text of the node (string or number).\n * @param {string | number | null} key The key for this virtual node, used when\n * diffing it against its children\n * @param {import('./internal').VNode[\"ref\"]} ref The ref property that will\n * receive a reference to its created child\n * @returns {import('./internal').VNode}\n */\nexport function createVNode(type, props, key, ref, original) {\n\t// V8 seems to be better at detecting type shapes if the object is allocated from the same call site\n\t// Do not inline into createElement and coerceToVNode!\n\t/** @type {import('./internal').VNode} */\n\tconst vnode = {\n\t\ttype,\n\t\tprops,\n\t\tkey,\n\t\tref,\n\t\t_children: NULL,\n\t\t_parent: NULL,\n\t\t_depth: 0,\n\t\t_dom: NULL,\n\t\t_component: NULL,\n\t\tconstructor: UNDEFINED,\n\t\t_original: original == NULL ? ++vnodeId : original,\n\t\t_index: -1,\n\t\t_flags: 0\n\t};\n\n\t// Only invoke the vnode hook if this was *not* a direct copy:\n\tif (original == NULL && options.vnode != NULL) options.vnode(vnode);\n\n\treturn vnode;\n}\n\nexport function createRef() {\n\treturn { current: NULL };\n}\n\nexport function Fragment(props) {\n\treturn props.children;\n}\n\n/**\n * Check if a the argument is a valid Preact VNode.\n * @param {*} vnode\n * @returns {vnode is VNode}\n */\nexport const isValidElement = vnode =>\n\tvnode != NULL && vnode.constructor === UNDEFINED;\n","import { assign } from './util';\nimport { diff, commitRoot } from './diff/index';\nimport options from './options';\nimport { Fragment } from './create-element';\nimport { MODE_HYDRATE, NULL } from './constants';\n\n/**\n * Base Component class. Provides `setState()` and `forceUpdate()`, which\n * trigger rendering\n * @param {object} props The initial component props\n * @param {object} context The initial context from parent components'\n * getChildContext\n */\nexport function BaseComponent(props, context) {\n\tthis.props = props;\n\tthis.context = context;\n}\n\n/**\n * Update component state and schedule a re-render.\n * @this {import('./internal').Component}\n * @param {object | ((s: object, p: object) => object)} update A hash of state\n * properties to update with new values or a function that given the current\n * state and props returns a new partial state\n * @param {() => void} [callback] A function to be called once component state is\n * updated\n */\nBaseComponent.prototype.setState = function (update, callback) {\n\t// only clone state when copying to nextState the first time.\n\tlet s;\n\tif (this._nextState != NULL && this._nextState != this.state) {\n\t\ts = this._nextState;\n\t} else {\n\t\ts = this._nextState = assign({}, this.state);\n\t}\n\n\tif (typeof update == 'function') {\n\t\t// Some libraries like `immer` mark the current state as readonly,\n\t\t// preventing us from mutating it, so we need to clone it. See #2716\n\t\tupdate = update(assign({}, s), this.props);\n\t}\n\n\tif (update) {\n\t\tassign(s, update);\n\t}\n\n\t// Skip update if updater function returned null\n\tif (update == NULL) return;\n\n\tif (this._vnode) {\n\t\tif (callback) {\n\t\t\tthis._stateCallbacks.push(callback);\n\t\t}\n\t\tenqueueRender(this);\n\t}\n};\n\n/**\n * Immediately perform a synchronous re-render of the component\n * @this {import('./internal').Component}\n * @param {() => void} [callback] A function to be called after component is\n * re-rendered\n */\nBaseComponent.prototype.forceUpdate = function (callback) {\n\tif (this._vnode) {\n\t\t// Set render mode so that we can differentiate where the render request\n\t\t// is coming from. We need this because forceUpdate should never call\n\t\t// shouldComponentUpdate\n\t\tthis._force = true;\n\t\tif (callback) this._renderCallbacks.push(callback);\n\t\tenqueueRender(this);\n\t}\n};\n\n/**\n * Accepts `props` and `state`, and returns a new Virtual DOM tree to build.\n * Virtual DOM is generally constructed via [JSX](https://jasonformat.com/wtf-is-jsx).\n * @param {object} props Props (eg: JSX attributes) received from parent\n * element/component\n * @param {object} state The component's current state\n * @param {object} context Context object, as returned by the nearest\n * ancestor's `getChildContext()`\n * @returns {ComponentChildren | void}\n */\nBaseComponent.prototype.render = Fragment;\n\n/**\n * @param {import('./internal').VNode} vnode\n * @param {number | null} [childIndex]\n */\nexport function getDomSibling(vnode, childIndex) {\n\tif (childIndex == NULL) {\n\t\t// Use childIndex==null as a signal to resume the search from the vnode's sibling\n\t\treturn vnode._parent\n\t\t\t? getDomSibling(vnode._parent, vnode._index + 1)\n\t\t\t: NULL;\n\t}\n\n\tlet sibling;\n\tfor (; childIndex < vnode._children.length; childIndex++) {\n\t\tsibling = vnode._children[childIndex];\n\n\t\tif (sibling != NULL && sibling._dom != NULL) {\n\t\t\t// Since updateParentDomPointers keeps _dom pointer correct,\n\t\t\t// we can rely on _dom to tell us if this subtree contains a\n\t\t\t// rendered DOM node, and what the first rendered DOM node is\n\t\t\treturn sibling._dom;\n\t\t}\n\t}\n\n\t// If we get here, we have not found a DOM node in this vnode's children.\n\t// We must resume from this vnode's sibling (in it's parent _children array)\n\t// Only climb up and search the parent if we aren't searching through a DOM\n\t// VNode (meaning we reached the DOM parent of the original vnode that began\n\t// the search)\n\treturn typeof vnode.type == 'function' ? getDomSibling(vnode) : NULL;\n}\n\n/**\n * Trigger in-place re-rendering of a component.\n * @param {import('./internal').Component} component The component to rerender\n */\nfunction renderComponent(component) {\n\tif (component._parentDom && component._dirty) {\n\t\tlet oldVNode = component._vnode,\n\t\t\toldDom = oldVNode._dom,\n\t\t\tcommitQueue = [],\n\t\t\trefQueue = [],\n\t\t\tnewVNode = assign({}, oldVNode);\n\t\tnewVNode._original = oldVNode._original + 1;\n\t\tif (options.vnode) options.vnode(newVNode);\n\n\t\tdiff(\n\t\t\tcomponent._parentDom,\n\t\t\tnewVNode,\n\t\t\toldVNode,\n\t\t\tcomponent._globalContext,\n\t\t\tcomponent._parentDom.namespaceURI,\n\t\t\toldVNode._flags & MODE_HYDRATE ? [oldDom] : NULL,\n\t\t\tcommitQueue,\n\t\t\toldDom == NULL ? getDomSibling(oldVNode) : oldDom,\n\t\t\t!!(oldVNode._flags & MODE_HYDRATE),\n\t\t\trefQueue\n\t\t);\n\n\t\tnewVNode._original = oldVNode._original;\n\t\tnewVNode._parent._children[newVNode._index] = newVNode;\n\t\tcommitRoot(commitQueue, newVNode, refQueue);\n\t\toldVNode._dom = oldVNode._parent = null;\n\n\t\tif (newVNode._dom != oldDom) {\n\t\t\tupdateParentDomPointers(newVNode);\n\t\t}\n\t}\n}\n\n/**\n * @param {import('./internal').VNode} vnode\n */\nfunction updateParentDomPointers(vnode) {\n\tif ((vnode = vnode._parent) != NULL && vnode._component != NULL) {\n\t\tvnode._dom = vnode._component.base = NULL;\n\t\tvnode._children.some(child => {\n\t\t\tif (child != NULL && child._dom != NULL) {\n\t\t\t\treturn (vnode._dom = vnode._component.base = child._dom);\n\t\t\t}\n\t\t});\n\n\t\treturn updateParentDomPointers(vnode);\n\t}\n}\n\n/**\n * The render queue\n * @type {Array<import('./internal').Component>}\n */\nlet rerenderQueue = [];\n\n/*\n * The value of `Component.debounce` must asynchronously invoke the passed in callback. It is\n * important that contributors to Preact can consistently reason about what calls to `setState`, etc.\n * do, and when their effects will be applied. See the links below for some further reading on designing\n * asynchronous APIs.\n * * [Designing APIs for Asynchrony](https://blog.izs.me/2013/08/designing-apis-for-asynchrony)\n * * [Callbacks synchronous and asynchronous](https://blog.ometer.com/2011/07/24/callbacks-synchronous-and-asynchronous/)\n */\n\nlet prevDebounce;\n\nconst defer =\n\ttypeof Promise == 'function'\n\t\t? Promise.prototype.then.bind(Promise.resolve())\n\t\t: setTimeout;\n\n/**\n * Enqueue a rerender of a component\n * @param {import('./internal').Component} c The component to rerender\n */\nexport function enqueueRender(c) {\n\tif (\n\t\t(!c._dirty &&\n\t\t\t(c._dirty = true) &&\n\t\t\trerenderQueue.push(c) &&\n\t\t\t!process._rerenderCount++) ||\n\t\tprevDebounce != options.debounceRendering\n\t) {\n\t\tprevDebounce = options.debounceRendering;\n\t\t(prevDebounce || defer)(process);\n\t}\n}\n\n/**\n * @param {import('./internal').Component} a\n * @param {import('./internal').Component} b\n */\nconst depthSort = (a, b) => a._vnode._depth - b._vnode._depth;\n\n/** Flush the render queue by rerendering all queued components */\nfunction process() {\n\ttry {\n\t\tlet c,\n\t\t\tl = 1;\n\n\t\t// Don't update `renderCount` yet. Keep its value non-zero to prevent unnecessary\n\t\t// process() calls from getting scheduled while `queue` is still being consumed.\n\t\twhile (rerenderQueue.length) {\n\t\t\t// Keep the rerender queue sorted by (depth, insertion order). The queue\n\t\t\t// will initially be sorted on the first iteration only if it has more than 1 item.\n\t\t\t//\n\t\t\t// New items can be added to the queue e.g. when rerendering a provider, so we want to\n\t\t\t// keep the order from top to bottom with those new items so we can handle them in a\n\t\t\t// single pass\n\t\t\tif (rerenderQueue.length > l) {\n\t\t\t\trerenderQueue.sort(depthSort);\n\t\t\t}\n\n\t\t\tc = rerenderQueue.shift();\n\t\t\tl = rerenderQueue.length;\n\n\t\t\trenderComponent(c);\n\t\t}\n\t} finally {\n\t\trerenderQueue.length = process._rerenderCount = 0;\n\t}\n}\n\nprocess._rerenderCount = 0;\n","import { IS_NON_DIMENSIONAL, NULL, SVG_NAMESPACE } from '../constants';\nimport options from '../options';\n\n// Per-instance unique key for event clock stamps. Each Preact copy on the page\n// gets its own random suffix so that `_dispatched` / `_attached` properties on\n// shared event objects and handler functions cannot collide across instances.\n// ~1 in 60M collision odds - if you have that many praect versions on the page,\n// you deserve some weird bugs.\n// In 11 we can replace this with a\n// Symbol\nlet _id = Math.random().toString(8),\n\tEVENT_DISPATCHED = '__d' + _id,\n\tEVENT_ATTACHED = '__a' + _id;\n\nfunction setStyle(style, key, value) {\n\tif (key[0] == '-') {\n\t\tstyle.setProperty(key, value == NULL ? '' : value);\n\t} else if (value == NULL) {\n\t\tstyle[key] = '';\n\t} else if (typeof value != 'number' || IS_NON_DIMENSIONAL.test(key)) {\n\t\tstyle[key] = value;\n\t} else {\n\t\tstyle[key] = value + 'px';\n\t}\n}\n\nconst CAPTURE_REGEX = /(PointerCapture)$|Capture$/i;\n\n// A logical clock to solve issues like https://github.com/preactjs/preact/issues/3927.\n// When the DOM performs an event it leaves micro-ticks in between bubbling up which means that\n// an event can trigger on a newly reated DOM-node while the event bubbles up.\n//\n// Originally inspired by Vue\n// (https://github.com/vuejs/core/blob/caeb8a68811a1b0f79/packages/runtime-dom/src/modules/events.ts#L90-L101),\n// but modified to use a logical clock instead of Date.now() in case event handlers get attached\n// and events get dispatched during the same millisecond.\n//\n// The clock is incremented after each new event dispatch. This allows 1 000 000 new events\n// per second for over 280 years before the value reaches Number.MAX_SAFE_INTEGER (2**53 - 1).\nlet eventClock = 0;\n\n/**\n * Set a property value on a DOM node\n * @param {import('../internal').PreactElement} dom The DOM node to modify\n * @param {string} name The name of the property to set\n * @param {*} value The value to set the property to\n * @param {*} oldValue The old value the property had\n * @param {string} namespace Whether or not this DOM node is an SVG node or not\n */\nexport function setProperty(dom, name, value, oldValue, namespace) {\n\tlet useCapture;\n\n\to: if (name == 'style') {\n\t\tif (typeof value == 'string') {\n\t\t\tdom.style.cssText = value;\n\t\t} else {\n\t\t\tif (typeof oldValue == 'string') {\n\t\t\t\tdom.style.cssText = oldValue = '';\n\t\t\t}\n\n\t\t\tif (oldValue) {\n\t\t\t\tfor (name in oldValue) {\n\t\t\t\t\tif (!(value && name in value)) {\n\t\t\t\t\t\tsetStyle(dom.style, name, '');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (value) {\n\t\t\t\tfor (name in value) {\n\t\t\t\t\tif (!oldValue || value[name] != oldValue[name]) {\n\t\t\t\t\t\tsetStyle(dom.style, name, value[name]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t// Benchmark for comparison: https://esbench.com/bench/574c954bdb965b9a00965ac6\n\telse if (name[0] == 'o' && name[1] == 'n') {\n\t\tuseCapture = name != (name = name.replace(CAPTURE_REGEX, '$1'));\n\t\tconst lowerCaseName = name.toLowerCase();\n\n\t\t// Infer correct casing for DOM built-in events:\n\t\tif (lowerCaseName in dom || name == 'onFocusOut' || name == 'onFocusIn')\n\t\t\tname = lowerCaseName.slice(2);\n\t\telse name = name.slice(2);\n\n\t\tif (!dom._listeners) dom._listeners = {};\n\t\tdom._listeners[name + useCapture] = value;\n\n\t\tif (value) {\n\t\t\tif (!oldValue) {\n\t\t\t\tvalue[EVENT_ATTACHED] = eventClock;\n\t\t\t\tdom.addEventListener(\n\t\t\t\t\tname,\n\t\t\t\t\tuseCapture ? eventProxyCapture : eventProxy,\n\t\t\t\t\tuseCapture\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tvalue[EVENT_ATTACHED] = oldValue[EVENT_ATTACHED];\n\t\t\t}\n\t\t} else {\n\t\t\tdom.removeEventListener(\n\t\t\t\tname,\n\t\t\t\tuseCapture ? eventProxyCapture : eventProxy,\n\t\t\t\tuseCapture\n\t\t\t);\n\t\t}\n\t} else {\n\t\tif (namespace == SVG_NAMESPACE) {\n\t\t\t// Normalize incorrect prop usage for SVG:\n\t\t\t// - xlink:href / xlinkHref --> href (xlink:href was removed from SVG and isn't needed)\n\t\t\t// - className --> class\n\t\t\tname = name.replace(/xlink(H|:h)/, 'h').replace(/sName$/, 's');\n\t\t} else if (\n\t\t\tname != 'width' &&\n\t\t\tname != 'height' &&\n\t\t\tname != 'href' &&\n\t\t\tname != 'list' &&\n\t\t\tname != 'form' &&\n\t\t\t// Default value in browsers is `-1` and an empty string is\n\t\t\t// cast to `0` instead\n\t\t\tname != 'tabIndex' &&\n\t\t\tname != 'download' &&\n\t\t\tname != 'rowSpan' &&\n\t\t\tname != 'colSpan' &&\n\t\t\tname != 'role' &&\n\t\t\tname != 'popover' &&\n\t\t\tname in dom\n\t\t) {\n\t\t\ttry {\n\t\t\t\tdom[name] = value == NULL ? '' : value;\n\t\t\t\t// labelled break is 1b smaller here than a return statement (sorry)\n\t\t\t\tbreak o;\n\t\t\t} catch (e) {}\n\t\t}\n\n\t\t// aria- and data- attributes have no boolean representation.\n\t\t// A `false` value is different from the attribute not being\n\t\t// present, so we can't remove it. For non-boolean aria\n\t\t// attributes we could treat false as a removal, but the\n\t\t// amount of exceptions would cost too many bytes. On top of\n\t\t// that other frameworks generally stringify `false`.\n\n\t\tif (typeof value == 'function') {\n\t\t\t// never serialize functions as attribute values\n\t\t} else if (value != NULL && (value !== false || name[4] == '-')) {\n\t\t\tdom.setAttribute(name, name == 'popover' && value == true ? '' : value);\n\t\t} else {\n\t\t\tdom.removeAttribute(name);\n\t\t}\n\t}\n}\n\n/**\n * Create an event proxy function.\n * @param {boolean} useCapture Is the event handler for the capture phase.\n * @private\n */\nfunction createEventProxy(useCapture) {\n\t/**\n\t * Proxy an event to hooked event handlers\n\t * @param {import('../internal').PreactEvent} e The event object from the browser\n\t * @private\n\t */\n\treturn function (e) {\n\t\tif (this._listeners) {\n\t\t\tconst eventHandler = this._listeners[e.type + useCapture];\n\t\t\tif (e[EVENT_DISPATCHED] == NULL) {\n\t\t\t\te[EVENT_DISPATCHED] = eventClock++;\n\n\t\t\t\t// When `e[EVENT_DISPATCHED]` is smaller than the time when the targeted event\n\t\t\t\t// handler was attached we know we have bubbled up to an element that was added\n\t\t\t\t// during patching the DOM.\n\t\t\t} else if (e[EVENT_DISPATCHED] < eventHandler[EVENT_ATTACHED]) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\treturn eventHandler(options.event ? options.event(e) : e);\n\t\t}\n\t};\n}\n\nconst eventProxy = createEventProxy(false);\nconst eventProxyCapture = createEventProxy(true);\n","import { enqueueRender } from './component';\nimport { NULL } from './constants';\n\nexport let i = 0;\n\nexport function createContext(defaultValue) {\n\tfunction Context(props) {\n\t\tif (!this.getChildContext) {\n\t\t\t/** @type {Set<import('./internal').Component> | null} */\n\t\t\tlet subs = new Set();\n\t\t\tlet ctx = {};\n\t\t\tctx[Context._id] = this;\n\n\t\t\tthis.getChildContext = () => ctx;\n\n\t\t\tthis.componentWillUnmount = () => {\n\t\t\t\tsubs = NULL;\n\t\t\t};\n\n\t\t\tthis.shouldComponentUpdate = function (_props) {\n\t\t\t\t// @ts-expect-error even\n\t\t\t\tif (this.props.value != _props.value) {\n\t\t\t\t\tsubs.forEach(c => {\n\t\t\t\t\t\tc._force = true;\n\t\t\t\t\t\tenqueueRender(c);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tthis.sub = c => {\n\t\t\t\tsubs.add(c);\n\t\t\t\tlet old = c.componentWillUnmount;\n\t\t\t\tc.componentWillUnmount = () => {\n\t\t\t\t\tif (subs) {\n\t\t\t\t\t\tsubs.delete(c);\n\t\t\t\t\t}\n\t\t\t\t\tif (old) old.call(c);\n\t\t\t\t};\n\t\t\t};\n\t\t}\n\n\t\treturn props.children;\n\t}\n\n\tContext._id = '__cC' + i++;\n\tContext._defaultValue = defaultValue;\n\n\t/** @type {import('./internal').FunctionComponent} */\n\tContext.Consumer = (props, contextValue) => {\n\t\treturn props.children(contextValue);\n\t};\n\n\t// we could also get rid of _contextRef entirely\n\tContext.Provider =\n\t\tContext._contextRef =\n\t\tContext.Consumer.contextType =\n\t\t\tContext;\n\n\treturn Context;\n}\n","import { diff, unmount, applyRef } from './index';\nimport { createVNode, Fragment } from '../create-element';\nimport {\n\tEMPTY_OBJ,\n\tEMPTY_ARR,\n\tINSERT_VNODE,\n\tMATCHED,\n\tUNDEFINED,\n\tNULL\n} from '../constants';\nimport { isArray } from '../util';\nimport { getDomSibling } from '../component';\n\n/**\n * @typedef {import('../internal').ComponentChildren} ComponentChildren\n * @typedef {import('../internal').Component} Component\n * @typedef {import('../internal').PreactElement} PreactElement\n * @typedef {import('../internal').VNode} VNode\n */\n\n/**\n * Diff the children of a virtual node\n * @param {PreactElement} parentDom The DOM element whose children are being\n * diffed\n * @param {ComponentChildren[]} renderResult\n * @param {VNode} newParentVNode The new virtual node whose children should be\n * diff'ed against oldParentVNode\n * @param {VNode} oldParentVNode The old virtual node whose children should be\n * diff'ed against newParentVNode\n * @param {object} globalContext The current context object - modified by\n * getChildContext\n * @param {string} namespace Current namespace of the DOM node (HTML, SVG, or MathML)\n * @param {Array<PreactElement>} excessDomChildren\n * @param {Array<Component>} commitQueue List of components which have callbacks\n * to invoke in commitRoot\n * @param {PreactElement} oldDom The current attached DOM element any new dom\n * elements should be placed around. Likely `null` on first render (except when\n * hydrating). Can be a sibling DOM element when diffing Fragments that have\n * siblings. In most cases, it starts out as `oldChildren[0]._dom`.\n * @param {boolean} isHydrating Whether or not we are in hydration\n * @param {any[]} refQueue an array of elements needed to invoke refs\n */\nexport function diffChildren(\n\tparentDom,\n\trenderResult,\n\tnewParentVNode,\n\toldParentVNode,\n\tglobalContext,\n\tnamespace,\n\texcessDomChildren,\n\tcommitQueue,\n\toldDom,\n\tisHydrating,\n\trefQueue\n) {\n\tlet i,\n\t\t/** @type {VNode} */\n\t\toldVNode,\n\t\t/** @type {VNode} */\n\t\tchildVNode,\n\t\t/** @type {PreactElement} */\n\t\tnewDom,\n\t\t/** @type {PreactElement} */\n\t\tfirstChildDom;\n\n\t// This is a compression of oldParentVNode!=null && oldParentVNode != EMPTY_OBJ && oldParentVNode._children || EMPTY_ARR\n\t// as EMPTY_OBJ._children should be `undefined`.\n\t/** @type {VNode[]} */\n\tlet oldChildren = (oldParentVNode && oldParentVNode._children) || EMPTY_ARR;\n\n\tlet newChildrenLength = renderResult.length;\n\n\toldDom = constructNewChildrenArray(\n\t\tnewParentVNode,\n\t\trenderResult,\n\t\toldChildren,\n\t\toldDom,\n\t\tnewChildrenLength\n\t);\n\n\tfor (i = 0; i < newChildrenLength; i++) {\n\t\tchildVNode = newParentVNode._children[i];\n\t\tif (childVNode == NULL) continue;\n\n\t\t// At this point, constructNewChildrenArray has assigned _index to be the\n\t\t// matchingIndex for this VNode's oldVNode (or -1 if there is no oldVNode).\n\t\toldVNode =\n\t\t\t(childVNode._index != -1 && oldChildren[childVNode._index]) || EMPTY_OBJ;\n\n\t\t// Update childVNode._index to its final index\n\t\tchildVNode._index = i;\n\n\t\t// Morph the old element into the new one, but don't append it to the dom yet\n\t\tlet result = diff(\n\t\t\tparentDom,\n\t\t\tchildVNode,\n\t\t\toldVNode,\n\t\t\tglobalContext,\n\t\t\tnamespace,\n\t\t\texcessDomChildren,\n\t\t\tcommitQueue,\n\t\t\toldDom,\n\t\t\tisHydrating,\n\t\t\trefQueue\n\t\t);\n\n\t\t// Adjust DOM nodes\n\t\tnewDom = childVNode._dom;\n\t\tif (childVNode.ref && oldVNode.ref != childVNode.ref) {\n\t\t\tif (oldVNode.ref) {\n\t\t\t\tapplyRef(oldVNode.ref, NULL, childVNode);\n\t\t\t}\n\t\t\trefQueue.push(\n\t\t\t\tchildVNode.ref,\n\t\t\t\tchildVNode._component || newDom,\n\t\t\t\tchildVNode\n\t\t\t);\n\t\t}\n\n\t\tif (firstChildDom == NULL && newDom != NULL) {\n\t\t\tfirstChildDom = newDom;\n\t\t}\n\n\t\tlet shouldPlace = !!(childVNode._flags & INSERT_VNODE);\n\t\tif (shouldPlace || oldVNode._children === childVNode._children) {\n\t\t\toldDom = insert(childVNode, oldDom, parentDom, shouldPlace);\n\n\t\t\t// When a matched VNode is physically moved via INSERT_VNODE, its old\n\t\t\t// _dom pointer becomes a stale positional reference. Clear it so that\n\t\t\t// getDomSibling (called from nested diffs) won't return this stale\n\t\t\t// reference and mis-place subsequent DOM nodes. See #5065.\n\t\t\tif (shouldPlace && oldVNode._dom) {\n\t\t\t\toldVNode._dom = NULL;\n\t\t\t}\n\t\t} else if (typeof childVNode.type == 'function' && result !== UNDEFINED) {\n\t\t\toldDom = result;\n\t\t} else if (newDom) {\n\t\t\toldDom = newDom.nextSibling;\n\t\t}\n\n\t\t// Unset diffing flags\n\t\tchildVNode._flags &= ~(INSERT_VNODE | MATCHED);\n\t}\n\n\tnewParentVNode._dom = firstChildDom;\n\n\treturn oldDom;\n}\n\n/**\n * @param {VNode} newParentVNode\n * @param {ComponentChildren[]} renderResult\n * @param {VNode[]} oldChildren\n */\nfunction constructNewChildrenArray(\n\tnewParentVNode,\n\trenderResult,\n\toldChildren,\n\toldDom,\n\tnewChildrenLength\n) {\n\t/** @type {number} */\n\tlet i;\n\t/** @type {VNode} */\n\tlet childVNode;\n\t/** @type {VNode} */\n\tlet oldVNode;\n\n\tlet oldChildrenLength = oldChildren.length,\n\t\tremainingOldChildren = oldChildrenLength;\n\n\tlet skew = 0;\n\n\tnewParentVNode._children = new Array(newChildrenLength);\n\tfor (i = 0; i < newChildrenLength; i++) {\n\t\t// @ts-expect-error We are reusing the childVNode variable to hold both the\n\t\t// pre and post normalized childVNode\n\t\tchildVNode = renderResult[i];\n\n\t\tif (\n\t\t\tchildVNode == NULL ||\n\t\t\ttypeof childVNode == 'boolean' ||\n\t\t\ttypeof childVNode == 'function'\n\t\t) {\n\t\t\tnewParentVNode._children[i] = NULL;\n\t\t\tcontinue;\n\t\t}\n\t\t// If this newVNode is being reused (e.g. <div>{reuse}{reuse}</div>) in the same diff,\n\t\t// or we are rendering a component (e.g. setState) copy the oldVNodes so it can have\n\t\t// it's own DOM & etc. pointers\n\t\telse if (\n\t\t\ttypeof childVNode == 'string' ||\n\t\t\ttypeof childVNode == 'number' ||\n\t\t\t// eslint-disable-next-line valid-typeof\n\t\t\ttypeof childVNode == 'bigint' ||\n\t\t\tchildVNode.constructor == String\n\t\t) {\n\t\t\tchildVNode = newParentVNode._children[i] = createVNode(\n\t\t\t\tNULL,\n\t\t\t\tchildVNode,\n\t\t\t\tNULL,\n\t\t\t\tNULL,\n\t\t\t\tNULL\n\t\t\t);\n\t\t} else if (isArray(childVNode)) {\n\t\t\tchildVNode = newParentVNode._children[i] = createVNode(\n\t\t\t\tFragment,\n\t\t\t\t{ children: childVNode },\n\t\t\t\tNULL,\n\t\t\t\tNULL,\n\t\t\t\tNULL\n\t\t\t);\n\t\t} else if (childVNode.constructor === UNDEFINED && childVNode._depth > 0) {\n\t\t\t// VNode is already in use, clone it. This can happen in the following\n\t\t\t// scenario:\n\t\t\t// const reuse = <div />\n\t\t\t// <div>{reuse}<span />{reuse}</div>\n\t\t\tchildVNode = newParentVNode._children[i] = createVNode(\n\t\t\t\tchildVNode.type,\n\t\t\t\tchildVNode.props,\n\t\t\t\tchildVNode.key,\n\t\t\t\tchildVNode.ref ? childVNode.ref : NULL,\n\t\t\t\tchildVNode._original\n\t\t\t);\n\t\t} else {\n\t\t\tnewParentVNode._children[i] = childVNode;\n\t\t}\n\n\t\tconst skewedIndex = i + skew;\n\t\tchildVNode._parent = newParentVNode;\n\t\tchildVNode._depth = newParentVNode._depth + 1;\n\n\t\t// Temporarily store the matchingIndex on the _index property so we can pull\n\t\t// out the oldVNode in diffChildren. We'll override this to the VNode's\n\t\t// final index after using this property to get the oldVNode\n\t\tconst matchingIndex = (childVNode._index = findMatchingIndex(\n\t\t\tchildVNode,\n\t\t\toldChildren,\n\t\t\tskewedIndex,\n\t\t\tremainingOldChildren\n\t\t));\n\n\t\toldVNode = NULL;\n\t\tif (matchingIndex != -1) {\n\t\t\toldVNode = oldChildren[matchingIndex];\n\t\t\tremainingOldChildren--;\n\t\t\tif (oldVNode) {\n\t\t\t\toldVNode._flags |= MATCHED;\n\t\t\t}\n\t\t}\n\n\t\t// Here, we define isMounting for the purposes of the skew diffing\n\t\t// algorithm. Nodes that are unsuspending are considered mounting and we detect\n\t\t// this by checking if oldVNode._original == null\n\t\tconst isMounting = oldVNode == NULL || oldVNode._original == NULL;\n\n\t\tif (isMounting) {\n\t\t\tif (matchingIndex == -1) {\n\t\t\t\t// When the array of children is growing we need to decrease the skew\n\t\t\t\t// as we are adding a new element to the array.\n\t\t\t\t// Example:\n\t\t\t\t// [1, 2, 3] --> [0, 1, 2, 3]\n\t\t\t\t// oldChildren newChildren\n\t\t\t\t//\n\t\t\t\t// The new element is at index 0, so our skew is 0,\n\t\t\t\t// we need to decrease the skew as we are adding a new element.\n\t\t\t\t// The decrease will cause us to compare the element at position 1\n\t\t\t\t// with value 1 with the element at position 0 with value 0.\n\t\t\t\t//\n\t\t\t\t// A linear concept is applied when the array is shrinking,\n\t\t\t\t// if the length is unchanged we can assume that no skew\n\t\t\t\t// changes are needed.\n\t\t\t\tif (newChildrenLength > oldChildrenLength) {\n\t\t\t\t\tskew--;\n\t\t\t\t} else if (newChildrenLength < oldChildrenLength) {\n\t\t\t\t\tskew++;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If we are mounting a DOM VNode, mark it for insertion\n\t\t\tif (typeof childVNode.type != 'function') {\n\t\t\t\tchildVNode._flags |= INSERT_VNODE;\n\t\t\t}\n\t\t} else if (matchingIndex != skewedIndex) {\n\t\t\t// When we move elements around i.e. [0, 1, 2] --> [1, 0, 2]\n\t\t\t// --> we diff 1, we find it at position 1 while our skewed index is 0 and our skew is 0\n\t\t\t// we set the skew to 1 as we found an offset.\n\t\t\t// --> we diff 0, we find it at position 0 while our skewed index is at 2 and our skew is 1\n\t\t\t// this makes us increase the skew again.\n\t\t\t// --> we diff 2, we find it at position 2 while our skewed index is at 4 and our skew is 2\n\t\t\t//\n\t\t\t// this becomes an optimization question where currently we see a 1 element offset as an insertion\n\t\t\t// or deletion i.e. we optimize for [0, 1, 2] --> [9, 0, 1, 2]\n\t\t\t// while a more than 1 offset we see as a swap.\n\t\t\t// We could probably build heuristics for having an optimized course of action here as well, but\n\t\t\t// might go at the cost of some bytes.\n\t\t\t//\n\t\t\t// If we wanted to optimize for i.e. only swaps we'd just do the last two code-branches and have\n\t\t\t// only the first item be a re-scouting and all the others fall in their skewed counter-part.\n\t\t\t// We could also further optimize for swaps\n\t\t\tif (matchingIndex == skewedIndex - 1) {\n\t\t\t\tskew--;\n\t\t\t} else if (matchingIndex == skewedIndex + 1) {\n\t\t\t\tskew++;\n\t\t\t} else {\n\t\t\t\tif (matchingIndex > skewedIndex) {\n\t\t\t\t\tskew--;\n\t\t\t\t} else {\n\t\t\t\t\tskew++;\n\t\t\t\t}\n\n\t\t\t\t// Move this VNode's DOM if the original index (matchingIndex) doesn't\n\t\t\t\t// match the new skew index (i + new skew)\n\t\t\t\t// In the former two branches we know that it matches after skewing\n\t\t\t\tchildVNode._flags |= INSERT_VNODE;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Remove remaining oldChildren if there are any. Loop forwards so that as we\n\t// unmount DOM from the beginning of the oldChildren, we can adjust oldDom to\n\t// point to the next child, which needs to be the first DOM node that won't be\n\t// unmounted.\n\tif (remainingOldChildren) {\n\t\tfor (i = 0; i < oldChildrenLength; i++) {\n\t\t\toldVNode = oldChildren[i];\n\t\t\tif (oldVNode != NULL && (oldVNode._flags & MATCHED) == 0) {\n\t\t\t\tif (oldVNode._dom == oldDom) {\n\t\t\t\t\toldDom = getDomSibling(oldVNode);\n\t\t\t\t}\n\n\t\t\t\tunmount(oldVNode, oldVNode);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn oldDom;\n}\n\n/**\n * @param {VNode} parentVNode\n * @param {PreactElement} oldDom\n * @param {PreactElement} parentDom\n * @param {boolean} shouldPlace\n * @returns {PreactElement}\n */\nfunction insert(parentVNode, oldDom, parentDom, shouldPlace) {\n\t// Note: VNodes in nested suspended trees may be missing _children.\n\n\tif (typeof parentVNode.type == 'function') {\n\t\tlet children = parentVNode._children;\n\t\tfor (let i = 0; children && i < children.length; i++) {\n\t\t\tif (children[i]) {\n\t\t\t\t// If we enter this code path on sCU bailout, where we copy\n\t\t\t\t// oldVNode._children to newVNode._children, we need to update the old\n\t\t\t\t// children's _parent pointer to point to the newVNode (parentVNode\n\t\t\t\t// here).\n\t\t\t\tchildren[i]._parent = parentVNode;\n\t\t\t\toldDom = insert(children[i], oldDom, parentDom, shouldPlace);\n\t\t\t}\n\t\t}\n\n\t\treturn oldDom;\n\t} else if (parentVNode._dom != oldDom) {\n\t\tif (shouldPlace) {\n\t\t\tif (oldDom && parentVNode.type && !oldDom.parentNode) {\n\t\t\t\toldDom = getDomSibling(parentVNode);\n\t\t\t}\n\t\t\tparentDom.insertBefore(parentVNode._dom, oldDom || NULL);\n\t\t}\n\t\toldDom = parentVNode._dom;\n\t}\n\n\tdo {\n\t\toldDom = oldDom && oldDom.nextSibling;\n\t} while (oldDom != NULL && oldDom.nodeType == 8);\n\n\treturn oldDom;\n}\n\n/**\n * Flatten and loop through the children of a virtual node\n * @param {ComponentChildren} children The unflattened children of a virtual\n * node\n * @returns {VNode[]}\n */\nexport function toChildArray(children, out) {\n\tout = out || [];\n\tif (children == NULL || typeof children == 'boolean') {\n\t} else if (isArray(children)) {\n\t\tchildren.some(child => {\n\t\t\ttoChildArray(child, out);\n\t\t});\n\t} else {\n\t\tout.push(children);\n\t}\n\treturn out;\n}\n\n/**\n * @param {VNode} childVNode\n * @param {VNode[]} oldChildren\n * @param {number} skewedIndex\n * @param {number} remainingOldChildren\n * @returns {number}\n */\nfunction findMatchingIndex(\n\tchildVNode,\n\toldChildren,\n\tskewedIndex,\n\tremainingOldChildren\n) {\n\tconst key = childVNode.key;\n\tconst type = childVNode.type;\n\tlet oldVNode = oldChildren[skewedIndex];\n\tconst matched = oldVNode != NULL && (oldVNode._flags & MATCHED) == 0;\n\n\t// We only need to perform a search if there are more children\n\t// (remainingOldChildren) to search. However, if the oldVNode we just looked\n\t// at skewedIndex was not already used in this diff, then there must be at\n\t// least 1 other (so greater than 1) remainingOldChildren to attempt to match\n\t// against. So the following condition checks that ensuring\n\t// remainingOldChildren > 1 if the oldVNode is not already used/matched. Else\n\t// if the oldVNode was null or matched, then there could needs to be at least\n\t// 1 (aka `remainingOldChildren > 0`) children to find and compare against.\n\t//\n\t// If there is an unkeyed functional VNode, that isn't a built-in like our Fragment,\n\t// we should not search as we risk re-using state of an unrelated VNode. (reverted for now)\n\tlet shouldSearch =\n\t\t// (typeof type != 'function' || type === Fragment || key) &&\n\t\tremainingOldChildren > (matched ? 1 : 0);\n\n\tif (\n\t\t(oldVNode === NULL && key == null) ||\n\t\t(matched && key == oldVNode.key && type == oldVNode.type)\n\t) {\n\t\treturn skewedIndex;\n\t} else if (shouldSearch) {\n\t\tlet x = skewedIndex - 1;\n\t\tlet y = skewedIndex + 1;\n\t\twhile (x >= 0 || y < oldChildren.length) {\n\t\t\tconst childIndex = x >= 0 ? x-- : y++;\n\t\t\toldVNode = oldChildren[childIndex];\n\t\t\tif (\n\t\t\t\toldVNode != NULL &&\n\t\t\t\t(oldVNode._flags & MATCHED) == 0 &&\n\t\t\t\tkey == oldVNode.key &&\n\t\t\t\ttype == oldVNode.type\n\t\t\t) {\n\t\t\t\treturn childIndex;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn -1;\n}\n","import {\n\tEMPTY_ARR,\n\tEMPTY_OBJ,\n\tMATH_NAMESPACE,\n\tMODE_HYDRATE,\n\tMODE_SUSPENDED,\n\tNULL,\n\tRESET_MODE,\n\tSVG_NAMESPACE,\n\tUNDEFINED,\n\tXHTML_NAMESPACE\n} from '../constants';\nimport { BaseComponent, getDomSibling } from '../component';\nimport { Fragment } from '../create-element';\nimport { diffChildren } from './children';\nimport { setProperty } from './props';\nimport { assign, isArray, removeNode, slice } from '../util';\nimport options from '../options';\n\n/**\n * @typedef {import('../internal').ComponentChildren} ComponentChildren\n * @typedef {import('../internal').Component} Component\n * @typedef {import('../internal').PreactElement} PreactElement\n * @typedef {import('../internal').VNode} VNode\n */\n\n/**\n * @template {any} T\n * @typedef {import('../internal').Ref<T>} Ref<T>\n */\n\n/**\n * Diff two virtual nodes and apply proper changes to the DOM\n * @param {PreactElement} parentDom The parent of the DOM element\n * @param {VNode} newVNode The new virtual node\n * @param {VNode} oldVNode The old virtual node\n * @param {object} globalContext The current context object. Modified by\n * getChildContext\n * @param {string} namespace Current namespace of the DOM node (HTML, SVG, or MathML)\n * @param {Array<PreactElement>} excessDomChildren\n * @param {Array<Component>} commitQueue List of components which have callbacks\n * to invoke in commitRoot\n * @param {PreactElement} oldDom The current attached DOM element any new dom\n * elements should be placed around. Likely `null` on first render (except when\n * hydrating). Can be a sibling DOM element when diffing Fragments that have\n * siblings. In most cases, it starts out as `oldChildren[0]._dom`.\n * @param {boolean} isHydrating Whether or not we are in hydration\n * @param {any[]} refQueue an array of elements needed to invoke refs\n */\nexport function diff(\n\tparentDom,\n\tnewVNode,\n\toldVNode,\n\tglobalContext,\n\tnamespace,\n\texcessDomChildren,\n\tcommitQueue,\n\toldDom,\n\tisHydrating,\n\trefQueue\n) {\n\t/** @type {any} */\n\tlet tmp,\n\t\tnewType = newVNode.type;\n\n\t// When passing through createElement it assigns the object\n\t// constructor as undefined. This to prevent JSON-injection.\n\tif (newVNode.constructor !== UNDEFINED) return NULL;\n\n\t// If the previous diff bailed out, resume creating/hydrating.\n\tif (oldVNode._flags & MODE_SUSPENDED) {\n\t\tisHydrating = !!(oldVNode._flags & MODE_HYDRATE);\n\t\toldDom = newVNode._dom = oldVNode._dom;\n\t\texcessDomChildren = [oldDom];\n\t}\n\n\tif ((tmp = options._diff)) tmp(newVNode);\n\n\touter: if (typeof newType == 'function') {\n\t\ttry {\n\t\t\tlet c, isNew, oldProps, oldState, snapshot, clearProcessingException;\n\t\t\tlet newProps = newVNode.props;\n\t\t\tconst isClassComponent = newType.prototype && newType.prototype.render;\n\n\t\t\t// Necessary for createContext api. Setting this property will pass\n\t\t\t// the context value as `this.context` just for this component.\n\t\t\ttmp = newType.contextType;\n\t\t\tlet provider = tmp && globalContext[tmp._id];\n\t\t\tlet componentContext = tmp\n\t\t\t\t? provider\n\t\t\t\t\t? provider.props.value\n\t\t\t\t\t: tmp._defaultValue\n\t\t\t\t: globalContext;\n\n\t\t\t// Get component and set it to `c`\n\t\t\tif (oldVNode._component) {\n\t\t\t\tc = newVNode._component = oldVNode._component;\n\t\t\t\tclearProcessingException = c._processingException = c._pendingError;\n\t\t\t} else {\n\t\t\t\t// Instantiate the new component\n\t\t\t\tif (isClassComponent) {\n\t\t\t\t\t// @ts-expect-error The check above verifies that newType is suppose to be constructed\n\t\t\t\t\tnewVNode._component = c = new newType(newProps, componentContext); // eslint-disable-line new-cap\n\t\t\t\t} else {\n\t\t\t\t\t// @ts-expect-error Trust me, Component implements the interface we want\n\t\t\t\t\tnewVNode._component = c = new BaseComponent(\n\t\t\t\t\t\tnewProps,\n\t\t\t\t\t\tcomponentContext\n\t\t\t\t\t);\n\t\t\t\t\tc.constructor = newType;\n\t\t\t\t\tc.render = doRender;\n\t\t\t\t}\n\t\t\t\tif (provider) provider.sub(c);\n\n\t\t\t\tif (!c.state) c.state = {};\n\t\t\t\tc._globalContext = globalContext;\n\t\t\t\tisNew = c._dirty = true;\n\t\t\t\tc._renderCallbacks = [];\n\t\t\t\tc._stateCallbacks = [];\n\t\t\t}\n\n\t\t\t// Invoke getDerivedStateFromProps\n\t\t\tif (isClassComponent && c._nextState == NULL) {\n\t\t\t\tc._nextState = c.state;\n\t\t\t}\n\n\t\t\tif (isClassComponent && newType.getDerivedStateFromProps != NULL) {\n\t\t\t\tif (c._nextState == c.state) {\n\t\t\t\t\tc._nextState = assign({}, c._nextState);\n\t\t\t\t}\n\n\t\t\t\tassign(\n\t\t\t\t\tc._nextState,\n\t\t\t\t\tnewType.getDerivedStateFromProps(newProps, c._nextState)\n\t\t\t\t);\n\t\t\t}\n\n\t\t\toldProps = c.props;\n\t\t\toldState = c.state;\n\t\t\tc._vnode = newVNode;\n\n\t\t\t// Invoke pre-render lifecycle methods\n\t\t\tif (isNew) {\n\t\t\t\tif (\n\t\t\t\t\tisClassComponent &&\n\t\t\t\t\tnewType.getDerivedStateFromProps == NULL &&\n\t\t\t\t\tc.componentWillMount != NULL\n\t\t\t\t) {\n\t\t\t\t\tc.componentWillMount();\n\t\t\t\t}\n\n\t\t\t\tif (isClassComponent && c.componentDidMount != NULL) {\n\t\t\t\t\tc._renderCallbacks.push(c.componentDidMount);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (\n\t\t\t\t\tisClassComponent &&\n\t\t\t\t\tnewType.getDerivedStateFromProps == NULL &&\n\t\t\t\t\tnewProps !== oldProps &&\n\t\t\t\t\tc.componentWillReceiveProps != NULL\n\t\t\t\t) {\n\t\t\t\t\tc.componentWillReceiveProps(newProps, componentContext);\n\t\t\t\t}\n\n\t\t\t\tif (\n\t\t\t\t\tnewVNode._original == oldVNode._original ||\n\t\t\t\t\t(!c._force &&\n\t\t\t\t\t\tc.shouldComponentUpdate != NULL &&\n\t\t\t\t\t\tc.shouldComponentUpdate(\n\t\t\t\t\t\t\tnewProps,\n\t\t\t\t\t\t\tc._nextState,\n\t\t\t\t\t\t\tcomponentContext\n\t\t\t\t\t\t) === false)\n\t\t\t\t) {\n\t\t\t\t\t// More info about this here: https://gist.github.com/JoviDeCroock/bec5f2ce93544d2e6070ef8e0036e4e8\n\t\t\t\t\tif (newVNode._original != oldVNode._original) {\n\t\t\t\t\t\t// When we are dealing with a bail because of sCU we have to update\n\t\t\t\t\t\t// the props, state and dirty-state.\n\t\t\t\t\t\t// when we are dealing with strict-equality we don't as the child could still\n\t\t\t\t\t\t// be dirtied see #3883\n\t\t\t\t\t\tc.props = newProps;\n\t\t\t\t\t\tc.state = c._nextState;\n\t\t\t\t\t\tc._dirty = false;\n\t\t\t\t\t}\n\n\t\t\t\t\tnewVNode._dom = oldVNode._dom;\n\t\t\t\t\tnewVNode._children = oldVNode._children;\n\t\t\t\t\tnewVNode._children.some(vnode => {\n\t\t\t\t\t\tif (vnode) vnode._parent = newVNode;\n\t\t\t\t\t});\n\n\t\t\t\t\tEMPTY_ARR.push.apply(c._renderCallbacks, c._stateCallbacks);\n\t\t\t\t\tc._stateCallbacks = [];\n\n\t\t\t\t\tif (c._renderCallbacks.length) {\n\t\t\t\t\t\tcommitQueue.push(c);\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak outer;\n\t\t\t\t}\n\n\t\t\t\tif (c.componentWillUpdate != NULL) {\n\t\t\t\t\tc.componentWillUpdate(newProps, c._nextState, componentContext);\n\t\t\t\t}\n\n\t\t\t\tif (isClassComponent && c.componentDidUpdate != NULL) {\n\t\t\t\t\tc._renderCallbacks.push(() => {\n\t\t\t\t\t\tc.componentDidUpdate(oldProps, oldState, snapshot);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tc.context = componentContext;\n\t\t\tc.props = newProps;\n\t\t\tc._parentDom = parentDom;\n\t\t\tc._force = false;\n\n\t\t\tlet renderHook = options._render,\n\t\t\t\tcount = 0;\n\t\t\tif (isClassComponent) {\n\t\t\t\tc.state = c._nextState;\n\t\t\t\tc._dirty = false;\n\n\t\t\t\tif (renderHook) renderHook(newVNode);\n\n\t\t\t\ttmp = c.render(c.props, c.state, c.context);\n\n\t\t\t\tEMPTY_ARR.push.apply(c._renderCallbacks, c._stateCallbacks);\n\t\t\t\tc._stateCallbacks = [];\n\t\t\t} else {\n\t\t\t\tdo {\n\t\t\t\t\tc._dirty = false;\n\t\t\t\t\tif (renderHook) renderHook(newVNode);\n\n\t\t\t\t\ttmp = c.render(c.props, c.state, c.context);\n\n\t\t\t\t\t// Handle setState called in render, see #2553\n\t\t\t\t\tc.state = c._nextState;\n\t\t\t\t} while (c._dirty && ++count < 25);\n\t\t\t}\n\n\t\t\t// Handle setState called in render, see #2553\n\t\t\tc.state = c._nextState;\n\n\t\t\tif (c.getChildContext != NULL) {\n\t\t\t\tglobalContext = assign(assign({}, globalContext), c.getChildContext());\n\t\t\t}\n\n\t\t\tif (isClassComponent && !isNew && c.getSnapshotBeforeUpdate != NULL) {\n\t\t\t\tsnapshot = c.getSnapshotBeforeUpdate(oldProps, oldState);\n\t\t\t}\n\n\t\t\tlet renderResult =\n\t\t\t\ttmp != NULL && tmp.type === Fragment && tmp.key == NULL\n\t\t\t\t\t? cloneNode(tmp.props.children)\n\t\t\t\t\t: tmp;\n\n\t\t\toldDom = diffChildren(\n\t\t\t\tparentDom,\n\t\t\t\tisArray(renderResult) ? renderResult : [renderResult],\n\t\t\t\tnewVNode,\n\t\t\t\toldVNode,\n\t\t\t\tglobalContext,\n\t\t\t\tnamespace,\n\t\t\t\texcessDomChildren,\n\t\t\t\tcommitQueue,\n\t\t\t\toldDom,\n\t\t\t\tisHydrating,\n\t\t\t\trefQueue\n\t\t\t);\n\n\t\t\tc.base = newVNode._dom;\n\n\t\t\t// We successfully rendered this VNode, unset any stored hydration/bailout state:\n\t\t\tnewVNode._flags &= RESET_MODE;\n\n\t\t\tif (c._renderCallbacks.length) {\n\t\t\t\tcommitQueue.push(c);\n\t\t\t}\n\n\t\t\tif (clearProcessingException) {\n\t\t\t\tc._pendingError = c._processingException = NULL;\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tnewVNode._original = NULL;\n\t\t\t// if hydrating or creating initial tree, bailout preserves DOM:\n\t\t\tif (isHydrating || excessDomChildren != NULL) {\n\t\t\t\tif (e.then) {\n\t\t\t\t\tnewVNode._flags |= isHydrating\n\t\t\t\t\t\t? MODE_HYDRATE | MODE_SUSPENDED\n\t\t\t\t\t\t: MODE_SUSPENDED;\n\n\t\t\t\t\twhile (oldDom && oldDom.nodeType == 8 && oldDom.nextSibling) {\n\t\t\t\t\t\toldDom = oldDom.nextSibling;\n\t\t\t\t\t}\n\n\t\t\t\t\texcessDomChildren[excessDomChildren.indexOf(oldDom)] = NULL;\n\t\t\t\t\tnewVNode._dom = oldDom;\n\t\t\t\t} else {\n\t\t\t\t\tfor (let i = excessDomChildren.length; i--; ) {\n\t\t\t\t\t\tremoveNode(excessDomChildren[i]);\n\t\t\t\t\t}\n\t\t\t\t\tmarkAsForce(newVNode);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tnewVNode._dom = oldVNode._dom;\n\t\t\t\tnewVNode._children = oldVNode._children;\n\t\t\t\tif (!e.then) markAsForce(newVNode);\n\t\t\t}\n\t\t\toptions._catchError(e, newVNode, oldVNode);\n\t\t}\n\t} else if (\n\t\texcessDomChildren == NULL &&\n\t\tnewVNode._original == oldVNode._original\n\t) {\n\t\tnewVNode._children = oldVNode._children;\n\t\tnewVNode._dom = oldVNode._dom;\n\t} else {\n\t\toldDom = newVNode._dom = diffElementNodes(\n\t\t\toldVNode._dom,\n\t\t\tnewVNode,\n\t\t\toldVNode,\n\t\t\tglobalContext,\n\t\t\tnamespace,\n\t\t\texcessDomChildren,\n\t\t\tcommitQueue,\n\t\t\tisHydrating,\n\t\t\trefQueue\n\t\t);\n\t}\n\n\tif ((tmp = options.diffed)) tmp(newVNode);\n\n\treturn newVNode._flags & MODE_SUSPENDED ? undefined : oldDom;\n}\n\nfunction markAsForce(vnode) {\n\tif (vnode) {\n\t\tif (vnode._component) vnode._component._force = true;\n\t\tif (vnode._children) vnode._children.some(markAsForce);\n\t}\n}\n\n/**\n * @param {Array<Component>} commitQueue List of components\n * which have callbacks to invoke in commitRoot\n * @param {VNode} root\n */\nexport function commitRoot(commitQueue, root, refQueue) {\n\tfor (let i = 0; i < refQueue.length; i++) {\n\t\tapplyRef(refQueue[i], refQueue[++i], refQueue[++i]);\n\t}\n\n\tif (options._commit) options._commit(root, commitQueue);\n\n\tcommitQueue.some(c => {\n\t\ttry {\n\t\t\t// @ts-expect-error Reuse the commitQueue variable here so the type changes\n\t\t\tcommitQueue = c._renderCallbacks;\n\t\t\tc._renderCallbacks = [];\n\t\t\tcommitQueue.some(cb => {\n\t\t\t\t// @ts-expect-error See above comment on commitQueue\n\t\t\t\tcb.call(c);\n\t\t\t});\n\t\t} catch (e) {\n\t\t\toptions._catchError(e, c._vnode);\n\t\t}\n\t});\n}\n\nfunction cloneNode(node) {\n\tif (typeof node != 'object' || node == NULL || node._depth > 0) {\n\t\treturn node;\n\t}\n\n\tif (isArray(node)) {\n\t\treturn node.map(cloneNode);\n\t}\n\n\treturn assign({}, node);\n}\n\n/**\n * Diff two virtual nodes representing DOM element\n * @param {PreactElement} dom The DOM element representing the virtual nodes\n * being diffed\n * @param {VNode} newVNode The new virtual node\n * @param {VNode} oldVNode The old virtual node\n * @param {object} globalContext The current context object\n * @param {string} namespace Current namespace of the DOM node (HTML, SVG, or MathML)\n * @param {Array<PreactElement>} excessDomChildren\n * @param {Array<Component>} commitQueue List of components which have callbacks\n * to invoke in commitRoot\n * @param {boolean} isHydrating Whether or not we are in hydration\n * @param {any[]} refQueue an array of elements needed to invoke refs\n * @returns {PreactElement}\n */\nfunction diffElementNodes(\n\tdom,\n\tnewVNode,\n\toldVNode,\n\tglobalContext,\n\tnamespace,\n\texcessDomChildren,\n\tcommitQueue,\n\tisHydrating,\n\trefQueue\n) {\n\tlet oldProps = oldVNode.props || EMPTY_OBJ;\n\tlet newProps = newVNode.props;\n\tlet nodeType = /** @type {string} */ (newVNode.type);\n\t/** @type {any} */\n\tlet i;\n\t/** @type {{ __html?: string }} */\n\tlet newHtml;\n\t/** @type {{ __html?: string }} */\n\tlet oldHtml;\n\t/** @type {ComponentChildren} */\n\tlet newChildren;\n\tlet value;\n\tlet inputValue;\n\tlet checked;\n\n\t// Tracks entering and exiting namespaces when descending through the tree.\n\tif (nodeType == 'svg') namespace = SVG_NAMESPACE;\n\telse if (nodeType == 'math') namespace = MATH_NAMESPACE;\n\telse if (!namespace) namespace = XHTML_NAMESPACE;\n\n\tif (excessDomChildren != NULL) {\n\t\tfor (i = 0; i < excessDomChildren.length; i++) {\n\t\t\tvalue = excessDomChildren[i];\n\n\t\t\t// if newVNode matches an element in excessDomChildren or the `dom`\n\t\t\t// argument matches an element in excessDomChildren, remove it from\n\t\t\t// excessDomChildren so it isn't later removed in diffChildren\n\t\t\tif (\n\t\t\t\tvalue &&\n\t\t\t\t'setAttribute' in value == !!nodeType &&\n\t\t\t\t(nodeType ? value.localName == nodeType : value.nodeType == 3)\n\t\t\t) {\n\t\t\t\tdom = value;\n\t\t\t\texcessDomChildren[i] = NULL;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (dom == NULL) {\n\t\tif (nodeType == NULL) {\n\t\t\treturn document.createTextNode(newProps);\n\t\t}\n\n\t\tdom = document.createElementNS(\n\t\t\tnamespace,\n\t\t\tnodeType,\n\t\t\tnewProps.is && newProps\n\t\t);\n\n\t\t// we are creating a new node, so we can assume this is a new subtree (in\n\t\t// case we are hydrating), this deopts the hydrate\n\t\tif (isHydrating) {\n\t\t\tif (options._hydrationMismatch)\n\t\t\t\toptions._hydrationMismatch(newVNode, excessDomChildren);\n\t\t\tisHydrating = false;\n\t\t}\n\t\t// we created a new parent, so none of the previously attached children can be reused:\n\t\texcessDomChildren = NULL;\n\t}\n\n\tif (nodeType == NULL) {\n\t\t// During hydration, we still have to split merged text from SSR'd HTML.\n\t\tif (oldProps !== newProps && (!isHydrating || dom.data != newProps)) {\n\t\t\tdom.data = newProps;\n\t\t}\n\t} else {\n\t\t// If excessDomChildren was not null, repopulate it with the current element's children:\n\t\texcessDomChildren = excessDomChildren && slice.call(dom.childNodes);\n\n\t\t// If we are in a situation where we are not hydrating but are using\n\t\t// existing DOM (e.g. replaceNode) we should read the existing DOM\n\t\t// attributes to diff them\n\t\tif (!isHydrating && excessDomChildren != NULL) {\n\t\t\toldProps = {};\n\t\t\tfor (i = 0; i < dom.attributes.length; i++) {\n\t\t\t\tvalue = dom.attributes[i];\n\t\t\t\toldProps[value.name] = value.value;\n\t\t\t}\n\t\t}\n\n\t\tfor (i in oldProps) {\n\t\t\tvalue = oldProps[i];\n\t\t\tif (i == 'dangerouslySetInnerHTML') {\n\t\t\t\toldHtml = value;\n\t\t\t} else if (\n\t\t\t\ti != 'children' &&\n\t\t\t\t!(i in newProps) &&\n\t\t\t\t!(i == 'value' && 'defaultValue' in newProps) &&\n\t\t\t\t!(i == 'checked' && 'defaultChecked' in newProps)\n\t\t\t) {\n\t\t\t\tsetProperty(dom, i, NULL, value, namespace);\n\t\t\t}\n\t\t}\n\n\t\t// During hydration, props are not diffed at all (including dangerouslySetInnerHTML)\n\t\t// @TODO we should warn in debug mode when props don't match here.\n\t\tfor (i in newProps) {\n\t\t\tvalue = newProps[i];\n\t\t\tif (i == 'children') {\n\t\t\t\tnewChildren = value;\n\t\t\t} else if (i == 'dangerouslySetInnerHTML') {\n\t\t\t\tnewHtml = value;\n\t\t\t} else if (i == 'value') {\n\t\t\t\tinputValue = value;\n\t\t\t} else if (i == 'checked') {\n\t\t\t\tchecked = value;\n\t\t\t} else if (\n\t\t\t\t(!isHydrating || typeof value == 'function') &&\n\t\t\t\toldProps[i] !== value\n\t\t\t) {\n\t\t\t\tsetProperty(dom, i, value, oldProps[i], namespace);\n\t\t\t}\n\t\t}\n\n\t\t// If the new vnode didn't have dangerouslySetInnerHTML, diff its children\n\t\tif (newHtml) {\n\t\t\t// Avoid re-applying the same '__html' if it did not changed between re-render\n\t\t\tif (\n\t\t\t\t!isHydrating &&\n\t\t\t\t(!oldHtml ||\n\t\t\t\t\t(newHtml.__html != oldHtml.__html && newHtml.__html != dom.innerHTML))\n\t\t\t) {\n\t\t\t\tdom.innerHTML = newHtml.__html;\n\t\t\t}\n\n\t\t\tnewVNode._children = [];\n\t\t} else {\n\t\t\tif (oldHtml) dom.innerHTML = '';\n\n\t\t\tdiffChildren(\n\t\t\t\t// @ts-expect-error\n\t\t\t\tnewVNode.type == 'template' ? dom.content : dom,\n\t\t\t\tisArray(newChildren) ? newChildren : [newChildren],\n\t\t\t\tnewVNode,\n\t\t\t\toldVNode,\n\t\t\t\tglobalContext,\n\t\t\t\tnodeType == 'foreignObject' ? XHTML_NAMESPACE : namespace,\n\t\t\t\texcessDomChildren,\n\t\t\t\tcommitQueue,\n\t\t\t\texcessDomChildren\n\t\t\t\t\t? excessDomChildren[0]\n\t\t\t\t\t: oldVNode._children && getDomSibling(oldVNode, 0),\n\t\t\t\tisHydrating,\n\t\t\t\trefQueue\n\t\t\t);\n\n\t\t\t// Remove children that are not part of any vnode.\n\t\t\tif (excessDomChildren != NULL) {\n\t\t\t\tfor (i = excessDomChildren.length; i--; ) {\n\t\t\t\t\tremoveNode(excessDomChildren[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// As above, don't diff props during hydration\n\t\tif (!isHydrating) {\n\t\t\ti = 'value';\n\t\t\tif (nodeType == 'progress' && inputValue == NULL) {\n\t\t\t\tdom.removeAttribute('value');\n\t\t\t} else if (\n\t\t\t\tinputValue != UNDEFINED &&\n\t\t\t\t// #2756 For the <progress>-element the initial value is 0,\n\t\t\t\t// despite the attribute not being present. When the attribute\n\t\t\t\t// is missing the progress bar is treated as indeterminate.\n\t\t\t\t// To fix that we'll always update it when it is 0 for progress elements\n\t\t\t\t(inputValue !== dom[i] ||\n\t\t\t\t\t(nodeType == 'progress' && !inputValue) ||\n\t\t\t\t\t// This is only for IE 11 to fix <select> value not being updated.\n\t\t\t\t\t// To avoid a stale select value we need to set the option.value\n\t\t\t\t\t// again, which triggers IE11 to re-evaluate the select value\n\t\t\t\t\t(nodeType == 'option' && inputValue != oldProps[i]))\n\t\t\t) {\n\t\t\t\tsetProperty(dom, i, inputValue, oldProps[i], namespace);\n\t\t\t}\n\n\t\t\ti = 'checked';\n\t\t\tif (checked != UNDEFINED && checked != dom[i]) {\n\t\t\t\tsetProperty(dom, i, checked, oldProps[i], namespace);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn dom;\n}\n\n/**\n * Invoke or update a ref, depending on whether it is a function or object ref.\n * @param {Ref<any> & { _unmount?: unknown }} ref\n * @param {any} value\n * @param {VNode} vnode\n */\nexport function applyRef(ref, value, vnode) {\n\ttry {\n\t\tif (typeof ref == 'function') {\n\t\t\tlet hasRefUnmount = typeof ref._unmount == 'function';\n\t\t\tif (hasRefUnmount) {\n\t\t\t\t// @ts-ignore TS doesn't like moving narrowing checks into variables\n\t\t\t\tref._unmount();\n\t\t\t}\n\n\t\t\tif (!hasRefUnmount || value != NULL) {\n\t\t\t\t// Store the cleanup function on the function\n\t\t\t\t// instance object itself to avoid shape\n\t\t\t\t// transitioning vnode\n\t\t\t\tref._unmount = ref(value);\n\t\t\t}\n\t\t} else ref.current = value;\n\t} catch (e) {\n\t\toptions._catchError(e, vnode);\n\t}\n}\n\n/**\n * Unmount a virtual node from the tree and apply DOM changes\n * @param {VNode} vnode The virtual node to unmount\n * @param {VNode} parentVNode The parent of the VNode that initiated the unmount\n * @param {boolean} [skipRemove] Flag that indicates that a parent node of the\n * current element is already detached from the DOM.\n */\nexport function unmount(vnode, parentVNode, skipRemove) {\n\tlet r;\n\tif (options.unmount) options.unmount(vnode);\n\n\tif ((r = vnode.ref)) {\n\t\tif (!r.current || r.current == vnode._dom) {\n\t\t\tapplyRef(r, NULL, parentVNode);\n\t\t}\n\t}\n\n\tif ((r = vnode._component) != NULL) {\n\t\tif (r.componentWillUnmount) {\n\t\t\ttry {\n\t\t\t\tr.componentWillUnmount();\n\t\t\t} catch (e) {\n\t\t\t\toptions._catchError(e, parentVNode);\n\t\t\t}\n\t\t}\n\n\t\tr.base = r._parentDom = NULL;\n\t}\n\n\tif ((r = vnode._children)) {\n\t\tfor (let i = 0; i < r.length; i++) {\n\t\t\tif (r[i]) {\n\t\t\t\tunmount(\n\t\t\t\t\tr[i],\n\t\t\t\t\tparentVNode,\n\t\t\t\t\tskipRemove || typeof vnode.type != 'function'\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!skipRemove) {\n\t\tremoveNode(vnode._dom);\n\t}\n\n\tvnode._component = vnode._parent = vnode._dom = UNDEFINED;\n}\n\n/** The `.render()` method for a PFC backing instance. */\nfunction doRender(props, state, context) {\n\treturn this.constructor(props, context);\n}\n","import { EMPTY_OBJ, NULL } from './constants';\nimport { commitRoot, diff } from './diff/index';\nimport { createElement, Fragment } from './create-element';\nimport options from './options';\nimport { slice } from './util';\n\n/**\n * Render a Preact virtual node into a DOM element\n * @param {import('./internal').ComponentChild} vnode The virtual node to render\n * @param {import('./internal').PreactElement} parentDom The DOM element to render into\n * @param {import('./internal').PreactElement | object} [replaceNode] Optional: Attempt to re-use an\n * existing DOM tree rooted at `replaceNode`\n */\nexport function render(vnode, parentDom, replaceNode) {\n\t// https://github.com/preactjs/preact/issues/3794\n\tif (parentDom == document) {\n\t\tparentDom = document.documentElement;\n\t}\n\n\tif (options._root) options._root(vnode, parentDom);\n\n\t// We abuse the `replaceNode` parameter in `hydrate()` to signal if we are in\n\t// hydration mode or not by passing the `hydrate` function instead of a DOM\n\t// element..\n\tlet isHydrating = typeof replaceNode == 'function';\n\n\t// To be able to support calling `render()` multiple times on the same\n\t// DOM node, we need to obtain a reference to the previous tree. We do\n\t// this by assigning a new `_children` property to DOM nodes which points\n\t// to the last rendered tree. By default this property is not present, which\n\t// means that we are mounting a new tree for the first time.\n\tlet oldVNode = isHydrating\n\t\t? NULL\n\t\t: (replaceNode && replaceNode._children) || parentDom._children;\n\n\tvnode = ((!isHydrating && replaceNode) || parentDom)._children =\n\t\tcreateElement(Fragment, NULL, [vnode]);\n\n\t// List of effects that need to be called after diffing.\n\tlet commitQueue = [],\n\t\trefQueue = [];\n\tdiff(\n\t\tparentDom,\n\t\t// Determine the new vnode tree and store it on the DOM element on\n\t\t// our custom `_children` property.\n\t\tvnode,\n\t\toldVNode || EMPTY_OBJ,\n\t\tEMPTY_OBJ,\n\t\tparentDom.namespaceURI,\n\t\t!isHydrating && replaceNode\n\t\t\t? [replaceNode]\n\t\t\t: oldVNode\n\t\t\t\t? NULL\n\t\t\t\t: parentDom.firstChild\n\t\t\t\t\t? slice.call(parentDom.childNodes)\n\t\t\t\t\t: NULL,\n\t\tcommitQueue,\n\t\t!isHydrating && replaceNode\n\t\t\t? replaceNode\n\t\t\t: oldVNode\n\t\t\t\t? oldVNode._dom\n\t\t\t\t: parentDom.firstChild,\n\t\tisHydrating,\n\t\trefQueue\n\t);\n\n\t// Flush all queued effects\n\tcommitRoot(commitQueue, vnode, refQueue);\n}\n\n/**\n * Update an existing DOM element with data from a Preact virtual node\n * @param {import('./internal').ComponentChild} vnode The virtual node to render\n * @param {import('./internal').PreactElement} parentDom The DOM element to update\n */\nexport function hydrate(vnode, parentDom) {\n\trender(vnode, parentDom, hydrate);\n}\n","import { assign, slice } from './util';\nimport { createVNode } from './create-element';\nimport { NULL, UNDEFINED } from './constants';\n\n/**\n * Clones the given VNode, optionally adding attributes/props and replacing its\n * children.\n * @param {import('./internal').VNode} vnode The virtual DOM element to clone\n * @param {object} props Attributes/props to add when cloning\n * @param {Array<import('./internal').ComponentChildren>} rest Any additional arguments will be used\n * as replacement children.\n * @returns {import('./internal').VNode}\n */\nexport function cloneElement(vnode, props, children) {\n\tlet normalizedProps = assign({}, vnode.props),\n\t\tkey,\n\t\tref,\n\t\ti;\n\n\tlet defaultProps;\n\n\tif (vnode.type && vnode.type.defaultProps) {\n\t\tdefaultProps = vnode.type.defaultProps;\n\t}\n\n\tfor (i in props) {\n\t\tif (i == 'key') key = props[i];\n\t\telse if (i == 'ref') ref = props[i];\n\t\telse if (props[i] === UNDEFINED && defaultProps != UNDEFINED) {\n\t\t\tnormalizedProps[i] = defaultProps[i];\n\t\t} else {\n\t\t\tnormalizedProps[i] = props[i];\n\t\t}\n\t}\n\n\tif (arguments.length > 2) {\n\t\tnormalizedProps.children =\n\t\t\targuments.length > 3 ? slice.call(arguments, 2) : children;\n\t}\n\n\treturn createVNode(\n\t\tvnode.type,\n\t\tnormalizedProps,\n\t\tkey || vnode.key,\n\t\tref || vnode.ref,\n\t\tNULL\n\t);\n}\n","import { NULL } from '../constants';\n\n/**\n * Find the closest error boundary to a thrown error and call it\n * @param {object} error The thrown value\n * @param {import('../internal').VNode} vnode The vnode that threw the error that was caught (except\n * for unmounting when this parameter is the highest parent that was being\n * unmounted)\n * @param {import('../internal').VNode} [oldVNode]\n * @param {import('../internal').ErrorInfo} [errorInfo]\n */\nexport function _catchError(error, vnode, oldVNode, errorInfo) {\n\t/** @type {import('../internal').Component} */\n\tlet component,\n\t\t/** @type {import('../internal').ComponentType} */\n\t\tctor,\n\t\t/** @type {boolean} */\n\t\thandled;\n\n\tfor (; (vnode = vnode._parent); ) {\n\t\tif ((component = vnode._component) && !component._processingException) {\n\t\t\ttry {\n\t\t\t\tctor = component.constructor;\n\n\t\t\t\tif (ctor && ctor.getDerivedStateFromError != NULL) {\n\t\t\t\t\tcomponent.setState(ctor.getDerivedStateFromError(error));\n\t\t\t\t\thandled = component._dirty;\n\t\t\t\t}\n\n\t\t\t\tif (component.componentDidCatch != NULL) {\n\t\t\t\t\tcomponent.componentDidCatch(error, errorInfo || {});\n\t\t\t\t\thandled = component._dirty;\n\t\t\t\t}\n\n\t\t\t\t// This is an error boundary. Mark it as having bailed out, and whether it was mid-hydration.\n\t\t\t\tif (handled) {\n\t\t\t\t\treturn (component._pendingError = component);\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\terror = e;\n\t\t\t}\n\t\t}\n\t}\n\n\tthrow error;\n}\n","import { options as _options } from 'preact';\n\n/** @type {number} */\nlet currentIndex;\n\n/** @type {import('./internal').Component} */\nlet currentComponent;\n\n/** @type {import('./internal').Component} */\nlet previousComponent;\n\n/** @type {number} */\nlet currentHook = 0;\n\n/** @type {Array<import('./internal').Component>} */\nlet afterPaintEffects = [];\n\n// Cast to use internal Options type\nconst options = /** @type {import('./internal').Options} */ (_options);\n\nlet oldBeforeDiff = options._diff;\nlet oldBeforeRender = options._render;\nlet oldAfterDiff = options.diffed;\nlet oldCommit = options._commit;\nlet oldBeforeUnmount = options.unmount;\nlet oldRoot = options._root;\n\n// We take the minimum timeout for requestAnimationFrame to ensure that\n// the callback is invoked after the next frame. 35ms is based on a 30hz\n// refresh rate, which is the minimum rate for a smooth user experience.\nconst RAF_TIMEOUT = 35;\nlet prevRaf;\n\n/** @type {(vnode: import('./internal').VNode) => void} */\noptions._diff = vnode => {\n\tcurrentComponent = null;\n\tif (oldBeforeDiff) oldBeforeDiff(vnode);\n};\n\noptions._root = (vnode, parentDom) => {\n\tif (vnode && parentDom._children && parentDom._children._mask) {\n\t\tvnode._mask = parentDom._children._mask;\n\t}\n\n\tif (oldRoot) oldRoot(vnode, parentDom);\n};\n\n/** @type {(vnode: import('./internal').VNode) => void} */\noptions._render = vnode => {\n\tif (oldBeforeRender) oldBeforeRender(vnode);\n\n\tcurrentComponent = vnode._component;\n\tcurrentIndex = 0;\n\n\tconst hooks = currentComponent.__hooks;\n\tif (hooks) {\n\t\tif (previousComponent === currentComponent) {\n\t\t\thooks._pendingEffects = [];\n\t\t\tcurrentComponent._renderCallbacks = [];\n\t\t\thooks._list.some(hookItem => {\n\t\t\t\tif (hookItem._nextValue) {\n\t\t\t\t\thookItem._value = hookItem._nextValue;\n\t\t\t\t}\n\t\t\t\thookItem._pendingArgs = hookItem._nextValue = undefined;\n\t\t\t});\n\t\t} else {\n\t\t\thooks._pendingEffects.some(invokeCleanup);\n\t\t\thooks._pendingEffects.some(invokeEffect);\n\t\t\thooks._pendingEffects = [];\n\t\t\tcurrentIndex = 0;\n\t\t}\n\t}\n\tpreviousComponent = currentComponent;\n};\n\n/** @type {(vnode: import('./internal').VNode) => void} */\noptions.diffed = vnode => {\n\tif (oldAfterDiff) oldAfterDiff(vnode);\n\n\tconst c = vnode._component;\n\tif (c && c.__hooks) {\n\t\tif (c.__hooks._pendingEffects.length) afterPaint(afterPaintEffects.push(c));\n\t\tc.__hooks._list.some(hookItem => {\n\t\t\tif (hookItem._pendingArgs) {\n\t\t\t\thookItem._args = hookItem._pendingArgs;\n\t\t\t}\n\t\t\thookItem._pendingArgs = undefined;\n\t\t});\n\t}\n\tpreviousComponent = currentComponent = null;\n};\n\n// TODO: Improve typing of commitQueue parameter\n/** @type {(vnode: import('./internal').VNode, commitQueue: any) => void} */\noptions._commit = (vnode, commitQueue) => {\n\tcommitQueue.some(component => {\n\t\ttry {\n\t\t\tcomponent._renderCallbacks.some(invokeCleanup);\n\t\t\tcomponent._renderCallbacks = component._renderCallbacks.filter(cb =>\n\t\t\t\tcb._value ? invokeEffect(cb) : true\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tcommitQueue.some(c => {\n\t\t\t\tif (c._renderCallbacks) c._renderCallbacks = [];\n\t\t\t});\n\t\t\tcommitQueue = [];\n\t\t\toptions._catchError(e, component._vnode);\n\t\t}\n\t});\n\n\tif (oldCommit) oldCommit(vnode, commitQueue);\n};\n\n/** @type {(vnode: import('./internal').VNode) => void} */\noptions.unmount = vnode => {\n\tif (oldBeforeUnmount) oldBeforeUnmount(vnode);\n\n\tconst c = vnode._component;\n\tif (c && c.__hooks) {\n\t\tlet hasErrored;\n\t\tc.__hooks._list.some(s => {\n\t\t\ttry {\n\t\t\t\tinvokeCleanup(s);\n\t\t\t} catch (e) {\n\t\t\t\thasErrored = e;\n\t\t\t}\n\t\t});\n\t\tc.__hooks = undefined;\n\t\tif (hasErrored) options._catchError(hasErrored, c._vnode);\n\t}\n};\n\n/**\n * Get a hook's state from the currentComponent\n * @param {number} index The index of the hook to get\n * @param {number} type The index of the hook to get\n * @returns {any}\n */\nfunction getHookState(index, type) {\n\tif (options._hook) {\n\t\toptions._hook(currentComponent, index, currentHook || type);\n\t}\n\tcurrentHook = 0;\n\n\t// Largely inspired by:\n\t// * https://github.com/michael-klein/funcy.js/blob/f6be73468e6ec46b0ff5aa3cc4c9baf72a29025a/src/hooks/core_hooks.mjs\n\t// * https://github.com/michael-klein/funcy.js/blob/650beaa58c43c33a74820a3c98b3c7079cf2e333/src/renderer.mjs\n\t// Other implementations to look at:\n\t// * https://codesandbox.io/s/mnox05qp8\n\tconst hooks =\n\t\tcurrentComponent.__hooks ||\n\t\t(currentComponent.__hooks = {\n\t\t\t_list: [],\n\t\t\t_pendingEffects: []\n\t\t});\n\n\tif (index >= hooks._list.length) {\n\t\thooks._list.push({});\n\t}\n\n\treturn hooks._list[index];\n}\n\n/**\n * @template {unknown} S\n * @param {import('./index').Dispatch<import('./index').StateUpdater<S>>} [initialState]\n * @returns {[S, (state: S) => void]}\n */\nexport function useState(initialState) {\n\tcurrentHook = 1;\n\treturn useReducer(invokeOrReturn, initialState);\n}\n\n/**\n * @template {unknown} S\n * @template {unknown} A\n * @param {import('./index').Reducer<S, A>} reducer\n * @param {import('./index').Dispatch<import('./index').StateUpdater<S>>} initialState\n * @param {(initialState: any) => void} [init]\n * @returns {[ S, (state: S) => void ]}\n */\nexport function useReducer(reducer, initialState, init) {\n\t/** @type {import('./internal').ReducerHookState} */\n\tconst hookState = getHookState(currentIndex++, 2);\n\thookState._reducer = reducer;\n\tif (!hookState._component) {\n\t\thookState._value = [\n\t\t\t!init ? invokeOrReturn(undefined, initialState) : init(initialState),\n\n\t\t\taction => {\n\t\t\t\tconst currentValue = hookState._nextValue\n\t\t\t\t\t? hookState._nextValue[0]\n\t\t\t\t\t: hookState._value[0];\n\t\t\t\tconst nextValue = hookState._reducer(currentValue, action);\n\n\t\t\t\tif (currentValue !== nextValue) {\n\t\t\t\t\thookState._nextValue = [nextValue, hookState._value[1]];\n\t\t\t\t\thookState._component.setState({});\n\t\t\t\t}\n\t\t\t}\n\t\t];\n\n\t\thookState._component = currentComponent;\n\n\t\tif (!currentComponent._hasScuFromHooks) {\n\t\t\tcurrentComponent._hasScuFromHooks = true;\n\t\t\tlet prevScu = currentComponent.shouldComponentUpdate;\n\t\t\tconst prevCWU = currentComponent.componentWillUpdate;\n\n\t\t\t// If we're dealing with a forced update `shouldComponentUpdate` will\n\t\t\t// not be called. But we use that to update the hook values, so we\n\t\t\t// need to call it.\n\t\t\tcurrentComponent.componentWillUpdate = function (p, s, c) {\n\t\t\t\tif (this._force) {\n\t\t\t\t\tlet tmp = prevScu;\n\t\t\t\t\t// Clear to avoid other sCU hooks from being called\n\t\t\t\t\tprevScu = undefined;\n\t\t\t\t\tupdateHookState(p, s, c);\n\t\t\t\t\tprevScu = tmp;\n\t\t\t\t}\n\n\t\t\t\tif (prevCWU) prevCWU.call(this, p, s, c);\n\t\t\t};\n\n\t\t\t// This SCU has the purpose of bailing out after repeated updates\n\t\t\t// to stateful hooks.\n\t\t\t// we store the next value in _nextValue[0] and keep doing that for all\n\t\t\t// state setters, if we have next states and\n\t\t\t// all next states within a component end up being equal to their original state\n\t\t\t// we are safe to bail out for this specific component.\n\t\t\t/**\n\t\t\t *\n\t\t\t * @type {import('./internal').Component[\"shouldComponentUpdate\"]}\n\t\t\t */\n\t\t\t// @ts-ignore - We don't use TS to downtranspile\n\t\t\t// eslint-disable-next-line no-inner-declarations\n\t\t\tfunction updateHookState(p, s, c) {\n\t\t\t\tif (!hookState._component.__hooks) return true;\n\n\t\t\t\tconst stateHooks = hookState._component.__hooks._list.filter(\n\t\t\t\t\tx => x._component\n\t\t\t\t);\n\n\t\t\t\tconst allHooksEmpty = stateHooks.every(x => !x._nextValue);\n\t\t\t\t// When we have no updated hooks in the component we invoke the previous SCU or\n\t\t\t\t// traverse the VDOM tree further.\n\t\t\t\tif (allHooksEmpty) {\n\t\t\t\t\treturn prevScu ? prevScu.call(this, p, s, c) : true;\n\t\t\t\t}\n\n\t\t\t\t// We check whether we have components with a nextValue set that\n\t\t\t\t// have values that aren't equal to one another this pushes\n\t\t\t\t// us to update further down the tree\n\t\t\t\tlet shouldUpdate = hookState._component.props !== p;\n\t\t\t\tstateHooks.some(hookItem => {\n\t\t\t\t\tif (hookItem._nextValue) {\n\t\t\t\t\t\tconst currentValue = hookItem._value[0];\n\t\t\t\t\t\thookItem._value = hookItem._nextValue;\n\t\t\t\t\t\thookItem._nextValue = undefined;\n\t\t\t\t\t\tif (currentValue !== hookItem._value[0]) shouldUpdate = true;\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\treturn prevScu\n\t\t\t\t\t? prevScu.call(this, p, s, c) || shouldUpdate\n\t\t\t\t\t: shouldUpdate;\n\t\t\t}\n\n\t\t\tcurrentComponent.shouldComponentUpdate = updateHookState;\n\t\t}\n\t}\n\n\treturn hookState._nextValue || hookState._value;\n}\n\n/**\n * @param {import('./internal').Effect} callback\n * @param {unknown[]} args\n * @returns {void}\n */\nexport function useEffect(callback, args) {\n\t/** @type {import('./internal').EffectHookState} */\n\tconst state = getHookState(currentIndex++, 3);\n\tif (!options._skipEffects && argsChanged(state._args, args)) {\n\t\tstate._value = callback;\n\t\tstate._pendingArgs = args;\n\n\t\tcurrentComponent.__hooks._pendingEffects.push(state);\n\t}\n}\n\n/**\n * @param {import('./internal').Effect} callback\n * @param {unknown[]} args\n * @returns {void}\n */\nexport function useLayoutEffect(callback, args) {\n\t/** @type {import('./internal').EffectHookState} */\n\tconst state = getHookState(currentIndex++, 4);\n\tif (!options._skipEffects && argsChanged(state._args, args)) {\n\t\tstate._value = callback;\n\t\tstate._pendingArgs = args;\n\n\t\tcurrentComponent._renderCallbacks.push(state);\n\t}\n}\n\n/** @type {(initialValue: unknown) => unknown} */\nexport function useRef(initialValue) {\n\tcurrentHook = 5;\n\treturn useMemo(() => ({ current: initialValue }), []);\n}\n\n/**\n * @param {object} ref\n * @param {() => object} createHandle\n * @param {unknown[]} args\n * @returns {void}\n */\nexport function useImperativeHandle(ref, createHandle, args) {\n\tcurrentHook = 6;\n\tuseLayoutEffect(\n\t\t() => {\n\t\t\tif (typeof ref == 'function') {\n\t\t\t\tconst result = ref(createHandle());\n\t\t\t\treturn () => {\n\t\t\t\t\tref(null);\n\t\t\t\t\tif (result && typeof result == 'function') result();\n\t\t\t\t};\n\t\t\t} else if (ref) {\n\t\t\t\tref.current = createHandle();\n\t\t\t\treturn () => (ref.current = null);\n\t\t\t}\n\t\t},\n\t\targs == null ? args : args.concat(ref)\n\t);\n}\n\n/**\n * @template {unknown} T\n * @param {() => T} factory\n * @param {unknown[]} args\n * @returns {T}\n */\nexport function useMemo(factory, args) {\n\t/** @type {import('./internal').MemoHookState<T>} */\n\tconst state = getHookState(currentIndex++, 7);\n\tif (argsChanged(state._args, args)) {\n\t\tstate._value = factory();\n\t\tstate._args = args;\n\t\tstate._factory = factory;\n\t}\n\n\treturn state._value;\n}\n\n/**\n * @param {() => void} callback\n * @param {unknown[]} args\n * @returns {() => void}\n */\nexport function useCallback(callback, args) {\n\tcurrentHook = 8;\n\treturn useMemo(() => callback, args);\n}\n\n/**\n * @param {import('./internal').PreactContext} context\n */\nexport function useContext(context) {\n\tconst provider = currentComponent.context[context._id];\n\t// We could skip this call here, but than we'd not call\n\t// `options._hook`. We need to do that in order to make\n\t// the devtools aware of this hook.\n\t/** @type {import('./internal').ContextHookState} */\n\tconst state = getHookState(currentIndex++, 9);\n\t// The devtools needs access to the context object to\n\t// be able to pull of the default value when no provider\n\t// is present in the tree.\n\tstate._context = context;\n\tif (!provider) return context._defaultValue;\n\t// This is probably not safe to convert to \"!\"\n\tif (state._value == null) {\n\t\tstate._value = true;\n\t\tprovider.sub(currentComponent);\n\t}\n\treturn provider.props.value;\n}\n\n/**\n * Display a custom label for a custom hook for the devtools panel\n * @type {<T>(value: T, cb?: (value: T) => string | number) => void}\n */\nexport function useDebugValue(value, formatter) {\n\tif (options.useDebugValue) {\n\t\toptions.useDebugValue(\n\t\t\tformatter ? formatter(value) : /** @type {any}*/ (value)\n\t\t);\n\t}\n}\n\n/**\n * @param {(error: unknown, errorInfo: import('preact').ErrorInfo) => void} cb\n * @returns {[unknown, () => void]}\n */\nexport function useErrorBoundary(cb) {\n\t/** @type {import('./internal').ErrorBoundaryHookState} */\n\tconst state = getHookState(currentIndex++, 10);\n\tconst errState = useState();\n\tstate._value = cb;\n\tif (!currentComponent.componentDidCatch) {\n\t\tcurrentComponent.componentDidCatch = (err, errorInfo) => {\n\t\t\tif (state._value) state._value(err, errorInfo);\n\t\t\terrState[1](err);\n\t\t};\n\t}\n\treturn [\n\t\terrState[0],\n\t\t() => {\n\t\t\terrState[1](undefined);\n\t\t}\n\t];\n}\n\n/** @type {() => string} */\nexport function useId() {\n\t/** @type {import('./internal').IdHookState} */\n\tconst state = getHookState(currentIndex++, 11);\n\tif (!state._value) {\n\t\t// Grab either the root node or the nearest async boundary node.\n\t\t/** @type {import('./internal').VNode} */\n\t\tlet root = currentComponent._vnode;\n\t\twhile (root !== null && !root._mask && root._parent !== null) {\n\t\t\troot = root._parent;\n\t\t}\n\n\t\tlet mask = root._mask || (root._mask = [0, 0]);\n\t\tstate._value = 'P' + mask[0] + '-' + mask[1]++;\n\t}\n\n\treturn state._value;\n}\n\n/**\n * After paint effects consumer.\n */\nfunction flushAfterPaintEffects() {\n\tlet component;\n\twhile ((component = afterPaintEffects.shift())) {\n\t\tconst hooks = component.__hooks;\n\t\tif (!component._parentDom || !hooks) continue;\n\t\ttry {\n\t\t\thooks._pendingEffects.some(invokeCleanup);\n\t\t\thooks._pendingEffects.some(invokeEffect);\n\t\t\thooks._pendingEffects = [];\n\t\t} catch (e) {\n\t\t\thooks._pendingEffects = [];\n\t\t\toptions._catchError(e, component._vnode);\n\t\t}\n\t}\n}\n\nlet HAS_RAF = typeof requestAnimationFrame == 'function';\n\n/**\n * Schedule a callback to be invoked after the browser has a chance to paint a new frame.\n * Do this by combining requestAnimationFrame (rAF) + setTimeout to invoke a callback after\n * the next browser frame.\n *\n * Also, schedule a timeout in parallel to the the rAF to ensure the callback is invoked\n * even if RAF doesn't fire (for example if the browser tab is not visible)\n *\n * @param {() => void} callback\n */\nfunction afterNextFrame(callback) {\n\tconst done = () => {\n\t\tclearTimeout(timeout);\n\t\tif (HAS_RAF) cancelAnimationFrame(raf);\n\t\tsetTimeout(callback);\n\t};\n\tconst timeout = setTimeout(done, RAF_TIMEOUT);\n\n\tlet raf;\n\tif (HAS_RAF) {\n\t\traf = requestAnimationFrame(done);\n\t}\n}\n\n// Note: if someone used options.debounceRendering = requestAnimationFrame,\n// then effects will ALWAYS run on the NEXT frame instead of the current one, incurring a ~16ms delay.\n// Perhaps this is not such a big deal.\n/**\n * Schedule afterPaintEffects flush after the browser paints\n * @param {number} newQueueLength\n * @returns {void}\n */\nfunction afterPaint(newQueueLength) {\n\tif (newQueueLength === 1 || prevRaf !== options.requestAnimationFrame) {\n\t\tprevRaf = options.requestAnimationFrame;\n\t\t(prevRaf || afterNextFrame)(flushAfterPaintEffects);\n\t}\n}\n\n/**\n * @param {import('./internal').HookState} hook\n * @returns {void}\n */\nfunction invokeCleanup(hook) {\n\t// A hook cleanup can introduce a call to render which creates a new root, this will call options.vnode\n\t// and move the currentComponent away.\n\tconst comp = currentComponent;\n\tlet cleanup = hook._cleanup;\n\tif (typeof cleanup == 'function') {\n\t\thook._cleanup = undefined;\n\t\tcleanup();\n\t}\n\n\tcurrentComponent = comp;\n}\n\n/**\n * Invoke a Hook's effect\n * @param {import('./internal').EffectHookState} hook\n * @returns {void}\n */\nfunction invokeEffect(hook) {\n\t// A hook call can introduce a call to render which creates a new root, this will call options.vnode\n\t// and move the currentComponent away.\n\tconst comp = currentComponent;\n\thook._cleanup = hook._value();\n\tcurrentComponent = comp;\n}\n\n/**\n * @param {unknown[]} oldArgs\n * @param {unknown[]} newArgs\n * @returns {boolean}\n */\nfunction argsChanged(oldArgs, newArgs) {\n\treturn (\n\t\t!oldArgs ||\n\t\toldArgs.length !== newArgs.length ||\n\t\tnewArgs.some((arg, index) => arg !== oldArgs[index])\n\t);\n}\n\n/**\n * @template Arg\n * @param {Arg} arg\n * @param {(arg: Arg) => any} f\n * @returns {any}\n */\nfunction invokeOrReturn(arg, f) {\n\treturn typeof f == 'function' ? f(arg) : f;\n}\n","/**\n * Bundled FR/EN strings for the QA Meter widget.\n *\n * Keys are the donor's `qaMeter.*` keys with the `qaMeter.` prefix stripped.\n * Values are copied verbatim from the donor locale files.\n *\n * Two new keys not present in the donor are appended at the end of each record:\n * - `panel.noTests`\n * - `panel.unknownPage`\n */\n\nexport const QA_STRINGS_FR = {\n 'pastille.label': 'Suivi des tests',\n 'pastille.loading': 'Chargement…',\n 'pastille.unavailable': 'Statuts indisponibles',\n 'pastille.unknownPage': 'Page inconnue',\n 'pastille.unknownPageHint': \"Cette route n’est pas encore au plan du site des tests.\",\n 'panel.freshness': 'Statuts g\\xe9n\\xe9r\\xe9s le {date} \\xb7 version {version} \\xb7 env {env}',\n 'panel.noScenarios': 'Aucun sc\\xe9nario li\\xe9 \\xe0 cette page.',\n 'panel.openSitemap': 'Voir le plan du site',\n 'panel.sitemapLink': 'Plan du site',\n 'panel.scenarioCountOne': '{count} sc\\xe9nario',\n 'panel.scenarioCountMany': '{count} sc\\xe9narios',\n 'panel.laneAutomated': 'Automatis\\xe9',\n 'panel.laneDev': 'D\\xe9v',\n 'panel.laneClient': 'Client',\n 'lane.notTested': 'Non test\\xe9',\n 'lane.validatedBy': 'Valid\\xe9 par {by}',\n 'lane.rejectedBy': 'Rejet\\xe9 par {by}',\n 'lane.stale': 'Valid\\xe9 en v{version}, version actuelle v{current}',\n 'lane.clientPending': 'Non test\\xe9 — plateforme requise',\n 'lane.bot': 'Automatis\\xe9 (bot)',\n 'lane.code': 'D\\xe9veloppeur (code)',\n 'lane.human': 'Humain',\n 'laneState.ok': '\\xc0 jour',\n 'laneState.issue': 'Probl\\xe8me',\n 'laneState.none': 'Non fait / p\\xe9rim\\xe9',\n 'color.green': 'Valid\\xe9',\n 'color.yellow': 'En attente',\n 'color.red': 'En \\xe9chec',\n 'color.gray': 'Non couvert',\n 'automated.passing': 'R\\xe9ussi',\n 'automated.failing': 'En \\xe9chec',\n 'automated.flaky': 'Instable',\n 'automated.neverRun': 'Jamais ex\\xe9cut\\xe9',\n 'source.testgen': 'g\\xe9n\\xe9r\\xe9',\n 'source.testgenHint': 'Sc\\xe9nario g\\xe9n\\xe9r\\xe9 automatiquement (testgen)',\n 'suite.cases': '{count} cas',\n 'case.covered': 'auto',\n 'case.manual': 'manuel',\n 'case.blocked': 'bloqu\\xe9',\n 'case.openQuestion': 'question produit',\n 'grounding.verified': 'attendu v\\xe9rifi\\xe9',\n 'grounding.confirmLive': '\\xe0 confirmer en live',\n 'grounding.productQuestion': 'd\\xe9cision produit',\n 'modal.title': 'Plan du site — suivi des tests',\n 'modal.close': 'Fermer',\n 'modal.tabPages': 'Pages',\n 'modal.tabScenarios': 'Sc\\xe9narios',\n 'modal.youAreHere': 'vous \\xeates ici',\n 'modal.search': 'Filtrer les sc\\xe9narios…',\n 'modal.noMatch': 'Aucun sc\\xe9nario ne correspond au filtre.',\n 'modal.traversed': 'Travers\\xe9es',\n 'modal.appliesTo': '{count} pages',\n 'modal.kpiPagesCovered': 'Pages avec ≥ 1 sc\\xe9nario',\n 'modal.kpiScenariosPassing': 'Sc\\xe9narios passants',\n // New keys (not in donor)\n 'panel.noTests': 'Aucun test ne couvre encore cette page.',\n 'panel.unknownPage': 'Page inconnue du registre QA.',\n} as const\n\nexport const QA_STRINGS_EN = {\n 'pastille.label': 'Test tracking',\n 'pastille.loading': 'Loading…',\n 'pastille.unavailable': 'Statuses unavailable',\n 'pastille.unknownPage': 'Unknown page',\n 'pastille.unknownPageHint': \"This route isn’t in the test sitemap yet.\",\n 'panel.freshness': 'Statuses generated on {date} \\xb7 version {version} \\xb7 env {env}',\n 'panel.noScenarios': 'No scenario linked to this page.',\n 'panel.openSitemap': 'View sitemap',\n 'panel.sitemapLink': 'Sitemap',\n 'panel.scenarioCountOne': '{count} scenario',\n 'panel.scenarioCountMany': '{count} scenarios',\n 'panel.laneAutomated': 'Automated',\n 'panel.laneDev': 'Dev',\n 'panel.laneClient': 'Client',\n 'lane.notTested': 'Not tested',\n 'lane.validatedBy': 'Validated by {by}',\n 'lane.rejectedBy': 'Rejected by {by}',\n 'lane.stale': 'Validated in v{version}, current version v{current}',\n 'lane.clientPending': 'Not tested — platform required',\n 'lane.bot': 'Automated (bot)',\n 'lane.code': 'Developer (code)',\n 'lane.human': 'Human',\n 'laneState.ok': 'Up to date',\n 'laneState.issue': 'Issue',\n 'laneState.none': 'Not done / stale',\n 'color.green': 'Validated',\n 'color.yellow': 'Pending',\n 'color.red': 'Failing',\n 'color.gray': 'Not covered',\n 'automated.passing': 'Passing',\n 'automated.failing': 'Failing',\n 'automated.flaky': 'Flaky',\n 'automated.neverRun': 'Never run',\n 'source.testgen': 'generated',\n 'source.testgenHint': 'Auto-generated scenario (testgen)',\n 'suite.cases': '{count} cases',\n 'case.covered': 'auto',\n 'case.manual': 'manual',\n 'case.blocked': 'blocked',\n 'case.openQuestion': 'product question',\n 'grounding.verified': 'expected verified',\n 'grounding.confirmLive': 'confirm live',\n 'grounding.productQuestion': 'product decision',\n 'modal.title': 'Sitemap — test tracking',\n 'modal.close': 'Close',\n 'modal.tabPages': 'Pages',\n 'modal.tabScenarios': 'Scenarios',\n 'modal.youAreHere': 'you are here',\n 'modal.search': 'Filter scenarios…',\n 'modal.noMatch': 'No scenario matches the filter.',\n 'modal.traversed': 'Traversed',\n 'modal.appliesTo': '{count} pages',\n 'modal.kpiPagesCovered': 'Pages with ≥ 1 scenario',\n 'modal.kpiScenariosPassing': 'Passing scenarios',\n // New keys (not in donor)\n 'panel.noTests': 'No tests cover this page yet.',\n 'panel.unknownPage': 'Page unknown to the QA registry.',\n} as const\n\nexport type QaStringKey = keyof typeof QA_STRINGS_FR\nexport type QaStrings = Record<QaStringKey, string>\n\nexport function resolveStrings(locale: 'fr' | 'en'): QaStrings {\n return locale === 'en' ? QA_STRINGS_EN : QA_STRINGS_FR\n}\n","/** Routerless page resolution: match window.location.pathname against the\n * artifact's path_patterns. Most-specific match wins (static segments beat\n * params; longer beats shorter). The host can override with a router-aware\n * getCurrentPage — this is only the default. */\n\nexport function normalizePattern(pattern: string | undefined | null): string {\n if (!pattern) return '/'\n let n = pattern.startsWith('/') ? pattern : `/${pattern}`\n n = n.replace(/\\/{2,}/g, '/')\n if (n.length > 1) n = n.replace(/\\/+$/, '')\n return n.length > 0 ? n : '/'\n}\n\nconst segments = (p: string): string[] => p.split('/').filter(Boolean)\n\nfunction matchesPattern(pathSegs: string[], patternSegs: string[]): boolean {\n let required = patternSegs.length\n while (required > 0) {\n const last = patternSegs[required - 1]\n if (last !== undefined && last.startsWith(':') && last.endsWith('?')) required--\n else break\n }\n if (pathSegs.length < required || pathSegs.length > patternSegs.length) return false\n return pathSegs.every((seg, i) => {\n const pat = patternSegs[i]\n return pat !== undefined && (pat.startsWith(':') || pat === seg)\n })\n}\n\nexport function resolvePage(pathname: string, patterns: ReadonlyArray<string>): string | null {\n const pathSegs = segments(normalizePattern(pathname))\n let best: string | null = null\n let bestScore = -1\n for (const pattern of patterns) {\n const patSegs = segments(pattern)\n if (!matchesPattern(pathSegs, patSegs)) continue\n const statics = patSegs.filter((s) => !s.startsWith(':')).length\n const score = statics * 100 + patSegs.length\n if (score > bestScore) { best = pattern; bestScore = score }\n }\n return best\n}\n","/**\n * QA Meter CSS styles — a `:host` block with CSS variables prefixed `--mqa-`.\n *\n * The QA Meter z-index is set one below the feedback widget's `2147483640`\n * so the feedback FAB wins overlaps.\n *\n * Color tokens (hardcoded to remove dependency on DaisyUI HSL variables):\n * green #16a34a\n * yellow #eab308\n * red #dc2626\n * gray #9ca3af\n *\n * Selectors ported from qa-pastille.vue, qa-test-panel.vue, qa-lane-icons.vue,\n * and qa-sitemap-modal.vue — adapted for standalone Shadow DOM use.\n */\nexport const QA_METER_STYLES = `\n:host {\n --mqa-z-index: 2147483639;\n --mqa-z-panel: 2147483638;\n\n /* Color tokens */\n --mqa-green: #16a34a;\n --mqa-yellow: #eab308;\n --mqa-red: #dc2626;\n --mqa-gray: #9ca3af;\n\n /* Surface */\n --mqa-bg: #ffffff;\n --mqa-surface: #f9fafb;\n --mqa-border: #e5e7eb;\n --mqa-text: #111827;\n --mqa-text-muted: #6b7280;\n --mqa-primary: #3b82f6;\n --mqa-shadow: 0 4px 12px rgba(0,0,0,0.08), 0 2px 4px rgba(0,0,0,0.12);\n --mqa-shadow-xl: 0 24px 56px rgba(0,0,0,0.18), 0 8px 20px rgba(0,0,0,0.10);\n\n /* Font */\n --mqa-font: system-ui, -apple-system, sans-serif;\n --mqa-radius: 8px;\n\n all: initial;\n font-family: var(--mqa-font);\n color: var(--mqa-text);\n position: fixed;\n z-index: var(--mqa-z-index);\n}\n\n@media (prefers-color-scheme: dark) {\n :host {\n --mqa-bg: #1e293b;\n --mqa-surface: #0f172a;\n --mqa-border: #334155;\n --mqa-text: #f8fafc;\n --mqa-text-muted: #94a3b8;\n --mqa-shadow-xl: 0 24px 56px rgba(0,0,0,0.55), 0 8px 20px rgba(0,0,0,0.40);\n }\n}\n\n/* ── Semaphore modifier classes ─────────────────────────────────────────── */\n\n.qa--green { background: var(--mqa-green); }\n.qa--yellow { background: var(--mqa-yellow); }\n.qa--red { background: var(--mqa-red); }\n.qa--gray { background: var(--mqa-gray); }\n\n/* Priority modifiers (color only — components set sizing). */\n.qa-prio--p0 { color: var(--mqa-red); }\n.qa-prio--p1 { color: var(--mqa-yellow); }\n.qa-prio--p2 { color: var(--mqa-green); }\n.qa-prio--p3 { color: var(--mqa-gray); }\n\n/* ── Pastille (FAB circle) ──────────────────────────────────────────────── */\n\n/* The wrap is the fixed, bottom-right anchor for the whole pastille cluster.\n It MUST be a positioning context: the hover panel .qa-panel is\n position:absolute and is a SIBLING of the pastille, so without a positioned\n wrap it would anchor to :host (fixed, no offsets — i.e. the top-left of the\n screen) instead of sitting above the FAB. The --mqa-right/--mqa-bottom knobs\n (set on the host, inherited here) keep driving the on-screen position. The\n fixed sitemap modal inside the wrap anchors to the viewport regardless, so\n it's unaffected. */\n.qa-pastille-wrap {\n position: fixed;\n right: var(--mqa-right, 1.5rem);\n bottom: var(--mqa-bottom, 1.5rem);\n z-index: var(--mqa-z-index);\n}\n\n.qa-pastille {\n position: relative;\n}\n\n.qa-pastille__dot {\n width: 48px;\n height: 48px;\n border-radius: 9999px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n box-shadow: var(--mqa-shadow);\n color: rgba(0, 0, 0, 0.5);\n /* No idle animation — the pastille stays static until interacted with, like\n the feedback FAB. (Previously pulsed on a perpetual keyframe loop.) Only\n the hover transition below animates. */\n transition: transform 0.15s ease, box-shadow 0.15s ease;\n border: none;\n}\n\n.qa-pastille__dot:hover {\n transform: scale(1.12);\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.3);\n}\n\n.qa-pastille__ecg {\n width: 30px;\n height: 15px;\n}\n\n/* Secondary size: half-scale pastille for the integrated second-FAB use. */\n:host([data-mqa-size=\"sm\"]) .qa-pastille__dot { width: 24px; height: 24px; }\n:host([data-mqa-size=\"sm\"]) .qa-pastille__ecg { width: 15px; height: 8px; }\n\n/* Color overrides: on colored backgrounds the ECG trace needs a dark stroke. */\n.qa-pastille__dot.qa--green,\n.qa-pastille__dot.qa--yellow,\n.qa-pastille__dot.qa--red {\n color: rgba(0, 0, 0, 0.6);\n}\n\n.qa-pastille__dot.qa--gray {\n background: rgba(156, 163, 175, 0.35);\n color: rgba(0, 0, 0, 0.45);\n}\n\n.qa-fade-enter-active,\n.qa-fade-leave-active {\n transition: opacity 0.15s ease, transform 0.15s ease;\n}\n.qa-fade-enter-from,\n.qa-fade-leave-to {\n opacity: 0;\n transform: translateY(4px);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .qa-pastille__dot { transition: none; }\n}\n\n/* ── Test panel ─────────────────────────────────────────────────────────── */\n\n/* The hover panel anchors above the pastille inside the fixed\n .qa-pastille-wrap. The ANCHOR (not the panel) is what's taken out of flow:\n it MUST be absolute so the wrap shrink-wraps to the pastille dot — an in-flow\n panel would stretch the wrap to 340px and either push the dot aside or (on a\n tall, scrolled host page) drive a scrollbar-reflow feedback loop that makes\n the FAB jump. (The donor's positioning lived on an orphaned\n .qa-pastille__panel selector that matched nothing after the Vue→Preact port\n renamed the root to .qa-panel.)\n\n The anchor's padding-bottom renders the 0.5rem visual gap between the dot\n and the panel AS PART of the hover region, so sweeping the cursor from the\n dot up onto the panel never crosses dead space → never fires the wrap's\n mouseleave → no flicker. (Paired with a small close-delay in mount.tsx.) */\n.qa-panel-anchor {\n position: absolute;\n bottom: 100%;\n right: 0;\n padding-bottom: 0.5rem;\n z-index: var(--mqa-z-panel);\n}\n\n.qa-panel {\n background: var(--mqa-bg);\n border-radius: var(--mqa-radius);\n border: 1px solid var(--mqa-border);\n box-shadow: var(--mqa-shadow-xl);\n width: 340px;\n max-height: 60vh;\n overflow: hidden auto;\n font-size: 0.8125rem;\n color: var(--mqa-text);\n}\n\n.qa-panel__empty {\n padding: 0.75rem;\n opacity: 0.7;\n}\n\n.qa-panel__list,\n.qa-panel__groups {\n margin: 0;\n padding: 0.25rem 0;\n list-style: none;\n}\n\n.qa-panel__foot {\n border-top: 1px solid var(--mqa-border);\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.5rem;\n padding: 0.5rem 0.75rem;\n position: sticky;\n bottom: 0;\n background: var(--mqa-bg);\n}\n\n.qa-panel__count {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n font-size: 0.6875rem;\n opacity: 0.65;\n}\n\n.qa-panel__link {\n flex: none;\n font-weight: 600;\n cursor: pointer;\n white-space: nowrap;\n color: var(--mqa-primary);\n background: none;\n border: none;\n font: inherit;\n}\n\n.qa-panel__link:hover { text-decoration: underline; }\n\n/* ── Suite groups ───────────────────────────────────────────────────────── */\n\n.qa-suite + .qa-suite { border-top: 1px solid var(--mqa-border); }\n\n.qa-suite__head {\n width: 100%;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.5rem 0.75rem;\n cursor: pointer;\n text-align: left;\n background: none;\n border: none;\n font: inherit;\n color: inherit;\n}\n\n.qa-suite__head:hover { background: var(--mqa-surface); }\n\n.qa-suite__chev {\n flex: none;\n font-size: 0.625rem;\n opacity: 0.6;\n transition: transform 0.12s ease;\n}\n\n.qa-suite__chev.is-open { transform: rotate(90deg); }\n\n.qa-suite__key {\n flex: none;\n width: 1.25rem;\n height: 1.25rem;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-size: 0.6875rem;\n font-weight: 700;\n background: var(--mqa-surface);\n border-radius: 4px;\n}\n\n.qa-suite__label {\n flex: 1;\n min-width: 0;\n font-weight: 600;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.qa-suite__count {\n flex: none;\n font-size: 0.6875rem;\n opacity: 0.55;\n}\n\n.qa-suite__list {\n background: var(--mqa-surface);\n margin: 0;\n padding: 0 0 0.25rem;\n list-style: none;\n}\n\n/* ── Scenario rows ──────────────────────────────────────────────────────── */\n\n.qa-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.75rem;\n padding: 0.5rem 0.75rem;\n}\n\n.qa-row + .qa-row { border-top: 1px solid var(--mqa-border); }\n\n.qa-row__title {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n min-width: 0;\n}\n\n.qa-row__name {\n flex: 1;\n min-width: 0;\n font-weight: 600;\n line-height: 1.25;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.qa-suite__list .qa-row { padding-left: 1.5rem; }\n\n.qa-tag {\n flex: none;\n font-size: 0.625rem;\n padding: 0 0.25rem;\n text-transform: uppercase;\n letter-spacing: 0.02em;\n opacity: 0.8;\n background: var(--mqa-surface);\n border-radius: 9999px;\n}\n\n/* ── Lane icons ─────────────────────────────────────────────────────────── */\n\n.qa-lanes {\n display: inline-flex;\n align-items: center;\n gap: 0.3125rem;\n flex: none;\n padding: 0.1875rem 0.4375rem;\n border-radius: 9999px;\n background: rgba(17, 24, 39, 0.06);\n}\n\n.qa-lanes--sm { font-size: 0.875rem; }\n.qa-lanes--md { font-size: 1rem; }\n\n.qa-lanes__icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: rgba(17, 24, 39, 0.4);\n}\n\n.qa-lanes__svg {\n width: 1em;\n height: 1em;\n display: block;\n}\n\n.qa-lanes__icon.qa--green { color: var(--mqa-green); background: none; }\n.qa-lanes__icon.qa--yellow { color: var(--mqa-yellow); background: none; }\n.qa-lanes__icon.qa--gray { color: rgba(17, 24, 39, 0.4); background: none; }\n\n/* ── Sitemap modal backdrop ─────────────────────────────────────────────── */\n\n.qa-modal__backdrop {\n background-color: rgba(17, 24, 39, 0.4);\n backdrop-filter: blur(1px);\n position: fixed;\n inset: 0;\n z-index: var(--mqa-z-index);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 1rem;\n}\n\n/* ── Sitemap modal ──────────────────────────────────────────────────────── */\n\n.qa-modal {\n background: var(--mqa-bg);\n border-radius: var(--mqa-radius);\n box-shadow: var(--mqa-shadow-xl);\n border: 1px solid var(--mqa-border);\n width: min(720px, 92vw);\n max-height: 86vh;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n color: var(--mqa-text);\n}\n\n.qa-modal__head {\n background: var(--mqa-primary);\n color: #ffffff;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.5rem 0.75rem;\n}\n\n.qa-modal__head h3 {\n font-weight: 700;\n font-size: 1rem;\n margin: 0;\n}\n\n.qa-modal__close {\n border: 1px solid rgba(255, 255, 255, 0.3);\n background: transparent;\n color: #ffffff;\n width: 24px;\n height: 24px;\n padding: 0;\n border-radius: var(--mqa-radius);\n cursor: pointer;\n display: grid;\n place-items: center;\n}\n\n.qa-modal__close:hover { background: var(--mqa-red); }\n\n.qa-modal__freshness {\n background: var(--mqa-surface);\n padding: 0.375rem 0.75rem;\n font-size: 0.6875rem;\n opacity: 0.8;\n}\n\n.qa-modal__body {\n overflow: auto;\n padding: 0.5rem 0;\n}\n\n/* ── KPI strip ──────────────────────────────────────────────────────────── */\n\n.qa-kpi {\n display: flex;\n gap: 1.5rem;\n padding: 0.625rem 0.75rem;\n}\n\n.qa-kpi__item {\n display: flex;\n flex-direction: column;\n}\n\n.qa-kpi__value {\n font-size: 1.25rem;\n font-weight: 700;\n line-height: 1;\n}\n\n.qa-kpi__label {\n font-size: 0.6875rem;\n opacity: 0.7;\n}\n\n/* ── Tabs ───────────────────────────────────────────────────────────────── */\n\n.qa-tabs {\n border-bottom: 1px solid var(--mqa-border);\n display: flex;\n gap: 0.25rem;\n padding: 0 0.5rem;\n}\n\n.qa-tabs__tab {\n padding: 0.375rem 0.75rem;\n font-weight: 600;\n font-size: 0.8125rem;\n border-bottom: 2px solid transparent;\n cursor: pointer;\n opacity: 0.6;\n background: none;\n border-left: none;\n border-right: none;\n border-top: none;\n font: inherit;\n color: inherit;\n}\n\n.qa-tabs__tab--active {\n color: var(--mqa-primary);\n border-bottom-color: var(--mqa-primary);\n opacity: 1;\n}\n\n/* ── Sitemap page tree ──────────────────────────────────────────────────── */\n\n.qa-tree {\n margin: 0;\n padding: 0;\n list-style: none;\n font-size: 0.8125rem;\n}\n\n.qa-tree__row {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n padding-top: 0.25rem;\n padding-bottom: 0.25rem;\n padding-right: 0.75rem;\n}\n\n.qa-tree__row--here { background: var(--mqa-surface); }\n\n.qa-tree__caret {\n width: 1rem;\n text-align: center;\n cursor: pointer;\n opacity: 0.6;\n font-size: 0.6875rem;\n background: none;\n border: none;\n font: inherit;\n color: inherit;\n}\n\n.qa-tree__caret--leaf { cursor: default; }\n\n.qa-tree__title { flex: 1; }\n.qa-tree__title--structural { font-style: italic; opacity: 0.75; }\n\n.qa-tree__here {\n color: var(--mqa-primary);\n font-size: 0.625rem;\n font-weight: 700;\n text-transform: uppercase;\n}\n\n.qa-tree__count {\n background: var(--mqa-surface);\n border-radius: 9999px;\n font-size: 0.6875rem;\n padding: 0 0.375rem;\n min-width: 1.25rem;\n text-align: center;\n}\n\n/* ── Scenarios tab ──────────────────────────────────────────────────────── */\n\n.qa-scenarios { padding: 0 0.75rem; }\n\n.qa-scenarios__search {\n border: 1px solid var(--mqa-border);\n border-radius: var(--mqa-radius);\n width: 100%;\n height: 2rem;\n padding: 0 0.75rem;\n font-size: 0.8125rem;\n margin-bottom: 0.5rem;\n background: var(--mqa-bg);\n color: var(--mqa-text);\n font-family: inherit;\n box-sizing: border-box;\n}\n\n.qa-scenarios__empty {\n opacity: 0.7;\n padding: 0.5rem 0;\n}\n\n.qa-scenarios__list {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.qa-scenarios__item {\n border-top: 1px solid var(--mqa-border);\n padding: 0.5rem 0;\n}\n\n.qa-scenarios__line {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n}\n\n.qa-scenarios__title {\n flex: 1;\n font-weight: 600;\n font-size: 0.8125rem;\n}\n\n.qa-scenarios__pages {\n display: flex;\n flex-wrap: wrap;\n gap: 0.75rem;\n font-size: 0.6875rem;\n opacity: 0.7;\n margin-top: 0.125rem;\n padding-left: 1rem;\n}\n\n/* ── Scenario dot (color indicator) ────────────────────────────────────── */\n\n.qa-dot {\n width: 0.625rem;\n height: 0.625rem;\n border-radius: 9999px;\n flex: none;\n background: rgba(17, 24, 39, 0.6);\n}\n\n/* Override modifier background for dot — dot uses background not text color. */\n.qa-dot.qa--green { background: var(--mqa-green); }\n.qa-dot.qa--yellow { background: var(--mqa-yellow); }\n.qa-dot.qa--red { background: var(--mqa-red); }\n.qa-dot.qa--gray { background: var(--mqa-gray); }\n\n/* ── Suite key badge ────────────────────────────────────────────────────── */\n\n.qa-suitekey {\n flex: none;\n font-size: 0.625rem;\n font-weight: 700;\n padding: 0 0.3125rem;\n line-height: 1.4;\n background: var(--mqa-surface);\n border-radius: 4px;\n}\n`\n","/**\n * Helpers de PRÉSENTATION du QA Meter (aucune logique de statut).\n *\n * La couleur d'un scénario / d'une page est calculée à la construction de\n * l'artefact (doc 03 §9.3) et arrive déjà décidée. Ici on ne fait que la\n * traduire en classe CSS + clé i18n, et formater les dates/versions.\n *\n * Ported from the donor `qa-meter.ui.ts`; type imports point to ./types,\n * i18n keys are unprefixed (no `qaMeter.` prefix), Vue-specific imports dropped.\n */\nimport type {\n QaAutomatedLane,\n QaAutomatedStatus,\n QaCaseStatus,\n QaColor,\n QaGrounding,\n QaHumanLane,\n QaPriority,\n QaTriColor,\n} from './types';\nimport type { QaStringKey } from './i18n';\n\n/** Classe modificatrice BEM appliquée aux pastilles/puces (cf. styles des composants). */\nexport const QA_COLOR_MODIFIER: Record<QaColor, string> = {\n green: 'qa--green',\n yellow: 'qa--yellow',\n red: 'qa--red',\n gray: 'qa--gray',\n};\n\n/** Clé i18n du libellé d'une couleur (référencée dynamiquement → présente dans les deux locales). */\nexport const QA_COLOR_LABEL_KEY: Record<QaColor, QaStringKey> = {\n green: 'color.green',\n yellow: 'color.yellow',\n red: 'color.red',\n gray: 'color.gray',\n};\n\n/** Clé i18n du libellé d'un statut automated. */\nexport const QA_AUTOMATED_LABEL_KEY: Record<QaAutomatedStatus, QaStringKey> = {\n passing: 'automated.passing',\n failing: 'automated.failing',\n flaky: 'automated.flaky',\n never_run: 'automated.neverRun',\n};\n\n/** Modificateur sémaphore d'un volet automated (le volet, pas le scénario). */\nexport const QA_AUTOMATED_MODIFIER: Record<QaAutomatedStatus, string> = {\n passing: 'qa--green',\n failing: 'qa--red',\n flaky: 'qa--yellow',\n never_run: 'qa--gray',\n};\n\n/**\n * Modificateur sémaphore d'un volet humain (dev/client) :\n * - `rejected` → rouge ; non validé → gris ; validé mais périmé/contredit → jaune ; validé → vert.\n */\nexport function humanLaneModifier(lane: { validated: boolean; rejected?: boolean; stale: boolean }): string {\n if (lane.rejected) {\n return 'qa--red';\n }\n if (!lane.validated) {\n return 'qa--gray';\n }\n return lane.stale ? 'qa--yellow' : 'qa--green';\n}\n\n/** Modificateur d'une puce de priorité (P0 = critique → rouge ; décroît vers neutre). */\nexport const QA_PRIORITY_MODIFIER: Record<QaPriority, string> = {\n P0: 'qa-prio--p0',\n P1: 'qa-prio--p1',\n P2: 'qa-prio--p2',\n P3: 'qa-prio--p3',\n};\n\n/** Clé i18n du tag de statut d'un cas (couvert / manuel / bloqué / question produit). */\nexport const QA_CASE_STATUS_LABEL_KEY: Record<QaCaseStatus, QaStringKey> = {\n covered: 'case.covered',\n manual: 'case.manual',\n blocked: 'case.blocked',\n 'open-question': 'case.openQuestion',\n};\n\n/** Clé i18n de l'ancrage de l'attendu (vérifié / à confirmer / décision produit). */\nexport const QA_GROUNDING_LABEL_KEY: Record<QaGrounding, QaStringKey> = {\n verified: 'grounding.verified',\n 'confirm-live': 'grounding.confirmLive',\n 'product-question': 'grounding.productQuestion',\n};\n\n// ── Voies code/bot/human (3 icônes indépendantes, couleur 3-états) ───────────\n\n/** Voie « bot » (exécution automatisée) : passe → vert ; échec/instable → jaune ; jamais exécuté → gris. */\nexport function botTriColor(lane: QaAutomatedLane): QaTriColor {\n if (lane.status === 'passing') {\n return 'green';\n }\n if (lane.status === 'failing' || lane.status === 'flaky') {\n return 'yellow';\n }\n return 'gray';\n}\n\n/** Voie humaine (« code » = dev, « human » = client) : validé & frais → vert ; rejeté → jaune ; non validé/périmé → gris. */\nexport function humanTriColor(lane: QaHumanLane): QaTriColor {\n if (lane.rejected) {\n return 'yellow';\n }\n if (lane.validated) {\n return lane.stale ? 'gray' : 'green';\n }\n return 'gray';\n}\n\n/** Agrège une voie sur un ensemble de cas : un jaune → jaune ; sinon tout vert → vert ; sinon gris. */\nexport function aggregateTri(colors: ReadonlyArray<QaTriColor>): QaTriColor {\n if (colors.some((c) => c === 'yellow')) {\n return 'yellow';\n }\n if (colors.length > 0 && colors.every((c) => c === 'green')) {\n return 'green';\n }\n return 'gray';\n}\n\n/** Rang de gravité d'une couleur, pour résumer un groupe par son « pire » état. */\nconst QA_COLOR_RANK: Record<QaColor, number> = { red: 3, yellow: 2, green: 1, gray: 0 };\n\n/** Couleur résumé d'un groupe : rouge domine ; sinon jaune ; sinon vert ; sinon gris. */\nexport function worstColor(colors: ReadonlyArray<QaColor>): QaColor {\n return colors.reduce<QaColor>((acc, c) => (QA_COLOR_RANK[c] > QA_COLOR_RANK[acc] ? c : acc), 'gray');\n}\n\n/** Priorité la plus haute (P0 < P1 < P2 < P3) parmi une liste, ou `undefined`. */\nexport function topPriority(priorities: ReadonlyArray<QaPriority | undefined>): QaPriority | undefined {\n return priorities.filter((p): p is QaPriority => Boolean(p)).sort()[0];\n}\n\n/**\n * Formate une estampille ISO en date+heure locale courte (fr-CA → AAAA-MM-JJ HH:MM).\n * Retourne `''` pour une valeur absente/invalide (l'appelant masque alors le segment).\n */\nexport function formatQaDate(iso: string | undefined | null): string {\n if (!iso) {\n return '';\n }\n const date = new Date(iso);\n if (Number.isNaN(date.getTime())) {\n return '';\n }\n return new Intl.DateTimeFormat('fr-CA', {\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n }).format(date);\n}\n\n/**\n * Version d'app = chaîne opaque (doc 03 §1.1, jamais parsée). Tronquée pour\n * l'affichage : un timestamp de build complet est illisible dans une puce.\n */\nexport function shortVersion(version: string | undefined | null): string {\n if (!version) {\n return '';\n }\n return version.length > 19 ? `${version.slice(0, 19)}…` : version;\n}\n","import { h } from 'preact'\n\nimport type { QaColor } from './types'\nimport type { QaStrings } from './i18n'\nimport { QA_COLOR_LABEL_KEY, QA_COLOR_MODIFIER } from './ui'\n\n/**\n * The heartbeat/ECG pastille — purely presentational. It renders the page color\n * as a semaphore modifier and emits hover/open callbacks; it owns NO state, NO\n * router, NO store. State (current page, fetched artifact) lives in the mount\n * container task that wraps this.\n *\n * Heartbeat/ECG SVG copied verbatim from the donor `qa-pastille.vue`.\n */\nexport interface PastilleProps {\n color: QaColor\n state: 'known' | 'no-tests' | 'unknown-page'\n strings: QaStrings\n onOpenModal: () => void\n onHover: () => void\n}\n\nexport function Pastille({ color, state, strings, onOpenModal, onHover }: PastilleProps) {\n // The color → class mapping is the same for every state; only the title text\n // differs slightly so a hover hint can explain the no-tests / unknown-page cases.\n const base = strings['pastille.label']\n let detail = strings[QA_COLOR_LABEL_KEY[color]]\n if (state === 'no-tests') {\n detail = strings['panel.noTests']\n } else if (state === 'unknown-page') {\n detail = strings['pastille.unknownPage']\n }\n const label = `${base} — ${detail}`\n\n // Mirror the donor's two-element structure: `.qa-pastille` is the fixed-position\n // CONTAINER; `.qa-pastille__dot` carries the size/background/animation (the\n // `.qa-pastille__dot.qa--*` rules in styles.ts are where color resolves).\n return h(\n 'div',\n { class: 'qa-pastille' },\n h(\n 'button',\n {\n type: 'button',\n 'data-testid': 'qa-pastille',\n class: `qa-pastille__dot ${QA_COLOR_MODIFIER[color]}`,\n 'aria-label': label,\n title: label,\n onClick: onOpenModal,\n onMouseEnter: onHover,\n onFocus: onHover,\n },\n h(\n 'svg',\n { class: 'qa-pastille__ecg', viewBox: '0 0 24 12', 'aria-hidden': 'true' },\n h('polyline', {\n points: '1,6 7,6 9,2 12,10 15,4 17,6 23,6',\n fill: 'none',\n stroke: 'currentColor',\n 'stroke-width': '1.5',\n 'stroke-linejoin': 'round',\n 'stroke-linecap': 'round',\n }),\n ),\n ),\n )\n}\n","import { h } from 'preact'\n\nimport type { QaTriColor } from './types'\nimport type { QaStrings } from './i18n'\n\n/**\n * A row of three independent lane chips for a scenario (or a suite aggregate):\n * - dev (`code`) = developer validation (the `</>` chevrons);\n * - bot (`bot`) = automated execution (Playwright);\n * - client (`human`) = human/client validation.\n *\n * Each chip carries its OWN tri-color (green/yellow/gray) — they are independent:\n * a human may have validated while the bot never ran, etc. Pure presentational:\n * no hooks, no state, no fetching.\n *\n * SVG path data copied verbatim from the donor `qa-lane-icons.vue`.\n */\nexport interface LaneIconsProps {\n dev: QaTriColor\n bot: QaTriColor\n human: QaTriColor\n strings: QaStrings\n}\n\n/** Tri-color → BEM modifier (donor mapping: `qa--green|qa--yellow|qa--gray`). */\nconst TRI_MODIFIER: Record<QaTriColor, string> = {\n green: 'qa--green',\n yellow: 'qa--yellow',\n gray: 'qa--gray',\n}\n\nexport function LaneIcons({ dev, bot, human, strings }: LaneIconsProps) {\n return h(\n 'span',\n { class: 'qa-lanes', 'data-testid': 'qa-lane-icons' },\n // dev — chevrons </>\n h(\n 'span',\n {\n class: `qa-lanes__icon qa-lane ${TRI_MODIFIER[dev]}`,\n title: strings['lane.code'],\n 'aria-label': strings['lane.code'],\n 'data-testid': 'qa-lane-dev',\n 'data-state': dev,\n },\n h(\n 'svg',\n { class: 'qa-lanes__svg', viewBox: '0 0 24 24', 'aria-hidden': 'true' },\n h('path', {\n d: 'M8 7l-5 5 5 5M16 7l5 5-5 5',\n fill: 'none',\n stroke: 'currentColor',\n 'stroke-width': '2.2',\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n }),\n ),\n ),\n // bot — robot head\n h(\n 'span',\n {\n class: `qa-lanes__icon qa-lane ${TRI_MODIFIER[bot]}`,\n title: strings['lane.bot'],\n 'aria-label': strings['lane.bot'],\n 'data-testid': 'qa-lane-bot',\n 'data-state': bot,\n },\n h(\n 'svg',\n {\n class: 'qa-lanes__svg',\n viewBox: '0 0 24 24',\n 'aria-hidden': 'true',\n fill: 'none',\n stroke: 'currentColor',\n 'stroke-width': '2',\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n },\n h('rect', { x: '4.5', y: '8.5', width: '15', height: '10.5', rx: '2.5' }),\n h('path', { d: 'M12 4.5v4' }),\n h('circle', { cx: '12', cy: '3.4', r: '1.3', fill: 'currentColor', stroke: 'none' }),\n h('path', { d: 'M2.5 12.5v3M21.5 12.5v3' }),\n h('circle', { cx: '9', cy: '13.5', r: '1.2', fill: 'currentColor', stroke: 'none' }),\n h('circle', { cx: '15', cy: '13.5', r: '1.2', fill: 'currentColor', stroke: 'none' }),\n ),\n ),\n // client — person\n h(\n 'span',\n {\n class: `qa-lanes__icon qa-lane ${TRI_MODIFIER[human]}`,\n title: strings['lane.human'],\n 'aria-label': strings['lane.human'],\n 'data-testid': 'qa-lane-client',\n 'data-state': human,\n },\n h(\n 'svg',\n {\n class: 'qa-lanes__svg',\n viewBox: '0 0 24 24',\n 'aria-hidden': 'true',\n fill: 'none',\n stroke: 'currentColor',\n 'stroke-width': '2',\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n },\n h('circle', { cx: '12', cy: '8', r: '3.6' }),\n h('path', { d: 'M5 20c0-3.6 3.1-6 7-6s7 2.4 7 6' }),\n ),\n ),\n )\n}\n","import { h } from 'preact'\nimport { useState } from 'preact/hooks'\n\nimport type { QaPageStatus, QaScenario, QaTriColor } from './types'\nimport type { QaStrings } from './i18n'\nimport { LaneIcons } from './LaneIcons'\nimport {\n aggregateTri,\n botTriColor,\n formatQaDate,\n humanTriColor,\n QA_CASE_STATUS_LABEL_KEY,\n QA_GROUNDING_LABEL_KEY,\n QA_PRIORITY_MODIFIER,\n shortVersion,\n} from './ui'\n\n/**\n * Preact port of the donor `qa-test-panel.vue`.\n *\n * Presentational (no data-fetching, router, or store). The container task feeds\n * it the resolved page-status, a `state` discriminator (so the two new empty\n * states can render even when `page` is null), and the run metadata for the\n * freshness banner. Local UI state (which suite groups are expanded) lives in a\n * `useState` hook — matching the donor's `expanded`/`toggleGroup` interaction.\n *\n * The donor's `<script setup>` computed properties (lane tri-color derivation and\n * suite grouping) are extracted here as the pure exported helpers `laneTriColor`\n * and `groupScenarios` so they're unit-testable and reusable by the mount task.\n */\nexport interface TestPanelProps {\n page: QaPageStatus | null\n state: 'known' | 'no-tests' | 'unknown-page'\n meta: { generated_at: string; app_version: string; environment: string }\n strings: QaStrings\n}\n\n/** The three independent lane tri-colors of a scenario — single source of the\n * dev/bot/human derivation (donor `lanesFor`). The client lane with no event is\n * gray (never green), because `humanTriColor` returns gray for an unvalidated lane. */\nexport interface ScenarioLanes {\n dev: QaTriColor\n bot: QaTriColor\n human: QaTriColor\n}\n\nexport function laneTriColor(scenario: QaScenario): ScenarioLanes {\n return {\n dev: humanTriColor(scenario.dev),\n bot: botTriColor(scenario.automated),\n human: humanTriColor(scenario.client),\n }\n}\n\nexport interface ScenarioGroupView {\n key: string\n label: string\n scenarios: QaScenario[]\n}\n\n/** Group scenarios by `scenario.group.key` (donor `grouped`). Scenarios with no\n * group fall into a `'·'` bucket. Keys are alphabetical with the inter-module\n * `X` suite forced last, matching the donor ordering. */\nexport function groupScenarios(scenarios: ReadonlyArray<QaScenario>): ScenarioGroupView[] {\n const map = new Map<string, QaScenario[]>()\n for (const scenario of scenarios) {\n const key = scenario.group?.key ?? '·'\n const bucket = map.get(key) ?? []\n bucket.push(scenario)\n map.set(key, bucket)\n }\n const keys = [...map.keys()].sort((a, b) => (a === 'X' ? 1 : b === 'X' ? -1 : a.localeCompare(b)))\n return keys.map((key) => {\n const bucket = map.get(key)!\n return { key, label: bucket[0]?.group?.label ?? '', scenarios: bucket }\n })\n}\n\nfunction interpolate(template: string, params: Record<string, string | number>): string {\n return template.replace(/\\{(\\w+)\\}/g, (_, k: string) => String(params[k] ?? ''))\n}\n\n/** One scenario row: title, priority chip, provenance + tags, and the three lanes. */\nfunction ScenarioRow({ scenario, strings }: { scenario: QaScenario; strings: QaStrings }) {\n const lanes = laneTriColor(scenario)\n const provenance = scenario.automated.provenance\n return h(\n 'li',\n { class: 'qa-row', 'data-testid': 'qa-scenario-row' },\n h(\n 'span',\n { class: 'qa-row__title' },\n scenario.priority\n ? h(\n 'span',\n { class: `qa-tag ${QA_PRIORITY_MODIFIER[scenario.priority]}`, 'data-testid': 'qa-priority' },\n scenario.priority,\n )\n : null,\n h('span', { class: 'qa-row__name' }, scenario.title),\n scenario.source === 'testgen'\n ? h('span', { class: 'qa-tag', title: strings['source.testgenHint'] }, strings['source.testgen'])\n : null,\n scenario.caseStatus\n ? h('span', { class: 'qa-tag' }, strings[QA_CASE_STATUS_LABEL_KEY[scenario.caseStatus]])\n : null,\n scenario.grounding\n ? h('span', { class: 'qa-tag' }, strings[QA_GROUNDING_LABEL_KEY[scenario.grounding]])\n : null,\n provenance ? h('span', { class: 'qa-tag', 'data-testid': 'qa-provenance' }, provenance) : null,\n ),\n h(LaneIcons, { dev: lanes.dev, bot: lanes.bot, human: lanes.human, strings }),\n )\n}\n\nexport function TestPanel({ page, state, meta, strings }: TestPanelProps) {\n // Local UI state: which suite-group keys are expanded. Matches the donor's\n // `expanded` ref, which starts empty → all groups COLLAPSED by default (so a\n // heavily-loaded page folds into a few theme headers, per QaScenarioGroup).\n const [expanded, setExpanded] = useState<ReadonlySet<string>>(new Set())\n const toggleGroup = (key: string): void => {\n setExpanded((prev) => {\n const next = new Set(prev)\n if (next.has(key)) {\n next.delete(key)\n } else {\n next.add(key)\n }\n return next\n })\n }\n\n const freshness = interpolate(strings['panel.freshness'], {\n date: formatQaDate(meta.generated_at),\n version: shortVersion(meta.app_version),\n env: meta.environment,\n })\n\n let body\n if (state === 'no-tests') {\n body = h('p', { class: 'qa-panel__empty' }, strings['panel.noTests'])\n } else if (state === 'unknown-page') {\n body = h('p', { class: 'qa-panel__empty' }, strings['panel.unknownPage'])\n } else {\n const scenarios = page?.scenarios ?? []\n const hasGroups = scenarios.some((s) => s.group)\n if (scenarios.length === 0) {\n body = h('p', { class: 'qa-panel__empty' }, strings['panel.noScenarios'])\n } else if (!hasGroups) {\n // Synthetic / ungrouped page (donor `hasGroups === false`): flat list, no\n // suite header — avoids a spurious empty-label group wrapper.\n body = h(\n 'ul',\n { class: 'qa-panel__list' },\n scenarios.map((scenario) => h(ScenarioRow, { key: scenario.id, scenario, strings })),\n )\n } else {\n const groups = groupScenarios(scenarios)\n body = h(\n 'ul',\n { class: 'qa-panel__groups', 'data-testid': 'qa-suite-groups' },\n groups.map((group) => {\n const isOpen = expanded.has(group.key)\n const agg = {\n dev: aggregateTri(group.scenarios.map((s) => humanTriColor(s.dev))),\n bot: aggregateTri(group.scenarios.map((s) => botTriColor(s.automated))),\n human: aggregateTri(group.scenarios.map((s) => humanTriColor(s.client))),\n }\n return h(\n 'li',\n { key: group.key, class: 'qa-suite', 'data-testid': 'qa-panel-group' },\n h(\n 'button',\n {\n type: 'button',\n class: 'qa-suite__head',\n 'aria-expanded': isOpen,\n 'data-testid': 'qa-suite-group',\n onClick: () => toggleGroup(group.key),\n },\n h(\n 'span',\n { class: `qa-suite__chev ${isOpen ? 'is-open' : ''}`, 'aria-hidden': 'true' },\n '▸',\n ),\n h('span', { class: 'qa-suite__key' }, group.key),\n h('span', { class: 'qa-suite__label' }, group.label),\n h(\n 'span',\n { class: 'qa-suite__count' },\n interpolate(strings['suite.cases'], { count: group.scenarios.length }),\n ),\n h(LaneIcons, { dev: agg.dev, bot: agg.bot, human: agg.human, strings }),\n ),\n isOpen\n ? h(\n 'ul',\n { class: 'qa-suite__list' },\n group.scenarios.map((scenario) =>\n h(ScenarioRow, { key: scenario.id, scenario, strings }),\n ),\n )\n : null,\n )\n }),\n )\n }\n }\n\n return h(\n 'section',\n { class: 'qa-panel', 'data-testid': 'qa-panel' },\n body,\n h(\n 'footer',\n { class: 'qa-panel__foot' },\n h(\n 'span',\n { class: 'qa-panel__count', title: freshness, 'data-testid': 'qa-panel-freshness' },\n freshness,\n ),\n ),\n )\n}\n","import { h } from 'preact'\nimport { useEffect, useMemo, useState } from 'preact/hooks'\n\nimport type { QaScenario, QaSitemapNode, QaStatusArtifact } from './types'\nimport type { QaStrings } from './i18n'\nimport { LaneIcons } from './LaneIcons'\nimport { normalizePattern } from './resolver'\nimport { botTriColor, formatQaDate, humanTriColor, QA_COLOR_MODIFIER, shortVersion } from './ui'\n\n/**\n * Preact port of the donor `qa-sitemap-modal.vue`.\n *\n * Presentational apart from the Escape-key listener (the one place a hook is\n * warranted — the donor used a `watch` + `window.addEventListener`). Data comes\n * in as the already-fetched artifact; this component owns no store or fetching.\n *\n * The donor's `<script setup>` tree reconstruction is extracted as the pure\n * exported `buildTree` helper so it's unit-testable and reusable by the mount\n * task.\n */\nexport interface SitemapModalProps {\n artifact: QaStatusArtifact\n currentPattern: string | null\n strings: QaStrings\n onClose: () => void\n}\n\n/** A node of the rebuilt sitemap tree, flattened depth-first for rendering. The\n * flat `artifact.sitemap` links children to parents via `parent_id`; we rebuild\n * the hierarchy and emit `depth` + `hasChildren` (donor `flatTree`). */\nexport interface TreeRow {\n node: QaSitemapNode\n depth: number\n hasChildren: boolean\n}\n\nexport function buildTree(nodes: ReadonlyArray<QaSitemapNode>): TreeRow[] {\n const childrenByParent = new Map<string | null, QaSitemapNode[]>()\n for (const node of nodes) {\n const bucket = childrenByParent.get(node.parent_id) ?? []\n bucket.push(node)\n childrenByParent.set(node.parent_id, bucket)\n }\n for (const bucket of childrenByParent.values()) {\n bucket.sort((a, b) => a.order - b.order)\n }\n const out: TreeRow[] = []\n const visit = (parentId: string | null, depth: number): void => {\n for (const node of childrenByParent.get(parentId) ?? []) {\n const children = childrenByParent.get(node.id) ?? []\n out.push({ node, depth, hasChildren: children.length > 0 })\n if (children.length > 0) {\n visit(node.id, depth + 1)\n }\n }\n }\n visit(null, 0)\n return out\n}\n\ninterface CaseEntry {\n scenario: QaScenario\n primaries: Set<string>\n traversed: Set<string>\n}\n\n/** Dedupe scenarios across pages by `external_key ?? id` (donor `dedupedCases`):\n * a cross-module/traversed case that appears on several pages lists ONCE, with\n * its primary/traversed pattern sets merged. */\nfunction dedupeScenarios(artifact: QaStatusArtifact): CaseEntry[] {\n const byKey = new Map<string, CaseEntry>()\n for (const page of artifact.pages) {\n for (const scenario of page.scenarios) {\n const key = scenario.external_key ?? scenario.id\n const entry = byKey.get(key) ?? { scenario, primaries: new Set<string>(), traversed: new Set<string>() }\n for (const ref of scenario.pages ?? []) {\n ;(ref.role === 'primary' ? entry.primaries : entry.traversed).add(ref.pattern)\n }\n byKey.set(key, entry)\n }\n }\n const rank = (p?: string): number => (p ? Number(p.slice(1)) : 9)\n return [...byKey.values()].sort((a, b) => {\n const ga = a.scenario.group?.key ?? '~'\n const gb = b.scenario.group?.key ?? '~'\n if (ga !== gb) {\n return ga.localeCompare(gb)\n }\n return rank(a.scenario.priority) - rank(b.scenario.priority) || a.scenario.title.localeCompare(b.scenario.title)\n })\n}\n\nfunction interpolate(template: string, params: Record<string, string | number>): string {\n return template.replace(/\\{(\\w+)\\}/g, (_, k: string) => String(params[k] ?? ''))\n}\n\n/** KPI formatting: the metric values are already whole percentages (0–100), as\n * emitted by `qa build` (`cli/src/qa/build.ts` rounds `100*num/den`). Render\n * them directly — do NOT multiply by 100, or a CLI artifact's `25` becomes\n * \"2500%\". See the QaMetric contract in `types.ts`. */\nfunction formatPct(pct: number): string {\n return `${Math.round(pct)}%`\n}\n\nexport function SitemapModal({ artifact, currentPattern, strings, onClose }: SitemapModalProps) {\n const [activeTab, setActiveTab] = useState<'pages' | 'scenarios'>('pages')\n const [search, setSearch] = useState('')\n\n // The one place a hook is warranted: close on Escape, cleaned up on unmount.\n useEffect(() => {\n const onKeydown = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') {\n onClose()\n }\n }\n window.addEventListener('keydown', onKeydown)\n return () => window.removeEventListener('keydown', onKeydown)\n }, [onClose])\n\n const here = currentPattern ? normalizePattern(currentPattern) : null\n const tree = useMemo(() => buildTree(artifact.sitemap), [artifact])\n const cases = useMemo(() => dedupeScenarios(artifact), [artifact])\n\n const titleByPattern = useMemo(() => {\n const map = new Map<string, string>()\n for (const node of artifact.sitemap) {\n map.set(node.path_pattern, node.title)\n }\n return map\n }, [artifact])\n const patternTitle = (pattern: string): string => titleByPattern.get(pattern) ?? pattern\n\n const query = search.trim().toLowerCase()\n const filteredCases = query\n ? cases.filter(\n (c) =>\n c.scenario.title.toLowerCase().includes(query) ||\n (c.scenario.group?.label ?? '').toLowerCase().includes(query),\n )\n : cases\n\n const freshness = interpolate(strings['panel.freshness'], {\n date: formatQaDate(artifact.generated_at),\n version: shortVersion(artifact.app_version),\n env: artifact.environment,\n })\n\n return h(\n 'div',\n {\n class: 'qa-modal__backdrop',\n 'data-testid': 'qa-sitemap-modal',\n onClick: (e: MouseEvent) => {\n if (e.target === e.currentTarget) onClose()\n },\n },\n h(\n 'div',\n { class: 'qa-modal', role: 'dialog', 'aria-modal': 'true' },\n h(\n 'header',\n { class: 'qa-modal__head' },\n h('h3', null, strings['modal.title']),\n h(\n 'button',\n {\n type: 'button',\n class: 'qa-modal__close',\n title: strings['modal.close'],\n 'aria-label': strings['modal.close'],\n 'data-testid': 'qa-modal-close',\n onClick: onClose,\n },\n '×',\n ),\n ),\n h('div', { class: 'qa-modal__freshness' }, freshness),\n // Dual metric (doc 01 D12) — rendered as rounded percentages.\n h(\n 'div',\n { class: 'qa-kpi', 'data-testid': 'qa-kpi' },\n h(\n 'div',\n { class: 'qa-kpi__item' },\n h('span', { class: 'qa-kpi__value' }, formatPct(artifact.metric.pages_with_scenarios_pct)),\n h('span', { class: 'qa-kpi__label' }, strings['modal.kpiPagesCovered']),\n ),\n h(\n 'div',\n { class: 'qa-kpi__item' },\n h('span', { class: 'qa-kpi__value' }, formatPct(artifact.metric.scenarios_passing_pct)),\n h('span', { class: 'qa-kpi__label' }, strings['modal.kpiScenariosPassing']),\n ),\n ),\n h(\n 'nav',\n { class: 'qa-tabs' },\n h(\n 'button',\n {\n type: 'button',\n class: `qa-tabs__tab ${activeTab === 'pages' ? 'qa-tabs__tab--active' : ''}`,\n 'data-testid': 'qa-tab-pages',\n onClick: () => setActiveTab('pages'),\n },\n strings['modal.tabPages'],\n ),\n h(\n 'button',\n {\n type: 'button',\n class: `qa-tabs__tab ${activeTab === 'scenarios' ? 'qa-tabs__tab--active' : ''}`,\n 'data-testid': 'qa-tab-scenarios',\n onClick: () => setActiveTab('scenarios'),\n },\n strings['modal.tabScenarios'],\n ),\n ),\n h(\n 'div',\n { class: 'qa-modal__body' },\n activeTab === 'pages'\n ? h(\n 'ul',\n { class: 'qa-tree' },\n tree.map((row) => {\n const isHere = here !== null && row.node.path_pattern === here\n return h(\n 'li',\n {\n key: row.node.id,\n class: `qa-tree__row ${isHere ? 'qa-tree__row--here' : ''}`,\n 'data-testid': isHere ? 'qa-tree-current' : 'qa-sitemap-node',\n style: { paddingLeft: `${0.5 + row.depth * 1.1}rem` },\n },\n h(\n 'span',\n {\n class: `qa-dot ${QA_COLOR_MODIFIER[row.node.color]}`,\n 'aria-hidden': 'true',\n },\n ),\n h(\n 'span',\n {\n class: `qa-tree__title ${row.node.structural ? 'qa-tree__title--structural' : ''}`,\n },\n row.node.title || row.node.path_pattern,\n ),\n isHere ? h('span', { class: 'qa-tree__here' }, ` ${row.node.path_pattern} `) : null,\n isHere ? h('span', { class: 'qa-tree__here' }, strings['modal.youAreHere']) : null,\n h('span', { class: 'qa-tree__count' }, String(row.node.counts.scenarios)),\n )\n }),\n )\n : h(\n 'div',\n { class: 'qa-scenarios' },\n h('input', {\n type: 'search',\n class: 'qa-scenarios__search',\n placeholder: strings['modal.search'],\n 'data-testid': 'qa-scenarios-search',\n value: search,\n onInput: (e: Event) => setSearch((e.target as HTMLInputElement).value),\n }),\n filteredCases.length === 0\n ? h('p', { class: 'qa-scenarios__empty' }, strings['modal.noMatch'])\n : h(\n 'ul',\n { class: 'qa-scenarios__list' },\n filteredCases.map((entry) => {\n const scenario = entry.scenario\n const lanes = {\n dev: humanTriColor(scenario.dev),\n bot: botTriColor(scenario.automated),\n human: humanTriColor(scenario.client),\n }\n const traversed = [...entry.traversed].map(patternTitle)\n return h(\n 'li',\n {\n key: scenario.external_key ?? scenario.id,\n class: 'qa-scenarios__item',\n 'data-testid': 'qa-scenario-row',\n },\n h(\n 'div',\n { class: 'qa-scenarios__line' },\n h('span', { class: `qa-dot ${QA_COLOR_MODIFIER[scenario.color]}`, 'aria-hidden': 'true' }),\n scenario.group?.key\n ? h('span', { class: 'qa-suitekey' }, scenario.group.key)\n : null,\n h('span', { class: 'qa-scenarios__title' }, scenario.title),\n scenario.source === 'testgen'\n ? h('span', { class: 'qa-tag' }, strings['source.testgen'])\n : null,\n h(LaneIcons, { dev: lanes.dev, bot: lanes.bot, human: lanes.human, strings }),\n ),\n h(\n 'div',\n { class: 'qa-scenarios__pages' },\n entry.primaries.size\n ? h(\n 'span',\n { class: 'qa-scenarios__primary' },\n interpolate(strings['modal.appliesTo'], { count: entry.primaries.size }),\n )\n : null,\n traversed.length\n ? h(\n 'span',\n { class: 'qa-scenarios__traversed' },\n `${strings['modal.traversed']}: ${traversed.join(', ')}`,\n )\n : null,\n ),\n )\n }),\n ),\n ),\n ),\n ),\n )\n}\n","import { h, render } from 'preact'\nimport { useCallback, useEffect, useState } from 'preact/hooks'\n\nimport type { QaColor, QaPageStatus, QaStatusArtifact } from './types'\nimport type { QaMeterHandle, QaMeterOptions } from './index'\nimport { resolveStrings } from './i18n'\nimport { resolvePage } from './resolver'\nimport { QA_METER_STYLES } from './styles'\nimport { Pastille } from './Pastille'\nimport { TestPanel } from './TestPanel'\nimport { SitemapModal } from './SitemapModal'\n\n/** The custom event the patched History API dispatches so the meter can\n * re-resolve the current page on SPA navigations (pushState/replaceState\n * don't fire `popstate`). Module-scoped name so every instance listens to\n * the same channel. */\nconst LOCATION_CHANGE_EVENT = 'mqa:locationchange'\n\n// ── History API patch (module-level, ref-counted) ──────────────────────────\n// We monkey-patch pushState/replaceState ONCE across all instances so a\n// `mqa:locationchange` CustomEvent fires on SPA navigations. A ref-count lets\n// the LAST disposed instance restore the originals; guarding on `patched`\n// prevents a second instance from double-wrapping (which would dispatch twice\n// and, worse, capture an already-wrapped \"original\").\nlet historyPatched = false\nlet historyRefCount = 0\nlet originalPushState: History['pushState'] | null = null\nlet originalReplaceState: History['replaceState'] | null = null\n\nfunction patchHistory(): void {\n historyRefCount++\n if (historyPatched) return\n historyPatched = true\n originalPushState = history.pushState\n originalReplaceState = history.replaceState\n const fire = (): void => {\n window.dispatchEvent(new CustomEvent(LOCATION_CHANGE_EVENT))\n }\n history.pushState = function patchedPushState(\n this: History,\n ...args: Parameters<History['pushState']>\n ) {\n const ret = originalPushState!.apply(this, args)\n fire()\n return ret\n }\n history.replaceState = function patchedReplaceState(\n this: History,\n ...args: Parameters<History['replaceState']>\n ) {\n const ret = originalReplaceState!.apply(this, args)\n fire()\n return ret\n }\n}\n\nfunction unpatchHistory(): void {\n if (historyRefCount > 0) historyRefCount--\n if (historyRefCount > 0 || !historyPatched) return\n historyPatched = false\n if (originalPushState) history.pushState = originalPushState\n if (originalReplaceState) history.replaceState = originalReplaceState\n originalPushState = null\n originalReplaceState = null\n}\n\n/** Default offset, matching the donor `qa-pastille.vue` (sits near the feedback\n * FAB). `.qa-pastille` anchors bottom-right via the `--mqa-right`/`--mqa-bottom`\n * custom properties, which default to `1.5rem` in the stylesheet. CSS custom\n * properties inherit from the host element THROUGH the shadow boundary, so a\n * host can nudge the pastille by setting those vars on the host (done below\n * when `options.position` is provided) — setting `host.style.right/bottom`\n * directly would NOT work, since `.qa-pastille` is itself `position: fixed`\n * and anchors to the viewport, not the host. */\n\n// ── Format validation ───────────────────────────────────────────────────────\nconst QA_ARTIFACT_FORMAT = 1\n\nfunction isValidArtifact(value: unknown): value is QaStatusArtifact {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { format?: unknown }).format === QA_ARTIFACT_FORMAT\n )\n}\n\nlet warnedBadFormat = false\nfunction warnBadFormatOnce(): void {\n if (warnedBadFormat) return\n warnedBadFormat = true\n if (typeof console !== 'undefined') {\n console.warn('[mqa] QA Meter artifact has an unsupported `format` — refusing to mount.')\n }\n}\n\nconst NOOP_HANDLE: QaMeterHandle = {\n open() {},\n close() {},\n dispose() {},\n}\n\n/** Every path_pattern an artifact knows — `pages[].page?.path_pattern` ∪\n * `sitemap[].path_pattern`. Feeds the default routerless resolver. */\nfunction patternsFromArtifact(artifact: QaStatusArtifact): string[] {\n const set = new Set<string>()\n for (const page of artifact.pages) {\n if (page.page) set.add(page.page.path_pattern)\n }\n for (const node of artifact.sitemap) {\n set.add(node.path_pattern)\n }\n return [...set]\n}\n\ninterface Resolved {\n state: 'known' | 'no-tests' | 'unknown-page'\n color: QaColor\n pattern: string | null\n page: QaPageStatus | null\n}\n\nconst UNKNOWN: Resolved = { state: 'unknown-page', color: 'gray', pattern: null, page: null }\n\n/** State machine (task spec): a matched `pages[]` entry → known/its color;\n * a pattern present only in the sitemap → no-tests/gray; no match (or no\n * artifact yet) → unknown-page/gray. Never throws. */\nfunction resolveCurrent(artifact: QaStatusArtifact | null, pattern: string | null): Resolved {\n if (!artifact || !pattern) return UNKNOWN\n const page = artifact.pages.find((p) => p.page && p.page.path_pattern === pattern)\n if (page && page.page) {\n return { state: 'known', color: page.color, pattern, page }\n }\n const inSitemap = artifact.sitemap.some((n) => n.path_pattern === pattern)\n if (inSitemap) {\n return { state: 'no-tests', color: 'gray', pattern, page: null }\n }\n return { state: 'unknown-page', color: 'gray', pattern, page: null }\n}\n\nexport function createQaMeter(options: QaMeterOptions): QaMeterHandle {\n const { source } = options\n const strings = resolveStrings(options.locale ?? 'fr')\n\n // For an INLINE artifact we must validate synchronously at create time, so a\n // wrong-format object never produces a host (the test asserts this after a\n // single microtask).\n const inlineArtifact = typeof source === 'object' ? source : null\n if (inlineArtifact && !isValidArtifact(inlineArtifact)) {\n warnBadFormatOnce()\n return NOOP_HANDLE\n }\n\n // Loaded artifact cache + lazy loader state. Inline artifacts are ready now;\n // string/function sources load lazily on first open/hover, exactly once.\n let artifact: QaStatusArtifact | null = inlineArtifact\n let loadStarted = false\n let loadPromise: Promise<void> | null = null\n\n function load(): void {\n if (artifact || loadStarted) return\n loadStarted = true\n const got: Promise<unknown> =\n typeof source === 'function'\n ? source()\n : typeof source === 'string'\n ? fetch(source).then((r) => r.json())\n : Promise.resolve(source)\n loadPromise = got\n .then((value) => {\n if (isValidArtifact(value)) {\n artifact = value\n forceRender()\n } else {\n warnBadFormatOnce()\n }\n })\n .catch(() => {\n // Never throw to the host: stay gray.\n })\n }\n\n // ── Host + shadow root + styles (mirrors widget/mount.tsx) ────────────────\n const host = document.createElement('div')\n host.setAttribute('data-mqa-host', '')\n host.setAttribute('data-mqa-size', options.size ?? 'md')\n // Offset knobs as CSS custom properties: they inherit through the shadow\n // boundary down to `.qa-pastille`, which reads them with a 1.5rem fallback.\n // Only set each when provided so the stylesheet default applies otherwise.\n if (options.position?.right !== undefined) {\n host.style.setProperty('--mqa-right', `${options.position.right}px`)\n }\n if (options.position?.bottom !== undefined) {\n host.style.setProperty('--mqa-bottom', `${options.position.bottom}px`)\n }\n document.body.appendChild(host)\n\n const shadow = host.attachShadow({ mode: 'open' })\n const style = document.createElement('style')\n style.textContent = QA_METER_STYLES\n shadow.appendChild(style)\n const mountPoint = document.createElement('div')\n shadow.appendChild(mountPoint)\n\n // Imperative re-render hook into the Preact tree. The container component\n // registers its dispatcher here so the loader/navigation callbacks can poke\n // it without React-ish prop threading.\n let forceRender: () => void = () => {}\n\n function getCurrentPattern(): string | null {\n if (options.getCurrentPage) return options.getCurrentPage()\n if (!artifact) return null\n return resolvePage(window.location.pathname, patternsFromArtifact(artifact))\n }\n\n function Container() {\n // A bump-counter is the simplest \"force re-render on external change\"\n // primitive (loader resolves, navigation, open/close).\n const [tick, setTick] = useState(0)\n void tick\n const bump = useCallback(() => setTick((n) => n + 1), [])\n forceRender = bump\n\n // open/close lives in module scope (so the handle can drive it) but we\n // read it via a closure variable bumped through `forceRender`.\n const resolved = resolveCurrent(artifact, getCurrentPattern())\n\n const meta = artifact\n ? {\n generated_at: artifact.generated_at,\n app_version: artifact.app_version,\n environment: artifact.environment,\n }\n : { generated_at: '', app_version: '', environment: '' }\n\n const onHover = useCallback(() => {\n // Lazy-load on first hover too (not just open), so the panel shows real\n // data before the user clicks.\n load()\n }, [])\n\n const onOpenModal = useCallback(() => {\n load()\n uiOpen = true\n bump()\n }, [])\n\n const closeModal = useCallback(() => {\n uiOpen = false\n bump()\n }, [])\n\n // Re-resolve current page on navigation. popstate covers back/forward;\n // the patched History API covers SPA pushState/replaceState.\n useEffect(() => {\n const onNav = (): void => bump()\n window.addEventListener('popstate', onNav)\n window.addEventListener(LOCATION_CHANGE_EVENT, onNav)\n return () => {\n window.removeEventListener('popstate', onNav)\n window.removeEventListener(LOCATION_CHANGE_EVENT, onNav)\n }\n }, [])\n\n return h(\n 'div',\n {\n class: 'qa-pastille-wrap',\n // Re-entering the wrap (the dot OR the panel above it — both live\n // inside) cancels a pending close; leaving schedules a debounced close.\n onMouseEnter: cancelHoverClose,\n onMouseLeave: scheduleHoverClose,\n },\n uiHover\n ? h(\n 'div',\n { class: 'qa-panel-anchor' },\n h(TestPanel, { page: resolved.page, state: resolved.state, meta, strings }),\n )\n : null,\n h(Pastille, {\n color: resolved.color,\n state: resolved.state,\n strings,\n onHover: () => {\n cancelHoverClose()\n uiHover = true\n onHover()\n bump()\n },\n onOpenModal,\n }),\n uiOpen && artifact\n ? h(SitemapModal, {\n artifact,\n currentPattern: resolved.pattern,\n strings,\n onClose: closeModal,\n })\n : null,\n )\n }\n\n // UI-local flags kept in closure scope so the imperative handle (open/close)\n // can flip them and `forceRender()` reflects the change. The container reads\n // them on each render.\n let uiOpen = false\n let uiHover = false\n\n // Closing the hover panel is debounced (rather than firing on the raw\n // mouseleave) so sweeping the cursor across the small gap between the dot and\n // the panel — or a jittery mouse at the dot's edge — doesn't unmount it\n // mid-sweep. Any mouseenter back into the wrap cancels the pending close.\n let hoverCloseTimer: ReturnType<typeof setTimeout> | null = null\n const HOVER_CLOSE_MS = 140\n function cancelHoverClose(): void {\n if (hoverCloseTimer !== null) {\n clearTimeout(hoverCloseTimer)\n hoverCloseTimer = null\n }\n }\n function scheduleHoverClose(): void {\n cancelHoverClose()\n hoverCloseTimer = setTimeout(() => {\n hoverCloseTimer = null\n if (disposed) return\n uiHover = false\n forceRender()\n }, HOVER_CLOSE_MS)\n }\n\n function mountTree(): void {\n render(h(Container, {}), mountPoint)\n }\n\n // Initial mount.\n mountTree()\n\n patchHistory()\n\n let disposed = false\n\n return {\n open() {\n if (disposed) return\n load()\n uiOpen = true\n forceRender()\n },\n close() {\n if (disposed) return\n uiOpen = false\n forceRender()\n },\n dispose() {\n if (disposed) return\n disposed = true\n cancelHoverClose()\n render(null, mountPoint)\n host.remove()\n unpatchHistory()\n void loadPromise\n },\n }\n}\n","import type { QaStatusArtifact } from './types'\nimport { createQaMeter as createQaMeterImpl } from './mount'\n\nexport interface QaMeterOptions {\n /** Where the QA status artifact comes from. An inline object renders\n * immediately; a URL is `fetch`ed lazily on first open/hover; a function is\n * invoked lazily likewise. */\n source: string | QaStatusArtifact | (() => Promise<QaStatusArtifact>)\n /** Router-aware override. Defaults to a routerless resolver over the\n * artifact's path_patterns + `window.location.pathname`. */\n getCurrentPage?: () => string | null\n locale?: 'fr' | 'en'\n /** Pastille size. 'md' (48px, default) for standalone; the feedback-widget\n * integration passes 'sm' (24px) so the QA FAB reads as secondary. */\n size?: 'sm' | 'md'\n position?: { right?: number; bottom?: number }\n}\n\nexport interface QaMeterHandle {\n open(): void\n close(): void\n dispose(): void\n}\n\nexport function createQaMeter(options: QaMeterOptions): QaMeterHandle {\n return createQaMeterImpl(options)\n}\n\nexport type {\n QaStatusArtifact,\n QaColor,\n QaScenario,\n QaPageStatus,\n QaSitemapNode,\n} from './types'\n","/**\n * The widget bundle the loader fetches at runtime.\n *\n * Built as an IIFE with `globalName: MhosaicFeedback`, so once the bundle\n * is parsed by the browser, `window.MhosaicFeedback = { createFeedback }`\n * is set. The loader (`src/loader/`) injects this script with SRI and\n * picks up the global.\n *\n * Distinct from `src/embed.ts` (which is the legacy CDN script-tag entry\n * that auto-initializes from `data-key` attributes). This entry deliberately\n * does NOT auto-init — it just exposes the API for the loader to call.\n *\n * Self-arming error tracking: this bundle wraps the instance with\n * `withErrorTracking` BY DEFAULT. The bundle is the only part of a\n * loader-path install that auto-updates (the host's pinned npm shim never\n * changes), so arming here is what lets already-deployed clients start\n * filing synthetic frontend reports on their next page load — no host\n * redeploy. The loader forwards the per-project `error_tracking` manifest\n * flag as `config.errorTracking`; pass `false` to opt out (default on, the\n * platform's chosen posture). `withErrorTracking` is idempotent, so a host\n * that ALSO wraps via <FeedbackProvider> won't double-register.\n */\n\nimport { createFeedback as baseCreateFeedback, type InternalConfig } from './core'\nimport { withErrorTracking, type ErrorTrackingOptions } from './modules/error-tracking'\n\nexport interface WidgetConfig extends InternalConfig {\n /** Off-switch for the bundle's default error capture. Default on. */\n errorTracking?: boolean | ErrorTrackingOptions\n}\n\nexport function createFeedback(config: WidgetConfig) {\n const { errorTracking, ...base } = config\n const fb = baseCreateFeedback(base)\n if (errorTracking !== false) {\n withErrorTracking(fb, typeof errorTracking === 'object' ? errorTracking : undefined)\n }\n return fb\n}\n\nexport type {\n FeedbackApi,\n FeedbackConfig,\n FeedbackEnv,\n FeedbackSeverity,\n FeedbackType,\n ReportPayload,\n SubmittedReport,\n UserIdentity,\n} from './types'\n","import type {\n BoardFilters,\n ReportPayload,\n SubmittedReport,\n WidgetBoardKpis,\n WidgetBoardRow,\n WidgetChangelogRow,\n WidgetCommentRow,\n WidgetReportDetail,\n WidgetReportRow,\n} from '../types'\n\n/** Page envelope DRF returns for paginated list endpoints. */\nexport interface BoardListPage {\n count: number\n next: string | null\n previous: string | null\n results: WidgetBoardRow[]\n}\n\nexport interface ApiClientOptions {\n apiKey: string\n endpoint: string\n fetch?: typeof fetch\n beforeSend?: (payload: ReportPayload) => ReportPayload | false | Promise<ReportPayload | false>\n /**\n * v0.13 — signed-identity callback. Returns the current\n * `(userHash, exp, email)` triple if the host has called\n * `identify({userHash, exp, ...})`. The API client reads this fresh\n * on every request so a delayed `identify()` lights up signed\n * headers on subsequent calls. Return `null` for the legacy\n * unsigned path (the default until the project issues a\n * `WidgetSigningSecret`).\n */\n getSignedIdentity?: () => { userHash: string; exp: number; email: string } | null\n}\n\nexport interface ApiClient {\n submitReport(payload: ReportPayload): Promise<SubmittedReport>\n /** POST /v1/widget/visibility/ — whether this end-user may see the widget\n * (Phase 4 allowlist). `email`, when the host identified one, lets an\n * email-based allowlist match (the backend matches external_id OR email).\n * Throws on error so the caller can fail closed. */\n checkVisibility(externalId: string, email?: string): Promise<boolean>\n /** GET /v1/reports/widget/mine/ — caller's own reports on this project. */\n listMine(externalId: string): Promise<WidgetReportRow[]>\n /** GET /v1/reports/widget/changelog/ — caller's resolved reports for the\n * \"This week\" tab; client groups by ISO week of `resolved_at`. */\n listChangelog(externalId: string): Promise<WidgetChangelogRow[]>\n /** GET /v1/reports/widget/<id>/ — single report + thread. */\n getReport(reportId: string, externalId: string): Promise<WidgetReportDetail>\n /** Resolve a report by its per-project #seq — powers clickable \"#NN\"\n * references and jump-by-number (#85). */\n getReportBySeq(seq: number, externalId: string): Promise<WidgetReportDetail>\n /** POST /v1/reports/widget/<id>/comments/ — submitter follow-up.\n * When `attachments` are supplied the request switches to multipart so\n * images ride along (#91); a text-only comment stays a JSON POST. */\n addComment(\n reportId: string,\n externalId: string,\n body: string,\n clientNonce?: string,\n attachments?: readonly Blob[],\n ): Promise<WidgetCommentRow>\n /** PATCH /v1/reports/widget/<id>/ — the author edits their report's\n * description after submitting (F4). Author-only server-side. */\n editDescription(\n reportId: string,\n externalId: string,\n description: string,\n ): Promise<WidgetReportDetail>\n /** PATCH /v1/reports/widget/<id>/ — close-as-resolved by the submitter. */\n closeAsResolved(\n reportId: string,\n externalId: string,\n ): Promise<WidgetReportDetail>\n /** PATCH /v1/reports/widget/<id>/ — reopen (→ in_progress) when the\n * submitter says the shipped fix didn't actually resolve the issue. */\n reopenUnresolved(\n reportId: string,\n externalId: string,\n ): Promise<WidgetReportDetail>\n /** GET /v1/reports/widget/board/ — paginated, filtered list backing the\n * v0.12 Board tab. Scope follows the project's\n * `share_reports_with_widget` flag (submitter-only when off, project-\n * wide when on); `filters.mine = true` narrows even when on. */\n listBoard(externalId: string, filters?: BoardFilters): Promise<BoardListPage>\n /** GET /v1/reports/widget/board/kpis/ — total + per-status counts +\n * resolution rate. Cheap; safe to poll alongside the list. */\n fetchBoardKpis(externalId: string, filters?: BoardFilters): Promise<WidgetBoardKpis>\n}\n\nconst SCALAR_FIELDS: Array<keyof ReportPayload> = [\n 'description', 'feedback_type', 'severity', 'env', 'page_url', 'user_agent', 'capture_method',\n]\n\nexport function assertSafeEndpoint(endpoint: string): void {\n let parsed: URL\n try {\n parsed = new URL(endpoint)\n } catch {\n throw new Error(\n `[mhosaic-feedback] \\`endpoint\\` is not a valid URL: ${endpoint}`,\n )\n }\n if (parsed.protocol === 'https:') return\n if (\n parsed.protocol === 'http:' &&\n (parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1' || parsed.hostname === '[::1]')\n ) {\n return\n }\n throw new Error(\n `[mhosaic-feedback] \\`endpoint\\` must use https:// (got ${parsed.protocol}//${parsed.hostname}). ` +\n 'http:// is only allowed for localhost in dev.',\n )\n}\n\n/**\n * A widget reader endpoint answered 429. Carried as a typed error so the UI\n * can show an actionable \"too many requests — retry in Xs\" message + a retry\n * button, instead of a generic failure or an infinite \"Loading…\" (#80/#81).\n */\nexport class RateLimitError extends Error {\n readonly retryAfterSeconds: number\n constructor(retryAfterSeconds: number) {\n super(`rate limited; retry after ${retryAfterSeconds}s`)\n this.name = 'RateLimitError'\n this.retryAfterSeconds = retryAfterSeconds\n }\n}\n\nfunction parseRetryAfter(response: Response): number {\n const raw = response.headers.get('Retry-After')\n const n = raw ? Number.parseInt(raw, 10) : NaN\n return Number.isFinite(n) && n >= 0 ? n : 0\n}\n\n// Turn a reader response into a throw on any non-ok status: 429 → typed\n// RateLimitError; everything else → a labelled Error. Callers must handle\n// 404 (empty-thread) BEFORE calling this.\nasync function ensureReadable(response: Response, label: string): Promise<void> {\n if (response.status === 429) throw new RateLimitError(parseRetryAfter(response))\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n throw new Error(`${label} failed: ${response.status} ${text}`)\n }\n}\n\n// A malformed-but-200 body used to be handed straight to the list views,\n// which set their rows to null/undefined and hung on \"Loading…\" forever.\n// Validating the shape here means a 200 always resolves the loading state —\n// to real data, an empty list, or (on a bad shape) an actionable error (#80).\nasync function readJsonArray<T>(response: Response, label: string): Promise<T[]> {\n await ensureReadable(response, label)\n const data = await response.json().catch(() => undefined)\n if (!Array.isArray(data)) {\n throw new Error(`${label}: unexpected response shape (expected an array)`)\n }\n return data as T[]\n}\n\nasync function readJsonObject<T>(response: Response, label: string): Promise<T> {\n await ensureReadable(response, label)\n const data = await response.json().catch(() => undefined)\n if (data === null || typeof data !== 'object' || Array.isArray(data)) {\n throw new Error(`${label}: unexpected response shape (expected an object)`)\n }\n return data as T\n}\n\nexport function createApiClient(options: ApiClientOptions): ApiClient {\n // No silent fallback to a hardcoded host — a misconfigured deploy used\n // to leak reports to https://core.mhosaic.com (which doesn't even\n // resolve). Failing fast at construction makes the misconfig obvious.\n const endpoint = (options.endpoint ?? '').replace(/\\/+$/, '')\n if (!endpoint) {\n throw new Error(\n '[mhosaic-feedback] `endpoint` is required (e.g. \"https://feedback.example.com\").',\n )\n }\n // SECURITY: endpoint is host-controlled config. Without a scheme guard a\n // misconfigured host (or a host XSS that injects a `<script\n // data-endpoint=\"http://attacker\">` embed tag) can steer the widget at\n // any URL and exfiltrate every captured report — including the\n // pk_proj_ key in the Authorization header — to attacker-controlled\n // infra. Only allow https:; permit http:// for localhost/127.0.0.1 so\n // dev sandboxes keep working.\n assertSafeEndpoint(endpoint)\n const fetcher = options.fetch ?? globalThis.fetch\n\n async function submitReport(input: ReportPayload): Promise<SubmittedReport> {\n let payload: ReportPayload | false = input\n if (options.beforeSend) payload = await options.beforeSend(input)\n if (payload === false) throw new Error('Submission cancelled by beforeSend')\n\n const form = new FormData()\n for (const field of SCALAR_FIELDS) {\n form.append(field, String(payload[field]))\n }\n form.append('technical_context', JSON.stringify(payload.technical_context))\n // Repeated `screenshot` parts — the backend reads FILES.getlist. The\n // first part keeps the legacy filename so single-shot requests stay\n // byte-identical to pre-multi widget builds.\n const screenshots = payload.screenshots?.length\n ? payload.screenshots\n : payload.screenshot\n ? [payload.screenshot]\n : []\n screenshots.forEach((blob, i) => {\n form.append('screenshot', blob, i === 0 ? 'screenshot.png' : `screenshot-${i + 1}.png`)\n })\n // Only emit `synthetic` when truthy — a `false` value would still trigger\n // the BooleanField parser on the backend, which is fine, but omitting it\n // keeps the request shape byte-identical for existing curated submissions.\n if (payload.synthetic) form.append('synthetic', 'true')\n // v0.7: nest the identity payload so DRF's WidgetUserIdentitySerializer\n // sees a real object. FormData can't carry nested objects directly —\n // use a JSON-encoded string under the `user` key; DRF parses it via\n // the JSONField semantics on the WidgetUser fields.\n if (payload.user?.id) {\n form.append('user', JSON.stringify(payload.user))\n }\n // v0.7.3: widget_version is build-time-stamped by tsup. Lets the\n // backend show \"currently running v0.7.2\" per project on the\n // operator Companies page (per-customer version observability).\n if (payload.widget_version) {\n form.append('widget_version', payload.widget_version)\n }\n if (payload.page_path) {\n form.append('page_path', payload.page_path)\n }\n\n // The backend honors `synthetic=true` only when this header is set,\n // so a hand-crafted curl with the public key can't smuggle a curated\n // report into the auto-error bucket (where the default operator view\n // hides it). The bundled debugger always sets payload.synthetic; the\n // user-facing widget never does.\n const headers: Record<string, string> = {\n Authorization: `Bearer ${options.apiKey}`,\n }\n if (payload.synthetic) {\n headers['X-Mhosaic-Capture-Source'] = 'error-tracking'\n }\n // Signed-identity headers on the SUBMIT path too: when the host has\n // wired up identify({userHash, exp, email}) and the backend project\n // has a WidgetSigningSecret, the request must carry the HMAC. We\n // also re-stamp `X-Mhosaic-User` so the verifier sees the\n // signed-over external_id directly (the body `user` block is still\n // sent — the backend prefers the header pair when signing is in\n // effect; see views.py:CreateFeedbackReportView.post).\n const signed = options.getSignedIdentity?.()\n if (signed && signed.userHash && signed.exp && payload.user?.id) {\n headers['X-Mhosaic-User'] = String(payload.user.id)\n headers['X-Mhosaic-User-Hmac'] = signed.userHash\n headers['X-Mhosaic-User-Exp'] = String(signed.exp)\n if (signed.email) headers['X-Mhosaic-User-Email'] = signed.email\n }\n const response = await fetcher(`${endpoint}/api/feedback/v1/reports/`, {\n method: 'POST',\n headers,\n body: form,\n })\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n throw new Error(`Feedback submit failed: ${response.status} ${text}`)\n }\n return response.json() as Promise<SubmittedReport>\n }\n\n function widgetHeaders(externalId: string): Record<string, string> {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${options.apiKey}`,\n 'X-Mhosaic-User': externalId,\n }\n // Signed-identity Phase 1 (v0.13): forward the HMAC envelope when the\n // host has supplied one via `identify({userHash, exp, email})`. The\n // backend gates on the project's `WidgetSigningSecret` row — projects\n // that haven't issued a secret continue to accept the bare header.\n const signed = options.getSignedIdentity?.()\n if (signed && signed.userHash && signed.exp) {\n headers['X-Mhosaic-User-Hmac'] = signed.userHash\n headers['X-Mhosaic-User-Exp'] = String(signed.exp)\n if (signed.email) headers['X-Mhosaic-User-Email'] = signed.email\n }\n return headers\n }\n\n async function listMine(externalId: string): Promise<WidgetReportRow[]> {\n const response = await fetcher(`${endpoint}/api/feedback/v1/reports/widget/mine/`, {\n method: 'GET',\n headers: widgetHeaders(externalId),\n })\n if (response.status === 404) return [] // submitter has no thread yet\n return readJsonArray<WidgetReportRow>(response, 'listMine')\n }\n\n async function listChangelog(externalId: string): Promise<WidgetChangelogRow[]> {\n const response = await fetcher(\n `${endpoint}/api/feedback/v1/reports/widget/changelog/`,\n { method: 'GET', headers: widgetHeaders(externalId) },\n )\n if (response.status === 404) return []\n return readJsonArray<WidgetChangelogRow>(response, 'listChangelog')\n }\n\n async function getReport(\n reportId: string,\n externalId: string,\n ): Promise<WidgetReportDetail> {\n const response = await fetcher(\n `${endpoint}/api/feedback/v1/reports/widget/${reportId}/`,\n { method: 'GET', headers: widgetHeaders(externalId) },\n )\n return readJsonObject<WidgetReportDetail>(response, 'getReport')\n }\n\n async function getReportBySeq(\n seq: number,\n externalId: string,\n ): Promise<WidgetReportDetail> {\n const response = await fetcher(\n `${endpoint}/api/feedback/v1/reports/widget/by-seq/${seq}/`,\n { method: 'GET', headers: widgetHeaders(externalId) },\n )\n return readJsonObject<WidgetReportDetail>(response, 'getReportBySeq')\n }\n\n async function addComment(\n reportId: string,\n externalId: string,\n body: string,\n clientNonce?: string,\n attachments?: readonly Blob[],\n ): Promise<WidgetCommentRow> {\n // Multipart only when images are attached — a plain follow-up stays a\n // JSON POST so its request shape is byte-identical to pre-#91 builds.\n // With FormData we must NOT set Content-Type ourselves: the browser\n // appends the multipart boundary, and a hardcoded value would lose it.\n let init: RequestInit\n if (attachments && attachments.length > 0) {\n const form = new FormData()\n form.append('body', body)\n if (clientNonce !== undefined) form.append('client_nonce', clientNonce)\n attachments.forEach((blob, i) =>\n form.append('attachment', blob, `attachment-${i + 1}.png`),\n )\n init = { method: 'POST', headers: widgetHeaders(externalId), body: form }\n } else {\n init = {\n method: 'POST',\n headers: {\n ...widgetHeaders(externalId),\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n body,\n ...(clientNonce !== undefined && { client_nonce: clientNonce }),\n }),\n }\n }\n const response = await fetcher(\n `${endpoint}/api/feedback/v1/reports/widget/${reportId}/comments/`,\n init,\n )\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n throw new Error(`addComment failed: ${response.status} ${text}`)\n }\n return response.json() as Promise<WidgetCommentRow>\n }\n\n async function editDescription(\n reportId: string,\n externalId: string,\n description: string,\n ): Promise<WidgetReportDetail> {\n const response = await fetcher(\n `${endpoint}/api/feedback/v1/reports/widget/${reportId}/`,\n {\n method: 'PATCH',\n headers: {\n ...widgetHeaders(externalId),\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ description }),\n },\n )\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n throw new Error(`editDescription failed: ${response.status} ${text}`)\n }\n return response.json() as Promise<WidgetReportDetail>\n }\n\n async function closeAsResolved(\n reportId: string,\n externalId: string,\n ): Promise<WidgetReportDetail> {\n const response = await fetcher(\n `${endpoint}/api/feedback/v1/reports/widget/${reportId}/`,\n {\n method: 'PATCH',\n headers: {\n ...widgetHeaders(externalId),\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ status: 'closed' }),\n },\n )\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n throw new Error(`closeAsResolved failed: ${response.status} ${text}`)\n }\n return response.json() as Promise<WidgetReportDetail>\n }\n\n async function reopenUnresolved(\n reportId: string,\n externalId: string,\n ): Promise<WidgetReportDetail> {\n const response = await fetcher(\n `${endpoint}/api/feedback/v1/reports/widget/${reportId}/`,\n {\n method: 'PATCH',\n headers: {\n ...widgetHeaders(externalId),\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ status: 'in_progress' }),\n },\n )\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n throw new Error(`reopenUnresolved failed: ${response.status} ${text}`)\n }\n return response.json() as Promise<WidgetReportDetail>\n }\n\n function boardQueryString(filters?: BoardFilters): string {\n if (!filters) return ''\n const params = new URLSearchParams()\n // Multi-value fields use append() so repeated keys stack the way DRF's\n // `request.query_params.getlist()` reads them on the server.\n filters.status?.forEach((s) => params.append('status', s))\n filters.type?.forEach((t) => params.append('type', t))\n filters.severity?.forEach((s) => params.append('severity', s))\n if (filters.q) params.set('q', filters.q)\n if (filters.mine) params.set('mine', '1')\n if (filters.ordering) params.set('ordering', filters.ordering)\n if (filters.page && filters.page > 1) params.set('page', String(filters.page))\n if (filters.pagePath) params.set('page_path', filters.pagePath)\n const qs = params.toString()\n return qs ? `?${qs}` : ''\n }\n\n async function listBoard(\n externalId: string,\n filters?: BoardFilters,\n ): Promise<BoardListPage> {\n const response = await fetcher(\n `${endpoint}/api/feedback/v1/reports/widget/board/${boardQueryString(filters)}`,\n { method: 'GET', headers: widgetHeaders(externalId) },\n )\n if (response.status === 404) {\n // Caller has no WidgetUser row yet — render an empty Board rather\n // than a hard error so the first-time experience is a clean slate.\n return { count: 0, next: null, previous: null, results: [] }\n }\n const page = await readJsonObject<BoardListPage>(response, 'listBoard')\n if (!Array.isArray(page.results)) {\n throw new Error('listBoard: unexpected response shape (missing results[])')\n }\n return page\n }\n\n async function fetchBoardKpis(\n externalId: string,\n filters?: BoardFilters,\n ): Promise<WidgetBoardKpis> {\n const response = await fetcher(\n `${endpoint}/api/feedback/v1/reports/widget/board/kpis/${boardQueryString(filters)}`,\n { method: 'GET', headers: widgetHeaders(externalId) },\n )\n if (response.status === 404) {\n return { total: 0, by_status: {}, resolution_rate: 0, scope: 'mine' }\n }\n return readJsonObject<WidgetBoardKpis>(response, 'fetchBoardKpis')\n }\n\n async function checkVisibility(\n externalId: string,\n email?: string,\n ): Promise<boolean> {\n const response = await fetcher(\n `${endpoint}/api/feedback/v1/widget/visibility/`,\n {\n method: 'POST',\n headers: {\n ...widgetHeaders(externalId),\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n external_id: externalId,\n ...(email ? { email } : {}),\n }),\n },\n )\n if (!response.ok) {\n throw new Error(`checkVisibility failed: ${response.status}`)\n }\n const data = (await response.json()) as { show?: boolean }\n return Boolean(data.show)\n }\n\n return {\n submitReport,\n checkVisibility,\n listMine,\n listChangelog,\n getReport,\n getReportBySeq,\n addComment,\n editDescription,\n closeAsResolved,\n reopenUnresolved,\n listBoard,\n fetchBoardKpis,\n }\n}\n","/**\n * URL sanitizer for captured network request URLs.\n *\n * Two layers of defense:\n *\n * 1) Param NAME match — anything whose name suggests a credential is\n * redacted unconditionally (token, key, password, secret, auth, etc.).\n *\n * 2) Param VALUE shape match — even when the name is innocent (\"id\",\n * \"redirect_uri\", \"context\"), if the VALUE looks like a JWT, a\n * Bearer token, an mhosaic-feedback API key, or a long opaque hex/b64\n * string we redact it. This catches the realistic case where a backend\n * routes credentials through generic-named params or where a careless\n * developer puts a token in a URL fragment for \"convenience.\"\n *\n * The path portion is preserved as-is — its purpose is operator-side\n * \"which page was this report filed from\", and stripping it would\n * destroy that signal. Hosts that route secrets through path segments\n * (uncommon) should mark those routes for the widget to skip via the\n * sanitizeUrl hook in createFeedback().\n */\n\nconst SENSITIVE_NAME = /token|key|password|secret|auth|session|sig|credential|bearer|cookie/i\n\nconst SENSITIVE_VALUE_PATTERNS: RegExp[] = [\n // JWT: three base64url segments separated by dots, header begins with eyJ.\n /^eyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$/,\n // Mhosaic feedback keys (the project's own format).\n /^(?:sk|pk)_proj_[A-Za-z0-9_-]{16,}$/,\n // GitHub PATs (ghp/gho/ghu/ghs/ghr prefixes, 36+ chars).\n /^gh[pousr]_[A-Za-z0-9]{30,}$/,\n // Stripe live/test secret keys.\n /^(?:sk|pk|rk|whsec)_(?:live|test)_[A-Za-z0-9]{16,}$/,\n // AWS access key id.\n /^AKIA[0-9A-Z]{12,}$/,\n // Slack bot tokens.\n /^xox[abprso]-[A-Za-z0-9-]{12,}$/,\n // Generic high-entropy: hex 40+ chars (SHA-1, SHA-256) or long alnum 32+.\n /^[a-f0-9]{40,}$/i,\n // Long opaque base64-ish string (common for OAuth codes & session ids).\n /^[A-Za-z0-9_-]{40,}$/,\n]\n\nfunction looksLikeCredential(value: string): boolean {\n return SENSITIVE_VALUE_PATTERNS.some((re) => re.test(value))\n}\n\nexport function sanitizeUrl(url: string): string {\n try {\n // Accept absolute URLs or root-relative paths; reject everything else\n const isAbsolute = /^https?:\\/\\//i.test(url)\n const isRootRelative = url.startsWith('/')\n if (!isAbsolute && !isRootRelative) return url\n\n const base = typeof window !== 'undefined' ? window.location.origin : 'http://localhost'\n const u = new URL(url, base)\n const clean = new URLSearchParams()\n u.searchParams.forEach((value, name) => {\n if (SENSITIVE_NAME.test(name) || looksLikeCredential(value)) {\n clean.set(name, '[redacted]')\n } else {\n clean.set(name, value)\n }\n })\n u.search = clean.toString()\n // Hash fragments occasionally carry credentials (OAuth implicit flow):\n // never log them.\n u.hash = u.hash ? '#[redacted]' : ''\n return u.toString()\n } catch {\n return url\n }\n}\n","/**\n * Shared credential-shaped-string scrubber.\n *\n * Used by every capture module (console, error, performance, network) so a\n * single regression in any one path can't leak credentials a sibling path\n * already cleans. Patterns mirror the ones in the URL sanitizer's\n * value-shape detector — keep them in sync.\n */\n\nexport const SENSITIVE_TOKEN_PATTERNS: RegExp[] = [\n // Mhosaic feedback keys. Match ANY tail length — a backend error echo can\n // truncate the suffix (e.g. `sk_proj_***abc`) and the redaction must still\n // catch the prefix.\n /\\b(?:sk|pk)_proj_[A-Za-z0-9_*-]+/g,\n // JWT shape (header.payload.signature).\n /\\beyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\b/g,\n // \"Bearer <token>\" and \"Authorization: …\" headers.\n /\\bBearer\\s+[A-Za-z0-9._~+/=-]{16,}\\b/g,\n /\\bAuthorization\\s*[:=]\\s*[\"']?[A-Za-z0-9._~+/=-]{16,}[\"']?/gi,\n // GitHub PATs.\n /\\bgh[pousr]_[A-Za-z0-9]{30,}\\b/g,\n // Stripe live/test secret keys + webhook secrets.\n /\\b(?:sk|pk|rk|whsec)_(?:live|test)_[A-Za-z0-9]{16,}\\b/g,\n // AWS access key id.\n /\\bAKIA[0-9A-Z]{12,}\\b/g,\n // Slack tokens.\n /\\bxox[abprso]-[A-Za-z0-9-]{12,}\\b/g,\n // Google API keys (Maps, GCP, Firebase, etc.).\n /\\bAIza[0-9A-Za-z_-]{35}\\b/g,\n // OpenAI API keys (legacy, project-scoped, service-account).\n /\\bsk-(?:proj-|svcacct-)?[A-Za-z0-9_-]{20,}\\b/g,\n // Anthropic API keys.\n /\\bsk-ant-(?:api03-)?[A-Za-z0-9_-]{40,}\\b/g,\n // Twilio account / API SIDs (paired secret would also be redacted by the\n // Stripe-shape regex if it's prefixed sk_).\n /\\bAC[a-f0-9]{32}\\b/g,\n /\\bSK[a-f0-9]{32}\\b/g,\n]\n\nexport function scrubCredentials(text: string): string {\n let out = text\n for (const re of SENSITIVE_TOKEN_PATTERNS) {\n out = out.replace(re, '[redacted-token]')\n }\n return out\n}\n","import type { DeviceContext } from '../types'\nimport { scrubCredentials } from './scrub'\nimport { sanitizeUrl } from './urlSanitizer'\n\nexport function collectDevice(): DeviceContext {\n const nav = navigator as Navigator & { connection?: { effectiveType?: string }; deviceMemory?: number }\n const connection = nav.connection?.effectiveType\n const deviceMemory = nav.deviceMemory\n // Auth flows commonly hand off via query-string tokens (`?token=…`,\n // `?code=…`); when the user clicks through to the host page, both the\n // raw referrer AND the host's own pathname can carry those values.\n // Run both through the same sanitizer used for fetch/XHR; collapse the\n // result back to a path-only string so we don't accidentally upload the\n // operator-side host's origin as if it were the captured page's URL.\n const rawReferrer = document.referrer || undefined\n const referrer = rawReferrer !== undefined ? sanitizeUrl(rawReferrer) : undefined\n const sanitizedHere = sanitizeUrl(window.location.pathname + window.location.search)\n let pathname: string\n try {\n const parsed = new URL(sanitizedHere, window.location.origin)\n pathname = parsed.pathname + parsed.search\n } catch {\n pathname = window.location.pathname\n }\n return {\n viewport: { w: window.innerWidth, h: window.innerHeight, dpr: window.devicePixelRatio || 1 },\n screen: { w: window.screen.width, h: window.screen.height },\n platform: nav.platform,\n language: nav.language,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n timezoneOffset: new Date().getTimezoneOffset(),\n ...(connection !== undefined && { connection }),\n online: nav.onLine,\n ...(deviceMemory !== undefined && { deviceMemory }),\n hardwareConcurrency: nav.hardwareConcurrency,\n ...(referrer !== undefined && { referrer }),\n // Hosts commonly stuff credentials / PII into page titles\n // (\"Inbox (3) — bob@x.com\", \"Reset password — token=abc\"). Cap to a\n // sane length and run through the same scrubber the console/error\n // paths use.\n title: scrubCredentials((document.title || '')).slice(0, 200),\n pathname,\n }\n}\n","import type { RingBuffer } from './ringBuffer'\nimport type { ConsoleEntry } from '../types'\nimport { scrubCredentials } from './scrub'\n\ntype ConsoleLevel = 'log' | 'info' | 'warn' | 'error' | 'debug'\n\nfunction safeStringify(arg: unknown): string {\n if (arg == null) return String(arg)\n if (typeof arg === 'string') return arg\n if (typeof arg === 'number' || typeof arg === 'boolean') return String(arg)\n if (arg instanceof Error) return `${arg.name}: ${arg.message}`\n if (arg instanceof Element) return `<${arg.tagName.toLowerCase()}>`\n try {\n return JSON.stringify(arg, (_k, v) => (typeof v === 'bigint' ? v.toString() : v))\n } catch {\n try { return String(arg) } catch { return '[unserializable]' }\n }\n}\n\nexport function installConsolePatch(buffer: RingBuffer<ConsoleEntry>): () => void {\n const levels: ConsoleLevel[] = ['log', 'info', 'warn', 'error', 'debug']\n const originals: Partial<Record<ConsoleLevel, (...args: unknown[]) => void>> = {}\n for (const level of levels) {\n const original = console[level] as ((...args: unknown[]) => void) | undefined\n if (typeof original !== 'function') continue\n originals[level] = original\n console[level] = function patched(...args: unknown[]) {\n try {\n const rawMessage = args.map(safeStringify).join(' ')\n const message = scrubCredentials(rawMessage).slice(0, 2000)\n const entry: ConsoleEntry = { level, message, ts: Date.now() }\n if (level === 'error') {\n const stack = new Error().stack\n if (stack) entry.stack = stack.split('\\n').slice(2, 8).join('\\n')\n }\n buffer.push(entry)\n } catch { /* never break console */ }\n original.apply(console, args)\n }\n }\n return () => {\n for (const [level, fn] of Object.entries(originals)) {\n (console as unknown as Record<string, (...args: unknown[]) => void>)[level] = fn\n }\n }\n}\n","import type { RingBuffer } from './ringBuffer'\nimport type { NetworkEntry } from '../types'\nimport { scrubCredentials } from './scrub'\n\nexport function installFetchPatch(buffer: RingBuffer<NetworkEntry>, sanitize: (url: string) => string): () => void {\n if (typeof window === 'undefined' || typeof window.fetch !== 'function') return () => {}\n const original = window.fetch.bind(window)\n window.fetch = async function patched(input: RequestInfo | URL, init?: RequestInit) {\n const start = performance.now()\n const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url\n const method = (init?.method || (input instanceof Request ? input.method : 'GET')).toUpperCase()\n try {\n const response = await original(input, init)\n buffer.push({ url: sanitize(url), method, status: response.status, durationMs: Math.round(performance.now() - start), ts: Date.now() })\n return response\n } catch (err) {\n // Error messages from fetch (CORS rejection, abort, etc.) sometimes\n // echo the requested URL back including its full query string. Scrub\n // for the same patterns the console path does.\n const rawErr = err instanceof Error ? err.message : String(err)\n buffer.push({\n url: sanitize(url), method, status: 0,\n durationMs: Math.round(performance.now() - start),\n ts: Date.now(),\n error: scrubCredentials(rawErr),\n })\n throw err\n }\n }\n return () => { window.fetch = original }\n}\n\nexport function installXhrPatch(buffer: RingBuffer<NetworkEntry>, sanitize: (url: string) => string): () => void {\n if (typeof window === 'undefined' || typeof window.XMLHttpRequest !== 'function') return () => {}\n const Original = window.XMLHttpRequest\n const originalOpen = Original.prototype.open\n const originalSend = Original.prototype.send\n\n Original.prototype.open = function patchedOpen(this: XMLHttpRequest & { __mfb?: { method: string; url: string; start: number } }, method: string, url: string | URL) {\n this.__mfb = { method: method.toUpperCase(), url: typeof url === 'string' ? url : url.toString(), start: performance.now() }\n return originalOpen.apply(this, arguments as unknown as Parameters<typeof originalOpen>)\n }\n\n Original.prototype.send = function patchedSend(this: XMLHttpRequest & { __mfb?: { method: string; url: string; start: number } }, body?: Document | XMLHttpRequestBodyInit | null) {\n this.addEventListener('loadend', () => {\n try {\n const ctx = this.__mfb\n if (!ctx) return\n buffer.push({\n url: sanitize(ctx.url),\n method: ctx.method,\n status: this.status,\n durationMs: Math.round(performance.now() - ctx.start),\n ts: Date.now(),\n })\n } catch { /* noop */ }\n })\n return originalSend.call(this, body ?? null)\n }\n\n return () => {\n Original.prototype.open = originalOpen\n Original.prototype.send = originalSend\n }\n}\n","import type { RingBuffer } from './ringBuffer'\nimport type { ErrorEntry } from '../types'\nimport { scrubCredentials } from './scrub'\n\n// Errors that bubble up to `window.error` or `unhandledrejection` often\n// carry credentials in plain text — e.g. an SDK throws `new Error(\"Bearer\n// eyJ... rejected (401)\")`, or a promise rejects with `{apiKey: \"sk_live_…\"}`.\n// Both ship to the central backend unless we scrub here.\n\nexport function installErrorHandlers(buffer: RingBuffer<ErrorEntry>): () => void {\n if (typeof window === 'undefined') return () => {}\n const onError = (e: ErrorEvent) => {\n const rawStack = e.error instanceof Error ? e.error.stack : undefined\n const stack = rawStack !== undefined ? scrubCredentials(rawStack) : undefined\n buffer.push({\n message: scrubCredentials(e.message || 'Unknown error'),\n ...(stack !== undefined && { stack }),\n ts: Date.now(),\n source: 'window.error',\n })\n }\n const onRejection = (e: PromiseRejectionEvent) => {\n const reason = e.reason\n const rawMessage =\n reason instanceof Error ? reason.message :\n typeof reason === 'string' ? reason :\n (() => { try { return JSON.stringify(reason) } catch { return String(reason) } })()\n const rawStack = reason instanceof Error ? reason.stack : undefined\n const stack = rawStack !== undefined ? scrubCredentials(rawStack) : undefined\n buffer.push({\n message: scrubCredentials(rawMessage),\n ...(stack !== undefined && { stack }),\n ts: Date.now(),\n source: 'unhandledrejection',\n })\n }\n window.addEventListener('error', onError)\n window.addEventListener('unhandledrejection', onRejection)\n return () => {\n window.removeEventListener('error', onError)\n window.removeEventListener('unhandledrejection', onRejection)\n }\n}\n","import { sanitizeUrl } from './urlSanitizer'\n\nexport interface PerformanceSnapshot {\n navigation?: { type: string; duration: number }\n longTasks: { duration: number; startTime: number }[]\n slowResources: { name: string; duration: number; initiatorType: string }[]\n}\n\nexport function createPerformanceCollector(slowResourceMs = 1000) {\n const longTasks: PerformanceSnapshot['longTasks'] = []\n const slowResources: PerformanceSnapshot['slowResources'] = []\n let observer: PerformanceObserver | null = null\n\n if (typeof PerformanceObserver !== 'undefined') {\n try {\n observer = new PerformanceObserver((list) => {\n for (const entry of list.getEntries()) {\n if (entry.entryType === 'longtask') {\n longTasks.push({ duration: entry.duration, startTime: entry.startTime })\n while (longTasks.length > 20) longTasks.shift()\n } else if (entry.entryType === 'resource') {\n const e = entry as PerformanceResourceTiming\n if (e.duration > slowResourceMs) {\n // sanitizeUrl strips credential-shaped query params + the hash\n // — the resource URL is otherwise raw input from any third-party\n // request the host page makes (analytics pixels, OAuth\n // callbacks with tokens in the query, etc.).\n slowResources.push({ name: sanitizeUrl(e.name), duration: e.duration, initiatorType: e.initiatorType })\n while (slowResources.length > 20) slowResources.shift()\n }\n }\n }\n })\n observer.observe({ entryTypes: ['longtask', 'resource'] })\n } catch { /* unsupported */ }\n }\n\n return {\n snapshot(): PerformanceSnapshot {\n const nav = typeof performance !== 'undefined' ? performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming | undefined : undefined\n const navigation = nav ? { type: nav.type, duration: nav.duration } : undefined\n return {\n ...(navigation !== undefined && { navigation }),\n longTasks: longTasks.slice(),\n slowResources: slowResources.slice(),\n }\n },\n dispose() { observer?.disconnect() },\n }\n}\n","export class RingBuffer<T> {\n private items: T[] = []\n constructor(private readonly max: number) {}\n\n push(item: T): void {\n this.items.push(item)\n while (this.items.length > this.max) this.items.shift()\n }\n\n snapshot(): T[] {\n return this.items.slice()\n }\n\n clear(): void {\n this.items.length = 0\n }\n}\n","import { sanitizeUrl } from './urlSanitizer'\nimport { collectDevice } from './device'\nimport { installConsolePatch } from './console'\nimport { installFetchPatch, installXhrPatch } from './network'\nimport { installErrorHandlers } from './errors'\nimport { createPerformanceCollector } from './performance'\nimport { RingBuffer } from './ringBuffer'\nimport type { CapturedContext, ConsoleEntry, ErrorEntry, NetworkEntry } from '../types'\n\nexport interface CaptureOptions {\n sanitizeUrl?: (url: string) => string\n maxConsole?: number\n maxNetwork?: number\n maxErrors?: number\n}\n\nexport interface CaptureHandle {\n snapshot(): CapturedContext\n clear(): void\n dispose(): void\n}\n\nexport function installCapture(options: CaptureOptions = {}): CaptureHandle {\n const { maxConsole = 50, maxNetwork = 50, maxErrors = 20 } = options\n const sanitize = options.sanitizeUrl ?? sanitizeUrl\n\n const consoleBuf = new RingBuffer<ConsoleEntry>(maxConsole)\n const networkBuf = new RingBuffer<NetworkEntry>(maxNetwork)\n const errorBuf = new RingBuffer<ErrorEntry>(maxErrors)\n\n const uninstallConsole = installConsolePatch(consoleBuf)\n const uninstallFetch = installFetchPatch(networkBuf, sanitize)\n const uninstallXhr = installXhrPatch(networkBuf, sanitize)\n const uninstallErrors = installErrorHandlers(errorBuf)\n const perf = createPerformanceCollector()\n\n return {\n snapshot(): CapturedContext {\n return {\n consoleLogs: consoleBuf.snapshot(),\n networkRequests: networkBuf.snapshot(),\n errors: errorBuf.snapshot(),\n device: collectDevice(),\n capturedAt: Date.now(),\n }\n },\n clear() {\n consoleBuf.clear(); networkBuf.clear(); errorBuf.clear()\n },\n dispose() {\n uninstallConsole(); uninstallFetch(); uninstallXhr(); uninstallErrors()\n perf.dispose()\n },\n }\n}\n","export const DEFAULT_STRINGS = {\n 'fab.label': 'Send feedback',\n 'page.badge.aria': 'Feedback — {n} open on this page',\n 'page.badge.aria.one': 'Feedback — 1 open on this page',\n 'page.strip.text': '{n} open reports on this page',\n 'page.strip.text.one': '1 open report on this page',\n 'page.strip.view': 'View',\n 'page.peek.title': 'On this page',\n 'page.peek.viewAll': 'View all {n}',\n 'page.peek.viewAll.one': 'View 1 open report',\n 'form.title': 'Send feedback',\n 'form.description.label': 'What happened?',\n 'form.description.placeholder': 'Describe the issue or idea in one or two sentences.',\n 'form.type.label': 'Type',\n 'form.severity.label': 'Severity',\n 'form.submit': 'Send',\n 'form.cancel': 'Cancel',\n 'form.close': 'Close',\n 'form.submitting': 'Sending…',\n 'form.success': 'Thanks — your feedback was sent.',\n 'form.success.seq': 'Thanks — report #{seq} was sent. ✓',\n 'form.discard.title': 'Unsaved changes',\n 'form.discard.body': 'Discard your feedback?',\n 'form.discard.keep': 'Keep editing',\n 'form.discard.confirm': 'Discard',\n 'form.error': 'Could not send. Please try again.',\n 'form.description.required': 'Please describe the issue before sending.',\n 'form.screenshot.label': 'Screenshot',\n 'form.screenshot.cta_click': 'Click',\n 'form.screenshot.cta_rest': 'drop, or paste an image',\n 'form.screenshot.formats': 'PNG, JPEG or WebP — up to 10 MB',\n 'form.screenshot.remove': 'Remove screenshot',\n 'form.screenshot.annotate': 'Annotate',\n 'form.screenshot.error_type': 'Unsupported file type. Use PNG, JPEG or WebP.',\n 'form.screenshot.error_size': 'File too large (max {max} MB).',\n 'form.screenshot.error_count': 'Too many screenshots (max {max}).',\n 'form.context.label': 'Page',\n 'form.capture.notice':\n 'Console, network activity and errors are captured automatically to help us debug.',\n 'type.bug': 'Bug',\n 'type.feature': 'Feature request',\n 'type.question': 'Question',\n 'type.praise': 'Praise',\n 'type.typo': 'Typo',\n 'severity.blocker': 'Blocker',\n 'severity.high': 'High',\n 'severity.medium': 'Medium',\n 'severity.low': 'Low',\n 'annotator.title': 'Annotate screenshot',\n 'annotator.tool.rectangle': 'Rectangle',\n 'annotator.tool.arrow': 'Arrow',\n 'annotator.tool.freehand': 'Freehand',\n 'annotator.tool.text': 'Text',\n 'annotator.tool.highlight': 'Highlight',\n 'annotator.tool.blur': 'Blur (hide sensitive data)',\n 'annotator.text_prompt': 'Enter text:',\n 'annotator.color_picker': 'Custom color',\n 'annotator.undo': 'Undo',\n 'annotator.redo': 'Redo',\n 'annotator.clear': 'Clear all',\n 'annotator.count_suffix': 'annotations',\n 'annotator.loading': 'Loading…',\n 'annotator.apply': 'Apply',\n 'annotator.applying': 'Applying…',\n 'tab.send': 'Send',\n 'tab.mine': 'My reports',\n 'tab.changelog': 'This week',\n 'tab.board': 'Board',\n 'board.kpi.total': 'Total',\n 'board.kpi.new': 'New',\n 'board.kpi.in_progress': 'In progress',\n 'board.kpi.awaiting_validation': 'Awaiting validation',\n 'board.kpi.closed': 'Closed',\n 'board.kpi.rejected': 'Rejected',\n 'board.kpi.resolution_rate': 'Resolution rate',\n 'board.sort': 'Sort',\n 'board.sort.recent': 'Newest first',\n 'board.sort.oldest': 'Oldest first',\n 'board.sort.updated': 'Recently active',\n 'board.sort.severity': 'Severity',\n 'board.sort.status': 'Status',\n 'board.filter.status': 'Status',\n 'board.filter.type': 'Type',\n 'board.filter.severity': 'Severity',\n 'board.filter.search.placeholder': 'Search…',\n 'board.filter.mine': 'Mine only',\n 'board.filter.clear': 'Clear filters',\n 'board.list.empty.title': 'Nothing here yet',\n 'board.list.empty.description': 'When reports land, they’ll show up here.',\n 'board.list.loading': 'Loading…',\n 'board.list.error': 'Couldn’t load reports.',\n 'board.retry': 'Try again',\n 'board.list.you': 'you',\n 'board.list.count': '{n} of {total}',\n 'board.detail.empty': 'Pick a report on the left to see its full thread.',\n 'board.detail.by': 'By',\n 'board.detail.confirm_resolution': 'Confirm resolution',\n 'board.detail.status_history': 'History',\n 'board.scope.project': 'Project reports',\n 'board.scope.mine': 'Your reports',\n 'board.scope.thisPage': 'This page',\n 'board.scope.allPages': 'All pages',\n 'board.scope.onThisPage': 'on this page',\n 'board.list.count.page': '{n} of {total} · this page',\n 'board.list.empty.page.title': 'No feedback on this page yet.',\n 'board.list.empty.page.description':\n 'Be the first to report something here — or browse the whole project.',\n 'board.back': 'Back',\n 'changelog.empty.title': 'Nothing resolved yet',\n 'changelog.empty.body': 'Once a report you sent is fixed it will appear here, grouped by week.',\n 'changelog.week_of': 'Week of {date}',\n 'changelog.resolved_one': '{count} resolved',\n 'changelog.resolved_many': '{count} resolved',\n 'mine.empty.title': 'No reports yet',\n 'mine.empty.body': 'Once you send feedback you can follow the thread here.',\n 'mine.refresh': 'Refresh',\n 'mine.loading': 'Loading…',\n 'mine.error': 'Could not load your reports.',\n 'mine.jump.placeholder': '#',\n 'mine.jump.go': 'Open',\n 'mine.jump.aria': 'Open a report by number',\n 'mine.jump.not_found': 'No report with that number.',\n 'error.rate_limited': 'Too many requests — retry in {seconds}s.',\n 'error.rate_limited_generic': 'Too many requests — try again in a moment.',\n 'mine.replies_one': '1 reply',\n 'mine.replies_many': '{count} replies',\n 'mine.filter.empty': 'No reports match this filter.',\n 'kpi.new': 'New',\n 'kpi.in_progress': 'In progress',\n 'kpi.awaiting_validation': 'Awaiting you',\n 'kpi.resolution_rate': 'Resolution rate',\n 'detail.back': 'Back',\n 'detail.thread': 'Conversation',\n 'detail.no_replies': 'No replies yet — we’ll let you know when an operator responds.',\n 'detail.compose_placeholder': 'Add a follow-up reply…',\n 'detail.compose_send': 'Reply',\n 'detail.compose_sending': 'Sending…',\n 'detail.attach_add': 'Add image',\n 'detail.attach_remove': 'Remove',\n 'detail.attachment_alt': 'Attached image',\n 'detail.copy_link': 'Copy link',\n 'detail.copied': 'Link copied',\n 'detail.edit': 'Edit',\n 'detail.edit_save': 'Save',\n 'detail.edit_cancel': 'Cancel',\n 'detail.edit_saving': 'Saving…',\n 'detail.edit_failed': 'Could not save your changes.',\n 'detail.close_cta': 'Mark as resolved',\n 'detail.close_busy': 'Marking…',\n 'detail.reopen_cta': 'Still not fixed',\n 'detail.reopen_busy': 'Reopening…',\n 'detail.teammate_notice':\n 'Viewing a teammate’s report — replies are private to the submitter.',\n 'detail.load_failed.title': 'Report not available',\n 'detail.load_failed.body':\n 'It may have been deleted, or you no longer have access to it.',\n 'detail.load_failed.cta': 'Back',\n 'detail.send_failed': 'Couldn’t send your reply. Try again.',\n 'detail.close_failed': 'Couldn’t mark as resolved. Try again.',\n 'detail.reopen_failed': 'Couldn’t reopen the report. Try again.',\n 'detail.history': 'Status history',\n 'detail.context.submitted_at': 'Submitted',\n 'detail.context.page': 'Page',\n 'detail.context.capture.manual': 'Manual capture',\n 'detail.context.capture.html2canvas': 'Auto capture',\n 'detail.context.capture.display_media': 'Screen share',\n 'detail.context.capture.none': 'No screenshot',\n 'detail.author.staff': 'Operator',\n 'detail.author.mcp': 'Mhosaic Team',\n 'detail.author.system': 'System',\n 'detail.tech.title': 'What we received',\n 'detail.tech.errors_one': 'error',\n 'detail.tech.errors_many': 'errors',\n 'detail.tech.device': 'Device',\n 'detail.tech.device.viewport': 'Viewport',\n 'detail.tech.device.platform': 'Platform',\n 'detail.tech.device.language': 'Language',\n 'detail.tech.device.timezone': 'Timezone',\n 'detail.tech.device.connection': 'Connection',\n 'detail.tech.errors': 'Runtime errors',\n 'detail.tech.console': 'Console (last 20)',\n 'detail.tech.network': 'Network (last 15)',\n 'status.new': 'New',\n 'status.in_progress': 'In progress',\n 'status.awaiting_validation': 'Awaiting your validation',\n 'status.closed': 'Closed',\n 'status.rejected': 'Rejected',\n 'status.duplicate': 'Duplicate',\n 'status.wontfix': 'Won’t fix',\n}\n\nexport type StringKey = keyof typeof DEFAULT_STRINGS\n\nconst FRENCH_STRINGS: Record<StringKey, string> = {\n 'fab.label': 'Envoyer un commentaire',\n 'page.badge.aria': 'Retours — {n} ouverts sur cette page',\n 'page.badge.aria.one': 'Retours — 1 ouvert sur cette page',\n 'page.strip.text': '{n} retours ouverts sur cette page',\n 'page.strip.text.one': '1 retour ouvert sur cette page',\n 'page.strip.view': 'Voir',\n 'page.peek.title': 'Sur cette page',\n 'page.peek.viewAll': 'Voir les {n}',\n 'page.peek.viewAll.one': 'Voir 1 retour ouvert',\n 'form.title': 'Envoyer un commentaire',\n 'form.description.label': 'Qu’est-il arrivé ?',\n 'form.description.placeholder': 'Décrivez le problème ou l’idée en une ou deux phrases.',\n 'form.type.label': 'Type',\n 'form.severity.label': 'Sévérité',\n 'form.submit': 'Envoyer',\n 'form.cancel': 'Annuler',\n 'form.close': 'Fermer',\n 'form.submitting': 'Envoi…',\n 'form.success': 'Merci — votre commentaire a été envoyé.',\n 'form.success.seq': 'Merci — rapport n°{seq} envoyé. ✓',\n 'form.discard.title': 'Modifications non enregistrées',\n 'form.discard.body': 'Abandonner votre commentaire ?',\n 'form.discard.keep': 'Continuer la saisie',\n 'form.discard.confirm': 'Abandonner',\n 'form.error': 'Échec d’envoi. Veuillez réessayer.',\n 'form.description.required': 'Décrivez le problème avant d’envoyer.',\n 'form.screenshot.label': 'Capture d’écran',\n 'form.screenshot.cta_click': 'Cliquez',\n 'form.screenshot.cta_rest': 'déposez ou collez une image',\n 'form.screenshot.formats': 'PNG, JPEG ou WebP — jusqu’à 10 Mo',\n 'form.screenshot.remove': 'Retirer la capture',\n 'form.screenshot.annotate': 'Annoter',\n 'form.screenshot.error_type': 'Format non supporté. Utilisez PNG, JPEG ou WebP.',\n 'form.screenshot.error_size': 'Fichier trop volumineux (max {max} Mo).',\n 'form.screenshot.error_count': 'Trop de captures d’écran (max {max}).',\n 'form.context.label': 'Page',\n 'form.capture.notice':\n 'La console, l’activité réseau et les erreurs sont capturées automatiquement pour faciliter le diagnostic.',\n 'type.bug': 'Bogue',\n 'type.feature': 'Suggestion',\n 'type.question': 'Question',\n 'type.praise': 'Compliment',\n 'type.typo': 'Coquille',\n 'severity.blocker': 'Bloquant',\n 'severity.high': 'Élevée',\n 'severity.medium': 'Moyenne',\n 'severity.low': 'Faible',\n 'annotator.title': 'Annoter la capture',\n 'annotator.tool.rectangle': 'Rectangle',\n 'annotator.tool.arrow': 'Flèche',\n 'annotator.tool.freehand': 'Dessin libre',\n 'annotator.tool.text': 'Texte',\n 'annotator.tool.highlight': 'Surligner',\n 'annotator.tool.blur': 'Flouter (données sensibles)',\n 'annotator.text_prompt': 'Entrez le texte :',\n 'annotator.color_picker': 'Couleur personnalisée',\n 'annotator.undo': 'Annuler',\n 'annotator.redo': 'Refaire',\n 'annotator.clear': 'Tout effacer',\n 'annotator.count_suffix': 'annotations',\n 'annotator.loading': 'Chargement…',\n 'annotator.apply': 'Appliquer',\n 'annotator.applying': 'Application…',\n 'tab.send': 'Envoyer',\n 'tab.mine': 'Mes rapports',\n 'tab.changelog': 'Cette semaine',\n 'tab.board': 'Tableau',\n 'board.kpi.total': 'Total',\n 'board.kpi.new': 'Nouveau',\n 'board.kpi.in_progress': 'En cours',\n 'board.kpi.awaiting_validation': 'À valider',\n 'board.kpi.closed': 'Fermé',\n 'board.kpi.rejected': 'Refusé',\n 'board.kpi.resolution_rate': 'Taux de résolution',\n 'board.sort': 'Trier',\n 'board.sort.recent': 'Plus récents',\n 'board.sort.oldest': 'Plus anciens',\n 'board.sort.updated': 'Activité récente',\n 'board.sort.severity': 'Sévérité',\n 'board.sort.status': 'Statut',\n 'board.filter.status': 'Statut',\n 'board.filter.type': 'Type',\n 'board.filter.severity': 'Sévérité',\n 'board.filter.search.placeholder': 'Rechercher…',\n 'board.filter.mine': 'Les miens',\n 'board.filter.clear': 'Effacer les filtres',\n 'board.list.empty.title': 'Rien à voir ici',\n 'board.list.empty.description': 'Les rapports apparaîtront ici dès qu’ils arrivent.',\n 'board.list.loading': 'Chargement…',\n 'board.list.error': 'Impossible de charger les rapports.',\n 'board.retry': 'Réessayer',\n 'board.list.you': 'vous',\n 'board.list.count': '{n} sur {total}',\n 'board.detail.empty': 'Sélectionnez un rapport à gauche pour voir le fil complet.',\n 'board.detail.by': 'Par',\n 'board.detail.confirm_resolution': 'Confirmer la résolution',\n 'board.detail.status_history': 'Historique',\n 'board.scope.project': 'Rapports du projet',\n 'board.scope.mine': 'Vos rapports',\n 'board.scope.thisPage': 'Cette page',\n 'board.scope.allPages': 'Toutes les pages',\n 'board.scope.onThisPage': 'sur cette page',\n 'board.list.count.page': '{n} sur {total} · cette page',\n 'board.list.empty.page.title': 'Aucun retour sur cette page pour l’instant.',\n 'board.list.empty.page.description':\n 'Soyez le premier à signaler quelque chose ici — ou parcourez tout le projet.',\n 'board.back': 'Retour',\n 'changelog.empty.title': 'Rien de résolu pour l’instant',\n 'changelog.empty.body': 'Quand un rapport que vous avez envoyé est corrigé, il apparaîtra ici, regroupé par semaine.',\n 'changelog.week_of': 'Semaine du {date}',\n 'changelog.resolved_one': '{count} résolu',\n 'changelog.resolved_many': '{count} résolus',\n 'mine.empty.title': 'Aucun rapport',\n 'mine.empty.body': 'Après votre premier envoi vous pourrez suivre la conversation ici.',\n 'mine.refresh': 'Actualiser',\n 'mine.loading': 'Chargement…',\n 'mine.error': 'Impossible de charger vos rapports.',\n 'mine.jump.placeholder': '#',\n 'mine.jump.go': 'Ouvrir',\n 'mine.jump.aria': 'Ouvrir un rapport par numéro',\n 'mine.jump.not_found': 'Aucun rapport avec ce numéro.',\n 'error.rate_limited': 'Trop de requêtes — réessaie dans {seconds} s.',\n 'error.rate_limited_generic': 'Trop de requêtes — réessaie dans un instant.',\n 'mine.replies_one': '1 réponse',\n 'mine.replies_many': '{count} réponses',\n 'mine.filter.empty': 'Aucun rapport ne correspond à ce filtre.',\n 'kpi.new': 'Nouveau',\n 'kpi.in_progress': 'En cours',\n 'kpi.awaiting_validation': 'À valider',\n 'kpi.resolution_rate': 'Taux de résolution',\n 'detail.back': 'Retour',\n 'detail.thread': 'Conversation',\n 'detail.no_replies': 'Pas encore de réponse — vous serez notifié dès qu’un opérateur répondra.',\n 'detail.compose_placeholder': 'Ajouter une réponse…',\n 'detail.compose_send': 'Répondre',\n 'detail.compose_sending': 'Envoi…',\n 'detail.attach_add': 'Ajouter une image',\n 'detail.attach_remove': 'Retirer',\n 'detail.attachment_alt': 'Image jointe',\n 'detail.copy_link': 'Copier le lien',\n 'detail.copied': 'Lien copié',\n 'detail.edit': 'Modifier',\n 'detail.edit_save': 'Enregistrer',\n 'detail.edit_cancel': 'Annuler',\n 'detail.edit_saving': 'Enregistrement…',\n 'detail.edit_failed': 'Impossible d’enregistrer vos modifications.',\n 'detail.close_cta': 'Marquer comme résolu',\n 'detail.close_busy': 'Validation…',\n 'detail.reopen_cta': 'Toujours pas réglé',\n 'detail.reopen_busy': 'Réouverture…',\n 'detail.teammate_notice':\n 'Vous consultez le rapport d’un coéquipier — les réponses sont privées au soumetteur.',\n 'detail.load_failed.title': 'Rapport indisponible',\n 'detail.load_failed.body':\n 'Il a peut-être été supprimé, ou vous n’y avez plus accès.',\n 'detail.load_failed.cta': 'Retour',\n 'detail.send_failed': 'Impossible d’envoyer votre réponse. Réessayez.',\n 'detail.close_failed':\n 'Impossible de marquer comme résolu. Réessayez.',\n 'detail.reopen_failed':\n 'Impossible de rouvrir le rapport. Réessayez.',\n 'detail.history': 'Historique du statut',\n 'detail.context.submitted_at': 'Envoyé',\n 'detail.context.page': 'Page',\n 'detail.context.capture.manual': 'Capture manuelle',\n 'detail.context.capture.html2canvas': 'Capture automatique',\n 'detail.context.capture.display_media': 'Partage d’écran',\n 'detail.context.capture.none': 'Sans capture',\n 'detail.author.staff': 'Opérateur',\n 'detail.author.mcp': 'Équipe Mhosaic',\n 'detail.author.system': 'Système',\n 'detail.tech.title': 'Ce que nous avons reçu',\n 'detail.tech.errors_one': 'erreur',\n 'detail.tech.errors_many': 'erreurs',\n 'detail.tech.device': 'Appareil',\n 'detail.tech.device.viewport': 'Fenêtre',\n 'detail.tech.device.platform': 'Plateforme',\n 'detail.tech.device.language': 'Langue',\n 'detail.tech.device.timezone': 'Fuseau horaire',\n 'detail.tech.device.connection': 'Connexion',\n 'detail.tech.errors': 'Erreurs d’exécution',\n 'detail.tech.console': 'Console (20 derniers)',\n 'detail.tech.network': 'Réseau (15 derniers)',\n 'status.new': 'Nouveau',\n 'status.in_progress': 'En cours',\n 'status.awaiting_validation': 'En attente de validation',\n 'status.closed': 'Fermé',\n 'status.rejected': 'Rejeté',\n 'status.duplicate': 'Doublon',\n 'status.wontfix': 'Non corrigé',\n}\n\nconst LOCALE_PACKS: Record<string, Record<StringKey, string>> = {\n fr: FRENCH_STRINGS,\n}\n\ninterface ResolveOptions {\n locale?: string\n}\n\nfunction packFor(locale: string | undefined): Record<StringKey, string> | null {\n if (!locale) return null\n // Match the language subtag only (fr-CA, fr-FR → fr).\n const tag = locale.toLowerCase().split(/[-_]/)[0]!\n return LOCALE_PACKS[tag] ?? null\n}\n\nexport function resolveStrings(\n overrides: Record<string, string>,\n options: ResolveOptions = {},\n): Record<StringKey, string> {\n const localePack = packFor(options.locale) ?? {}\n return {\n ...DEFAULT_STRINGS,\n ...localePack,\n ...overrides,\n } as Record<StringKey, string>\n}\n","import { h, render } from 'preact'\nimport { useCallback, useEffect, useRef, useState } from 'preact/hooks'\n\nimport type { ApiClient } from '../api/client'\nimport type { SubmittedReport } from '../types'\nimport { currentPagePath } from './currentPage'\nimport { BoardView } from './BoardView'\nimport { ChangelogList } from './ChangelogList'\nimport { Fab } from './Fab'\nimport { Form, type FormValues } from './Form'\nimport { MineList } from './MineList'\nimport { Modal } from './Modal'\nimport { PageActivityStrip } from './PageActivityStrip'\nimport { PagePeekPanel } from './PagePeekPanel'\nimport { usePageOpenCount } from './pageSignal'\nimport { ReportDetailView } from './ReportDetailView'\nimport { WIDGET_STYLES } from './styles'\nimport type { StringKey } from './i18n'\n\nexport interface MountOptions {\n host: HTMLElement\n strings: Record<StringKey, string>\n showFAB: boolean\n onSubmit: (values: FormValues) => Promise<SubmittedReport | void>\n /** v0.7: ApiClient for the conversation-loop reader UI. Optional so\n * the synthetic / auto-error path keeps working with no host changes. */\n api?: ApiClient\n /** v0.7: callback that returns the host's identified user id, or\n * undefined when no `identify()` has been called yet. The \"My\n * reports\" tab + FAB visibility both depend on this. */\n getExternalId?: () => string | undefined\n /** Phase 4: when true (driven by the manifest's requires_visibility_check),\n * the FAB renders only if `checkVisibility` confirms this end-user is on the\n * project allowlist. Off (default) → the legacy showFAB/identity path. */\n requiresVisibilityCheck?: boolean\n checkVisibility?: (externalId: string) => Promise<boolean>\n /** Host override for the current-page path — same contract as\n * FeedbackConfig.getCurrentPage. Threaded to BoardView so the \"Cette page\"\n * chip resolves identically to what the submit side records. */\n getCurrentPage?: () => string | null\n /** Opt-in: FAB opens to the page-scoped Board when the current page has\n * feedback (else Send). Default false — never hijack the submit flow. */\n openToCurrentPageFeedback?: boolean\n /** Ambient page-activity surfaces (badge/peek/strip). Default true. */\n showPageActivity?: boolean\n /** #85 — deep-link / permalink support. On mount the widget reads this\n * query param from the host URL and, if it holds a report id or #seq,\n * opens that report directly (read-only — the host URL is never mutated).\n * The detail view also offers a \"copy link\" button built from it.\n * Default `'mhfeedback'`; pass `false` to disable entirely. */\n deepLinkParam?: string | false\n}\n\n/** Pure FAB-visibility decision (Phase 4 adds the `visibilityAllowed` gate on\n * top of the showFAB + identity rules). Exported for unit testing. */\nexport function computeFabVisible(\n opts: Pick<MountOptions, 'showFAB' | 'getExternalId' | 'requiresVisibilityCheck'>,\n externalId: string | undefined,\n visibilityAllowed: boolean,\n): boolean {\n if (!opts.showFAB) return false\n if (opts.getExternalId !== undefined && !externalId) return false\n if (opts.requiresVisibilityCheck && !visibilityAllowed) return false\n return true\n}\n\nexport interface MountHandle {\n open(): void\n close(): void\n dispose(): void\n /** Force a re-render — e.g. after the host calls `identify()` so\n * the FAB becomes visible without a page reload. */\n notifyIdentityChanged(): void\n}\n\ntype Status = 'idle' | 'submitting' | 'error' | 'success'\ntype Tab = 'send' | 'mine' | 'changelog' | 'board'\ninterface State {\n open: boolean\n status: Status\n error?: string\n tab: Tab\n /** When set, MineList is replaced by the detail view for that report id. */\n selectedReportId?: string\n /** Set by a peek-panel row click — BoardView opens with this report\n * pre-selected (spec §3.2). */\n boardSelectId?: string\n /** Sequence number of the just-submitted report, shown in the success\n * acknowledgement (\"report #42 sent\") (#82). */\n submittedSeq?: number\n /** When true, the \"discard unsaved changes?\" confirmation is shown over\n * the form instead of closing on an accidental backdrop/Esc dismiss (#89). */\n confirmingDiscard?: boolean\n}\n\nexport function mountWidget(options: MountOptions): MountHandle {\n const shadow = options.host.attachShadow({ mode: 'open' })\n const style = document.createElement('style')\n style.textContent = WIDGET_STYLES\n shadow.appendChild(style)\n const mountPoint = document.createElement('div')\n shadow.appendChild(mountPoint)\n\n let currentState: State = { open: false, status: 'idle', tab: 'send' }\n let postSubmitTimer: ReturnType<typeof setTimeout> | null = null\n // Tracks whether the send Form currently holds unsaved input, so an\n // accidental backdrop/Esc dismiss can be guarded with a confirmation (#89).\n let formDirty = false\n\n function rerender(state: State) {\n currentState = state\n render(h(Root, { state }), mountPoint)\n }\n\n /** Strip selectedReportId rather than reassigning `undefined` — under\n * exactOptionalPropertyTypes the explicit `undefined` is not assignable\n * back into the optional slot. */\n function clearSelected(s: State): State {\n const { selectedReportId: _drop, boardSelectId: _drop2, ...rest } = s\n void _drop\n void _drop2\n return rest\n }\n\n /** Open the modal to `send` immediately (non-blocking), then switch to\n * `board` if the current page already has feedback (total > 0).\n * Errors from fetchBoardKpis are silently swallowed — a slow or failing\n * network must never delay or break the FAB open. */\n function openWidget(externalId: string | undefined): void {\n rerender({ ...currentState, open: true, tab: 'send' })\n if (options.openToCurrentPageFeedback && options.api && externalId) {\n options.api\n .fetchBoardKpis(externalId, { pagePath: currentPagePath(options) })\n .then((kpis) => {\n if (kpis.total > 0 && currentState.open && currentState.tab === 'send') {\n rerender({ ...currentState, tab: 'board' })\n }\n })\n .catch(() => {\n // best-effort; stay on send\n })\n }\n }\n\n function Root({ state }: { state: State }) {\n const handleSubmit = useCallback(async (values: FormValues) => {\n rerender({ ...currentState, status: 'submitting' })\n try {\n const result = await options.onSubmit(values)\n pageSignal.refresh()\n rerender({\n ...currentState,\n status: 'success',\n ...(result && result.seq !== undefined && { submittedSeq: result.seq }),\n })\n // Track the close-the-modal timer so dispose() can cancel it.\n // Without this, calling shutdown() mid-submit (e.g. SPA route\n // change right after the user hits send) lets the timer fire\n // after teardown and rerender into a stale shadow root.\n if (postSubmitTimer !== null) clearTimeout(postSubmitTimer)\n postSubmitTimer = setTimeout(() => {\n postSubmitTimer = null\n rerender({\n ...currentState,\n open: false,\n status: 'idle',\n // After a successful submit, jump the user to \"My reports\" so\n // they immediately see their just-filed report in the list\n // (and the conversation that's about to start).\n tab: options.api ? 'mine' : 'send',\n })\n }, 1200)\n } catch (err) {\n // Don't leak raw API errors to the form's error banner (e.g.\n // `Feedback submit failed: 429 Too Many Requests {…}` straight\n // into a `<div class=\"error\">`). The strings table already has\n // `form.error` — a friendly fallback. Raw error to console.\n if (typeof console !== 'undefined')\n console.warn('[mhosaic] submit:', err)\n rerender({\n ...currentState,\n status: 'error',\n error: options.strings['form.error'],\n })\n }\n }, [])\n\n const externalId = options.getExternalId?.()\n // Resolve a report by its per-project #seq and open its detail. Shared by\n // the jump-by-#seq box and clickable \"#NN\" references (#85). Resolves\n // false when the seq can't be opened (unknown / no access / throttled).\n const openSeq = (seq: number): Promise<boolean> => {\n if (!options.api || !externalId) return Promise.resolve(false)\n return options.api\n .getReportBySeq(seq, externalId)\n .then((r) => {\n rerender({ ...currentState, open: true, tab: 'mine', selectedReportId: r.id })\n return true\n })\n .catch(() => false)\n }\n // Build a shareable permalink for a report id (#85), unless deep-linking\n // is disabled. Sets/replaces the param on the current URL without mutating\n // the live location (the returned string is only ever copied to clipboard).\n const buildPermalink =\n options.deepLinkParam === false\n ? undefined\n : (id: string): string => {\n const param = options.deepLinkParam || 'mhfeedback'\n try {\n const u = new URL(window.location.href)\n u.searchParams.set(param, id)\n return u.toString()\n } catch {\n return ''\n }\n }\n const pageActivityEnabled = options.showPageActivity !== false\n const pageSignal = usePageOpenCount({\n ...(options.api !== undefined && { api: options.api }),\n ...(externalId !== undefined && { externalId }),\n ...(options.getCurrentPage !== undefined && { getCurrentPage: options.getCurrentPage }),\n enabled: pageActivityEnabled,\n })\n const fabLabel =\n pageSignal.open === 1\n ? options.strings['page.badge.aria.one']\n : pageSignal.open > 0\n ? options.strings['page.badge.aria'].replace('{n}', String(pageSignal.open))\n : options.strings['fab.label']\n // Phase 4: per-end-user server visibility. Default true unless the project\n // requires a check; then false until checkVisibility() confirms allowlist\n // membership. Re-runs whenever the resolved identity changes.\n const [visibilityAllowed, setVisibilityAllowed] = useState(\n !options.requiresVisibilityCheck,\n )\n useEffect(() => {\n if (!options.requiresVisibilityCheck) {\n setVisibilityAllowed(true)\n return\n }\n if (!externalId || !options.checkVisibility) {\n setVisibilityAllowed(false)\n return\n }\n let cancelled = false\n options\n .checkVisibility(externalId)\n .then((show) => {\n if (!cancelled) setVisibilityAllowed(show)\n })\n .catch(() => {\n if (!cancelled) setVisibilityAllowed(false)\n })\n return () => {\n cancelled = true\n }\n }, [externalId])\n\n // FAB-visibility gate: showFAB + (identity resolved when the host wired\n // identity) + (server visibility when the project gates it). Legacy hosts\n // that never wired identity stay on the old \"showFAB → visible\" path.\n const fabVisible = computeFabVisible(options, externalId, visibilityAllowed)\n const showMineTab = Boolean(options.api && externalId)\n\n const [peekOpen, setPeekOpen] = useState(false)\n // Hover intent: 250ms to open (no accidental flashes), 140ms debounce to\n // close (QA-Meter precedent) so moving FAB→panel doesn't flicker.\n const peekTimers = useRef<{\n open?: ReturnType<typeof setTimeout>\n close?: ReturnType<typeof setTimeout>\n }>({})\n const peekEnter = () => {\n if (peekTimers.current.close) clearTimeout(peekTimers.current.close)\n peekTimers.current.open = setTimeout(() => setPeekOpen(true), 250)\n }\n const peekLeave = () => {\n if (peekTimers.current.open) clearTimeout(peekTimers.current.open)\n peekTimers.current.close = setTimeout(() => setPeekOpen(false), 140)\n }\n\n return (\n <>\n {fabVisible && (\n <div\n class=\"fab-area\"\n onMouseEnter={peekEnter}\n onMouseLeave={peekLeave}\n onFocusIn={(e: FocusEvent) => {\n // Modal restores focus to the FAB on every dismissal (X,\n // backdrop, Escape, the post-submit auto-close) — that's a\n // programmatic re-focus, not deliberate intent, so only\n // open for keyboard-modality focus (:focus-visible). This\n // also clears any pending mouse-leave close timer so a\n // hover→tab handoff doesn't get closed out from under a\n // keyboard user by a stale 140ms timeout.\n if (peekTimers.current.close) clearTimeout(peekTimers.current.close)\n if ((e.target as HTMLElement).matches?.(':focus-visible')) setPeekOpen(true)\n }}\n onFocusOut={() => setPeekOpen(false)}\n onKeyDown={(e: KeyboardEvent) => e.key === 'Escape' && setPeekOpen(false)}\n >\n {peekOpen &&\n !state.open &&\n pageActivityEnabled &&\n pageSignal.open > 0 &&\n options.api &&\n externalId && (\n <PagePeekPanel\n api={options.api}\n externalId={externalId}\n strings={options.strings}\n {...(options.getCurrentPage !== undefined && {\n getCurrentPage: options.getCurrentPage,\n })}\n open={pageSignal.open}\n onViewAll={() => {\n setPeekOpen(false)\n rerender({ ...currentState, open: true, tab: 'board' })\n }}\n onPickRow={(reportId) => {\n setPeekOpen(false)\n rerender({ ...currentState, open: true, tab: 'board', boardSelectId: reportId })\n }}\n onSend={() => {\n setPeekOpen(false)\n openWidget(externalId)\n }}\n />\n )}\n <Fab\n label={fabLabel}\n onClick={() => openWidget(externalId)}\n {...(pageSignal.open > 0 && { count: pageSignal.open })}\n />\n </div>\n )}\n {state.open && (\n <Modal\n onDismiss={(reason) => {\n // Guard an accidental dismiss (backdrop/Esc) while the send\n // form holds unsaved input — show a discard confirmation\n // instead of closing. The explicit ✕ (reason 'button') and\n // the Cancel action always close (#89).\n if (\n reason !== 'button' &&\n formDirty &&\n currentState.tab === 'send' &&\n currentState.status !== 'submitting'\n ) {\n rerender({ ...currentState, confirmingDiscard: true })\n return\n }\n formDirty = false\n rerender(\n clearSelected({\n ...currentState,\n open: false,\n status: 'idle',\n confirmingDiscard: false,\n }),\n )\n }}\n closeLabel={options.strings['form.close']}\n expanded={state.tab === 'board'}\n >\n {state.confirmingDiscard && (\n <div class=\"discard-confirm\" role=\"alertdialog\" aria-modal=\"true\">\n <div class=\"discard-confirm-card\">\n <p class=\"discard-confirm-title\">\n {options.strings['form.discard.title']}\n </p>\n <p class=\"discard-confirm-body\">\n {options.strings['form.discard.body']}\n </p>\n <div class=\"discard-confirm-actions\">\n <button\n type=\"button\"\n class=\"btn\"\n onClick={() =>\n rerender({ ...currentState, confirmingDiscard: false })\n }\n >\n {options.strings['form.discard.keep']}\n </button>\n <button\n type=\"button\"\n class=\"btn btn--primary\"\n onClick={() => {\n formDirty = false\n rerender(\n clearSelected({\n ...currentState,\n open: false,\n status: 'idle',\n confirmingDiscard: false,\n }),\n )\n }}\n >\n {options.strings['form.discard.confirm']}\n </button>\n </div>\n </div>\n </div>\n )}\n {showMineTab && (\n <div class=\"tab-strip\" role=\"tablist\">\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={state.tab === 'send'}\n class={`tab-button ${state.tab === 'send' ? 'is-active' : ''}`}\n onClick={() =>\n rerender(clearSelected({ ...currentState, tab: 'send' }))\n }\n >\n {options.strings['tab.send']}\n </button>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={state.tab === 'mine'}\n class={`tab-button ${state.tab === 'mine' ? 'is-active' : ''}`}\n onClick={() =>\n rerender(clearSelected({ ...currentState, tab: 'mine' }))\n }\n >\n {options.strings['tab.mine']}\n </button>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={state.tab === 'changelog'}\n class={`tab-button ${state.tab === 'changelog' ? 'is-active' : ''}`}\n onClick={() =>\n rerender(clearSelected({ ...currentState, tab: 'changelog' }))\n }\n >\n {options.strings['tab.changelog']}\n </button>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={state.tab === 'board'}\n class={`tab-button tab-button--board ${state.tab === 'board' ? 'is-active' : ''}`}\n onClick={() =>\n rerender(clearSelected({ ...currentState, tab: 'board' }))\n }\n >\n {options.strings['tab.board']}\n </button>\n </div>\n )}\n {state.tab === 'send' && (\n <>\n {showMineTab && pageActivityEnabled && (\n <PageActivityStrip\n open={pageSignal.open}\n strings={options.strings}\n onView={() =>\n rerender(clearSelected({ ...currentState, tab: 'board' }))\n }\n />\n )}\n <Form\n strings={options.strings}\n onSubmit={handleSubmit}\n onCancel={() =>\n rerender({ ...currentState, open: false, status: 'idle' })\n }\n status={state.status}\n {...(state.error !== undefined && { errorMessage: state.error })}\n {...(state.submittedSeq !== undefined && {\n submittedSeq: state.submittedSeq,\n })}\n onDirtyChange={(d) => {\n formDirty = d\n }}\n />\n </>\n )}\n {state.tab === 'mine' && options.api && externalId && !state.selectedReportId && (\n <MineList\n api={options.api}\n externalId={externalId}\n strings={options.strings}\n onSelect={(row) =>\n rerender({ ...currentState, selectedReportId: row.id })\n }\n onOpenSeq={openSeq}\n />\n )}\n {(state.tab === 'mine' || state.tab === 'changelog') &&\n options.api &&\n externalId &&\n state.selectedReportId && (\n <ReportDetailView\n api={options.api}\n externalId={externalId}\n reportId={state.selectedReportId}\n strings={options.strings}\n onBack={() =>\n rerender(clearSelected({ ...currentState }))\n }\n onOpenSeq={openSeq}\n {...(buildPermalink && { buildPermalink })}\n />\n )}\n {state.tab === 'changelog' &&\n options.api &&\n externalId &&\n !state.selectedReportId && (\n <ChangelogList\n api={options.api}\n externalId={externalId}\n strings={options.strings}\n onSelect={(row) =>\n rerender({ ...currentState, selectedReportId: row.id })\n }\n />\n )}\n {state.tab === 'board' && options.api && externalId && (\n // BoardView owns its own master/detail navigation — no\n // selectedReportId routing through the modal-level state.\n <BoardView\n api={options.api}\n externalId={externalId}\n strings={options.strings}\n {...(options.getCurrentPage !== undefined && { getCurrentPage: options.getCurrentPage })}\n {...(state.boardSelectId !== undefined && { initialSelectedId: state.boardSelectId })}\n />\n )}\n </Modal>\n )}\n </>\n )\n }\n\n rerender(currentState)\n\n /** #85 — open a report by id or #seq. Numeric refs resolve via the\n * by-seq endpoint; anything else is treated as a report id. Best-effort:\n * a stale/inaccessible ref (or a missing identity) just no-ops. */\n function openReportRef(ref: string): void {\n const api = options.api\n const externalId = options.getExternalId?.()\n if (!api || !externalId || !ref) return\n const seq = /^\\d+$/.test(ref) ? parseInt(ref, 10) : null\n const resolve =\n seq !== null ? api.getReportBySeq(seq, externalId) : api.getReport(ref, externalId)\n void resolve\n .then((r) =>\n rerender({ ...currentState, open: true, tab: 'mine', selectedReportId: r.id }),\n )\n .catch(() => {\n /* unknown ref / no access / throttled — silent */\n })\n }\n\n // Deep-link on load: if the host URL carries the permalink param, open that\n // report. Read-only — we never write back to the host's URL.\n if (options.deepLinkParam !== false) {\n try {\n const param = options.deepLinkParam || 'mhfeedback'\n const value = new URLSearchParams(window.location.search).get(param)\n if (value) openReportRef(value.trim())\n } catch {\n /* no window / malformed URL — skip */\n }\n }\n\n return {\n open() {\n openWidget(options.getExternalId?.())\n },\n close() {\n rerender({ ...currentState, open: false, status: 'idle' })\n },\n dispose() {\n if (postSubmitTimer !== null) {\n clearTimeout(postSubmitTimer)\n postSubmitTimer = null\n }\n render(null, mountPoint)\n options.host.innerHTML = ''\n },\n notifyIdentityChanged() {\n rerender({ ...currentState })\n },\n }\n}\n","/** Current host-page path for \"feedback on this page\". A host getCurrentPage()\n * override (same contract as QaMeterConfig.getCurrentPage) wins; else the raw\n * pathname. The backend turns this into the canonical key (derive_page_key),\n * so the widget never computes the key itself. */\nexport function currentPagePath(opts: { getCurrentPage?: () => string | null }): string {\n const override = opts.getCurrentPage?.()\n if (override) return override\n return typeof window !== 'undefined' ? window.location.pathname : '/'\n}\n\nconst LOCATION_CHANGE = 'mfb:locationchange'\nlet patched = false\n\nfunction patchHistory(): void {\n if (patched || typeof history === 'undefined') return\n patched = true\n for (const m of ['pushState', 'replaceState'] as const) {\n const orig = history[m]\n history[m] = function (this: History, ...args: unknown[]) {\n const r = (orig as (...a: unknown[]) => unknown).apply(this, args)\n window.dispatchEvent(new Event(LOCATION_CHANGE))\n return r\n } as History[typeof m]\n }\n}\n\n/** Subscribe to client-side navigation (SPA + back/forward). Returns an\n * unsubscribe. Mirrors the QA Meter's history-patch approach. */\nexport function onLocationChange(cb: () => void): () => void {\n patchHistory()\n window.addEventListener('popstate', cb)\n window.addEventListener(LOCATION_CHANGE, cb)\n return () => {\n window.removeEventListener('popstate', cb)\n window.removeEventListener(LOCATION_CHANGE, cb)\n }\n}\n","/**\n * BoardView — the \"Voir tout\" surface (v0.12).\n *\n * Renders the project's reports in a master/detail layout with a KPI\n * strip on top and a filter row below it. Scope follows the project's\n * `share_reports_with_widget` flag server-side; the client just renders\n * what the endpoint returns and uses `is_mine` to permission-gate the\n * detail-pane status buttons.\n *\n * Architecture notes:\n * - The list polls every 30s while open (same cadence as MineList),\n * pausing in hidden tabs and backing off on failures — see poll.ts.\n * Filters trigger an immediate refetch.\n * - The KPI strip refetches alongside the list so the counts always\n * match what the user sees.\n * - Detail pane is the existing ReportDetailView used by My-reports +\n * This-week. Permission-gating happens via the `canModerate` prop\n * (false for project-scoped rows that aren't `is_mine`).\n * - The whole tab assumes the Modal it's rendered into has been\n * `expanded` — see Modal.tsx + .modal.is-expanded in styles.ts.\n */\n\nimport { h, type JSX } from 'preact'\nimport { useEffect, useMemo, useState } from 'preact/hooks'\n\nimport type { ApiClient, BoardListPage } from '../api/client'\nimport type {\n BoardFilters,\n BoardSortKey,\n FeedbackSeverity,\n FeedbackType,\n ReportStatus,\n WidgetBoardKpis,\n WidgetBoardRow,\n} from '../types'\nimport type { StringKey } from './i18n'\nimport { loadBoardView, saveBoardView } from './boardViewStore'\nimport { currentPagePath, onLocationChange } from './currentPage'\nimport { startPoll } from './poll'\nimport { rateLimitMessage } from './rateLimit'\nimport { ReportDetailView } from './ReportDetailView'\n\nconst POLL_MS = 30_000\n\n// The search box debounces before it drives a fetch. Every keystroke used to\n// spin up an immediate project-wide listBoard + fetchBoardKpis pair, so a fast\n// typist fired a burst of expensive `q=` scans that OOM-restarted the shared\n// backend and blanked the board. 300ms matches the admin reports search (#284).\nconst SEARCH_DEBOUNCE_MS = 300\n\nconst SORT_OPTIONS: BoardSortKey[] = ['recent', 'oldest', 'updated', 'severity', 'status']\n\nconst STATUSES: ReportStatus[] = [\n 'new',\n 'in_progress',\n 'awaiting_validation',\n 'closed',\n 'rejected',\n]\nconst TYPES: FeedbackType[] = ['bug', 'feature', 'question', 'praise', 'typo']\nconst SEVERITIES: FeedbackSeverity[] = ['blocker', 'high', 'medium', 'low']\n\ninterface BoardViewProps {\n api: ApiClient\n externalId: string\n strings: Record<StringKey, string>\n getCurrentPage?: () => string | null\n /** Set by the hover peek panel's row click — Board opens with this\n * report pre-selected instead of the empty detail pane (spec §3.2). */\n initialSelectedId?: string\n}\n\nexport function BoardView({\n api,\n externalId,\n strings,\n getCurrentPage,\n initialSelectedId,\n}: BoardViewProps) {\n const [filters, setFilters] = useState<BoardFilters>(() => loadBoardView(externalId))\n // `thisPage` is transient (not persisted) — it tracks whether the board\n // is scoped to the current page path. Defaults on.\n const [thisPage, setThisPage] = useState(true)\n useEffect(() => {\n saveBoardView(externalId, filters)\n }, [externalId, filtersHash(filters)])\n const [page, setPage] = useState<BoardListPage | null>(null)\n const [kpis, setKpis] = useState<WidgetBoardKpis | null>(null)\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<string | null>(null)\n const [selectedId, setSelectedId] = useState<string | null>(initialSelectedId ?? null)\n // Detail-pane mount tick — increments when the user picks a row so the\n // panel can re-mount and re-fetch without us caching old data.\n const [detailKey, setDetailKey] = useState(0)\n const [reloadTick, setReloadTick] = useState(0)\n\n // Search text is kept as a local draft (updated per keystroke so typing stays\n // snappy) and debounced into `filters.q` — the value that actually drives the\n // list + KPI fetch. Committing per keystroke is what stormed the backend.\n const [qDraft, setQDraft] = useState<string>(filters.q ?? '')\n const debouncedQ = useDebouncedValue(qDraft, SEARCH_DEBOUNCE_MS)\n // Commit the settled query into the fetch-driving filters (drop the key when\n // empty, matching the exactOptionalPropertyTypes convention below).\n useEffect(() => {\n setFilters((f) => {\n if ((f.q ?? '') === debouncedQ) return f\n if (debouncedQ === '') {\n const { q: _drop, ...rest } = f\n void _drop\n return rest\n }\n return { ...f, q: debouncedQ }\n })\n }, [debouncedQ])\n // Keep the draft in sync when q is reset elsewhere (the Clear button).\n useEffect(() => {\n setQDraft(filters.q ?? '')\n }, [filters.q])\n\n const activeFilterCount = useMemo(() => {\n return (\n (filters.status?.length ?? 0) +\n (filters.type?.length ?? 0) +\n (filters.severity?.length ?? 0) +\n (filters.q ? 1 : 0) +\n (filters.mine ? 1 : 0)\n )\n }, [filters])\n\n // When thisPage is on and the host SPA navigates, bump reloadTick so the\n // fetch effect re-runs with the new currentPagePath. Clean up on unmount or\n // when thisPage is toggled off.\n useEffect(() => {\n if (!thisPage) return\n return onLocationChange(() => setReloadTick((t) => t + 1))\n }, [thisPage])\n\n // Merge the transient page-path scope into a fetch-only filters object.\n // `pagePath` is never written into the persisted boardViewStore blob.\n // A search query always searches the whole project — the \"Cette page\"\n // scope silently hid cross-page matches, so search looked broken (#92).\n const fetchFilters: BoardFilters = useMemo(() => {\n const searching = !!filters.q?.trim()\n return thisPage && !searching\n ? { ...filters, pagePath: currentPagePath(getCurrentPage !== undefined ? { getCurrentPage } : {}) }\n : filters\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [filtersHash(filters), thisPage, reloadTick])\n\n // Single source of truth for list + KPIs — they refetch together so the\n // counts never drift from the visible rows. Cadence, hidden-tab pause and\n // failure backoff live in startPoll.\n useEffect(() => {\n let cancelled = false\n // The query changed (scope toggle, filter, search, host navigation) — drop\n // the previous result set so the refetch shows a loading state instead of\n // the old scope's rows under the new label. Without this the board looks\n // inert when the backend is slow: you flip This page / All pages and the\n // stale list lingers until the fetch lands, so nothing appears to change.\n // Background 30s polls run inside startPoll and never re-enter this effect,\n // so they keep their data — no flicker on refresh.\n setPage(null)\n setKpis(null)\n setLoading(true)\n const poll = startPoll(async () => {\n try {\n const [list, k] = await Promise.all([\n api.listBoard(externalId, fetchFilters),\n api.fetchBoardKpis(externalId, fetchFilters),\n ])\n if (cancelled) return true\n setPage(list)\n setKpis(k)\n setError(null)\n return true\n } catch (e) {\n if (cancelled) return false\n // 429 → actionable \"retry in Xs\"; otherwise the generic friendly\n // copy. Never store the raw error text (it used to be shown) (#80).\n setError(rateLimitMessage(e, strings) ?? strings['board.list.error'])\n return false\n } finally {\n if (!cancelled) setLoading(false)\n }\n }, POLL_MS)\n return () => {\n cancelled = true\n poll.stop()\n }\n // fetchFilters already encodes filters + thisPage + reloadTick.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [api, externalId, JSON.stringify(fetchFilters)])\n\n const selectedRow = useMemo(() => {\n if (!selectedId || !page) return null\n return page.results.find((r) => r.id === selectedId) ?? null\n }, [selectedId, page])\n\n const onPickRow = (row: WidgetBoardRow) => {\n setSelectedId(row.id)\n setDetailKey((k) => k + 1)\n }\n\n return (\n <div class=\"board-view\">\n <BoardHeader strings={strings} kpis={kpis} thisPage={thisPage} />\n <BoardFilters\n filters={filters}\n onChange={setFilters}\n searchDraft={qDraft}\n onSearchDraftChange={setQDraft}\n activeCount={activeFilterCount}\n strings={strings}\n thisPage={thisPage}\n onToggleThisPage={() => setThisPage((v) => !v)}\n />\n <div class=\"board-body\">\n <div class=\"board-list-wrap\" aria-busy={loading && !page}>\n {error && (\n <div class=\"board-error\" role=\"alert\">\n <span>{error}</span>\n <button\n type=\"button\"\n class=\"btn btn--ghost\"\n onClick={() => {\n setError(null)\n setLoading(true)\n setReloadTick((t) => t + 1)\n }}\n >\n {strings['board.retry']}\n </button>\n </div>\n )}\n {!error && loading && !page && (\n <BoardListSkeleton />\n )}\n {!error && page && page.results.length === 0 && !loading && (\n <BoardEmpty\n strings={strings}\n thisPage={thisPage}\n onAllPages={() => setThisPage(false)}\n />\n )}\n {!error && page && page.results.length > 0 && (\n <BoardList\n rows={page.results}\n selectedId={selectedId}\n onPick={onPickRow}\n strings={strings}\n total={page.count}\n thisPage={thisPage}\n />\n )}\n </div>\n <div class={`board-detail-wrap ${selectedRow ? 'has-selection' : ''}`}>\n {selectedRow ? (\n <ReportDetailView\n key={detailKey}\n api={api}\n externalId={externalId}\n reportId={selectedRow.id}\n strings={strings}\n onBack={() => setSelectedId(null)}\n canModerate={selectedRow.can_moderate ?? selectedRow.is_mine}\n variant=\"board\"\n onOpenSeq={(seq) => {\n // Resolve the #NN reference and open it in the detail pane.\n void api\n .getReportBySeq(seq, externalId)\n .then((r) => {\n setSelectedId(r.id)\n setDetailKey((k) => k + 1)\n })\n .catch(() => {\n // Best-effort — a stale/inaccessible #ref just no-ops.\n })\n }}\n />\n ) : (\n <div class=\"board-detail-empty\">\n <DetailEmptyIllustration />\n <p>{strings['board.detail.empty']}</p>\n </div>\n )}\n </div>\n </div>\n </div>\n )\n}\n\n// ---------- subcomponents -------------------------------------------------\n\nfunction BoardHeader({\n strings,\n kpis,\n thisPage,\n}: {\n strings: Record<StringKey, string>\n kpis: WidgetBoardKpis | null\n thisPage: boolean\n}) {\n const scopeLabel = thisPage\n ? strings['board.scope.onThisPage']\n : kpis?.scope === 'project'\n ? strings['board.scope.project']\n : strings['board.scope.mine']\n return (\n <header class=\"board-header\">\n <div class=\"board-header-title\">\n <span class=\"board-header-emoji\" aria-hidden=\"true\">📋</span>\n <div>\n <h2 class=\"board-header-h\">{strings['tab.board']}</h2>\n <p class=\"board-header-sub\">\n {kpis ? `${kpis.total} · ${scopeLabel}` : scopeLabel}\n </p>\n </div>\n </div>\n <BoardKpiStrip kpis={kpis} strings={strings} />\n </header>\n )\n}\n\nfunction BoardKpiStrip({\n kpis,\n strings,\n}: {\n kpis: WidgetBoardKpis | null\n strings: Record<StringKey, string>\n}) {\n // Always render 4 cards so the strip's height is stable while the\n // first fetch is in flight — eliminates the layout shift the PNR\n // page has on its first paint.\n const cells: { key: string; label: string; value: string; tone: string }[] = [\n {\n key: 'new',\n label: strings['board.kpi.new'],\n value: String(kpis?.by_status?.new ?? 0),\n tone: 'tone-new',\n },\n {\n key: 'in_progress',\n label: strings['board.kpi.in_progress'],\n value: String(kpis?.by_status?.in_progress ?? 0),\n tone: 'tone-progress',\n },\n {\n key: 'awaiting_validation',\n label: strings['board.kpi.awaiting_validation'],\n value: String(kpis?.by_status?.awaiting_validation ?? 0),\n tone: 'tone-validation',\n },\n {\n key: 'rate',\n label: strings['board.kpi.resolution_rate'],\n value: kpis ? `${Math.round((kpis.resolution_rate || 0) * 100)}%` : '—',\n tone: 'tone-rate',\n },\n ]\n return (\n <div class={`board-kpi-strip ${kpis ? 'is-ready' : 'is-loading'}`} aria-live=\"polite\">\n {cells.map((c) => (\n <div key={c.key} class={`board-kpi-card ${c.tone}`}>\n <div class=\"board-kpi-value\">{c.value}</div>\n <div class=\"board-kpi-label\">{c.label}</div>\n </div>\n ))}\n </div>\n )\n}\n\nfunction BoardFilters({\n filters,\n onChange,\n searchDraft,\n onSearchDraftChange,\n activeCount,\n strings,\n thisPage,\n onToggleThisPage,\n}: {\n filters: BoardFilters\n onChange: (f: BoardFilters) => void\n /** Live search text (debounced into `filters.q` by the parent). */\n searchDraft: string\n onSearchDraftChange: (q: string) => void\n activeCount: number\n strings: Record<StringKey, string>\n thisPage: boolean\n onToggleThisPage: () => void\n}) {\n // Under `exactOptionalPropertyTypes: true` we can't set keys to\n // `undefined` to clear them — the optional slot doesn't accept the\n // explicit undefined. Drop the key via destructuring instead.\n const setStatus = (value: ReportStatus | '') => {\n if (value === '') {\n const { status: _drop, ...rest } = filters\n void _drop\n onChange(rest)\n } else {\n onChange({ ...filters, status: [value] })\n }\n }\n const setType = (value: FeedbackType | '') => {\n if (value === '') {\n const { type: _drop, ...rest } = filters\n void _drop\n onChange(rest)\n } else {\n onChange({ ...filters, type: [value] })\n }\n }\n const setSeverity = (value: FeedbackSeverity | '') => {\n if (value === '') {\n const { severity: _drop, ...rest } = filters\n void _drop\n onChange(rest)\n } else {\n onChange({ ...filters, severity: [value] })\n }\n }\n const setOrdering = (value: BoardSortKey) => onChange({ ...filters, ordering: value })\n const toggleMine = () => {\n if (filters.mine) {\n const { mine: _drop, ...rest } = filters\n void _drop\n onChange(rest)\n } else {\n onChange({ ...filters, mine: true })\n }\n }\n const clear = () => onChange({})\n return (\n <div class=\"board-filters\">\n <div class=\"board-scope\" role=\"group\" aria-label={strings['board.scope.thisPage']}>\n <button\n type=\"button\"\n class={`board-scope-btn ${thisPage ? 'is-active' : ''}`}\n aria-pressed={thisPage}\n onClick={() => !thisPage && onToggleThisPage()}\n >\n {strings['board.scope.thisPage']}\n </button>\n <button\n type=\"button\"\n class={`board-scope-btn ${!thisPage ? 'is-active' : ''}`}\n aria-pressed={!thisPage}\n onClick={() => thisPage && onToggleThisPage()}\n >\n {strings['board.scope.allPages']}\n </button>\n </div>\n <select\n class=\"board-filter-select\"\n aria-label={strings['board.sort']}\n value={filters.ordering ?? 'recent'}\n onChange={(e) => setOrdering((e.target as HTMLSelectElement).value as BoardSortKey)}\n >\n {SORT_OPTIONS.map((o) => (\n <option key={o} value={o}>\n {strings[`board.sort.${o}` as StringKey]}\n </option>\n ))}\n </select>\n <select\n class=\"board-filter-select\"\n aria-label={strings['board.filter.status']}\n value={filters.status?.[0] ?? ''}\n onChange={(e) =>\n setStatus((e.target as HTMLSelectElement).value as ReportStatus | '')\n }\n >\n <option value=\"\">{strings['board.filter.status']}</option>\n {STATUSES.map((s) => (\n <option key={s} value={s}>\n {strings[`board.kpi.${s}` as StringKey] ?? s}\n </option>\n ))}\n </select>\n <select\n class=\"board-filter-select\"\n aria-label={strings['board.filter.type']}\n value={filters.type?.[0] ?? ''}\n onChange={(e) =>\n setType((e.target as HTMLSelectElement).value as FeedbackType | '')\n }\n >\n <option value=\"\">{strings['board.filter.type']}</option>\n {TYPES.map((t) => (\n <option key={t} value={t}>\n {strings[`type.${t}` as StringKey] ?? t}\n </option>\n ))}\n </select>\n <select\n class=\"board-filter-select\"\n aria-label={strings['board.filter.severity']}\n value={filters.severity?.[0] ?? ''}\n onChange={(e) =>\n setSeverity((e.target as HTMLSelectElement).value as FeedbackSeverity | '')\n }\n >\n <option value=\"\">{strings['board.filter.severity']}</option>\n {SEVERITIES.map((s) => (\n <option key={s} value={s}>\n {strings[`severity.${s}` as StringKey] ?? s}\n </option>\n ))}\n </select>\n <input\n type=\"search\"\n class=\"board-filter-search\"\n placeholder={strings['board.filter.search.placeholder']}\n value={searchDraft}\n onInput={(e) => onSearchDraftChange((e.target as HTMLInputElement).value)}\n />\n <label class=\"board-filter-toggle\">\n <input\n type=\"checkbox\"\n checked={Boolean(filters.mine)}\n onChange={toggleMine}\n />\n <span>{strings['board.filter.mine']}</span>\n </label>\n {activeCount > 0 && (\n <button type=\"button\" class=\"board-filter-clear\" onClick={clear}>\n <CloseIcon />\n {strings['board.filter.clear']}\n </button>\n )}\n </div>\n )\n}\n\nfunction BoardList({\n rows,\n selectedId,\n onPick,\n strings,\n total,\n thisPage,\n}: {\n rows: WidgetBoardRow[]\n selectedId: string | null\n onPick: (row: WidgetBoardRow) => void\n strings: Record<StringKey, string>\n total: number\n thisPage: boolean\n}) {\n return (\n <>\n <div class=\"board-list-count\">\n {strings[thisPage ? 'board.list.count.page' : 'board.list.count']\n .replace('{n}', String(rows.length))\n .replace('{total}', String(total))}\n </div>\n <ul class=\"board-list\" role=\"list\">\n {rows.map((row) => (\n <BoardRowCard\n key={row.id}\n row={row}\n selected={row.id === selectedId}\n onPick={onPick}\n strings={strings}\n />\n ))}\n </ul>\n </>\n )\n}\n\nfunction BoardRowCard({\n row,\n selected,\n onPick,\n strings,\n}: {\n row: WidgetBoardRow\n selected: boolean\n onPick: (row: WidgetBoardRow) => void\n strings: Record<StringKey, string>\n}) {\n const author = row.is_mine\n ? strings['board.list.you']\n : row.author_label || '—'\n return (\n <li>\n <button\n type=\"button\"\n class={`board-row ${selected ? 'is-selected' : ''}`}\n onClick={() => onPick(row)}\n aria-pressed={selected}\n >\n <div class=\"board-row-badges\">\n <span class={`badge status-${row.status}`}>\n {strings[`status.${row.status}` as StringKey] ?? row.status}\n </span>\n <span class={`badge severity-${row.severity}`}>\n {strings[`severity.${row.severity}` as StringKey] ?? row.severity}\n </span>\n </div>\n <div class=\"board-row-description\">{row.description}</div>\n <div class=\"board-row-meta\">\n <span class=\"board-row-author\">{author}</span>\n {row.comment_count > 0 && (\n <span class=\"board-row-comments\">\n <CommentIcon /> {row.comment_count}\n </span>\n )}\n <span class=\"board-row-time\">{formatRelative(row.created_at)}</span>\n </div>\n </button>\n </li>\n )\n}\n\nfunction BoardEmpty({\n strings,\n thisPage,\n onAllPages,\n}: {\n strings: Record<StringKey, string>\n thisPage: boolean\n onAllPages: () => void\n}) {\n return (\n <div class=\"board-empty\">\n <EmptyIllustration />\n <h3>{strings[thisPage ? 'board.list.empty.page.title' : 'board.list.empty.title']}</h3>\n <p>\n {strings[thisPage ? 'board.list.empty.page.description' : 'board.list.empty.description']}\n </p>\n {thisPage && (\n <button type=\"button\" class=\"btn btn--ghost\" onClick={onAllPages}>\n {strings['board.scope.allPages']}\n </button>\n )}\n </div>\n )\n}\n\nfunction BoardListSkeleton() {\n return (\n <ul class=\"board-list board-list-skeleton\" aria-hidden=\"true\">\n {Array.from({ length: 5 }).map((_, i) => (\n <li key={i}>\n <div class=\"board-row skeleton-row\">\n <div class=\"skeleton-line skeleton-badges\" />\n <div class=\"skeleton-line skeleton-text\" />\n <div class=\"skeleton-line skeleton-text short\" />\n </div>\n </li>\n ))}\n </ul>\n )\n}\n\n// ---------- icons ---------------------------------------------------------\n\nfunction CommentIcon(): JSX.Element {\n return (\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\" />\n </svg>\n )\n}\n\nfunction CloseIcon(): JSX.Element {\n return (\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n )\n}\n\nfunction EmptyIllustration(): JSX.Element {\n return (\n <svg width=\"64\" height=\"64\" viewBox=\"0 0 64 64\" fill=\"none\" aria-hidden=\"true\">\n <circle cx=\"32\" cy=\"32\" r=\"28\" stroke=\"currentColor\" stroke-opacity=\"0.18\" stroke-width=\"2\" />\n <path d=\"M22 32h20M32 22v20\" stroke=\"currentColor\" stroke-opacity=\"0.35\" stroke-width=\"2\" stroke-linecap=\"round\" />\n </svg>\n )\n}\n\nfunction DetailEmptyIllustration(): JSX.Element {\n return (\n <svg width=\"80\" height=\"80\" viewBox=\"0 0 64 64\" fill=\"none\" aria-hidden=\"true\">\n <rect x=\"10\" y=\"12\" width=\"44\" height=\"36\" rx=\"4\" stroke=\"currentColor\" stroke-opacity=\"0.22\" stroke-width=\"2\" />\n <line x1=\"18\" y1=\"22\" x2=\"46\" y2=\"22\" stroke=\"currentColor\" stroke-opacity=\"0.22\" stroke-width=\"2\" stroke-linecap=\"round\" />\n <line x1=\"18\" y1=\"30\" x2=\"38\" y2=\"30\" stroke=\"currentColor\" stroke-opacity=\"0.18\" stroke-width=\"2\" stroke-linecap=\"round\" />\n <line x1=\"18\" y1=\"38\" x2=\"32\" y2=\"38\" stroke=\"currentColor\" stroke-opacity=\"0.14\" stroke-width=\"2\" stroke-linecap=\"round\" />\n </svg>\n )\n}\n\n// ---------- helpers -------------------------------------------------------\n\n/** Returns `value` delayed by `ms`, re-timing on every change so a burst of\n * updates collapses to the last one. Keeps the board search from firing a\n * project-wide query on every keystroke. */\nfunction useDebouncedValue<T>(value: T, ms: number): T {\n const [debounced, setDebounced] = useState(value)\n useEffect(() => {\n const t = setTimeout(() => setDebounced(value), ms)\n return () => clearTimeout(t)\n }, [value, ms])\n return debounced\n}\n\nfunction filtersHash(f: BoardFilters): string {\n // Stable serialization for the useEffect dep array — avoids re-running\n // when the BoardView re-renders with a new object identity but the\n // same filter values.\n return JSON.stringify({\n s: f.status?.slice().sort(),\n t: f.type?.slice().sort(),\n sv: f.severity?.slice().sort(),\n q: f.q ?? '',\n m: Boolean(f.mine),\n o: f.ordering ?? '',\n })\n}\n\nfunction formatRelative(iso: string): string {\n // Tiny \"5m / 3h / 2d / 5w\" formatter — same shape PNR uses. Doesn't\n // depend on date-fns; keeps the bundle lean.\n const then = new Date(iso).getTime()\n if (Number.isNaN(then)) return ''\n const delta = Math.max(0, Date.now() - then)\n const m = Math.floor(delta / 60_000)\n if (m < 1) return 'just now'\n if (m < 60) return `${m}m`\n const h = Math.floor(m / 60)\n if (h < 24) return `${h}h`\n const d = Math.floor(h / 24)\n if (d < 7) return `${d}d`\n const w = Math.floor(d / 7)\n return `${w}w`\n}\n","import type { BoardFilters } from '../types'\n\n// Per-user persisted Board view (sort + filters). A widget instance is\n// scoped to one project, so the external id is a sufficient key. Best-\n// effort: storage may be disabled or hold a stale/corrupt blob — every\n// path falls back to an empty view rather than throwing into render.\nconst PREFIX = 'mhosaic.boardView.'\n\nexport function loadBoardView(externalId: string): BoardFilters {\n try {\n const raw = localStorage.getItem(PREFIX + externalId)\n if (!raw) return {}\n const parsed = JSON.parse(raw)\n return parsed && typeof parsed === 'object' ? (parsed as BoardFilters) : {}\n } catch {\n return {}\n }\n}\n\nexport function saveBoardView(externalId: string, view: BoardFilters): void {\n try {\n localStorage.setItem(PREFIX + externalId, JSON.stringify(view))\n } catch {\n /* storage disabled / quota — non-fatal, just no persistence */\n }\n}\n","/**\n * startPoll — shared scheduler for the widget's 30s refresh loops\n * (BoardView, MineList, ChangelogList, ReportDetailView).\n *\n * Born from the 2026-07-07 OOM outage: board tabs left open overnight kept\n * polling at full cadence — even while every response was a 429 — and the\n * accumulated fleet-wide churn restart-looped the backend. Two rules fix\n * that class of failure at the source:\n *\n * - Hidden tabs don't poll. When a tick comes due while `document.hidden`,\n * the loop parks instead of fetching; a `visibilitychange` listener\n * fires an immediate refresh when the tab comes back, so a returning\n * user still sees fresh data without ever paying for a background tab.\n * - Failures back off. Any failed tick doubles the delay, capped at\n * MAX_BACKOFF_MULTIPLIER × interval — a throttled (429) or down backend\n * sees pressure decay instead of a steady hammer. One success resets\n * the cadence.\n *\n * The first tick runs immediately (every caller renders from it). `tick`\n * reports success via its return value instead of throwing — callers\n * already own their error state; a throw is treated as a failed tick so a\n * bug in a caller can never kill the loop.\n */\n\nexport interface PollHandle {\n stop(): void\n}\n\n/** Backoff ceiling: 30s cadence degrades no further than 5 min. */\nconst MAX_BACKOFF_MULTIPLIER = 10\n\nexport function startPoll(\n tick: () => Promise<boolean>,\n intervalMs: number,\n): PollHandle {\n let stopped = false\n let parked = false\n let failures = 0\n let timer: ReturnType<typeof setTimeout> | null = null\n const doc = typeof document !== 'undefined' ? document : null\n\n const delayMs = () =>\n Math.min(intervalMs * 2 ** failures, intervalMs * MAX_BACKOFF_MULTIPLIER)\n\n const run = async () => {\n if (stopped) return\n if (doc?.hidden) {\n parked = true\n return\n }\n let ok = false\n try {\n ok = await tick()\n } catch {\n ok = false\n }\n if (stopped) return\n failures = ok ? 0 : failures + 1\n timer = setTimeout(() => void run(), delayMs())\n }\n\n const onVisibilityChange = () => {\n if (stopped || !parked || doc?.hidden) return\n parked = false\n void run()\n }\n\n doc?.addEventListener('visibilitychange', onVisibilityChange)\n void run()\n\n return {\n stop() {\n stopped = true\n if (timer) clearTimeout(timer)\n doc?.removeEventListener('visibilitychange', onVisibilityChange)\n },\n }\n}\n","import { RateLimitError } from '../api/client'\nimport type { StringKey } from './i18n'\n\n/**\n * Map a caught reader error to an actionable \"too many requests\" message when\n * it's a 429 (#80/#81), else null so the caller can fall back to its generic\n * error copy. Prevents the widget from showing an infinite \"Loading…\" or a\n * false \"Report not available\" when the real cause is a rate limit.\n */\nexport function rateLimitMessage(\n err: unknown,\n strings: Record<StringKey, string>,\n): string | null {\n if (!(err instanceof RateLimitError)) return null\n return err.retryAfterSeconds > 0\n ? strings['error.rate_limited'].replace(\n '{seconds}',\n String(err.retryAfterSeconds),\n )\n : strings['error.rate_limited_generic']\n}\n","/**\n * ReportDetailView — selected report's full thread.\n *\n * Renders: status pill + description + screenshot + comment thread +\n * compose box + (when status=awaiting_validation) \"Mark as resolved\"\n * button. Polls every 30 s while open so the user sees an operator's\n * reply without manually refreshing.\n */\n\nimport { h } from 'preact'\nimport { useEffect, useRef, useState } from 'preact/hooks'\n\nimport { linkifySeq } from './linkifySeq'\nimport { rateLimitMessage } from './rateLimit'\n\nimport type { ApiClient } from '../api/client'\nimport type {\n CapturedContext,\n ConsoleEntry,\n ErrorEntry,\n NetworkEntry,\n WidgetCommentRow,\n WidgetReportDetail,\n WidgetStatusChangeRow,\n} from '../types'\nimport { CommentBubble } from './CommentBubble'\nimport type { StringKey } from './i18n'\nimport { startPoll } from './poll'\nimport {\n MAX_SCREENSHOTS,\n safeExternalHref,\n truncateUrl,\n validateScreenshotFile,\n} from './screenshot-utils'\n\ninterface ReportDetailViewProps {\n api: ApiClient\n externalId: string\n reportId: string\n strings: Record<StringKey, string>\n onBack: () => void\n /** When false, the self-service \"Confirm resolution\" button is hidden\n * even if the report is in awaiting_validation. The Board passes false\n * for project-scoped rows that don't belong to the current user. */\n canModerate?: boolean\n /** Layout flavor. \"modal\" (default) keeps the original My-reports look —\n * scrollable card centered in the modal. \"board\" tightens the padding\n * + drops the top \"← back\" because the Board renders the back action\n * in its own header. */\n variant?: 'modal' | 'board'\n /** Open another report from a clickable \"#NN\" reference in the description\n * or a comment (#85). Absent → references render as plain text. */\n onOpenSeq?: (seq: number) => void\n /** Build a shareable permalink for this report id (#85). Absent → the\n * \"copy link\" affordance is hidden. */\n buildPermalink?: (reportId: string) => string\n}\n\nconst POLL_MS = 30_000\n\nexport function ReportDetailView({\n api,\n externalId,\n reportId,\n strings,\n onBack,\n canModerate = true,\n variant = 'modal',\n onOpenSeq,\n buildPermalink,\n}: ReportDetailViewProps) {\n const [detail, setDetail] = useState<WidgetReportDetail | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [copied, setCopied] = useState(false)\n const [editing, setEditing] = useState(false)\n const [editBody, setEditBody] = useState('')\n const [savingEdit, setSavingEdit] = useState(false)\n const [editError, setEditError] = useState(false)\n const [composeBody, setComposeBody] = useState('')\n const [sending, setSending] = useState(false)\n const [closing, setClosing] = useState(false)\n const [reopening, setReopening] = useState(false)\n // Pending images to attach to the next comment (#91). Same {blob, preview}\n // shape and limits as the report compose form.\n const [composeShots, setComposeShots] = useState<\n Array<{ blob: Blob; preview: string }>\n >([])\n const [attachError, setAttachError] = useState('')\n const fileInputRef = useRef<HTMLInputElement>(null)\n const mountedRef = useRef(true)\n\n // Revoke every pending preview URL on unmount (individual revokes happen\n // on remove/send).\n const shotsRef = useRef(composeShots)\n shotsRef.current = composeShots\n useEffect(() => {\n return () => shotsRef.current.forEach((s) => URL.revokeObjectURL(s.preview))\n }, [])\n\n const acceptComposeFiles = (files: File[]) => {\n setAttachError('')\n const remaining = MAX_SCREENSHOTS - composeShots.length\n const batch = files.slice(0, Math.max(0, remaining))\n for (const file of batch) {\n const err = validateScreenshotFile(file)\n if (err) {\n setAttachError(\n err.kind === 'type'\n ? strings['form.screenshot.error_type']\n : strings['form.screenshot.error_size'].replace('{max}', String(err.maxMb)),\n )\n return\n }\n }\n if (batch.length) {\n const incoming = batch.map((blob) => ({\n blob,\n preview: URL.createObjectURL(blob),\n }))\n setComposeShots((prev) => [...prev, ...incoming])\n }\n if (files.length > batch.length) {\n setAttachError(\n strings['form.screenshot.error_count'].replace('{max}', String(MAX_SCREENSHOTS)),\n )\n }\n }\n\n const handleAttachChange = (e: Event) => {\n const input = e.target as HTMLInputElement\n const files = Array.from(input.files ?? [])\n if (files.length) acceptComposeFiles(files)\n input.value = '' // re-picking the same file must re-fire change\n }\n\n const removeComposeShot = (index: number) => {\n const shot = composeShots[index]\n if (shot) URL.revokeObjectURL(shot.preview)\n setComposeShots((prev) => prev.filter((_, i) => i !== index))\n setAttachError('')\n }\n\n const fetchDetail = async () => {\n try {\n const next = await api.getReport(reportId, externalId)\n if (!mountedRef.current) return true\n setDetail(next)\n setError(null)\n return true\n } catch (err) {\n // The verbatim `err.message` (e.g. `getReport failed: 404 …`) leaked\n // straight into the panel pre-v0.15.3. Friendly UI via the\n // `!detail` empty-state branch below; raw error to console.\n if (typeof console !== 'undefined') console.warn('[mhosaic] getReport:', err)\n if (!mountedRef.current) return false\n // `load_failed` is a sentinel — the render branch swaps to the\n // friendly \"not available\" empty-state when `error` is that sentinel\n // and `detail` is null. A 429 must NOT claim the report was deleted\n // (it wasn't — it's just throttled), so we store the actionable\n // rate-limit message instead and the render shows it verbatim (#80/#81).\n const rl = rateLimitMessage(err, strings)\n setError(rl ?? 'load_failed')\n return false\n }\n }\n\n useEffect(() => {\n mountedRef.current = true\n const poll = startPoll(fetchDetail, POLL_MS)\n return () => {\n mountedRef.current = false\n poll.stop()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [reportId, externalId])\n\n const handleSend = async () => {\n // A comment may be text, images, or both — send if either is present.\n if ((!composeBody.trim() && composeShots.length === 0) || sending) return\n setSending(true)\n try {\n const nonce = `${reportId}:${Date.now()}`\n const attachments = composeShots.map((s) => s.blob)\n const created = await api.addComment(\n reportId,\n externalId,\n composeBody.trim(),\n nonce,\n attachments.length ? attachments : undefined,\n )\n if (!mountedRef.current) return\n setComposeBody('')\n composeShots.forEach((s) => URL.revokeObjectURL(s.preview))\n setComposeShots([])\n setAttachError('')\n // Optimistically append + then refetch so server-rendered timestamps\n // and any race with operator replies stay coherent.\n setDetail((prev) =>\n prev ? { ...prev, comments: appendComment(prev.comments, created) } : prev,\n )\n void fetchDetail()\n } catch (err) {\n // Don't surface raw API messages (e.g. `addComment failed: 403 …`).\n // Friendly fallback in the UI, raw error in the console.\n if (typeof console !== 'undefined') console.warn('[mhosaic] addComment:', err)\n if (!mountedRef.current) return\n setError(strings['detail.send_failed'])\n } finally {\n if (mountedRef.current) setSending(false)\n }\n }\n\n const handleCopyLink = async () => {\n if (!buildPermalink || !detail) return\n try {\n await navigator.clipboard.writeText(buildPermalink(detail.id))\n if (!mountedRef.current) return\n setCopied(true)\n setTimeout(() => {\n if (mountedRef.current) setCopied(false)\n }, 2000)\n } catch {\n /* clipboard blocked (permissions / insecure context) — no-op */\n }\n }\n\n const startEdit = () => {\n if (!detail) return\n setEditBody(detail.description)\n setEditError(false)\n setEditing(true)\n }\n const cancelEdit = () => {\n setEditing(false)\n setEditError(false)\n }\n const handleSaveEdit = async () => {\n if (!detail || savingEdit) return\n const next = editBody.trim()\n if (!next) return\n setSavingEdit(true)\n setEditError(false)\n try {\n const updated = await api.editDescription(reportId, externalId, next)\n if (!mountedRef.current) return\n setDetail(updated)\n setEditing(false)\n } catch (err) {\n if (typeof console !== 'undefined') console.warn('[mhosaic] editDescription:', err)\n if (!mountedRef.current) return\n setEditError(true)\n } finally {\n if (mountedRef.current) setSavingEdit(false)\n }\n }\n\n const handleClose = async () => {\n if (closing || reopening) return\n setClosing(true)\n try {\n const next = await api.closeAsResolved(reportId, externalId)\n if (!mountedRef.current) return\n setDetail(next)\n } catch (err) {\n if (typeof console !== 'undefined')\n console.warn('[mhosaic] closeAsResolved:', err)\n if (!mountedRef.current) return\n setError(strings['detail.close_failed'])\n } finally {\n if (mountedRef.current) setClosing(false)\n }\n }\n\n const handleReopen = async () => {\n if (closing || reopening) return\n setReopening(true)\n try {\n const next = await api.reopenUnresolved(reportId, externalId)\n if (!mountedRef.current) return\n setDetail(next)\n } catch (err) {\n if (typeof console !== 'undefined')\n console.warn('[mhosaic] reopenUnresolved:', err)\n if (!mountedRef.current) return\n setError(strings['detail.reopen_failed'])\n } finally {\n if (mountedRef.current) setReopening(false)\n }\n }\n\n if (!detail && !error) {\n return <div class=\"mine-loading\">{strings['mine.loading']}</div>\n }\n\n if (!detail) {\n // A 429 stores an actionable rate-limit message rather than the\n // 'load_failed' sentinel — show it with a Retry instead of the (false)\n // \"may have been deleted\" copy (#80/#81).\n if (error && error !== 'load_failed') {\n return (\n <div class=\"report-detail-empty-state\" role=\"alert\">\n <p class=\"report-detail-empty-state-body\">{error}</p>\n <button type=\"button\" class=\"btn\" onClick={() => void fetchDetail()}>\n {strings['board.retry']}\n </button>\n <button type=\"button\" class=\"btn\" onClick={onBack}>\n ← {strings['detail.load_failed.cta']}\n </button>\n </div>\n )\n }\n // The raw `error.message` from the api client used to render here as\n // e.g. `getReport failed: 404 {\"detail\":\"Report not found.\"}` — a\n // verbatim leak of the backend error. The friendly variant + a Back\n // button is what end users (and external testers) expect to see.\n return (\n <div class=\"report-detail-empty-state\" role=\"alert\">\n <p class=\"report-detail-empty-state-title\">\n {strings['detail.load_failed.title']}\n </p>\n <p class=\"report-detail-empty-state-body\">\n {strings['detail.load_failed.body']}\n </p>\n <button type=\"button\" class=\"btn\" onClick={onBack}>\n ← {strings['detail.load_failed.cta']}\n </button>\n </div>\n )\n }\n\n // Permission gating. `canModerate` is passed by the parent (Board\n // passes `is_mine`; My-reports defaults to true). v0.15.3 also reads\n // `detail.is_mine` from the server when present, so a direct fetch\n // (no parent context) still gates correctly.\n const isMine = detail.is_mine ?? canModerate\n // `can_moderate` (server-computed) widens the action strip to\n // colleagues when the project opted into widget_peer_validation;\n // older backends omit it and we fall back to the submitter-only rule.\n const canAct = detail.can_moderate ?? isMine\n // \"Mark as resolved\" is the only self-service status transition\n // the widget allows (awaiting_validation → closed).\n const showCloseCta = canAct && detail.status === 'awaiting_validation'\n // Reply compose box: submitter, or a peer under peer validation.\n // Hidden (not just disabled) otherwise, since the action is genuinely\n // unavailable.\n const showComposeBox = canAct\n\n return (\n <div class={`report-detail variant-${variant}`}>\n {variant === 'modal' && (\n <div class=\"report-detail-header\">\n <button type=\"button\" class=\"btn\" onClick={onBack}>\n ← {strings['detail.back']}\n </button>\n <span class={`pill pill-status pill-status--${detail.status}`}>\n {strings[`status.${detail.status}` as StringKey] ?? detail.status}\n </span>\n </div>\n )}\n {variant === 'board' && (\n <div class=\"report-detail-header report-detail-header--board\">\n <button\n type=\"button\"\n class=\"btn btn--ghost report-detail-back\"\n onClick={onBack}\n aria-label={strings['board.back']}\n >\n ← {strings['board.back']}\n </button>\n <span class={`pill pill-status pill-status--${detail.status}`}>\n {strings[`status.${detail.status}` as StringKey] ?? detail.status}\n </span>\n </div>\n )}\n\n <div class=\"report-detail-body\">\n <ContextBlock detail={detail} strings={strings} />\n {editing ? (\n <div class=\"report-detail-edit\">\n <textarea\n class=\"report-detail-edit-input\"\n value={editBody}\n onInput={(e) => {\n setEditBody((e.target as HTMLTextAreaElement).value)\n setEditError(false)\n }}\n disabled={savingEdit}\n />\n {editError && (\n <p class=\"error\" role=\"alert\">\n {strings['detail.edit_failed']}\n </p>\n )}\n <div class=\"report-detail-edit-actions\">\n <button\n type=\"button\"\n class=\"btn btn--ghost\"\n onClick={cancelEdit}\n disabled={savingEdit}\n >\n {strings['detail.edit_cancel']}\n </button>\n <button\n type=\"button\"\n class=\"btn btn--primary\"\n onClick={handleSaveEdit}\n disabled={savingEdit || !editBody.trim()}\n >\n {savingEdit\n ? strings['detail.edit_saving']\n : strings['detail.edit_save']}\n </button>\n </div>\n </div>\n ) : (\n <div class=\"report-detail-description-row\">\n <p class=\"report-detail-description\">\n {linkifySeq(detail.description, onOpenSeq)}\n </p>\n {isMine && (\n <button\n type=\"button\"\n class=\"btn btn--ghost report-detail-edit\"\n onClick={startEdit}\n >\n {strings['detail.edit']}\n </button>\n )}\n </div>\n )}\n {buildPermalink && (\n <div class=\"report-detail-permalink\">\n <button\n type=\"button\"\n class=\"btn btn--ghost report-detail-copy-link\"\n onClick={handleCopyLink}\n >\n {copied ? strings['detail.copied'] : strings['detail.copy_link']}\n </button>\n </div>\n )}\n {(detail.screenshots?.length\n ? detail.screenshots.map((s) => s.url)\n : [detail.screenshot_url]\n )\n .filter((url): url is string => Boolean(url))\n .map((url) => {\n const safeHref = safeExternalHref(url)\n // No clickable open-in-new-tab for non-http(s) screenshot URLs\n // (defense-in-depth — server returns presigned https URLs, but\n // we don't trust client-rendered content with the operator's\n // origin). The inline preview is `<img src>`, which is safe\n // even if the URL is weird because Preact escapes attrs and\n // the browser refuses to render non-image content.\n return safeHref ? (\n <a\n class=\"report-detail-screenshot\"\n href={safeHref}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n <img src={url} alt=\"\" loading=\"lazy\" />\n </a>\n ) : (\n <div class=\"report-detail-screenshot\">\n <img src={url} alt=\"\" loading=\"lazy\" />\n </div>\n )\n })}\n\n <h3 class=\"report-detail-section\">{strings['detail.thread']}</h3>\n {detail.comments.length === 0 ? (\n <p class=\"report-detail-empty\">{strings['detail.no_replies']}</p>\n ) : (\n <ul class=\"report-comments\">\n {detail.comments.map((c) => (\n <li>\n <CommentBubble\n comment={c}\n strings={strings}\n {...(onOpenSeq && { onOpenSeq })}\n />\n </li>\n ))}\n </ul>\n )}\n\n {detail.status_history && detail.status_history.length > 0 && (\n <StatusHistorySection rows={detail.status_history} strings={strings} />\n )}\n\n {detail.technical_context && (\n <TechnicalContextDrawer ctx={detail.technical_context} strings={strings} />\n )}\n\n {showComposeBox ? (\n <div class=\"report-compose\">\n <textarea\n value={composeBody}\n placeholder={strings['detail.compose_placeholder']}\n onInput={(e) =>\n setComposeBody((e.target as HTMLTextAreaElement).value)\n }\n disabled={sending}\n />\n {composeShots.length > 0 && (\n <div class=\"compose-attachments\">\n {composeShots.map((s, i) => (\n <div class=\"compose-attachment\" key={s.preview}>\n <img src={s.preview} alt={strings['detail.attachment_alt']} />\n <button\n type=\"button\"\n class=\"compose-attachment-remove\"\n aria-label={strings['detail.attach_remove']}\n onClick={() => removeComposeShot(i)}\n disabled={sending}\n >\n ×\n </button>\n </div>\n ))}\n </div>\n )}\n {attachError && (\n <p class=\"compose-attach-error\" role=\"alert\">\n {attachError}\n </p>\n )}\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/png,image/jpeg,image/webp\"\n multiple\n class=\"compose-attach-input\"\n onChange={handleAttachChange}\n hidden\n />\n <div class=\"report-compose-actions\">\n <button\n type=\"button\"\n class=\"btn btn--ghost compose-attach-btn\"\n onClick={() => fileInputRef.current?.click()}\n disabled={sending || composeShots.length >= MAX_SCREENSHOTS}\n >\n {strings['detail.attach_add']}\n </button>\n {showCloseCta && (\n <button\n type=\"button\"\n class=\"btn\"\n onClick={handleClose}\n disabled={closing || reopening}\n >\n {closing\n ? strings['detail.close_busy']\n : strings['detail.close_cta']}\n </button>\n )}\n {showCloseCta && (\n <button\n type=\"button\"\n class=\"btn btn--ghost\"\n onClick={handleReopen}\n disabled={closing || reopening}\n >\n {reopening\n ? strings['detail.reopen_busy']\n : strings['detail.reopen_cta']}\n </button>\n )}\n <button\n type=\"button\"\n class=\"btn btn--primary\"\n onClick={handleSend}\n disabled={\n (!composeBody.trim() && composeShots.length === 0) || sending\n }\n >\n {sending\n ? strings['detail.compose_sending']\n : strings['detail.compose_send']}\n </button>\n </div>\n </div>\n ) : (\n // Teammate is reading a project-wide row. Replies are private\n // to the submitter, so we hide the compose box entirely (action\n // is genuinely unavailable) and surface a short notice so the\n // viewer understands why.\n <p class=\"report-detail-teammate-notice\" role=\"note\">\n {strings['detail.teammate_notice']}\n </p>\n )}\n\n {error && <div class=\"error\">{error}</div>}\n </div>\n </div>\n )\n}\n\nfunction appendComment(\n current: WidgetCommentRow[],\n next: WidgetCommentRow,\n): WidgetCommentRow[] {\n if (current.some((c) => c.id === next.id)) return current\n return [...current, next]\n}\n\ninterface ContextBlockProps {\n detail: WidgetReportDetail\n strings: Record<StringKey, string>\n}\n\n/**\n * The original-context block that sits above the description on the\n * detail page. Surfaces what the user originally reported (type +\n * severity at submit time, page they were on, when they sent it,\n * how the screenshot was captured) so they can recall the report\n * without having to re-derive it from the description.\n */\nfunction ContextBlock({ detail, strings }: ContextBlockProps) {\n const captureKey: StringKey = `detail.context.capture.${detail.capture_method}` as StringKey\n const captureLabel = strings[captureKey] ?? detail.capture_method\n return (\n <div class=\"report-detail-context\">\n <div class=\"report-detail-context-pills\">\n <span class=\"pill pill-type\">{strings[`type.${detail.feedback_type}` as StringKey]}</span>\n <span class={`pill pill-severity pill-severity--${detail.severity}`}>\n {strings[`severity.${detail.severity}` as StringKey]}\n </span>\n <span class=\"pill pill-capture\">{captureLabel}</span>\n </div>\n <div class=\"report-detail-context-line\">\n <span class=\"report-detail-context-label\">{strings['detail.context.submitted_at']}</span>\n <span>{formatSubmittedAt(detail.created_at)}</span>\n </div>\n {detail.page_url && (() => {\n const safeHref = safeExternalHref(detail.page_url)\n return (\n <div class=\"report-detail-context-line\" title={detail.page_url}>\n <span class=\"report-detail-context-label\">{strings['detail.context.page']}</span>\n {safeHref ? (\n <a\n class=\"report-detail-context-url\"\n href={safeHref}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {truncateUrl(detail.page_url, 64)}\n </a>\n ) : (\n <span class=\"report-detail-context-url\">{truncateUrl(detail.page_url, 64)}</span>\n )}\n </div>\n )\n })()}\n </div>\n )\n}\n\nfunction formatSubmittedAt(iso: string): string {\n try {\n return new Date(iso).toLocaleString(undefined, {\n dateStyle: 'medium',\n timeStyle: 'short',\n })\n } catch {\n return iso\n }\n}\n\ninterface TechnicalContextDrawerProps {\n ctx: CapturedContext\n strings: Record<StringKey, string>\n}\n\nconst TECH_CONSOLE_LIMIT = 20\nconst TECH_NETWORK_LIMIT = 15\n\n/**\n * The \"transparency drawer\" — collapsible panel showing the user the\n * exact diagnostic data their browser submitted with the report (console\n * tail, network tail, JS errors, device summary). Closed by default so\n * the regular conversation flow stays uncluttered; one click reveals\n * everything. Borrowed from thePnr's customer-facing `TechnicalContextBlock`.\n */\nfunction TechnicalContextDrawer({ ctx, strings }: TechnicalContextDrawerProps) {\n const errors = ctx.errors ?? []\n const consoleLogs = (ctx.consoleLogs ?? []).slice(-TECH_CONSOLE_LIMIT)\n const network = (ctx.networkRequests ?? []).slice(-TECH_NETWORK_LIMIT)\n const device = ctx.device\n const hasAny =\n !!device || errors.length > 0 || consoleLogs.length > 0 || network.length > 0\n if (!hasAny) return null\n const errorCount = errors.length\n const summary =\n errorCount > 0\n ? `${strings['detail.tech.title']} · ${errorCount} ${\n errorCount === 1 ? strings['detail.tech.errors_one'] : strings['detail.tech.errors_many']\n }`\n : strings['detail.tech.title']\n return (\n <details class=\"report-detail-tech\">\n <summary>{summary}</summary>\n <div class=\"tech-body\">\n {device && <DeviceSection device={device} strings={strings} />}\n {errors.length > 0 && <ErrorsSection errors={errors} strings={strings} />}\n {consoleLogs.length > 0 && <ConsoleSection logs={consoleLogs} strings={strings} />}\n {network.length > 0 && <NetworkSection rows={network} strings={strings} />}\n </div>\n </details>\n )\n}\n\nfunction DeviceSection({\n device,\n strings,\n}: {\n device: CapturedContext['device']\n strings: Record<StringKey, string>\n}) {\n const parts: Array<{ label: string; value: string }> = []\n if (device?.viewport) {\n const dpr = device.viewport.dpr ? ` @${device.viewport.dpr}x` : ''\n parts.push({\n label: strings['detail.tech.device.viewport'],\n value: `${device.viewport.w}×${device.viewport.h}${dpr}`,\n })\n }\n if (device?.platform) parts.push({ label: strings['detail.tech.device.platform'], value: device.platform })\n if (device?.language) parts.push({ label: strings['detail.tech.device.language'], value: device.language })\n if (device?.timezone) parts.push({ label: strings['detail.tech.device.timezone'], value: device.timezone })\n if (device?.connection) parts.push({ label: strings['detail.tech.device.connection'], value: device.connection })\n if (parts.length === 0) return null\n return (\n <div class=\"tech-section\">\n <h4>{strings['detail.tech.device']}</h4>\n <dl class=\"tech-device\">\n {parts.map((p) => (\n <>\n <dt>{p.label}</dt>\n <dd>{p.value}</dd>\n </>\n ))}\n </dl>\n </div>\n )\n}\n\nfunction ErrorsSection({\n errors,\n strings,\n}: {\n errors: ErrorEntry[]\n strings: Record<StringKey, string>\n}) {\n return (\n <div class=\"tech-section\">\n <h4>{strings['detail.tech.errors']}</h4>\n <ul class=\"tech-errors\">\n {errors.map((e) => (\n <li>\n <div class=\"tech-errors-msg\">{e.message}</div>\n {e.stack && <pre class=\"tech-errors-stack\">{truncateStack(e.stack)}</pre>}\n </li>\n ))}\n </ul>\n </div>\n )\n}\n\nfunction ConsoleSection({\n logs,\n strings,\n}: {\n logs: ConsoleEntry[]\n strings: Record<StringKey, string>\n}) {\n return (\n <div class=\"tech-section\">\n <h4>{strings['detail.tech.console']}</h4>\n <ul class=\"tech-console\">\n {logs.map((l) => (\n <li class={`tech-console-row tech-console-row--${l.level}`}>\n <span class=\"tech-console-level\">{l.level}</span>\n <span class=\"tech-console-msg\">{l.message}</span>\n </li>\n ))}\n </ul>\n </div>\n )\n}\n\nfunction NetworkSection({\n rows,\n strings,\n}: {\n rows: NetworkEntry[]\n strings: Record<StringKey, string>\n}) {\n return (\n <div class=\"tech-section\">\n <h4>{strings['detail.tech.network']}</h4>\n <ul class=\"tech-network\">\n {rows.map((n) => {\n const failed = n.status >= 400 || n.status === 0\n return (\n <li class={`tech-network-row${failed ? ' tech-network-row--fail' : ''}`}>\n <span class=\"tech-network-status\">{n.status || '—'}</span>\n <span class=\"tech-network-method\">{n.method}</span>\n <span class=\"tech-network-url\" title={n.url}>\n {truncateUrl(n.url, 56)}\n </span>\n <span class=\"tech-network-time\">{Math.round(n.durationMs)}ms</span>\n </li>\n )\n })}\n </ul>\n </div>\n )\n}\n\nfunction truncateStack(stack: string): string {\n // Clip to first 12 frames so the drawer doesn't blow out vertically on\n // a deep React stack — the user can always click through to a full view\n // if we add one later.\n const lines = stack.split('\\n')\n if (lines.length <= 12) return stack\n return lines.slice(0, 12).join('\\n') + '\\n …'\n}\n\ninterface StatusHistorySectionProps {\n rows: WidgetStatusChangeRow[]\n strings: Record<StringKey, string>\n}\n\n/**\n * The full audit trail of who changed the report's status, when, and via\n * which surface (operator UI, MCP agent, or system). Read-only — purely\n * a transparency / \"you're being heard by real humans\" gesture borrowed\n * from thePnr's customer-facing detail view.\n */\nfunction StatusHistorySection({ rows, strings }: StatusHistorySectionProps) {\n return (\n <div class=\"report-detail-history\">\n <h3 class=\"report-detail-section\">{strings['detail.history']}</h3>\n <ol class=\"status-history\">\n {rows.map((r) => {\n const from = strings[`status.${r.from_status}` as StringKey] ?? r.from_status\n const to = strings[`status.${r.to_status}` as StringKey] ?? r.to_status\n const sourceKey: StringKey =\n r.changed_by_source === 'mcp'\n ? 'detail.author.mcp'\n : r.changed_by_source === 'system'\n ? 'detail.author.system'\n : 'detail.author.staff'\n return (\n <li class=\"status-history-row\">\n <span class=\"status-history-time\">{formatSubmittedAt(r.created_at)}</span>\n <span class=\"status-history-transition\">\n <span class={`pill pill-status pill-status--${r.from_status}`}>{from}</span>\n <span class=\"status-history-arrow\" aria-hidden=\"true\">→</span>\n <span class={`pill pill-status pill-status--${r.to_status}`}>{to}</span>\n </span>\n <span class={`status-history-source status-history-source--${r.changed_by_source}`}>\n {strings[sourceKey]}\n </span>\n </li>\n )\n })}\n </ol>\n </div>\n )\n}\n","const ENCODED_ENTITIES = /[\"&<]/;\n\n/** @param {string} str */\nexport function encodeEntities(str) {\n\t// Skip all work for strings with no entities needing encoding:\n\tif (str.length === 0 || ENCODED_ENTITIES.test(str) === false) return str;\n\n\tlet last = 0,\n\t\ti = 0,\n\t\tout = '',\n\t\tch = '';\n\n\t// Seek forward in str until the next entity char:\n\tfor (; i < str.length; i++) {\n\t\tswitch (str.charCodeAt(i)) {\n\t\t\tcase 34:\n\t\t\t\tch = '"';\n\t\t\t\tbreak;\n\t\t\tcase 38:\n\t\t\t\tch = '&';\n\t\t\t\tbreak;\n\t\t\tcase 60:\n\t\t\t\tch = '<';\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tcontinue;\n\t\t}\n\t\t// Append skipped/buffered characters and the encoded entity:\n\t\tif (i !== last) out += str.slice(last, i);\n\t\tout += ch;\n\t\t// Start the next seek/buffer after the entity's offset:\n\t\tlast = i + 1;\n\t}\n\tif (i !== last) out += str.slice(last, i);\n\treturn out;\n}\n","/** Normal hydration that attaches to a DOM tree but does not diff it. */\nexport const MODE_HYDRATE = 1 << 5;\n/** Signifies this VNode suspended on the previous render */\nexport const MODE_SUSPENDED = 1 << 7;\n/** Indicates that this node needs to be inserted while patching children */\nexport const INSERT_VNODE = 1 << 2;\n/** Indicates a VNode has been matched with another VNode in the diff */\nexport const MATCHED = 1 << 1;\n\n/** Reset all mode flags */\nexport const RESET_MODE = ~(MODE_HYDRATE | MODE_SUSPENDED);\n\nexport const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';\nexport const XHTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';\nexport const MATH_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';\n\nexport const NULL = null;\nexport const UNDEFINED = undefined;\nexport const EMPTY_OBJ = /** @type {any} */ ({});\nexport const EMPTY_ARR = [];\nexport const IS_NON_DIMENSIONAL =\n\t/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;\n","import { options, Fragment } from 'preact';\nimport { encodeEntities } from './utils';\nimport { IS_NON_DIMENSIONAL } from '../../src/constants';\n\nlet vnodeId = 0;\n\nconst isArray = Array.isArray;\n\n/**\n * @fileoverview\n * This file exports various methods that implement Babel's \"automatic\" JSX runtime API:\n * - jsx(type, props, key)\n * - jsxs(type, props, key)\n * - jsxDEV(type, props, key, __source, __self)\n *\n * The implementation of createVNode here is optimized for performance.\n * Benchmarks: https://esbench.com/bench/5f6b54a0b4632100a7dcd2b3\n */\n\n/**\n * JSX.Element factory used by Babel's {runtime:\"automatic\"} JSX transform\n * @param {VNode['type']} type\n * @param {VNode['props']} props\n * @param {VNode['key']} [key]\n * @param {unknown} [isStaticChildren]\n * @param {unknown} [__source]\n * @param {unknown} [__self]\n */\nfunction createVNode(type, props, key, isStaticChildren, __source, __self) {\n\tif (!props) props = {};\n\t// We'll want to preserve `ref` in props to get rid of the need for\n\t// forwardRef components in the future, but that should happen via\n\t// a separate PR.\n\tlet normalizedProps = props,\n\t\tref,\n\t\ti;\n\n\tif ('ref' in normalizedProps) {\n\t\tnormalizedProps = {};\n\t\tfor (i in props) {\n\t\t\tif (i == 'ref') {\n\t\t\t\tref = props[i];\n\t\t\t} else {\n\t\t\t\tnormalizedProps[i] = props[i];\n\t\t\t}\n\t\t}\n\t}\n\n\t/** @type {VNode & { __source: any; __self: any }} */\n\tconst vnode = {\n\t\ttype,\n\t\tprops: normalizedProps,\n\t\tkey,\n\t\tref,\n\t\t_children: null,\n\t\t_parent: null,\n\t\t_depth: 0,\n\t\t_dom: null,\n\t\t_component: null,\n\t\tconstructor: undefined,\n\t\t_original: --vnodeId,\n\t\t_index: -1,\n\t\t_flags: 0,\n\t\t__source,\n\t\t__self\n\t};\n\n\t// If a Component VNode, check for and apply defaultProps.\n\t// Note: `type` is often a String, and can be `undefined` in development.\n\tif (typeof type === 'function' && (ref = type.defaultProps)) {\n\t\tfor (i in ref)\n\t\t\tif (normalizedProps[i] === undefined) {\n\t\t\t\tnormalizedProps[i] = ref[i];\n\t\t\t}\n\t}\n\n\tif (options.vnode) options.vnode(vnode);\n\treturn vnode;\n}\n\n/**\n * Create a template vnode. This function is not expected to be\n * used directly, but rather through a precompile JSX transform\n * @param {string[]} templates\n * @param {Array<string | null | VNode>} exprs\n * @returns {VNode}\n */\nfunction jsxTemplate(templates, ...exprs) {\n\tconst vnode = createVNode(Fragment, { tpl: templates, exprs });\n\t// Bypass render to string top level Fragment optimization\n\tvnode.key = vnode._vnode;\n\treturn vnode;\n}\n\nconst JS_TO_CSS = {};\nconst CSS_REGEX = /[A-Z]/g;\n\n/**\n * Unwrap potential signals.\n * @param {*} value\n * @returns {*}\n */\nfunction normalizeAttrValue(value) {\n\treturn value !== null &&\n\t\ttypeof value === 'object' &&\n\t\ttypeof value.valueOf === 'function'\n\t\t? value.valueOf()\n\t\t: value;\n}\n\n/**\n * Serialize an HTML attribute to a string. This function is not\n * expected to be used directly, but rather through a precompile\n * JSX transform\n * @param {string} name The attribute name\n * @param {*} value The attribute value\n * @returns {string}\n */\nfunction jsxAttr(name, value) {\n\tif (options.attr) {\n\t\tconst result = options.attr(name, value);\n\t\tif (typeof result === 'string') return result;\n\t}\n\n\tvalue = normalizeAttrValue(value);\n\n\tif (name === 'ref' || name === 'key') return '';\n\tif (name === 'style' && typeof value === 'object') {\n\t\tlet str = '';\n\t\tfor (let prop in value) {\n\t\t\tlet val = value[prop];\n\t\t\tif (val != null && val !== '') {\n\t\t\t\tconst name =\n\t\t\t\t\tprop[0] == '-'\n\t\t\t\t\t\t? prop\n\t\t\t\t\t\t: JS_TO_CSS[prop] ||\n\t\t\t\t\t\t\t(JS_TO_CSS[prop] = prop.replace(CSS_REGEX, '-$&').toLowerCase());\n\n\t\t\t\tlet suffix = ';';\n\t\t\t\tif (\n\t\t\t\t\ttypeof val === 'number' &&\n\t\t\t\t\t// Exclude custom-attributes\n\t\t\t\t\t!name.startsWith('--') &&\n\t\t\t\t\t!IS_NON_DIMENSIONAL.test(name)\n\t\t\t\t) {\n\t\t\t\t\tsuffix = 'px;';\n\t\t\t\t}\n\t\t\t\tstr = str + name + ':' + val + suffix;\n\t\t\t}\n\t\t}\n\t\treturn name + '=\"' + encodeEntities(str) + '\"';\n\t}\n\n\tif (\n\t\tvalue == null ||\n\t\tvalue === false ||\n\t\ttypeof value === 'function' ||\n\t\ttypeof value === 'object'\n\t) {\n\t\treturn '';\n\t} else if (value === true) return name;\n\n\treturn name + '=\"' + encodeEntities('' + value) + '\"';\n}\n\n/**\n * Escape a dynamic child passed to `jsxTemplate`. This function\n * is not expected to be used directly, but rather through a\n * precompile JSX transform\n * @param {*} value\n * @returns {string | null | VNode | Array<string | null | VNode>}\n */\nfunction jsxEscape(value) {\n\tif (\n\t\tvalue == null ||\n\t\ttypeof value === 'boolean' ||\n\t\ttypeof value === 'function'\n\t) {\n\t\treturn null;\n\t}\n\n\tif (typeof value === 'object') {\n\t\t// Check for VNode\n\t\tif (value.constructor === undefined) return value;\n\n\t\tif (isArray(value)) {\n\t\t\tfor (let i = 0; i < value.length; i++) {\n\t\t\t\tvalue[i] = jsxEscape(value[i]);\n\t\t\t}\n\t\t\treturn value;\n\t\t}\n\t}\n\n\treturn encodeEntities('' + value);\n}\n\nexport {\n\tcreateVNode as jsx,\n\tcreateVNode as jsxs,\n\tcreateVNode as jsxDEV,\n\tFragment,\n\t// precompiled JSX transform\n\tjsxTemplate,\n\tjsxAttr,\n\tjsxEscape\n};\n","import { h, type VNode } from 'preact'\n\nconst SEQ_RE = /#(\\d+)/g\n\n/**\n * Split text on \"#NN\" report references and render each as a clickable link\n * that opens that report (#85 — \"voir #81\" used to be dead text). Returns the\n * original text as a single item when there's nothing to linkify or no\n * handler is supplied (so callers can render the result directly).\n */\nexport function linkifySeq(\n text: string | null | undefined,\n onOpenSeq?: (seq: number) => void,\n): (string | VNode)[] {\n const value = text ?? ''\n if (!value || !onOpenSeq) return [value]\n const parts: (string | VNode)[] = []\n let last = 0\n SEQ_RE.lastIndex = 0\n let m: RegExpExecArray | null\n while ((m = SEQ_RE.exec(value)) !== null) {\n if (m.index > last) parts.push(value.slice(last, m.index))\n const seq = Number(m[1])\n parts.push(\n <button\n type=\"button\"\n class=\"seq-link\"\n onClick={() => onOpenSeq(seq)}\n aria-label={`Open report #${seq}`}\n >\n #{seq}\n </button>,\n )\n last = m.index + m[0].length\n }\n if (last < value.length) parts.push(value.slice(last))\n return parts.length ? parts : [value]\n}\n","/**\n * CommentBubble — single comment rendered inside the report detail.\n *\n * USER comments float right (the user's own follow-ups). MCP / STAFF /\n * SYSTEM float left and pick up an author label so you can tell at a\n * glance who wrote what. The \"Mhosaic Team\" label for MCP comments\n * matches PNR's pattern; hosts can hide it via a project flag (future).\n */\n\nimport { h } from 'preact'\n\nimport type { WidgetCommentRow } from '../types'\nimport type { StringKey } from './i18n'\nimport { linkifySeq } from './linkifySeq'\n\ninterface CommentBubbleProps {\n comment: WidgetCommentRow\n strings: Record<StringKey, string>\n /** Make \"#NN\" references in the comment body clickable (#85). */\n onOpenSeq?: (seq: number) => void\n}\n\nexport function CommentBubble({ comment, strings, onOpenSeq }: CommentBubbleProps) {\n // The submitter's own follow-ups float right; everything else (operator,\n // agent, system) floats left with an attribution label. Both submitter\n // and operator comments arrive as `author_source = USER` from the\n // backend, so we rely on the server-computed `is_mine` flag to tell\n // them apart instead of guessing from the source enum.\n const isMine = comment.is_mine\n const isAgent = !isMine && comment.author_source === 'mcp'\n const isSystem = !isMine && comment.author_source === 'system'\n const variant: 'mcp' | 'system' | 'staff' = isAgent\n ? 'mcp'\n : isSystem\n ? 'system'\n : 'staff'\n const labelKey: StringKey =\n variant === 'mcp'\n ? 'detail.author.mcp'\n : variant === 'system'\n ? 'detail.author.system'\n : 'detail.author.staff'\n const label = comment.author_label || strings[labelKey]\n // Only attachments the store could presign are renderable (#91).\n const images = (comment.attachments ?? []).filter((a) => a.url)\n return (\n <div class={`comment-bubble ${isMine ? 'is-mine' : 'is-other'}`}>\n {!isMine && label && (\n <div class={`comment-author comment-author--${variant}`}>{label}</div>\n )}\n {comment.body && (\n <div class=\"comment-body\">{linkifySeq(comment.body, onOpenSeq)}</div>\n )}\n {images.length > 0 && (\n <div class=\"comment-attachments\">\n {images.map((a) => (\n <a\n key={a.id}\n class=\"comment-attachment\"\n href={a.url!}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n <img src={a.url!} alt={strings['detail.attachment_alt']} loading=\"lazy\" />\n </a>\n ))}\n </div>\n )}\n <div class=\"comment-time\">{formatTime(comment.created_at)}</div>\n </div>\n )\n}\n\nfunction formatTime(iso: string): string {\n try {\n return new Date(iso).toLocaleString(undefined, {\n dateStyle: 'short',\n timeStyle: 'short',\n })\n } catch {\n return iso\n }\n}\n","/**\n * Helpers for the manual-screenshot-upload UX (v0.6.0).\n *\n * Kept tiny and dependency-free so they can be unit-tested without DOM/jsdom\n * setup. The interesting work — drag/drop, paste, file picker — lives in\n * Form.tsx.\n */\n\nexport const ALLOWED_IMAGE_TYPES = [\n 'image/png',\n 'image/jpeg',\n 'image/webp',\n] as const\n\nexport const MAX_SCREENSHOT_BYTES = 10 * 1024 * 1024 // 10 MB\n\n/** Per-report cap — mirrors the backend SCREENSHOT_MAX_COUNT default. */\nexport const MAX_SCREENSHOTS = 5\n\nexport type ScreenshotValidationError =\n | { kind: 'type' }\n | { kind: 'size'; maxMb: number }\n\nexport function validateScreenshotFile(file: File): ScreenshotValidationError | null {\n if (\n !ALLOWED_IMAGE_TYPES.includes(\n file.type as (typeof ALLOWED_IMAGE_TYPES)[number],\n )\n ) {\n return { kind: 'type' }\n }\n if (file.size > MAX_SCREENSHOT_BYTES) {\n return { kind: 'size', maxMb: MAX_SCREENSHOT_BYTES / (1024 * 1024) }\n }\n return null\n}\n\n/**\n * Truncate a URL while keeping the start and end visible. Used in the form's\n * \"Vous étiez ici\" footer so the user can verify the URL we'll send without\n * the long path overflowing the modal.\n *\n * \"https://app.example.com/very/long/deep/path?a=1&b=2\" (53 chars, max 30)\n * → \"https://app.examp…h?a=1&b=2\"\n */\nexport function truncateUrl(url: string, maxLength = 80): string {\n if (url.length <= maxLength) return url\n const keepStart = Math.floor((maxLength - 1) / 2)\n const keepEnd = maxLength - 1 - keepStart\n return `${url.slice(0, keepStart)}…${url.slice(url.length - keepEnd)}`\n}\n\n/**\n * Return `url` only if its scheme is in the safe-for-href allowlist —\n * `https:` always, `http:` for localhost only. Otherwise return `undefined`,\n * which renders as a non-clickable element (and React/Preact omit the href\n * attribute entirely). This stops a malicious widget user from planting\n * `javascript:`, `data:`, `vbscript:`, or `blob:` URLs in their report's\n * `page_url` / `screenshot_url` and getting an operator's click to execute\n * arbitrary code in the widget host's origin.\n */\nexport function safeExternalHref(url: string | null | undefined): string | undefined {\n if (!url) return undefined\n let parsed: URL\n try {\n parsed = new URL(url)\n } catch {\n return undefined\n }\n if (parsed.protocol === 'https:') return parsed.toString()\n if (\n parsed.protocol === 'http:' &&\n (parsed.hostname === 'localhost' ||\n parsed.hostname === '127.0.0.1' ||\n parsed.hostname === '[::1]')\n ) {\n return parsed.toString()\n }\n return undefined\n}\n\n","/**\n * ChangelogList — the \"This week\" tab.\n *\n * Renders the user's resolved reports grouped by ISO week of resolution\n * date, most recent week first. Trust-building surface borrowed from\n * thePnr's `FeedbackChangelogView` (\"see what's been fixed\"). Polls every\n * 30 s while open, same cadence as MineList.\n *\n * Scope is the submitter's own resolved reports only — multi-tenant\n * privacy default. A per-project \"show all-project changelog to\n * customers\" setting can expand the scope in a later release.\n */\n\nimport { h } from 'preact'\nimport { useEffect, useMemo, useRef, useState } from 'preact/hooks'\n\nimport type { ApiClient } from '../api/client'\nimport type { WidgetChangelogRow } from '../types'\nimport type { StringKey } from './i18n'\nimport { startPoll } from './poll'\nimport { ReportRow } from './ReportRow'\n\ninterface ChangelogListProps {\n api: ApiClient\n externalId: string\n strings: Record<StringKey, string>\n onSelect: (report: WidgetChangelogRow) => void\n}\n\nconst POLL_MS = 30_000\n\ninterface WeekGroup {\n /** ISO date (YYYY-MM-DD) of the Monday anchoring this week. */\n weekKey: string\n /** Localized \"Week of {Mon DD, YYYY}\" label, computed once per group. */\n label: string\n rows: WidgetChangelogRow[]\n}\n\n/**\n * Week-of-resolution key for a row. Anchored on Monday in UTC so the\n * grouping is consistent across timezones — the user-visible label is\n * localized at render time.\n */\nexport function isoWeekKey(iso: string): string {\n const d = new Date(iso)\n if (Number.isNaN(d.getTime())) return ''\n // JS Sunday=0; we want Monday=0 to anchor weeks the way thePnr does.\n const day = (d.getUTCDay() + 6) % 7\n const monday = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate() - day))\n return monday.toISOString().slice(0, 10)\n}\n\nexport function groupRowsByWeek(rows: WidgetChangelogRow[]): WeekGroup[] {\n const buckets = new Map<string, WidgetChangelogRow[]>()\n for (const row of rows) {\n const key = isoWeekKey(row.resolved_at)\n if (!key) continue\n const existing = buckets.get(key)\n if (existing) existing.push(row)\n else buckets.set(key, [row])\n }\n // Sort buckets by week descending (most recent week first); within each\n // bucket the server already returned -resolved_at order.\n return Array.from(buckets.entries())\n .sort(([a], [b]) => (a < b ? 1 : -1))\n .map(([weekKey, weekRows]) => ({\n weekKey,\n label: formatWeekLabel(weekKey),\n rows: weekRows,\n }))\n}\n\nfunction formatWeekLabel(weekKey: string): string {\n // Render just the date portion (\"May 5, 2026\"); the i18n template\n // wraps it as \"Week of {date}\".\n try {\n return new Date(weekKey).toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n })\n } catch {\n return weekKey\n }\n}\n\nexport function ChangelogList({ api, externalId, strings, onSelect }: ChangelogListProps) {\n const [rows, setRows] = useState<WidgetChangelogRow[] | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [refreshing, setRefreshing] = useState(false)\n const mountedRef = useRef(true)\n\n const fetchRows = async () => {\n setRefreshing(true)\n setError(null)\n try {\n const next = await api.listChangelog(externalId)\n if (!mountedRef.current) return true\n setRows(next)\n return true\n } catch (err) {\n // Friendly UI fallback; raw error to console for the developer.\n if (typeof console !== 'undefined')\n console.warn('[mhosaic] listChangelog:', err)\n if (!mountedRef.current) return false\n setError(strings['mine.error'])\n return false\n } finally {\n if (mountedRef.current) setRefreshing(false)\n }\n }\n\n useEffect(() => {\n mountedRef.current = true\n const poll = startPoll(fetchRows, POLL_MS)\n return () => {\n mountedRef.current = false\n poll.stop()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [externalId])\n\n const groups = useMemo(() => (rows ? groupRowsByWeek(rows) : []), [rows])\n const isLoading = rows === null && !error\n const isEmpty = rows !== null && rows.length === 0\n\n return (\n <div class=\"mine-list\">\n <div class=\"mine-list-header\">\n <h2>{strings['tab.changelog']}</h2>\n <button\n type=\"button\"\n class=\"btn\"\n onClick={() => {\n void fetchRows()\n }}\n disabled={refreshing}\n >\n {refreshing ? strings['mine.loading'] : strings['mine.refresh']}\n </button>\n </div>\n {isLoading && <div class=\"mine-loading\">{strings['mine.loading']}</div>}\n {error && <div class=\"error\">{error}</div>}\n {isEmpty && (\n <div class=\"mine-empty\">\n <strong>{strings['changelog.empty.title']}</strong>\n <p>{strings['changelog.empty.body']}</p>\n </div>\n )}\n {groups.length > 0 && (\n <div class=\"changelog-groups\">\n {groups.map((g) => (\n <section class=\"changelog-group\" key={g.weekKey}>\n <header class=\"changelog-group-header\">\n <span class=\"changelog-group-marker\" aria-hidden=\"true\">●</span>\n <span class=\"changelog-group-label\">\n {strings['changelog.week_of'].replace('{date}', g.label)}\n </span>\n <span class=\"changelog-group-rule\" aria-hidden=\"true\" />\n <span class=\"changelog-group-count\">\n {strings[\n g.rows.length === 1 ? 'changelog.resolved_one' : 'changelog.resolved_many'\n ].replace('{count}', String(g.rows.length))}\n </span>\n </header>\n <ul class=\"mine-rows\">\n {g.rows.map((row) => (\n <li>\n <ReportRow row={row} strings={strings} onClick={() => onSelect(row)} />\n </li>\n ))}\n </ul>\n </section>\n ))}\n </div>\n )}\n </div>\n )\n}\n","/**\n * ReportRow — single list item in MineList.\n *\n * Shows status / type / severity badges, a one-line description preview,\n * a relative timestamp, and a \"N replies\" hint pulled from the\n * comment_count annotation on the backend. Whole row is the click\n * target so keyboard nav (Tab + Enter) works.\n */\n\nimport { h } from 'preact'\n\nimport type { WidgetReportRow } from '../types'\nimport type { StringKey } from './i18n'\n\ninterface ReportRowProps {\n row: WidgetReportRow\n strings: Record<StringKey, string>\n onClick: () => void\n}\n\nfunction statusClassName(status: string): string {\n return `pill pill-status pill-status--${status}`\n}\n\nfunction severityClassName(severity: string): string {\n return `pill pill-severity pill-severity--${severity}`\n}\n\nfunction typeClassName(): string {\n return 'pill pill-type'\n}\n\nfunction formatRelative(iso: string): string {\n const then = Date.parse(iso)\n if (!Number.isFinite(then)) return ''\n const seconds = Math.max(1, Math.round((Date.now() - then) / 1000))\n if (seconds < 60) return `${seconds}s`\n const minutes = Math.round(seconds / 60)\n if (minutes < 60) return `${minutes}m`\n const hours = Math.round(minutes / 60)\n if (hours < 48) return `${hours}h`\n const days = Math.round(hours / 24)\n return `${days}d`\n}\n\nfunction repliesLabel(count: number, strings: Record<StringKey, string>): string {\n if (count === 1) return strings['mine.replies_one']\n return strings['mine.replies_many'].replace('{count}', String(count))\n}\n\nexport function ReportRow({ row, strings, onClick }: ReportRowProps) {\n const preview =\n row.description.length > 120\n ? row.description.slice(0, 117) + '…'\n : row.description\n return (\n <button type=\"button\" class=\"mine-row\" onClick={onClick}>\n <div class=\"mine-row-pills\">\n <span class={statusClassName(row.status)}>{strings[`status.${row.status}` as StringKey] ?? row.status}</span>\n <span class={typeClassName()}>{strings[`type.${row.feedback_type}` as StringKey]}</span>\n <span class={severityClassName(row.severity)}>{strings[`severity.${row.severity}` as StringKey]}</span>\n </div>\n <div class=\"mine-row-preview\">{preview}</div>\n <div class=\"mine-row-meta\">\n <span>{formatRelative(row.updated_at || row.created_at)}</span>\n {row.comment_count > 0 && <span>· {repliesLabel(row.comment_count, strings)}</span>}\n </div>\n </button>\n )\n}\n","import { h } from 'preact'\n\ninterface FabProps {\n label: string\n onClick: () => void\n /** Open reports on the current page. 0/undefined → no badge. Display caps at 9+. */\n count?: number\n}\n\n/**\n * Bug glyph from lucide.dev (`Bug` icon), inlined as a single-color SVG.\n * Sized at 20×20 inside the 48×48 FAB so the icon reads as a confident\n * accent rather than dominating the circle (vs the prior 24/56 ratio,\n * which felt heavy). Picks up `currentColor` from `--mfb-fab-icon`\n * set on the .fab class.\n */\nfunction BugIcon() {\n return (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n <path d=\"m8 2 1.88 1.88\" />\n <path d=\"M14.12 3.88 16 2\" />\n <path d=\"M9 7.13v-1a3.003 3.003 0 1 1 6 0v1\" />\n <path d=\"M12 20c-3.3 0-6-2.7-6-6v-3a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v3c0 3.3-2.7 6-6 6\" />\n <path d=\"M12 20v-9\" />\n <path d=\"M6.53 9C4.6 8.8 3 7.1 3 5\" />\n <path d=\"M6 13H2\" />\n <path d=\"M3 21c0-2.1 1.7-3.9 3.8-4\" />\n <path d=\"M20.97 5c0 2.1-1.6 3.8-3.5 4\" />\n <path d=\"M22 13h-4\" />\n <path d=\"M17.2 17c2.1.1 3.8 1.9 3.8 4\" />\n </svg>\n )\n}\n\nexport function Fab({ label, onClick, count }: FabProps) {\n return (\n <button type=\"button\" class=\"fab\" aria-label={label} title={label} onClick={onClick}>\n <BugIcon />\n {count !== undefined && count > 0 && (\n <span class=\"fab-badge\" aria-hidden=\"true\">\n {count > 9 ? '9+' : String(count)}\n </span>\n )}\n </button>\n )\n}\n","import { h } from 'preact'\nimport { useEffect, useRef, useState } from 'preact/hooks'\n\nimport type { FeedbackSeverity, FeedbackType } from '../types'\nimport { Annotator } from './Annotator'\nimport type { StringKey } from './i18n'\nimport { MAX_SCREENSHOTS, truncateUrl, validateScreenshotFile } from './screenshot-utils'\n\nexport type FormStatus = 'idle' | 'submitting' | 'error' | 'success'\n\nexport interface FormValues {\n description: string\n feedback_type: FeedbackType\n severity: FeedbackSeverity\n /** Optional user-attached screenshots — via upload, paste, drag-drop, or\n * the annotator, in attach order (max MAX_SCREENSHOTS). v0.12 dropped\n * html2canvas-on-submit; v0.7.1 dropped the getDisplayMedia \"Capture this\n * page\" path (asked the operator for screen share permission on every\n * click, too high a friction for the value). */\n screenshots?: Blob[]\n /** Always \"manual\" when screenshots are attached — kept as a discriminated\n * field so the backend schema (which still accepts the legacy values) can\n * evolve independently. Undefined when no screenshot is attached. */\n capture_method?: 'manual'\n}\n\ninterface AttachedShot {\n blob: Blob\n preview: string\n}\n\ninterface FormProps {\n strings: Record<StringKey, string>\n onSubmit: (values: FormValues) => void\n onCancel: () => void\n status: FormStatus\n errorMessage?: string\n /** Sequence number of the just-created report, surfaced in the success\n * acknowledgement (\"report #42 sent\") so the submitter knows it\n * persisted (#82). Absent → the generic \"sent\" copy is shown. */\n submittedSeq?: number\n /** Reports whether the form holds unsaved input (typed text or attached\n * screenshots). The mount uses it to guard an accidental modal close (#89). */\n onDirtyChange?: (dirty: boolean) => void\n}\n\nconst TYPES: FeedbackType[] = ['bug', 'feature', 'question', 'praise', 'typo']\nconst SEVERITIES: FeedbackSeverity[] = ['blocker', 'high', 'medium', 'low']\n\nexport function Form({\n strings,\n onSubmit,\n onCancel,\n status,\n errorMessage,\n submittedSeq,\n onDirtyChange,\n}: FormProps) {\n const [description, setDescription] = useState('')\n const [feedbackType, setFeedbackType] = useState<FeedbackType>('bug')\n const [severity, setSeverity] = useState<FeedbackSeverity>('medium')\n const [localError, setLocalError] = useState('')\n const [shots, setShots] = useState<AttachedShot[]>([])\n const [isDragOver, setIsDragOver] = useState(false)\n const [annotatingIndex, setAnnotatingIndex] = useState<number | null>(null)\n const fileInputRef = useRef<HTMLInputElement | null>(null)\n const dropZoneRef = useRef<HTMLDivElement | null>(null)\n\n const submitting = status === 'submitting'\n const submitLabel = submitting ? strings['form.submitting'] : strings['form.submit']\n const pageUrl = typeof window !== 'undefined' ? window.location.href : ''\n\n // Report unsaved-input state up so the mount can guard an accidental close\n // (backdrop/Esc) with a discard confirmation (#89).\n const isDirty = description.trim() !== '' || shots.length > 0\n useEffect(() => {\n onDirtyChange?.(isDirty)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isDirty])\n\n // Revoke every object URL when the form unmounts; individual revokes\n // happen on remove/replace.\n const shotsRef = useRef(shots)\n shotsRef.current = shots\n useEffect(() => {\n return () => {\n shotsRef.current.forEach((s) => URL.revokeObjectURL(s.preview))\n }\n }, [])\n\n const acceptFiles = (files: Array<File | Blob>) => {\n setLocalError('')\n const remaining = MAX_SCREENSHOTS - shots.length\n const batch = files.slice(0, Math.max(0, remaining))\n for (const file of batch) {\n if (file instanceof File) {\n const err = validateScreenshotFile(file)\n if (err) {\n setLocalError(\n err.kind === 'type'\n ? strings['form.screenshot.error_type']\n : strings['form.screenshot.error_size'].replace('{max}', String(err.maxMb)),\n )\n return\n }\n }\n }\n if (batch.length) {\n const incoming = batch.map((blob) => ({ blob, preview: URL.createObjectURL(blob) }))\n setShots((prev) => [...prev, ...incoming])\n }\n if (files.length > batch.length) {\n setLocalError(\n strings['form.screenshot.error_count'].replace('{max}', String(MAX_SCREENSHOTS)),\n )\n }\n }\n\n const removeShot = (index: number) => {\n const shot = shots[index]\n if (shot) URL.revokeObjectURL(shot.preview)\n setShots((prev) => prev.filter((_, i) => i !== index))\n setLocalError('')\n if (fileInputRef.current) fileInputRef.current.value = ''\n }\n\n const handleFileInputChange = (e: Event) => {\n const input = e.target as HTMLInputElement\n const files = Array.from(input.files ?? [])\n if (files.length) acceptFiles(files)\n // Reset so picking the same file again re-triggers `change`.\n input.value = ''\n }\n\n const handleDragOver = (e: DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n setIsDragOver(true)\n }\n const handleDragLeave = (e: DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n if (e.currentTarget === e.target) setIsDragOver(false)\n }\n const handleDrop = (e: DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n setIsDragOver(false)\n const files = Array.from(e.dataTransfer?.files ?? [])\n if (files.length) acceptFiles(files)\n }\n\n // Clipboard paste, scoped to the dropzone — so we don't hijack paste from\n // textareas elsewhere in the page or in our own description field.\n useEffect(() => {\n const zone = dropZoneRef.current\n if (!zone) return\n const onPaste = (e: ClipboardEvent) => {\n const items = e.clipboardData?.items\n if (!items) return\n for (const item of Array.from(items)) {\n if (item.kind === 'file' && item.type.startsWith('image/')) {\n const file = item.getAsFile()\n if (file) {\n e.preventDefault()\n acceptFiles([file])\n return\n }\n }\n }\n }\n zone.addEventListener('paste', onPaste)\n return () => zone.removeEventListener('paste', onPaste)\n // The dropzone unmounts at the cap and remounts when a slot frees up,\n // so re-attach whenever the count changes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [shots.length])\n\n const handleAnnotated = (annotated: Blob) => {\n const index = annotatingIndex\n if (index === null || !shots[index]) {\n setAnnotatingIndex(null)\n return\n }\n URL.revokeObjectURL(shots[index].preview)\n const replacement = { blob: annotated, preview: URL.createObjectURL(annotated) }\n setShots((prev) => prev.map((s, i) => (i === index ? replacement : s)))\n setAnnotatingIndex(null)\n }\n\n const handleSubmit = (e: Event) => {\n e.preventDefault()\n if (!description.trim()) {\n setLocalError(strings['form.description.required'])\n return\n }\n setLocalError('')\n const values: FormValues = {\n description: description.trim(),\n feedback_type: feedbackType,\n severity,\n }\n if (shots.length) {\n values.screenshots = shots.map((s) => s.blob)\n values.capture_method = 'manual'\n }\n onSubmit(values)\n }\n\n return (\n <form onSubmit={handleSubmit}>\n <h2>{strings['form.title']}</h2>\n\n <div class=\"field\">\n <label for=\"mfb-desc\">{strings['form.description.label']}</label>\n <textarea\n id=\"mfb-desc\"\n value={description}\n placeholder={strings['form.description.placeholder']}\n onInput={(e) => setDescription((e.target as HTMLTextAreaElement).value)}\n />\n </div>\n\n <div class=\"row\">\n <div class=\"field\">\n <label for=\"mfb-type\">{strings['form.type.label']}</label>\n <select\n id=\"mfb-type\"\n value={feedbackType}\n onChange={(e) => setFeedbackType((e.target as HTMLSelectElement).value as FeedbackType)}\n >\n {TYPES.map((t) => <option value={t}>{strings[`type.${t}` as StringKey]}</option>)}\n </select>\n </div>\n <div class=\"field\">\n <label for=\"mfb-sev\">{strings['form.severity.label']}</label>\n <select\n id=\"mfb-sev\"\n value={severity}\n onChange={(e) => setSeverity((e.target as HTMLSelectElement).value as FeedbackSeverity)}\n >\n {SEVERITIES.map((s) => <option value={s}>{strings[`severity.${s}` as StringKey]}</option>)}\n </select>\n </div>\n </div>\n\n <div class=\"field\">\n <label>{strings['form.screenshot.label']}</label>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/png,image/jpeg,image/webp\"\n multiple\n class=\"mfb-sr-only\"\n onChange={handleFileInputChange}\n aria-hidden=\"true\"\n tabIndex={-1}\n />\n {shots.length > 0 && (\n <div class=\"screenshot-strip\">\n {shots.map((shot, index) => (\n <div class=\"screenshot-preview\" key={shot.preview}>\n <img src={shot.preview} alt=\"\" />\n <div class=\"screenshot-preview-actions\">\n <button\n type=\"button\"\n class=\"btn btn--primary screenshot-annotate\"\n onClick={() => setAnnotatingIndex(index)}\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\"/><path d=\"M18.5 2.5a2.12 2.12 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z\"/></svg>\n {strings['form.screenshot.annotate']}\n </button>\n <button type=\"button\" class=\"btn\" onClick={() => removeShot(index)}>\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6h14z\"/></svg>\n {strings['form.screenshot.remove']}\n </button>\n </div>\n </div>\n ))}\n </div>\n )}\n {shots.length < MAX_SCREENSHOTS && (\n <div\n ref={dropZoneRef}\n class={`screenshot-dropzone ${isDragOver ? 'is-dragover' : ''}`}\n tabIndex={0}\n role=\"button\"\n aria-label={strings['form.screenshot.label']}\n onClick={() => fileInputRef.current?.click()}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n fileInputRef.current?.click()\n }\n }}\n onDragOver={handleDragOver}\n onDragLeave={handleDragLeave}\n onDrop={handleDrop}\n >\n <svg class=\"screenshot-icon\" width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/>\n <polyline points=\"17 8 12 3 7 8\"/>\n <line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\"/>\n </svg>\n <div class=\"screenshot-cta\">\n <strong>{strings['form.screenshot.cta_click']}</strong>\n {', '}\n {strings['form.screenshot.cta_rest']}\n </div>\n <div class=\"screenshot-formats\">{strings['form.screenshot.formats']}</div>\n </div>\n )}\n </div>\n\n {pageUrl && (\n <div class=\"page-context\" title={pageUrl}>\n <span class=\"page-context-label\">{strings['form.context.label']}</span>\n <span class=\"page-context-url\">{truncateUrl(pageUrl, 90)}</span>\n </div>\n )}\n\n {/* REQ-A7: be transparent that technical context is captured. */}\n <div class=\"capture-notice\">{strings['form.capture.notice']}</div>\n\n {localError && <div class=\"error\">{localError}</div>}\n {status === 'error' && errorMessage && <div class=\"error\">{errorMessage}</div>}\n {status === 'success' && (\n <div class=\"success\">\n {submittedSeq !== undefined\n ? strings['form.success.seq'].replace('{seq}', String(submittedSeq))\n : strings['form.success']}\n </div>\n )}\n\n <div class=\"actions\">\n <button type=\"button\" class=\"btn\" onClick={onCancel} disabled={submitting}>{strings['form.cancel']}</button>\n <button type=\"submit\" class=\"btn btn--primary\" disabled={submitting}>{submitLabel}</button>\n </div>\n\n {annotatingIndex !== null && shots[annotatingIndex] && (\n <Annotator\n imageBlob={shots[annotatingIndex].blob}\n strings={strings}\n onCancel={() => setAnnotatingIndex(null)}\n onSave={handleAnnotated}\n />\n )}\n </form>\n )\n}\n","/**\n * Annotator — screenshot annotation modal.\n *\n * v0.6.0: rectangle, arrow, freehand, text. Custom canvas, no external lib.\n * v0.10.0: + highlight (semi-transparent fill), blur (pixelate sensitive\n * regions), full undo/redo stack, native color picker alongside swatches.\n *\n * Save rasterizes the original image + every shape onto a fresh canvas\n * and returns a PNG Blob. Blurs are always sampled from the source image\n * pixels (not the canvas), so strokes/text/highlights drawn near a blurred\n * region don't get re-blurred by accident.\n */\n\nimport { h } from 'preact'\nimport { useEffect, useLayoutEffect, useRef, useState } from 'preact/hooks'\n\nimport type { StringKey } from './i18n'\n\ntype Tool = 'rectangle' | 'arrow' | 'freehand' | 'text' | 'highlight' | 'blur'\n\ninterface BaseShape {\n color: string\n lineWidth: number\n}\n\ninterface RectShape extends BaseShape {\n kind: 'rectangle'\n x: number\n y: number\n w: number\n h: number\n}\n\ninterface ArrowShape extends BaseShape {\n kind: 'arrow'\n x1: number\n y1: number\n x2: number\n y2: number\n}\n\ninterface FreehandShape extends BaseShape {\n kind: 'freehand'\n points: Array<{ x: number; y: number }>\n}\n\ninterface TextShape extends BaseShape {\n kind: 'text'\n x: number\n y: number\n text: string\n fontSize: number\n}\n\ninterface HighlightShape extends BaseShape {\n kind: 'highlight'\n x: number\n y: number\n w: number\n h: number\n}\n\ninterface BlurShape extends BaseShape {\n kind: 'blur'\n x: number\n y: number\n w: number\n h: number\n /** Tile size in source-image pixels. Scales with canvas width. */\n tile: number\n}\n\ntype Shape =\n | RectShape\n | ArrowShape\n | FreehandShape\n | TextShape\n | HighlightShape\n | BlurShape\n\nconst COLORS: string[] = ['#ef4444', '#f59e0b', '#10b981', '#3b82f6', '#ffffff']\nconst HIGHLIGHT_ALPHA = 0.35\n\n// ---------------------------------------------------------------------------\n// Drawing helpers\n// ---------------------------------------------------------------------------\n\nfunction drawShape(\n ctx: CanvasRenderingContext2D,\n shape: Shape,\n sourceImage: HTMLImageElement | null,\n) {\n ctx.save()\n ctx.strokeStyle = shape.color\n ctx.fillStyle = shape.color\n ctx.lineWidth = shape.lineWidth\n ctx.lineCap = 'round'\n ctx.lineJoin = 'round'\n\n if (shape.kind === 'rectangle') {\n ctx.strokeRect(shape.x, shape.y, shape.w, shape.h)\n } else if (shape.kind === 'arrow') {\n drawArrow(ctx, shape.x1, shape.y1, shape.x2, shape.y2)\n } else if (shape.kind === 'freehand') {\n if (shape.points.length < 2) {\n ctx.restore()\n return\n }\n ctx.beginPath()\n ctx.moveTo(shape.points[0]!.x, shape.points[0]!.y)\n for (let i = 1; i < shape.points.length; i++) {\n ctx.lineTo(shape.points[i]!.x, shape.points[i]!.y)\n }\n ctx.stroke()\n } else if (shape.kind === 'text') {\n ctx.font = `bold ${shape.fontSize}px -apple-system, BlinkMacSystemFont, sans-serif`\n ctx.textBaseline = 'top'\n const metrics = ctx.measureText(shape.text)\n const padding = 4\n const w = metrics.width + padding * 2\n const hh = shape.fontSize + padding * 2\n ctx.fillStyle = 'rgba(0, 0, 0, 0.6)'\n ctx.fillRect(shape.x - padding, shape.y - padding, w, hh)\n ctx.fillStyle = shape.color\n ctx.fillText(shape.text, shape.x, shape.y)\n } else if (shape.kind === 'highlight') {\n // Semi-transparent filled rect. Multiply-ish blend so the underlying\n // pixels show through — typical marker-pen look.\n ctx.globalAlpha = HIGHLIGHT_ALPHA\n ctx.fillRect(\n normalizeStart(shape.x, shape.w),\n normalizeStart(shape.y, shape.h),\n Math.abs(shape.w),\n Math.abs(shape.h),\n )\n } else if (shape.kind === 'blur') {\n drawBlur(ctx, shape, sourceImage)\n }\n ctx.restore()\n}\n\nfunction normalizeStart(start: number, size: number): number {\n return size < 0 ? start + size : start\n}\n\nfunction drawArrow(\n ctx: CanvasRenderingContext2D,\n x1: number,\n y1: number,\n x2: number,\n y2: number,\n) {\n const headLen = 14\n const angle = Math.atan2(y2 - y1, x2 - x1)\n ctx.beginPath()\n ctx.moveTo(x1, y1)\n ctx.lineTo(x2, y2)\n ctx.stroke()\n ctx.beginPath()\n ctx.moveTo(x2, y2)\n ctx.lineTo(\n x2 - headLen * Math.cos(angle - Math.PI / 6),\n y2 - headLen * Math.sin(angle - Math.PI / 6),\n )\n ctx.lineTo(\n x2 - headLen * Math.cos(angle + Math.PI / 6),\n y2 - headLen * Math.sin(angle + Math.PI / 6),\n )\n ctx.closePath()\n ctx.fill()\n}\n\n/**\n * Pixelate a rectangular region by averaging tiles of the source image.\n * Always samples from the original `<img>` so blurs over text/strokes\n * still hide the underlying pixels, not the marks drawn on top.\n */\nfunction drawBlur(\n ctx: CanvasRenderingContext2D,\n shape: BlurShape,\n sourceImage: HTMLImageElement | null,\n) {\n if (!sourceImage) return\n const x = normalizeStart(shape.x, shape.w)\n const y = normalizeStart(shape.y, shape.h)\n const w = Math.abs(shape.w)\n const h = Math.abs(shape.h)\n if (w < 2 || h < 2) return\n const tile = Math.max(4, shape.tile)\n // Drop tiles aligned to the source image — pixelation is at native res\n // so the result is crisp regardless of CSS scale on the display canvas.\n for (let yy = y; yy < y + h; yy += tile) {\n for (let xx = x; xx < x + w; xx += tile) {\n const tw = Math.min(tile, x + w - xx)\n const th = Math.min(tile, y + h - yy)\n ctx.drawImage(\n sourceImage,\n xx,\n yy,\n tw,\n th, // source rect from the image\n xx,\n yy,\n tw,\n th, // dest rect on the canvas (same coords)\n )\n }\n }\n // Sampling a single tile-sized chunk and re-painting it at tile size\n // gives the mosaic. To get the visual look, downscale + upscale via\n // imageSmoothingDisabled.\n ctx.imageSmoothingEnabled = false\n for (let yy = y; yy < y + h; yy += tile) {\n for (let xx = x; xx < x + w; xx += tile) {\n const tw = Math.min(tile, x + w - xx)\n const th = Math.min(tile, y + h - yy)\n // Sample the top-left pixel of this tile from the source and stretch.\n ctx.drawImage(\n sourceImage,\n xx,\n yy,\n 1,\n 1, // single source pixel\n xx,\n yy,\n tw,\n th, // stretched dest tile\n )\n }\n }\n ctx.imageSmoothingEnabled = true\n}\n\n// ---------------------------------------------------------------------------\n// Inline icons\n// ---------------------------------------------------------------------------\n\nconst Icon = {\n rect: (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <rect x=\"2\" y=\"3\" width=\"12\" height=\"10\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" />\n </svg>\n ),\n arrow: (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M2 8h11M9 4l4 4-4 4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n ),\n pencil: (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M11.5 2.5l2 2L5 13H3v-2l8.5-8.5z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\" />\n </svg>\n ),\n text: (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M3 3h10M8 3v10M5 13h6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n ),\n highlight: (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M3 13l3-3L11 5l-2-2-5 5-3 3v2h2zM10 4l2 2\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n <path d=\"M2 14h12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n ),\n blur: (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <rect x=\"2\" y=\"2\" width=\"4\" height=\"4\" fill=\"currentColor\" opacity=\"0.85\" />\n <rect x=\"7\" y=\"2\" width=\"3\" height=\"3\" fill=\"currentColor\" opacity=\"0.55\" />\n <rect x=\"11\" y=\"2\" width=\"3\" height=\"4\" fill=\"currentColor\" opacity=\"0.4\" />\n <rect x=\"2\" y=\"7\" width=\"3\" height=\"3\" fill=\"currentColor\" opacity=\"0.6\" />\n <rect x=\"6\" y=\"7\" width=\"3\" height=\"3\" fill=\"currentColor\" opacity=\"0.85\" />\n <rect x=\"10\" y=\"7\" width=\"4\" height=\"3\" fill=\"currentColor\" opacity=\"0.5\" />\n <rect x=\"2\" y=\"11\" width=\"4\" height=\"3\" fill=\"currentColor\" opacity=\"0.4\" />\n <rect x=\"7\" y=\"11\" width=\"3\" height=\"3\" fill=\"currentColor\" opacity=\"0.65\" />\n <rect x=\"11\" y=\"11\" width=\"3\" height=\"3\" fill=\"currentColor\" opacity=\"0.85\" />\n </svg>\n ),\n undo: (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M4 7l3-3M4 7l3 3M4 7h6a3 3 0 0 1 0 6H7\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n ),\n redo: (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M12 7l-3-3M12 7l-3 3M12 7H6a3 3 0 0 0 0 6h2\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n ),\n trash: (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M3 4h10M6 4V2.5h4V4M5 4l.5 9h5L11 4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n ),\n close: (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M4 4l8 8M12 4l-8 8\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n ),\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nexport interface AnnotatorProps {\n imageBlob: Blob\n strings: Record<StringKey, string>\n onSave: (annotated: Blob) => void\n onCancel: () => void\n}\n\nexport function Annotator({ imageBlob, strings, onSave, onCancel }: AnnotatorProps) {\n const canvasRef = useRef<HTMLCanvasElement | null>(null)\n const containerRef = useRef<HTMLDivElement | null>(null)\n const imageRef = useRef<HTMLImageElement | null>(null)\n const [tool, setTool] = useState<Tool>('rectangle')\n const [color, setColor] = useState<string>(COLORS[0]!)\n const [shapes, setShapes] = useState<Shape[]>([])\n const [past, setPast] = useState<Shape[][]>([])\n const [future, setFuture] = useState<Shape[][]>([])\n const isDrawingRef = useRef(false)\n const [draftShape, setDraftShape] = useState<Shape | null>(null)\n const [imageLoaded, setImageLoaded] = useState(false)\n const [saving, setSaving] = useState(false)\n const scaleRef = useRef(1)\n\n function addShape(shape: Shape) {\n setShapes((s) => {\n // Use the functional setState so we read the most recent shape list\n // even when this fires from a stale-closure event handler. The past\n // stack captures `s` here (the truly current state).\n setPast((p) => [...p, s])\n return [...s, shape]\n })\n setFuture([])\n }\n\n function clearShapes() {\n setShapes((s) => {\n setPast((p) => [...p, s])\n return []\n })\n setFuture([])\n }\n\n function undo() {\n setPast((p) => {\n if (p.length === 0) return p\n const prev = p[p.length - 1]!\n setShapes((s) => {\n setFuture((f) => [s, ...f])\n return prev\n })\n return p.slice(0, -1)\n })\n }\n\n function redo() {\n setFuture((f) => {\n if (f.length === 0) return f\n const next = f[0]!\n setShapes((s) => {\n setPast((p) => [...p, s])\n return next\n })\n return f.slice(1)\n })\n }\n\n // Esc handler uses useLayoutEffect (not useEffect) so the listener is\n // attached SYNCHRONOUSLY after DOM commit, BEFORE paint. Tests (and\n // fast human users) that press Escape the moment the annotator becomes\n // visible would otherwise hit a brief window where the listener isn't\n // yet attached. The parent Modal's keydown handler bails when it sees\n // .annotator-backdrop in the shadow root, so we don't double-fire.\n useLayoutEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.stopPropagation()\n onCancel()\n }\n }\n window.addEventListener('keydown', onKey)\n return () => window.removeEventListener('keydown', onKey)\n }, [onCancel])\n\n // Cmd/Ctrl-Z = undo, Cmd/Ctrl-Shift-Z (or Cmd/Ctrl-Y) = redo. Re-binds\n // on each state mutation to capture fresh shapes/past/future closures.\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n const mod = e.metaKey || e.ctrlKey\n if (!mod) return\n const k = e.key.toLowerCase()\n if (k === 'z') {\n e.preventDefault()\n e.stopPropagation()\n if (e.shiftKey) redo()\n else undo()\n } else if (k === 'y') {\n e.preventDefault()\n e.stopPropagation()\n redo()\n }\n }\n window.addEventListener('keydown', onKey)\n return () => window.removeEventListener('keydown', onKey)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [past, future, shapes])\n\n useEffect(() => {\n const url = URL.createObjectURL(imageBlob)\n const img = new Image()\n img.onload = () => {\n imageRef.current = img\n setImageLoaded(true)\n }\n img.src = url\n return () => URL.revokeObjectURL(url)\n }, [imageBlob])\n\n // useLayoutEffect (not useEffect) so the canvas's width/height/style\n // are set SYNCHRONOUSLY after Preact's render commit and BEFORE the\n // browser's paint. Without this, the canvas first mounts at the HTML\n // default 300×150 and only resizes to the image's dimensions on the\n // next microtask — a window during which Playwright's\n // `canvas.boundingBox()` can read the stale layout. Tests then fire\n // mouse events at coordinates that no longer land inside the canvas,\n // mousedown is dispatched to the wrap/backdrop instead, no draftShape\n // is created, and `.annotator-count` stays at 0 → flake.\n useLayoutEffect(() => {\n if (\n !imageLoaded ||\n !canvasRef.current ||\n !imageRef.current ||\n !containerRef.current\n ) {\n return\n }\n const img = imageRef.current\n const container = containerRef.current\n const maxW = container.clientWidth - 16\n const maxH = window.innerHeight * 0.65\n const fitScale = Math.min(maxW / img.width, maxH / img.height, 1)\n scaleRef.current = fitScale\n\n const canvas = canvasRef.current\n canvas.width = img.width\n canvas.height = img.height\n canvas.style.width = `${img.width * fitScale}px`\n canvas.style.height = `${img.height * fitScale}px`\n redraw()\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [imageLoaded])\n\n useEffect(() => {\n redraw()\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [shapes, draftShape])\n\n function redraw() {\n const canvas = canvasRef.current\n const img = imageRef.current\n if (!canvas || !img) return\n const ctx = canvas.getContext('2d')\n if (!ctx) return\n ctx.clearRect(0, 0, canvas.width, canvas.height)\n ctx.drawImage(img, 0, 0)\n for (const s of shapes) drawShape(ctx, s, img)\n if (draftShape) drawShape(ctx, draftShape, img)\n }\n\n function getCanvasCoords(e: MouseEvent) {\n const canvas = canvasRef.current!\n const rect = canvas.getBoundingClientRect()\n return {\n x: (e.clientX - rect.left) / scaleRef.current,\n y: (e.clientY - rect.top) / scaleRef.current,\n }\n }\n\n const handleMouseDown = (e: MouseEvent) => {\n if (!imageLoaded) return\n const { x, y } = getCanvasCoords(e)\n const canvasW = canvasRef.current!.width\n const lineWidth = Math.max(3, Math.round(canvasW / 400))\n const blurTile = Math.max(8, Math.round(canvasW / 80))\n\n if (tool === 'text') {\n const text = window.prompt(strings['annotator.text_prompt'])\n if (text && text.trim()) {\n addShape({\n kind: 'text',\n x,\n y,\n text: text.trim(),\n color,\n fontSize: Math.max(16, Math.round(canvasW / 50)),\n lineWidth: 1,\n })\n }\n return\n }\n\n isDrawingRef.current = true\n if (tool === 'rectangle') {\n setDraftShape({ kind: 'rectangle', x, y, w: 0, h: 0, color, lineWidth })\n } else if (tool === 'arrow') {\n setDraftShape({ kind: 'arrow', x1: x, y1: y, x2: x, y2: y, color, lineWidth })\n } else if (tool === 'freehand') {\n setDraftShape({ kind: 'freehand', points: [{ x, y }], color, lineWidth })\n } else if (tool === 'highlight') {\n setDraftShape({\n kind: 'highlight',\n x,\n y,\n w: 0,\n h: 0,\n color: color === '#ffffff' ? '#fde047' : color,\n lineWidth: 1,\n })\n } else if (tool === 'blur') {\n setDraftShape({\n kind: 'blur',\n x,\n y,\n w: 0,\n h: 0,\n color: '#000000',\n lineWidth: 0,\n tile: blurTile,\n })\n }\n }\n\n const handleMouseMove = (e: MouseEvent) => {\n if (!isDrawingRef.current) return\n const { x, y } = getCanvasCoords(e)\n // Functional setState: read the most recent draftShape rather than the\n // closure-captured value. Playwright (and fast humans) can fire\n // mousedown→mousemove faster than Preact commits the re-render, leaving\n // the closure's `draftShape` at the previous frame (often null right\n // after mousedown). audit R6 e2e regression fix.\n setDraftShape((current) => {\n if (!current) return current\n if (\n current.kind === 'rectangle' ||\n current.kind === 'highlight' ||\n current.kind === 'blur'\n ) {\n return { ...current, w: x - current.x, h: y - current.y }\n }\n if (current.kind === 'arrow') {\n return { ...current, x2: x, y2: y }\n }\n if (current.kind === 'freehand') {\n return { ...current, points: [...current.points, { x, y }] }\n }\n return current\n })\n }\n\n const handleMouseUp = () => {\n // Same closure-staleness concern as handleMouseMove: read the\n // committed draft via the functional setter rather than rely on the\n // capture from this render.\n setDraftShape((current) => {\n if (isDrawingRef.current && current) {\n const isTiny =\n ((current.kind === 'rectangle' ||\n current.kind === 'highlight' ||\n current.kind === 'blur') &&\n Math.abs(current.w) < 4 &&\n Math.abs(current.h) < 4) ||\n (current.kind === 'arrow' &&\n Math.hypot(\n current.x2 - current.x1,\n current.y2 - current.y1,\n ) < 4) ||\n (current.kind === 'freehand' && current.points.length < 3)\n if (!isTiny) {\n addShape(current)\n }\n }\n return null\n })\n isDrawingRef.current = false\n }\n\n const handleSave = async () => {\n const canvas = canvasRef.current\n if (!canvas) return\n setSaving(true)\n try {\n const blob = await new Promise<Blob | null>((resolve) =>\n canvas.toBlob(resolve, 'image/png', 0.92),\n )\n if (blob) onSave(blob)\n } finally {\n setSaving(false)\n }\n }\n\n const tools: Array<{ id: Tool; icon: typeof Icon.rect; label: string }> = [\n { id: 'rectangle', icon: Icon.rect, label: strings['annotator.tool.rectangle'] },\n { id: 'arrow', icon: Icon.arrow, label: strings['annotator.tool.arrow'] },\n { id: 'freehand', icon: Icon.pencil, label: strings['annotator.tool.freehand'] },\n { id: 'text', icon: Icon.text, label: strings['annotator.tool.text'] },\n { id: 'highlight', icon: Icon.highlight, label: strings['annotator.tool.highlight'] },\n { id: 'blur', icon: Icon.blur, label: strings['annotator.tool.blur'] },\n ]\n\n return (\n <div\n class=\"annotator-backdrop\"\n role=\"presentation\"\n onClick={(e) => {\n if (e.target === e.currentTarget) onCancel()\n }}\n >\n <div class=\"annotator\" role=\"dialog\" aria-modal=\"true\" aria-label={strings['annotator.title']}>\n <div class=\"annotator-header\">\n <span>{strings['annotator.title']}</span>\n <button\n type=\"button\"\n class=\"modal-close\"\n aria-label={strings['form.close']}\n onClick={onCancel}\n >\n {Icon.close}\n </button>\n </div>\n\n <div class=\"annotator-toolbar\">\n <div class=\"annotator-tools\">\n {tools.map((t) => (\n <button\n key={t.id}\n type=\"button\"\n onClick={() => setTool(t.id)}\n title={t.label}\n aria-label={t.label}\n aria-pressed={tool === t.id}\n class={`annotator-tool ${tool === t.id ? 'is-active' : ''}`}\n >\n {t.icon}\n </button>\n ))}\n </div>\n\n <span class=\"annotator-sep\" />\n\n <div class=\"annotator-colors\">\n {COLORS.map((c) => (\n <button\n key={c}\n type=\"button\"\n onClick={() => setColor(c)}\n aria-label={c}\n aria-pressed={color === c}\n class={`annotator-color ${color === c ? 'is-active' : ''}`}\n style={{ backgroundColor: c }}\n />\n ))}\n <label class=\"annotator-color annotator-color-picker\" title={strings['annotator.color_picker']}>\n <input\n type=\"color\"\n value={color}\n onInput={(e) => setColor((e.target as HTMLInputElement).value)}\n aria-label={strings['annotator.color_picker']}\n />\n </label>\n </div>\n\n <span class=\"annotator-sep\" />\n\n <button\n type=\"button\"\n class=\"annotator-btn\"\n onClick={undo}\n disabled={past.length === 0}\n title={strings['annotator.undo']}\n >\n {Icon.undo}\n <span>{strings['annotator.undo']}</span>\n </button>\n <button\n type=\"button\"\n class=\"annotator-btn\"\n onClick={redo}\n disabled={future.length === 0}\n title={strings['annotator.redo']}\n >\n {Icon.redo}\n <span>{strings['annotator.redo']}</span>\n </button>\n <button\n type=\"button\"\n class=\"annotator-btn\"\n onClick={clearShapes}\n disabled={shapes.length === 0}\n >\n {Icon.trash}\n <span>{strings['annotator.clear']}</span>\n </button>\n\n <span class=\"annotator-spacer\" />\n\n <span class=\"annotator-count\">\n {shapes.length} {strings['annotator.count_suffix']}\n </span>\n </div>\n\n <div ref={containerRef} class=\"annotator-canvas-wrap\">\n {!imageLoaded ? (\n <span class=\"annotator-loading\">{strings['annotator.loading']}</span>\n ) : (\n <canvas\n ref={canvasRef}\n onMouseDown={handleMouseDown}\n onMouseMove={handleMouseMove}\n onMouseUp={handleMouseUp}\n onMouseLeave={handleMouseUp}\n class=\"annotator-canvas\"\n />\n )}\n </div>\n\n <div class=\"annotator-footer\">\n <button type=\"button\" class=\"btn\" onClick={onCancel}>\n {strings['form.cancel']}\n </button>\n <button\n type=\"button\"\n class=\"btn btn--primary\"\n onClick={handleSave}\n disabled={saving || !imageLoaded}\n >\n {saving ? strings['annotator.applying'] : strings['annotator.apply']}\n </button>\n </div>\n </div>\n </div>\n )\n}\n","/**\n * MineList — the user's own reports on this project.\n *\n * Polls every 30 s while mounted (Sentry-style staleTime + manual\n * refresh). Empty state when the submitter has no thread yet (404 from\n * the backend is mapped to []). Click a row to open the detail view.\n */\n\nimport { h } from 'preact'\nimport { useEffect, useRef, useState } from 'preact/hooks'\n\nimport type { ApiClient } from '../api/client'\nimport { rateLimitMessage } from './rateLimit'\nimport type { WidgetReportRow } from '../types'\nimport type { StringKey } from './i18n'\nimport { KpiStrip, type KpiFilter, rowsMatchingFilter } from './KpiStrip'\nimport { startPoll } from './poll'\nimport { ReportRow } from './ReportRow'\n\ninterface MineListProps {\n api: ApiClient\n externalId: string\n strings: Record<StringKey, string>\n onSelect: (report: WidgetReportRow) => void\n /** Resolve a report by its per-project #seq and open it. Resolves false\n * when no such report is visible (powers the jump-by-#seq box, #85).\n * When omitted the jump form is not rendered. */\n onOpenSeq?: (seq: number) => Promise<boolean>\n}\n\nconst POLL_MS = 30_000\n\nexport function MineList({ api, externalId, strings, onSelect, onOpenSeq }: MineListProps) {\n const [rows, setRows] = useState<WidgetReportRow[] | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [refreshing, setRefreshing] = useState(false)\n const [filter, setFilter] = useState<KpiFilter>('all')\n const [jumpValue, setJumpValue] = useState('')\n const [jumpError, setJumpError] = useState(false)\n const [jumping, setJumping] = useState(false)\n const mountedRef = useRef(true)\n\n const handleJump = async (e: Event) => {\n e.preventDefault()\n if (!onOpenSeq || jumping) return\n const seq = parseInt(jumpValue.replace(/^#/, '').trim(), 10)\n if (!Number.isInteger(seq) || seq <= 0) {\n setJumpError(true)\n return\n }\n setJumping(true)\n setJumpError(false)\n try {\n const found = await onOpenSeq(seq)\n if (!mountedRef.current) return\n if (found) setJumpValue('')\n else setJumpError(true)\n } finally {\n if (mountedRef.current) setJumping(false)\n }\n }\n\n const fetchRows = async () => {\n setRefreshing(true)\n setError(null)\n try {\n const next = await api.listMine(externalId)\n if (!mountedRef.current) return true\n setRows(next)\n return true\n } catch (err) {\n // Don't surface raw API error messages to the user — they used to\n // render as e.g. `listMine failed: 403 {\"detail\":\"Public API key…\"}`\n // straight into the panel. Friendly fallback in the UI, raw error\n // in the console for the developer.\n if (typeof console !== 'undefined') console.warn('[mhosaic] listMine:', err)\n if (!mountedRef.current) return false\n // A 429 gets an actionable \"retry in Xs\" message; anything else falls\n // back to the generic copy. Either way loading resolves (#80/#81).\n setError(rateLimitMessage(err, strings) ?? strings['mine.error'])\n return false\n } finally {\n if (mountedRef.current) setRefreshing(false)\n }\n }\n\n useEffect(() => {\n mountedRef.current = true\n const poll = startPoll(fetchRows, POLL_MS)\n return () => {\n mountedRef.current = false\n poll.stop()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [externalId])\n\n const isEmpty = rows !== null && rows.length === 0\n const isLoading = rows === null && !error\n const visibleRows = rows ? rowsMatchingFilter(rows, filter) : null\n const visibleEmpty = !!rows && rows.length > 0 && (visibleRows?.length ?? 0) === 0\n\n return (\n <div class=\"mine-list\">\n <div class=\"mine-list-header\">\n <h2>{strings['tab.mine']}</h2>\n <div class=\"mine-list-header-actions\">\n {onOpenSeq && (\n <form class=\"mine-jump\" onSubmit={handleJump}>\n <input\n type=\"text\"\n inputMode=\"numeric\"\n value={jumpValue}\n placeholder={strings['mine.jump.placeholder']}\n aria-label={strings['mine.jump.aria']}\n onInput={(e) => {\n setJumpValue((e.target as HTMLInputElement).value)\n setJumpError(false)\n }}\n disabled={jumping}\n />\n <button type=\"submit\" class=\"btn\" disabled={jumping || !jumpValue.trim()}>\n {strings['mine.jump.go']}\n </button>\n </form>\n )}\n <button\n type=\"button\"\n class=\"btn\"\n onClick={() => {\n void fetchRows()\n }}\n disabled={refreshing}\n >\n {refreshing ? strings['mine.loading'] : strings['mine.refresh']}\n </button>\n </div>\n </div>\n {jumpError && (\n <div class=\"mine-jump-error error\" role=\"alert\">\n {strings['mine.jump.not_found']}\n </div>\n )}\n {rows && rows.length > 0 && (\n <KpiStrip rows={rows} filter={filter} onFilter={setFilter} strings={strings} />\n )}\n {isLoading && <div class=\"mine-loading\">{strings['mine.loading']}</div>}\n {error && <div class=\"error\">{error}</div>}\n {isEmpty && (\n <div class=\"mine-empty\">\n <strong>{strings['mine.empty.title']}</strong>\n <p>{strings['mine.empty.body']}</p>\n </div>\n )}\n {visibleEmpty && (\n <div class=\"mine-empty\">\n <p>{strings['mine.filter.empty']}</p>\n </div>\n )}\n {visibleRows && visibleRows.length > 0 && (\n <ul class=\"mine-rows\">\n {visibleRows.map((row) => (\n <li>\n <ReportRow row={row} strings={strings} onClick={() => onSelect(row)} />\n </li>\n ))}\n </ul>\n )}\n </div>\n )\n}\n","/**\n * KpiStrip — four-pill summary at the top of the user's \"My reports\" tab.\n *\n * Computes counts client-side from the same `WidgetReportRow[]` that\n * MineList already fetches; no extra endpoint. Each pill is clickable\n * and toggles a status filter on the list below — matches thePnr's\n * customer-facing pattern (`FeedbackKPICards.tsx`).\n */\n\nimport { h } from 'preact'\n\nimport type { ReportStatus, WidgetReportRow } from '../types'\nimport type { StringKey } from './i18n'\n\nexport type KpiFilter = 'all' | 'new' | 'in_progress' | 'awaiting_validation' | 'resolved'\n\ninterface KpiStripProps {\n rows: WidgetReportRow[]\n filter: KpiFilter\n onFilter: (next: KpiFilter) => void\n strings: Record<StringKey, string>\n}\n\ninterface Counts {\n new: number\n in_progress: number\n awaiting_validation: number\n resolved: number\n total: number\n}\n\nexport function computeKpiCounts(rows: WidgetReportRow[]): Counts {\n const counts: Counts = {\n new: 0,\n in_progress: 0,\n awaiting_validation: 0,\n resolved: 0,\n total: 0,\n }\n for (const row of rows) {\n counts.total += 1\n switch (row.status) {\n case 'new':\n counts.new += 1\n break\n case 'in_progress':\n counts.in_progress += 1\n break\n case 'awaiting_validation':\n counts.awaiting_validation += 1\n break\n case 'closed':\n case 'rejected':\n case 'duplicate':\n case 'wontfix':\n counts.resolved += 1\n break\n }\n }\n return counts\n}\n\n/**\n * Resolution rate = (awaiting_validation + closed + rejected + duplicate +\n * wontfix) / total. Includes operator-resolved-as-no-action so the\n * percentage reflects \"your reports the team has acted on\" rather than\n * \"your reports that turned into shipped fixes\". Returns null if there\n * are no rows yet — avoids showing \"0%\" before the user has filed\n * anything, which reads as ominous.\n */\nexport function computeResolutionRate(counts: Counts): number | null {\n if (counts.total === 0) return null\n return Math.round(\n ((counts.awaiting_validation + counts.resolved) / counts.total) * 100,\n )\n}\n\nconst FILTER_FOR_STATUS: Record<ReportStatus, KpiFilter> = {\n new: 'new',\n in_progress: 'in_progress',\n awaiting_validation: 'awaiting_validation',\n closed: 'resolved',\n rejected: 'resolved',\n duplicate: 'resolved',\n wontfix: 'resolved',\n}\n\nexport function rowsMatchingFilter(\n rows: WidgetReportRow[],\n filter: KpiFilter,\n): WidgetReportRow[] {\n if (filter === 'all') return rows\n return rows.filter((r) => FILTER_FOR_STATUS[r.status] === filter)\n}\n\nexport function KpiStrip({ rows, filter, onFilter, strings }: KpiStripProps) {\n const counts = computeKpiCounts(rows)\n const resolutionRate = computeResolutionRate(counts)\n const cells: Array<{ key: KpiFilter; label: string; value: string }> = [\n { key: 'new', label: strings['kpi.new'], value: String(counts.new) },\n { key: 'in_progress', label: strings['kpi.in_progress'], value: String(counts.in_progress) },\n {\n key: 'awaiting_validation',\n label: strings['kpi.awaiting_validation'],\n value: String(counts.awaiting_validation),\n },\n {\n key: 'resolved',\n label: strings['kpi.resolution_rate'],\n value: resolutionRate === null ? '—' : `${resolutionRate}%`,\n },\n ]\n return (\n <div class=\"kpi-strip\" role=\"toolbar\">\n {cells.map((c) => {\n const active = filter === c.key\n const toggleTo: KpiFilter = active ? 'all' : c.key\n return (\n <button\n type=\"button\"\n class={`kpi-cell kpi-cell--${c.key}${active ? ' is-active' : ''}`}\n onClick={() => onFilter(toggleTo)}\n aria-pressed={active}\n >\n <span class=\"kpi-value\">{c.value}</span>\n <span class=\"kpi-label\">{c.label}</span>\n </button>\n )\n })}\n </div>\n )\n}\n","import { h, type ComponentChildren } from 'preact'\nimport { useEffect, useRef } from 'preact/hooks'\n\n/** Why the modal is being dismissed. `backdrop`/`escape` are \"accidental\"\n * gestures a caller may want to guard (e.g. unsaved input, #89); `button`\n * is the explicit ✕ and always closes. */\nexport type DismissReason = 'backdrop' | 'escape' | 'button'\n\ninterface ModalProps {\n onDismiss: (reason: DismissReason) => void\n children: ComponentChildren\n closeLabel?: string\n /** v0.12: when true, the modal grows toward near-fullscreen on desktop\n * and full-screen on mobile. Drives the \"transport\" feel for the Board\n * tab — opening the tab feels like clicking through to a real page. */\n expanded?: boolean\n}\n\nexport function Modal({ onDismiss, children, closeLabel = 'Close', expanded = false }: ModalProps) {\n const modalRef = useRef<HTMLDivElement>(null)\n const previouslyFocused = useRef<Element | null>(null)\n\n // Esc-to-close + focus management. Without this, users couldn't dismiss\n // with the keyboard (a real-world UX expectation). Listener installed on\n // window so it works regardless of which element inside the modal has focus.\n useEffect(() => {\n previouslyFocused.current = document.activeElement\n const onKey = (e: KeyboardEvent) => {\n if (e.key !== 'Escape') return\n // Ignore Esc when a topmost dialog (e.g. the Annotator) is mounted in\n // our shadow root — it owns the keystroke. Without this guard the\n // Esc fires both handlers on the same event and dismisses the form\n // behind the annotator.\n const root = modalRef.current?.getRootNode()\n if (\n root instanceof ShadowRoot &&\n root.querySelector('.annotator-backdrop')\n ) {\n return\n }\n e.stopPropagation()\n onDismiss('escape')\n }\n window.addEventListener('keydown', onKey)\n // Focus first focusable element inside the modal so the keyboard user\n // lands inside, not still on the FAB or whatever opened the modal.\n const first = modalRef.current?.querySelector<HTMLElement>(\n 'textarea, input, select, button',\n )\n first?.focus()\n return () => {\n window.removeEventListener('keydown', onKey)\n // Restore focus on dismiss so screen-readers don't lose place.\n const prev = previouslyFocused.current as HTMLElement | null\n if (prev && typeof prev.focus === 'function') prev.focus()\n }\n }, [onDismiss])\n\n return (\n <div\n class={`backdrop ${expanded ? 'is-expanded' : ''}`}\n role=\"presentation\"\n onClick={(e) => {\n if (e.target === e.currentTarget) onDismiss('backdrop')\n }}\n >\n <div\n ref={modalRef}\n class={`modal ${expanded ? 'is-expanded' : ''}`}\n role=\"dialog\"\n aria-modal=\"true\"\n >\n <button\n type=\"button\"\n class=\"modal-close\"\n aria-label={closeLabel}\n onClick={() => onDismiss('button')}\n >\n ×\n </button>\n {children}\n </div>\n </div>\n )\n}\n","/** One passive line on the Send tab: \"N open reports on this page — View\".\n * Dedup affordance (spec §3.3) — never blocks, never steals focus. */\nimport { h } from 'preact'\n\nimport type { StringKey } from './i18n'\n\nexport function PageActivityStrip({\n open,\n strings,\n onView,\n}: {\n open: number\n strings: Record<StringKey, string>\n onView: () => void\n}) {\n if (open === 0) return null\n const text =\n open === 1\n ? strings['page.strip.text.one']\n : strings['page.strip.text'].replace('{n}', String(open))\n return (\n <div class=\"page-strip\">\n <span>{text}</span>\n <button type=\"button\" class=\"page-strip-view\" onClick={onView}>\n {strings['page.strip.view']}\n </button>\n </div>\n )\n}\n","/** Desktop hover peek: up to 5 of this page's reports above the FAB\n * (spec §3.2). Non-modal; failures degrade to the footer actions. */\nimport { h } from 'preact'\nimport { useEffect, useState } from 'preact/hooks'\n\nimport type { ApiClient } from '../api/client'\nimport type { WidgetBoardRow } from '../types'\nimport { currentPagePath } from './currentPage'\nimport type { StringKey } from './i18n'\n\nexport function PagePeekPanel({\n api,\n externalId,\n strings,\n getCurrentPage,\n open,\n onViewAll,\n onPickRow,\n onSend,\n}: {\n api: ApiClient\n externalId: string\n strings: Record<StringKey, string>\n getCurrentPage?: () => string | null\n open: number\n onViewAll: () => void\n onPickRow?: (reportId: string) => void\n onSend: () => void\n}) {\n const [rows, setRows] = useState<WidgetBoardRow[] | null>(null)\n const [failed, setFailed] = useState(false)\n\n useEffect(() => {\n let cancelled = false\n const pagePath = currentPagePath(getCurrentPage !== undefined ? { getCurrentPage } : {})\n api\n .listBoard(externalId, {\n pagePath,\n status: ['new', 'in_progress', 'awaiting_validation'],\n })\n .then((page) => {\n if (!cancelled) setRows(page.results.slice(0, 5))\n })\n .catch(() => {\n if (!cancelled) setFailed(true)\n })\n return () => {\n cancelled = true\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [api, externalId])\n\n return (\n <div class=\"page-peek\" role=\"region\" aria-label={strings['page.peek.title']}>\n <div class=\"page-peek-title\">{strings['page.peek.title']}</div>\n {rows === null && !failed && (\n <div class=\"page-peek-skeleton\" aria-hidden=\"true\">\n <div /> <div />\n </div>\n )}\n {rows !== null &&\n rows.map((row) => (\n <button\n key={row.id}\n type=\"button\"\n class=\"page-peek-row\"\n onClick={() => onPickRow?.(row.id)}\n >\n <span class={`page-peek-dot status-${row.status}`} aria-hidden=\"true\" />\n <span class=\"page-peek-desc\">{row.description}</span>\n </button>\n ))}\n <div class=\"page-peek-footer\">\n <button type=\"button\" class=\"page-peek-viewall\" onClick={onViewAll}>\n {open === 1\n ? strings['page.peek.viewAll.one']\n : strings['page.peek.viewAll'].replace('{n}', String(open))}\n </button>\n <button type=\"button\" class=\"page-peek-send\" onClick={onSend}>\n {strings['fab.label']}\n </button>\n </div>\n </div>\n )\n}\n","/** \"What's open on this page\" — one signal powering the FAB badge, the\n * hover peek and the Send-tab strip (spec 2026-07-06-page-aware-widget).\n * Identity-gated server-side; every failure is silent (surfaces hide). */\nimport { useCallback, useEffect, useState } from 'preact/hooks'\n\nimport type { ApiClient } from '../api/client'\nimport type { WidgetBoardKpis } from '../types'\nimport { currentPagePath, onLocationChange } from './currentPage'\n\nconst TTL_MS = 60_000\n\nexport function computeOpenCount(kpis: WidgetBoardKpis): number {\n const s = kpis.by_status\n return (s.new ?? 0) + (s.in_progress ?? 0) + (s.awaiting_validation ?? 0)\n}\n\n// Module-level so remounts (modal open/close) reuse the fact.\nconst cache = new Map<string, { open: number; fetchedAt: number }>()\nconst inflight = new Map<string, Promise<number>>()\n\nexport function invalidatePageSignal(): void {\n cache.clear()\n}\n\nexport function _resetPageSignalCache(): void {\n cache.clear()\n inflight.clear()\n}\n\nfunction fetchOpen(api: ApiClient, externalId: string, path: string): Promise<number> {\n const key = `${externalId}|${path}`\n const hit = cache.get(key)\n if (hit && Date.now() - hit.fetchedAt < TTL_MS) return Promise.resolve(hit.open)\n const pending = inflight.get(key)\n if (pending) return pending\n const p = api\n .fetchBoardKpis(externalId, { pagePath: path })\n .then((kpis) => {\n const open = computeOpenCount(kpis)\n cache.set(key, { open, fetchedAt: Date.now() })\n return open\n })\n .finally(() => inflight.delete(key))\n inflight.set(key, p)\n return p\n}\n\nexport function usePageOpenCount(opts: {\n api?: ApiClient\n externalId?: string\n getCurrentPage?: () => string | null\n enabled: boolean\n}): { open: number; refresh: () => void } {\n const { api, externalId, getCurrentPage, enabled } = opts\n const [open, setOpen] = useState(0)\n const [tick, setTick] = useState(0)\n\n const refresh = useCallback(() => {\n invalidatePageSignal()\n setTick((t) => t + 1)\n }, [])\n\n useEffect(() => {\n if (!enabled || !api || !externalId) {\n setOpen(0)\n return\n }\n let cancelled = false\n const load = () => {\n const path = currentPagePath(getCurrentPage !== undefined ? { getCurrentPage } : {})\n fetchOpen(api, externalId, path)\n .then((n) => {\n if (!cancelled) setOpen(n)\n })\n .catch(() => {\n // Silent by design — a failing count must never surface UI.\n if (!cancelled) setOpen(0)\n })\n }\n load()\n const unsub = onLocationChange(load)\n return () => {\n cancelled = true\n unsub()\n }\n }, [api, externalId, enabled, tick])\n\n return { open, refresh }\n}\n","export const WIDGET_STYLES = `\n:host {\n --mfb-accent: #3b82f6;\n --mfb-accent-contrast: #ffffff;\n\n /* FAB-specific palette — Mhosaic brand (steel blue family, per\n mhosaic-core's design system v4). Soft-black surface + light\n steel-blue glyph. Kept separate from --mfb-accent on purpose:\n the accent is used everywhere inside the panel for submit\n buttons / focus rings, and we don't want changing the FAB chrome\n to inadvertently restyle the whole widget. */\n --mfb-fab-bg: #1c2230; /* Mhosaic --black (oklch ≈ 0.22 0.025 258) */\n --mfb-fab-icon: #8aa9e5; /* Mhosaic --blue-300 (oklch ≈ 0.70 0.13 258) */\n --mfb-bg: #ffffff;\n --mfb-surface: #f9fafb;\n --mfb-surface-2: #f3f4f6;\n --mfb-text: #0a0a0a;\n --mfb-text-muted: #6b7280;\n --mfb-border: #e5e7eb;\n --mfb-border-strong: #d1d5db;\n --mfb-radius: 8px;\n --mfb-radius-lg: 14px;\n --mfb-font: system-ui, -apple-system, sans-serif;\n --mfb-z-index: 2147483640;\n\n /* Spacing scale — 4px base, used everywhere instead of magic numbers. */\n --mfb-space-1: 4px;\n --mfb-space-2: 8px;\n --mfb-space-3: 12px;\n --mfb-space-4: 16px;\n --mfb-space-5: 24px;\n --mfb-space-6: 32px;\n --mfb-space-7: 48px;\n\n /* Type scale — fixed sizes, no fluid scaling. Body 14px sits in the\n \"comfortable on every viewport\" range; headings step up gracefully. */\n --mfb-text-xs: 11px;\n --mfb-text-sm: 13px;\n --mfb-text-base: 14px;\n --mfb-text-md: 15px;\n --mfb-text-lg: 17px;\n --mfb-text-xl: 20px;\n\n /* Elevation — three tiers for layering. */\n --mfb-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.06), 0 1px 3px rgba(0, 0, 0, 0.08);\n --mfb-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.12);\n --mfb-shadow-lg: 0 24px 56px rgba(0, 0, 0, 0.18), 0 8px 20px rgba(0, 0, 0, 0.10);\n\n all: initial;\n font-family: var(--mfb-font);\n color: var(--mfb-text);\n position: fixed;\n z-index: var(--mfb-z-index);\n}\n\n@media (prefers-color-scheme: dark) {\n :host {\n --mfb-bg: #0f172a;\n --mfb-surface: #1e293b;\n --mfb-surface-2: #253349;\n --mfb-text: #f8fafc;\n --mfb-text-muted: #94a3b8;\n --mfb-border: #334155;\n --mfb-border-strong: #475569;\n --mfb-shadow-lg: 0 24px 56px rgba(0, 0, 0, 0.55), 0 8px 20px rgba(0, 0, 0, 0.40);\n }\n}\n\n/* FAB — 48px circle (down from Material's 56px) for a more discrete\n presence on client surfaces (matches PNR's 48px pattern). Mhosaic\n soft-black background with a light steel-blue lucide-bug glyph;\n custom SVG inlined (no emoji — emoji renders inconsistently across\n OSes and can't inherit color). Two-layer elevation, scale-on-press. */\n.fab-area {\n position: fixed;\n bottom: 24px;\n right: 24px;\n /* No z-index on purpose: the modal .backdrop/.modal are later siblings in\n the shadow root, so DOM order must keep them painting above the FAB —\n an explicit z-index here would trap the FAB on top of the open modal. */\n}\n.fab-area .fab {\n position: relative;\n right: auto;\n bottom: auto;\n}\n.fab {\n width: 48px;\n height: 48px;\n border-radius: 999px;\n background: var(--mfb-fab-bg);\n color: var(--mfb-fab-icon);\n border: none;\n cursor: pointer;\n /* Two-layer elevation: ambient (soft, large) + key (tighter, near). */\n box-shadow:\n 0 4px 12px rgba(0, 0, 0, 0.08),\n 0 2px 4px rgba(0, 0, 0, 0.12);\n display: grid;\n place-items: center;\n transition: transform 180ms cubic-bezier(0.4, 0, 0.2, 1),\n box-shadow 180ms cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.fab:hover {\n transform: translateY(-2px);\n box-shadow:\n 0 8px 24px rgba(0, 0, 0, 0.12),\n 0 3px 6px rgba(0, 0, 0, 0.16);\n}\n.fab:active {\n transform: translateY(0) scale(0.96);\n box-shadow:\n 0 3px 8px rgba(0, 0, 0, 0.10),\n 0 1px 2px rgba(0, 0, 0, 0.14);\n}\n.fab:focus-visible {\n outline: 2px solid var(--mfb-fab-icon);\n outline-offset: 3px;\n box-shadow:\n 0 0 0 4px var(--mfb-fab-bg),\n 0 4px 12px rgba(0, 0, 0, 0.08),\n 0 2px 4px rgba(0, 0, 0, 0.12);\n}\n@media (prefers-color-scheme: dark) {\n /* Slightly desaturate so the FAB doesn't glow against dark backgrounds. */\n .fab { box-shadow:\n 0 4px 12px rgba(0, 0, 0, 0.32),\n 0 2px 4px rgba(0, 0, 0, 0.40); }\n}\n\n/* Page-activity badge — absolute so it never shifts the FAB's layout. */\n.fab-badge {\n position: absolute;\n top: -2px;\n right: -2px;\n min-width: 16px;\n height: 16px;\n padding: 0 4px;\n box-sizing: border-box;\n border-radius: 999px;\n background: var(--mfb-accent);\n color: #fff;\n font-size: 10px;\n font-weight: 700;\n line-height: 16px;\n text-align: center;\n box-shadow: 0 0 0 2px var(--mfb-fab-bg);\n animation: mfb-badge-in 150ms ease-out;\n}\n@keyframes mfb-badge-in {\n from {\n opacity: 0;\n transform: scale(0.6);\n }\n to {\n opacity: 1;\n transform: scale(1);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .fab { transition: none; }\n .fab:hover, .fab:active { transform: none; }\n .fab-badge {\n animation: none;\n }\n}\n\n/* Backdrop — fade in with a slight blur for depth. The blur gives the\n modal that \"page that opens\" weight: the underlying page recedes\n visually so the widget feels foregrounded, not pasted on. */\n.backdrop {\n position: fixed;\n inset: 0;\n background: rgba(15, 23, 42, 0.55);\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n display: grid;\n place-items: center;\n animation: mfb-backdrop-in 220ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n@keyframes mfb-backdrop-in {\n from { opacity: 0; backdrop-filter: blur(0px); -webkit-backdrop-filter: blur(0px); }\n to { opacity: 1; backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }\n}\n\n.modal {\n background: var(--mfb-bg);\n border-radius: var(--mfb-radius-lg);\n box-shadow: var(--mfb-shadow-lg);\n /* 720px is the \"page that opens\" sweet spot — wide enough to feel\n like a workspace, narrow enough to not dominate the screen. Was\n 440px before; the bump trades a denser modal for a calmer canvas. */\n width: min(720px, calc(100vw - var(--mfb-space-7)));\n padding: var(--mfb-space-6);\n display: flex;\n flex-direction: column;\n gap: var(--mfb-space-5);\n position: relative;\n /* Cap height with breathing room. The form scrolls internally if\n the user attaches a tall screenshot. */\n max-height: min(820px, calc(100vh - var(--mfb-space-7)));\n overflow-y: auto;\n /* Entrance: opacity-only. Avoids any geometric shift during the\n animation so interaction tests that read boundingBox immediately\n after the modal becomes visible get stable coordinates. 220ms\n ease-out is snappy enough that human users barely register it. */\n animation: mfb-modal-in 220ms ease-out;\n /* Transition lives on the BASE rule with a SHORTER duration so the\n \"back\" trip (Board → other tab, where .is-expanded is removed)\n feels snappy. The grow direction overrides this with a slower\n curve in .modal.is-expanded below — the asymmetry is intentional:\n a slow grow reads as a transport, a slow shrink reads as\n hesitation. */\n transition:\n width 200ms cubic-bezier(0.4, 0, 0.2, 1),\n height 200ms cubic-bezier(0.4, 0, 0.2, 1),\n max-height 200ms cubic-bezier(0.4, 0, 0.2, 1),\n padding 200ms cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n@keyframes mfb-modal-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n/* Mobile: full-height sheet that slides up from the bottom. The\n slide-up gesture matches platform-native sheets users already know. */\n@media (max-width: 640px) {\n .backdrop { place-items: end center; }\n .modal {\n width: 100vw;\n height: calc(100vh - var(--mfb-space-7));\n max-height: none;\n border-radius: var(--mfb-radius-lg) var(--mfb-radius-lg) 0 0;\n padding: var(--mfb-space-5) var(--mfb-space-4) var(--mfb-space-4);\n animation: mfb-sheet-up 280ms cubic-bezier(0.16, 1, 0.3, 1);\n }\n}\n\n@keyframes mfb-sheet-up {\n from { transform: translateY(100%); }\n to { transform: translateY(0); }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .backdrop, .modal { animation: none; }\n}\n\n.modal h2 {\n margin: 0;\n font-size: var(--mfb-text-xl);\n font-weight: 600;\n padding-right: var(--mfb-space-6);\n letter-spacing: -0.015em;\n line-height: 1.2;\n}\n\n.modal form {\n display: flex;\n flex-direction: column;\n gap: var(--mfb-space-5);\n /* Match the entrance motion the Board/MineList/Changelog containers\n * use so switching tabs (especially Board → Send, where the modal\n * is mid-shrink) doesn't show a content snap behind the transition.\n * 40ms delay lets the modal's shrink finish first. */\n animation: mfb-board-in 220ms ease-out 40ms both;\n}\n@media (prefers-reduced-motion: reduce) {\n .modal form { animation: none; }\n}\n\n.modal-close {\n position: absolute;\n top: var(--mfb-space-3);\n right: var(--mfb-space-3);\n width: 36px;\n height: 36px;\n display: grid;\n place-items: center;\n background: transparent;\n border: none;\n border-radius: var(--mfb-radius);\n color: var(--mfb-text-muted);\n font: inherit;\n font-size: 22px;\n line-height: 1;\n cursor: pointer;\n transition: background 120ms ease, color 120ms ease;\n}\n.modal-close:hover { background: var(--mfb-surface); color: var(--mfb-text); }\n.modal-close:focus-visible { outline: 2px solid var(--mfb-accent); outline-offset: 2px; }\n\n/* Each field group: label + input stacked with breathing room.\n The 24px modal-level gap separates groups. */\n.field { display: flex; flex-direction: column; gap: var(--mfb-space-2); font-size: var(--mfb-text-sm); }\n\n.field label {\n color: var(--mfb-text-muted);\n font-weight: 500;\n font-size: var(--mfb-text-xs);\n letter-spacing: 0.03em;\n text-transform: uppercase;\n}\n\n.field input, .field select, .field textarea {\n font-family: inherit;\n font-size: var(--mfb-text-base);\n color: inherit;\n padding: 11px 14px;\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n background: var(--mfb-surface);\n transition: border-color 120ms ease, box-shadow 120ms ease, background 120ms ease;\n}\n\n.field input:hover, .field select:hover, .field textarea:hover { border-color: var(--mfb-border-strong); }\n.field input:focus, .field select:focus, .field textarea:focus {\n outline: none;\n border-color: var(--mfb-accent);\n background: var(--mfb-bg);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mfb-accent) 22%, transparent);\n}\n\n.field select {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n padding-right: 28px;\n background-image: url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path fill='none' stroke='%236b7280' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' d='M1 1l4 4 4-4'/></svg>\");\n background-repeat: no-repeat;\n background-position: right 10px center;\n}\n\n.field textarea { min-height: 120px; resize: vertical; line-height: 1.5; }\n\n.row { display: flex; gap: var(--mfb-space-3); }\n.row > * { flex: 1; }\n\n/* Footer: subtle separation via border, slightly more vertical space. */\n.actions {\n display: flex;\n gap: var(--mfb-space-2);\n justify-content: flex-end;\n padding-top: var(--mfb-space-4);\n margin-top: var(--mfb-space-1);\n border-top: 1px solid var(--mfb-border);\n}\n\n.btn {\n padding: 10px 18px;\n border-radius: var(--mfb-radius);\n border: 1px solid var(--mfb-border);\n background: var(--mfb-bg);\n color: var(--mfb-text);\n font: inherit;\n font-size: var(--mfb-text-sm);\n font-weight: 500;\n cursor: pointer;\n transition: background 120ms ease, border-color 120ms ease, transform 80ms ease;\n}\n.btn:hover { background: var(--mfb-surface); border-color: var(--mfb-border-strong); }\n.btn:active { transform: scale(0.98); }\n\n.btn--primary {\n background: var(--mfb-accent);\n color: var(--mfb-accent-contrast);\n border-color: var(--mfb-accent);\n}\n.btn--primary:hover {\n background: color-mix(in srgb, var(--mfb-accent) 88%, black);\n border-color: color-mix(in srgb, var(--mfb-accent) 88%, black);\n}\n\n/* Subdued button — borrows accent text but transparent background. Used\n * for the \"Capture this page\" alt-action that sits below the dropzone,\n * where a full --primary would compete with the form's main submit. */\n.btn--ghost {\n background: transparent;\n color: var(--mfb-accent);\n border-color: color-mix(in srgb, var(--mfb-accent) 40%, transparent);\n}\n.btn--ghost:hover {\n background: color-mix(in srgb, var(--mfb-accent) 8%, transparent);\n border-color: var(--mfb-accent);\n}\n\n.btn[disabled] { opacity: 0.6; cursor: not-allowed; }\n\n.error { color: #dc2626; font-size: 13px; }\n.success { color: #059669; font-size: 13px; }\n\n/* REQ-A7: quiet transparency line under the form. */\n.capture-notice { color: var(--mfb-muted, #6b7280); font-size: 11px; line-height: 1.4; margin-top: 6px; }\n\n/* #85: inline \"#NN\" report reference rendered as a link (a button for a11y). */\n.seq-link {\n display: inline;\n padding: 0;\n border: none;\n background: none;\n color: var(--mfb-accent);\n font: inherit;\n cursor: pointer;\n}\n.seq-link:hover { text-decoration: underline; }\n\n/* ---- #89: discard-unsaved-changes confirmation over the form -------- */\n.discard-confirm {\n position: absolute;\n inset: 0;\n z-index: 5;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 16px;\n background: rgba(15, 23, 42, 0.45);\n border-radius: inherit;\n}\n.discard-confirm-card {\n max-width: 260px;\n text-align: center;\n background: var(--mfb-bg);\n color: var(--mfb-text);\n border-radius: var(--mfb-radius-lg);\n padding: 18px 16px;\n box-shadow: 0 8px 30px rgba(0, 0, 0, 0.18);\n}\n.discard-confirm-title { font-weight: 600; font-size: 15px; margin: 0 0 4px; }\n.discard-confirm-body { font-size: 13px; margin: 0 0 14px; opacity: 0.8; }\n.discard-confirm-actions { display: flex; gap: 8px; justify-content: center; }\n\n/* ---- v0.6.0: manual screenshot upload + annotator -------------------- */\n\n.mfb-sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\n\n/* Dropzone — bigger, more inviting; the icon + heading + sub-line\n hierarchy makes it feel like a deliberate target, not an afterthought. */\n.screenshot-dropzone {\n border: 1.5px dashed var(--mfb-border-strong);\n border-radius: var(--mfb-radius-lg);\n padding: var(--mfb-space-6) var(--mfb-space-4);\n text-align: center;\n cursor: pointer;\n background: var(--mfb-surface);\n transition: border-color 160ms ease, background 160ms ease, transform 120ms ease;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--mfb-space-2);\n}\n.screenshot-dropzone:hover {\n border-color: var(--mfb-accent);\n background: color-mix(in srgb, var(--mfb-accent) 4%, var(--mfb-surface));\n}\n.screenshot-dropzone.is-dragover {\n border-color: var(--mfb-accent);\n border-style: solid;\n background: color-mix(in srgb, var(--mfb-accent) 10%, var(--mfb-surface));\n transform: scale(1.005);\n}\n.screenshot-dropzone:focus-visible {\n outline: 2px solid var(--mfb-accent);\n outline-offset: 2px;\n}\n.screenshot-icon {\n color: var(--mfb-text-muted);\n opacity: 0.7;\n margin-bottom: var(--mfb-space-1);\n transition: color 160ms ease, opacity 160ms ease;\n}\n.screenshot-dropzone:hover .screenshot-icon,\n.screenshot-dropzone.is-dragover .screenshot-icon {\n color: var(--mfb-accent);\n opacity: 1;\n}\n.screenshot-cta { font-size: var(--mfb-text-base); color: var(--mfb-text); font-weight: 500; }\n.screenshot-cta strong { color: var(--mfb-accent); font-weight: 600; }\n.screenshot-formats { font-size: var(--mfb-text-xs); color: var(--mfb-text-muted); }\n\n.screenshot-strip {\n display: flex;\n flex-direction: column;\n gap: var(--mfb-space-2);\n margin-bottom: var(--mfb-space-2);\n}\n.screenshot-preview {\n position: relative;\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n overflow: hidden;\n background: var(--mfb-surface);\n display: flex;\n flex-direction: column;\n}\n.screenshot-preview img {\n display: block;\n width: 100%;\n height: auto;\n max-height: 280px;\n object-fit: contain;\n background: #1a1a1a;\n}\n/* In the multi-shot strip each preview stays compact so several fit. */\n.screenshot-strip .screenshot-preview img {\n max-height: 120px;\n}\n.screenshot-preview-actions {\n display: flex;\n gap: var(--mfb-space-2);\n padding: var(--mfb-space-2);\n border-top: 1px solid var(--mfb-border);\n background: var(--mfb-bg);\n}\n.screenshot-preview-actions .btn {\n flex: 1;\n padding: 8px 12px;\n font-size: var(--mfb-text-sm);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n}\n.screenshot-preview-actions .btn--primary {\n background: var(--mfb-bg);\n color: var(--mfb-accent);\n border-color: var(--mfb-accent);\n}\n.screenshot-preview-actions .btn--primary:hover {\n background: color-mix(in srgb, var(--mfb-accent) 8%, var(--mfb-bg));\n border-color: var(--mfb-accent);\n color: var(--mfb-accent);\n}\n.screenshot-remove {\n /* Kept for legacy markup that uses the old corner-cross button. */\n position: absolute;\n top: 6px;\n right: 6px;\n width: 26px;\n height: 26px;\n display: grid;\n place-items: center;\n background: rgba(255, 255, 255, 0.9);\n border: 1px solid var(--mfb-border);\n border-radius: 999px;\n font-size: 18px;\n line-height: 1;\n cursor: pointer;\n color: #111827;\n}\n.screenshot-remove:hover { background: #fff; }\n.screenshot-annotate {\n /* Kept for legacy; new markup uses .screenshot-preview-actions. */\n border-radius: 0;\n border-width: 0;\n border-top: 1px solid var(--mfb-border);\n background: var(--mfb-bg);\n}\n.screenshot-annotate:hover { background: var(--mfb-surface); }\n\n.page-context {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 11px;\n color: var(--mfb-text-muted);\n}\n.page-context-label {\n text-transform: uppercase;\n font-weight: 600;\n letter-spacing: 0.04em;\n font-size: 10px;\n}\n.page-context-url {\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11px;\n color: var(--mfb-text-muted);\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Annotator modal — sits above the feedback modal (z-index +1). */\n\n.annotator-backdrop {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.78);\n display: grid;\n place-items: center;\n z-index: 1;\n padding: 12px;\n}\n.annotator {\n position: relative;\n background: var(--mfb-bg);\n color: var(--mfb-text);\n border-radius: calc(var(--mfb-radius) * 1.5);\n width: min(960px, 96vw);\n max-height: calc(100vh - 24px);\n display: flex;\n flex-direction: column;\n box-shadow: 0 24px 60px rgba(0, 0, 0, 0.4);\n}\n.annotator-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 14px;\n border-bottom: 1px solid var(--mfb-border);\n font-size: 13px;\n font-weight: 600;\n}\n.annotator-toolbar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n padding: 8px 14px;\n border-bottom: 1px solid var(--mfb-border);\n}\n.annotator-tools, .annotator-colors { display: flex; gap: 4px; }\n.annotator-sep {\n display: inline-block;\n width: 1px;\n height: 18px;\n background: var(--mfb-border);\n margin: 0 4px;\n}\n.annotator-spacer { flex: 1; }\n.annotator-tool {\n width: 30px;\n height: 30px;\n display: grid;\n place-items: center;\n background: var(--mfb-bg);\n color: var(--mfb-text);\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n cursor: pointer;\n}\n.annotator-tool:hover { background: var(--mfb-surface); }\n.annotator-tool.is-active {\n background: var(--mfb-text);\n color: var(--mfb-bg);\n border-color: var(--mfb-text);\n}\n.annotator-color {\n width: 24px;\n height: 24px;\n border-radius: 999px;\n border: 2px solid var(--mfb-border);\n cursor: pointer;\n padding: 0;\n transition: transform 120ms ease;\n position: relative;\n}\n.annotator-color.is-active {\n transform: scale(1.12);\n border-color: var(--mfb-text);\n}\n/* Color-picker swatch: rainbow gradient fill so users see this isn't\n a preset, plus a tiny native <input type=\"color\"> overlaid invisibly. */\n.annotator-color-picker {\n display: grid;\n place-items: center;\n background: conic-gradient(from 180deg, #ef4444, #f59e0b, #10b981, #3b82f6, #8b5cf6, #ec4899, #ef4444);\n overflow: hidden;\n}\n.annotator-color-picker input[type=\"color\"] {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n border: 0;\n padding: 0;\n margin: 0;\n opacity: 0;\n cursor: pointer;\n}\n.annotator-btn {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n height: 30px;\n padding: 0 10px;\n font: inherit;\n font-size: 12px;\n background: var(--mfb-bg);\n color: var(--mfb-text);\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n cursor: pointer;\n}\n.annotator-btn:hover { background: var(--mfb-surface); }\n.annotator-btn[disabled] { opacity: 0.5; cursor: not-allowed; }\n.annotator-count { font-size: 11px; color: var(--mfb-text-muted); }\n.annotator-canvas-wrap {\n flex: 1;\n overflow: auto;\n background: #1a1a1a;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 12px;\n min-height: 200px;\n}\n.annotator-canvas {\n cursor: crosshair;\n box-shadow: 0 8px 28px rgba(0, 0, 0, 0.45);\n background: #fff;\n}\n.annotator-loading {\n color: rgba(255, 255, 255, 0.7);\n font-size: 13px;\n}\n.annotator-footer {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 8px;\n padding: 10px 14px;\n border-top: 1px solid var(--mfb-border);\n}\n\n/* ---- v0.7: tabs + reader UI -------------------------------------- */\n\n.tab-strip {\n display: flex;\n gap: var(--mfb-space-1);\n border-bottom: 1px solid var(--mfb-border);\n margin: 0;\n padding: 0;\n}\n.tab-button {\n appearance: none;\n background: transparent;\n border: 0;\n border-bottom: 2px solid transparent;\n padding: var(--mfb-space-3) var(--mfb-space-4);\n margin-bottom: -1px;\n font: inherit;\n font-size: var(--mfb-text-sm);\n font-weight: 500;\n color: var(--mfb-text-muted);\n cursor: pointer;\n transition: color 120ms ease, border-color 120ms ease;\n}\n.tab-button:hover { color: var(--mfb-text); }\n.tab-button.is-active {\n color: var(--mfb-accent);\n border-bottom-color: var(--mfb-accent);\n}\n.tab-button[aria-selected=\"true\"] { font-weight: 600; }\n\n.mine-list {\n display: flex;\n flex-direction: column;\n gap: var(--mfb-space-3);\n /* Matches .board-view's entrance so switching tabs feels like a real\n * page transition rather than a flicker. */\n animation: mfb-board-in 280ms ease-out 40ms both;\n}\n@media (prefers-reduced-motion: reduce) {\n .mine-list { animation: none; }\n}\n.mine-list-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n}\n.mine-list-header h2 { margin: 0; font-size: var(--mfb-text-lg); font-weight: 600; letter-spacing: -0.01em; }\n/* #85 — header actions: jump-by-#seq box + refresh. */\n.mine-list-header-actions {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n.mine-jump {\n display: flex;\n align-items: center;\n gap: 4px;\n}\n.mine-jump input {\n font: inherit;\n font-size: 13px;\n width: 56px;\n padding: 5px 8px;\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n background: var(--mfb-surface);\n color: inherit;\n}\n.mine-jump-error {\n margin-top: 6px;\n font-size: 12px;\n}\n.mine-loading { color: var(--mfb-text-muted); font-size: 13px; }\n.mine-empty {\n text-align: center;\n padding: 24px 12px;\n color: var(--mfb-text-muted);\n font-size: 13px;\n}\n.mine-empty strong { display: block; color: var(--mfb-text); margin-bottom: 4px; }\n\n.mine-rows {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 6px;\n max-height: 380px;\n overflow-y: auto;\n}\n.mine-row {\n appearance: none;\n text-align: left;\n background: var(--mfb-surface);\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n padding: 10px 12px;\n font: inherit;\n color: inherit;\n cursor: pointer;\n display: flex;\n flex-direction: column;\n gap: 4px;\n width: 100%;\n transition:\n border-color 140ms ease,\n background 140ms ease,\n transform 100ms ease,\n box-shadow 140ms ease;\n}\n.mine-row:hover {\n border-color: var(--mfb-border-strong);\n background: var(--mfb-bg);\n transform: translateY(-1px);\n box-shadow: var(--mfb-shadow);\n}\n.mine-row:active { transform: scale(0.997); }\n.mine-row:focus-visible {\n outline: 2px solid var(--mfb-accent);\n outline-offset: 2px;\n}\n.mine-row-pills { display: flex; gap: 4px; flex-wrap: wrap; }\n.mine-row-preview {\n font-size: 13px;\n color: var(--mfb-text);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n.mine-row-meta {\n display: flex;\n gap: 6px;\n font-size: 11px;\n color: var(--mfb-text-muted);\n}\n\n.pill {\n display: inline-block;\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n padding: 2px 8px;\n border-radius: 999px;\n border: 1px solid transparent;\n}\n.pill-status { background: #eff6ff; color: #1e40af; border-color: #dbeafe; }\n.pill-status--in_progress { background: #fffbeb; color: #92400e; border-color: #fde68a; }\n.pill-status--awaiting_validation { background: #faf5ff; color: #6b21a8; border-color: #e9d5ff; }\n.pill-status--closed { background: #ecfdf5; color: #065f46; border-color: #a7f3d0; }\n.pill-status--rejected, .pill-status--wontfix, .pill-status--duplicate { background: #f3f4f6; color: #374151; border-color: #e5e7eb; }\n.pill-type { background: var(--mfb-surface); color: var(--mfb-text-muted); border-color: var(--mfb-border); }\n.pill-severity { background: var(--mfb-surface); color: var(--mfb-text-muted); border-color: var(--mfb-border); }\n.pill-severity--blocker { background: #fef2f2; color: #991b1b; border-color: #fecaca; }\n.pill-severity--high { background: #fff7ed; color: #9a3412; border-color: #fed7aa; }\n.pill-severity--medium { background: #fefce8; color: #854d0e; border-color: #fef08a; }\n.pill-severity--low { background: var(--mfb-surface); color: var(--mfb-text-muted); }\n\n.report-detail { display: flex; flex-direction: column; gap: 12px; }\n.report-detail-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n.report-detail-body { display: flex; flex-direction: column; gap: 10px; }\n.report-detail-description {\n font-size: 14px;\n white-space: pre-wrap;\n margin: 0;\n}\n.report-detail-screenshot {\n display: block;\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n overflow: hidden;\n background: #1a1a1a;\n}\n.report-detail-screenshot img {\n display: block;\n width: 100%;\n max-height: 200px;\n object-fit: contain;\n}\n.report-detail-section {\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mfb-text-muted);\n margin: 8px 0 4px;\n font-weight: 600;\n}\n\n.report-detail-context {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 10px 12px;\n background: var(--mfb-surface);\n border-radius: var(--mfb-radius);\n border: 1px solid var(--mfb-border);\n}\n.report-detail-context-pills { display: flex; gap: 4px; flex-wrap: wrap; }\n.report-detail-context-line {\n display: flex;\n align-items: baseline;\n gap: 8px;\n font-size: 12px;\n color: var(--mfb-text);\n}\n.report-detail-context-label {\n text-transform: uppercase;\n letter-spacing: 0.04em;\n font-size: 10px;\n font-weight: 600;\n color: var(--mfb-text-muted);\n min-width: 56px;\n}\n.report-detail-context-url {\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n color: var(--mfb-accent);\n text-decoration: none;\n font-size: 11px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.report-detail-context-url:hover { text-decoration: underline; }\n.pill-capture { background: var(--mfb-bg); color: var(--mfb-text-muted); border-color: var(--mfb-border); }\n.report-detail-empty {\n font-size: 12px;\n color: var(--mfb-text-muted);\n margin: 0;\n}\n\n.report-comments {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 6px;\n max-height: 300px;\n overflow-y: auto;\n}\n\n.comment-bubble {\n padding: 8px 10px;\n border-radius: 12px;\n max-width: 88%;\n font-size: 13px;\n line-height: 1.4;\n}\n.comment-bubble.is-mine {\n align-self: flex-end;\n background: var(--mfb-accent);\n color: var(--mfb-accent-contrast);\n}\n.comment-bubble.is-other {\n align-self: flex-start;\n background: var(--mfb-surface);\n border: 1px solid var(--mfb-border);\n color: var(--mfb-text);\n}\n.comment-author {\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n font-weight: 600;\n margin-bottom: 2px;\n}\n.comment-author--mcp { color: #6b21a8; }\n.comment-author--staff { color: #1e40af; }\n.comment-author--system { color: var(--mfb-text-muted); }\n.comment-body { white-space: pre-wrap; }\n/* #91 — image attachments rendered inside a comment bubble. */\n.comment-attachments {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n margin-top: 6px;\n}\n.comment-attachment {\n display: block;\n line-height: 0;\n border-radius: 8px;\n overflow: hidden;\n border: 1px solid var(--mfb-border);\n}\n.comment-attachment img {\n width: 96px;\n height: 96px;\n object-fit: cover;\n display: block;\n}\n.comment-time {\n font-size: 10px;\n margin-top: 4px;\n opacity: 0.7;\n}\n\n/* F4 — author edits the description in place. */\n.report-detail-description-row {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n}\n.report-detail-description-row .report-detail-description {\n flex: 1;\n margin: 0;\n}\n.report-detail-edit {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.report-detail-edit-input {\n font: inherit;\n font-size: 13px;\n padding: 8px 10px;\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n background: var(--mfb-surface);\n color: inherit;\n min-height: 72px;\n resize: vertical;\n}\n.report-detail-edit-actions {\n display: flex;\n justify-content: flex-end;\n gap: 6px;\n}\n.report-detail-edit.btn,\nbutton.report-detail-edit {\n flex: 0 0 auto;\n font-size: 12px;\n padding: 4px 8px;\n}\n/* #85 — shareable-permalink affordance under the description. */\n.report-detail-permalink {\n margin: -2px 0 6px;\n}\n.report-detail-copy-link {\n font-size: 12px;\n padding: 4px 8px;\n}\n.report-compose {\n display: flex;\n flex-direction: column;\n gap: 6px;\n margin-top: 4px;\n}\n.report-compose textarea {\n font: inherit;\n font-size: 13px;\n padding: 8px 10px;\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n background: var(--mfb-surface);\n color: inherit;\n min-height: 64px;\n resize: vertical;\n}\n.report-compose-actions {\n display: flex;\n justify-content: flex-end;\n gap: 6px;\n}\n/* #91 — pending image attachments in the reply compose box. */\n.compose-attachments {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n.compose-attachment {\n position: relative;\n width: 64px;\n height: 64px;\n border-radius: 8px;\n overflow: hidden;\n border: 1px solid var(--mfb-border);\n}\n.compose-attachment img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n}\n.compose-attachment-remove {\n position: absolute;\n top: 2px;\n right: 2px;\n width: 20px;\n height: 20px;\n display: grid;\n place-items: center;\n padding: 0;\n background: rgba(255, 255, 255, 0.92);\n border: 1px solid var(--mfb-border);\n border-radius: 999px;\n font-size: 15px;\n line-height: 1;\n cursor: pointer;\n color: #111827;\n}\n.compose-attachment-remove:hover { background: #fff; }\n.compose-attach-btn { margin-right: auto; }\n.compose-attach-error {\n margin: 0;\n font-size: 12px;\n color: var(--mfb-danger, #b91c1c);\n}\n\n/* v0.15.3 — teammate-viewing notice. Shown in place of the compose\n * box when a non-submitter opens a project-wide row from the Board\n * tab. Replies are private to the submitter; we surface this once\n * (calm tone, neutral surface) so the viewer understands why no\n * Reply box appears, instead of leaving them wondering. */\n.report-detail-teammate-notice {\n margin: 0;\n padding: var(--mfb-space-3) var(--mfb-space-4);\n background: var(--mfb-surface);\n border: 1px dashed var(--mfb-border);\n border-radius: var(--mfb-radius);\n font-size: var(--mfb-text-sm);\n color: var(--mfb-text-muted);\n font-style: italic;\n}\n\n/* v0.15.3 — friendly \"report not available\" empty state. Replaces\n * the raw API error message (e.g. getReport failed: 404 …) that\n * used to leak from setError(err.message) straight into the DOM. */\n.report-detail-empty-state {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--mfb-space-3);\n padding: var(--mfb-space-5);\n background: var(--mfb-surface);\n border-radius: var(--mfb-radius);\n border: 1px solid var(--mfb-border);\n}\n.report-detail-empty-state-title {\n margin: 0;\n font-size: var(--mfb-text-md);\n font-weight: 600;\n color: var(--mfb-text);\n}\n.report-detail-empty-state-body {\n margin: 0;\n font-size: var(--mfb-text-sm);\n color: var(--mfb-text-muted);\n}\n\n/* Status history — read-only audit trail visible to the submitter.\n Timeline of who flipped the status and when (operator vs. agent vs.\n system). Bordered, neutral surface so it doesn't compete visually\n with the conversation thread above. */\n.report-detail-history {\n display: flex;\n flex-direction: column;\n gap: 6px;\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n background: var(--mfb-surface);\n padding: 8px 10px;\n}\n.report-detail-history .report-detail-section {\n margin: 0;\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mfb-text-muted);\n}\n.status-history {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.status-history-row {\n display: grid;\n grid-template-columns: auto 1fr auto;\n gap: 8px;\n align-items: center;\n font-size: 11px;\n}\n.status-history-time {\n color: var(--mfb-text-muted);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n.status-history-transition {\n display: inline-flex;\n gap: 4px;\n align-items: center;\n flex-wrap: wrap;\n}\n.status-history-arrow {\n color: var(--mfb-text-muted);\n font-size: 11px;\n}\n.status-history-source {\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n font-weight: 600;\n white-space: nowrap;\n}\n.status-history-source--mcp { color: #6b21a8; }\n.status-history-source--user { color: #1e40af; }\n.status-history-source--system { color: var(--mfb-text-muted); }\n\n/* Transparency drawer — closed by default. The user can click open to\n verify what their browser sent: device, errors, console tail, network\n tail. Read-only; purely a trust-building gesture. */\n.report-detail-tech {\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n background: var(--mfb-surface);\n}\n.report-detail-tech > summary {\n cursor: pointer;\n padding: 8px 10px;\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mfb-text-muted);\n user-select: none;\n list-style: none;\n}\n.report-detail-tech > summary::-webkit-details-marker { display: none; }\n.report-detail-tech > summary::before {\n content: '▸';\n display: inline-block;\n margin-right: 6px;\n transition: transform 120ms;\n}\n.report-detail-tech[open] > summary::before { transform: rotate(90deg); }\n.tech-body {\n padding: 0 10px 10px 10px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n.tech-section h4 {\n margin: 0 0 4px 0;\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mfb-text-muted);\n font-weight: 600;\n}\n.tech-device {\n display: grid;\n grid-template-columns: max-content 1fr;\n column-gap: 8px;\n row-gap: 2px;\n margin: 0;\n font-size: 11px;\n font-variant-numeric: tabular-nums;\n}\n.tech-device dt { color: var(--mfb-text-muted); }\n.tech-device dd { margin: 0; }\n.tech-errors, .tech-console, .tech-network {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n font-size: 11px;\n font-family: ui-monospace, \"SF Mono\", Menlo, monospace;\n max-height: 200px;\n overflow-y: auto;\n}\n.tech-errors li { padding: 4px 0; border-top: 1px solid var(--mfb-border); }\n.tech-errors li:first-child { border-top: 0; }\n.tech-errors-msg { color: #991b1b; font-weight: 600; }\n.tech-errors-stack {\n margin: 4px 0 0 0;\n padding: 4px 6px;\n background: var(--mfb-bg);\n border-radius: 4px;\n font-size: 10px;\n white-space: pre-wrap;\n color: var(--mfb-text-muted);\n max-height: 120px;\n overflow-y: auto;\n}\n.tech-console-row {\n display: grid;\n grid-template-columns: 48px 1fr;\n gap: 6px;\n padding: 1px 0;\n}\n.tech-console-level {\n text-transform: uppercase;\n font-size: 9px;\n font-weight: 600;\n align-self: center;\n color: var(--mfb-text-muted);\n}\n.tech-console-row--warn .tech-console-level { color: #92400e; }\n.tech-console-row--error .tech-console-level { color: #991b1b; }\n.tech-console-row--info .tech-console-level { color: #1e40af; }\n.tech-console-msg { word-break: break-word; }\n.tech-network-row {\n display: grid;\n grid-template-columns: 48px 56px 1fr 56px;\n gap: 6px;\n padding: 1px 0;\n}\n.tech-network-row--fail .tech-network-status { color: #991b1b; font-weight: 700; }\n.tech-network-status { font-variant-numeric: tabular-nums; }\n.tech-network-method { color: var(--mfb-text-muted); }\n.tech-network-url { word-break: break-all; }\n.tech-network-time { text-align: right; color: var(--mfb-text-muted); font-variant-numeric: tabular-nums; }\n\n/* KPI strip — four pills above the My Reports list. Each is clickable\n to filter the rows below. Borrowed from thePnr's FeedbackKPICards. */\n.kpi-strip {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 6px;\n}\n.kpi-cell {\n appearance: none;\n background: var(--mfb-bg);\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n padding: 10px 10px 10px 14px;\n cursor: pointer;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 2px;\n font-family: inherit;\n color: inherit;\n position: relative;\n overflow: hidden;\n transition:\n background 140ms ease,\n border-color 140ms ease,\n transform 100ms ease,\n box-shadow 140ms ease;\n}\n.kpi-cell::before {\n /* Vertical tone accent — matches the Board KPI cards' visual rhythm so\n * the two surfaces feel like they belong to the same product. v0.12\n * polish pass. */\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n width: 3px;\n height: 100%;\n background: var(--mfb-border);\n transition: background 140ms ease;\n}\n.kpi-cell--new::before { background: #3b82f6; }\n.kpi-cell--in_progress::before { background: #f59e0b; }\n.kpi-cell--awaiting_validation::before { background: #a855f7; }\n.kpi-cell--resolved::before { background: #10b981; }\n.kpi-cell:hover {\n background: var(--mfb-surface);\n transform: translateY(-1px);\n box-shadow: var(--mfb-shadow);\n}\n.kpi-cell.is-active {\n border-color: var(--mfb-accent);\n background: rgba(59, 130, 246, 0.08);\n}\n.kpi-cell.is-active.kpi-cell--in_progress { border-color: #d97706; background: rgba(251, 191, 36, 0.10); }\n.kpi-cell.is-active.kpi-cell--awaiting_validation { border-color: #9333ea; background: rgba(168, 85, 247, 0.10); }\n.kpi-cell.is-active.kpi-cell--resolved { border-color: #059669; background: rgba(16, 185, 129, 0.10); }\n.kpi-value {\n font-size: 18px;\n font-weight: 700;\n font-variant-numeric: tabular-nums;\n line-height: 1;\n}\n.kpi-label {\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mfb-text-muted);\n}\n\n/* Changelog (\"This week\") — week-grouped resolved-report timeline.\n Borrowed from thePnr's FeedbackChangelogView; the header per group\n reads \"● Week of {date} ━━━ {n} resolved\". */\n.changelog-groups {\n display: flex;\n flex-direction: column;\n gap: 12px;\n animation: mfb-board-in 280ms ease-out 40ms both;\n}\n@media (prefers-reduced-motion: reduce) {\n .changelog-groups { animation: none; }\n}\n.changelog-group {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.changelog-group-header {\n display: grid;\n grid-template-columns: auto auto 1fr auto;\n gap: 6px;\n align-items: center;\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mfb-text-muted);\n}\n.changelog-group-marker {\n color: var(--mfb-accent);\n font-size: 14px;\n line-height: 1;\n}\n.changelog-group-label { font-weight: 600; }\n.changelog-group-rule {\n height: 1px;\n background: var(--mfb-border);\n align-self: center;\n}\n.changelog-group-count {\n font-variant-numeric: tabular-nums;\n font-weight: 600;\n}\n\n/* ---- v0.12: Board tab + expanded modal (\"transport\" feel) ------------ */\n\n/* When the Board tab is active, the modal grows toward near-fullscreen\n * on desktop and full-screen on mobile. The CSS transition gives the\n * sense of clicking through to a real page rather than switching tabs.\n * Width/height/max-* are animated together; padding too, since the\n * board's denser layout needs less of the modal's default padding. */\n.modal.is-expanded {\n /* Margin around the expanded board view. --mfb-space-7 (48px) matches\n * the breathing room of the default modal — 24px top + 24px bottom +\n * 24px on each side — so the panel reads as \"near-fullscreen with a\n * visible frame\" instead of \"the whole viewport, content cut off\".\n *\n * The base .modal rule uses the browser default content-box, so\n * without an override the 24px padding would be ADDED to the\n * calc(100vh - 48px) height — the rendered box would exactly equal\n * the viewport again, defeating the margin. Force border-box so\n * padding lives INSIDE the height we set. */\n box-sizing: border-box;\n width: min(1280px, calc(100vw - var(--mfb-space-7)));\n max-height: calc(100vh - var(--mfb-space-7));\n height: calc(100vh - var(--mfb-space-7));\n padding: var(--mfb-space-5);\n transition:\n width 320ms cubic-bezier(0.22, 1, 0.36, 1),\n height 320ms cubic-bezier(0.22, 1, 0.36, 1),\n max-height 320ms cubic-bezier(0.22, 1, 0.36, 1),\n padding 320ms cubic-bezier(0.22, 1, 0.36, 1);\n}\n@media (prefers-reduced-motion: reduce) {\n .modal.is-expanded { transition: none; }\n}\n.backdrop.is-expanded { /* hook for any backdrop-level overrides */ }\n\n@media (max-width: 640px) {\n /* On mobile the sheet still snaps to the bottom edge and stretches\n * almost the full viewport — 24px gap from the top is enough to\n * convey \"this is a panel, not a takeover\", but we don't pull margin\n * off the sides where users expect the sheet to fill the viewport. */\n .modal.is-expanded {\n width: 100vw;\n height: calc(100vh - var(--mfb-space-5));\n border-radius: var(--mfb-radius-lg) var(--mfb-radius-lg) 0 0;\n }\n}\n\n/* Decorative purple accent on the Board tab button so users discover\n * it's a different surface — subtle, not loud. */\n.tab-button--board.is-active {\n color: #6b21a8;\n box-shadow: inset 0 -2px 0 0 #a855f7;\n}\n.tab-button--board:not(.is-active):hover {\n color: #6b21a8;\n}\n\n/* ---- Board layout ---- */\n\n.board-view {\n display: flex;\n flex-direction: column;\n gap: var(--mfb-space-4);\n flex: 1 1 auto;\n min-height: 0; /* allow inner panes to scroll */\n animation: mfb-board-in 280ms ease-out 40ms both;\n}\n@keyframes mfb-board-in {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n@media (prefers-reduced-motion: reduce) {\n .board-view { animation: none; }\n}\n\n.board-header {\n display: flex;\n flex-direction: column;\n gap: var(--mfb-space-4);\n}\n.board-header-title {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n.board-header-emoji {\n font-size: 26px;\n line-height: 1;\n}\n.board-header-h {\n margin: 0;\n font-size: var(--mfb-text-2xl);\n font-weight: 600;\n letter-spacing: -0.01em;\n}\n.board-header-sub {\n margin: 2px 0 0 0;\n color: var(--mfb-text-muted);\n font-size: var(--mfb-text-sm);\n}\n\n/* KPI strip — 4 cards, equal width, subtle tone-tints by bucket. */\n.board-kpi-strip {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 12px;\n}\n.board-kpi-card {\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n padding: 14px 16px;\n background: var(--mfb-bg);\n display: flex;\n flex-direction: column;\n gap: 4px;\n position: relative;\n overflow: hidden;\n transition: transform 160ms ease, box-shadow 160ms ease;\n}\n.board-kpi-card::after {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n width: 3px;\n height: 100%;\n background: var(--mfb-border);\n transition: background 160ms ease;\n}\n.board-kpi-card:hover {\n transform: translateY(-1px);\n box-shadow: var(--mfb-shadow);\n}\n.board-kpi-card.tone-new::after { background: #3b82f6; }\n.board-kpi-card.tone-progress::after { background: #f59e0b; }\n.board-kpi-card.tone-validation::after { background: #a855f7; }\n.board-kpi-card.tone-rate::after { background: #10b981; }\n.board-kpi-value {\n font-size: 28px;\n font-weight: 700;\n line-height: 1;\n letter-spacing: -0.02em;\n color: var(--mfb-text);\n font-variant-numeric: tabular-nums;\n}\n.board-kpi-label {\n font-size: var(--mfb-text-xs);\n color: var(--mfb-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n font-weight: 500;\n}\n.board-kpi-strip.is-loading .board-kpi-value {\n opacity: 0.35;\n}\n@media (max-width: 720px) {\n .board-kpi-strip { grid-template-columns: repeat(2, 1fr); }\n}\n\n/* Filter row */\n.board-filters {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n.board-filter-select,\n.board-filter-search {\n padding: 6px 10px;\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n background: var(--mfb-bg);\n color: var(--mfb-text);\n font: inherit;\n font-size: var(--mfb-text-sm);\n height: 32px;\n transition: border-color 120ms ease, box-shadow 120ms ease;\n}\n.board-filter-select:focus-visible,\n.board-filter-search:focus-visible {\n outline: none;\n border-color: var(--mfb-accent);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mfb-accent) 18%, transparent);\n}\n.board-filter-search { min-width: 180px; flex: 1 1 220px; }\n.board-filter-toggle {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: var(--mfb-text-sm);\n color: var(--mfb-text);\n cursor: pointer;\n user-select: none;\n}\n.board-filter-toggle input { margin: 0; }\n.board-filter-clear {\n background: transparent;\n border: 0;\n color: var(--mfb-text-muted);\n font: inherit;\n font-size: var(--mfb-text-sm);\n display: inline-flex;\n align-items: center;\n gap: 4px;\n cursor: pointer;\n padding: 4px 6px;\n border-radius: var(--mfb-radius);\n transition: background 120ms ease, color 120ms ease;\n}\n.board-filter-clear:hover {\n color: var(--mfb-text);\n background: var(--mfb-surface);\n}\n\n/* Scope segmented control — [This page | All pages], front of the filter row. */\n.board-scope {\n display: inline-flex;\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n overflow: hidden;\n flex: none;\n}\n.board-scope-btn {\n border: none;\n background: none;\n padding: 6px 10px;\n font: inherit;\n font-size: var(--mfb-text-sm);\n color: var(--mfb-text-muted);\n cursor: pointer;\n}\n.board-scope-btn.is-active {\n background: color-mix(in srgb, var(--mfb-accent) 14%, transparent);\n color: var(--mfb-accent);\n font-weight: 600;\n}\n.board-scope-btn:focus-visible {\n outline: 2px solid var(--mfb-accent);\n outline-offset: -2px;\n}\n\n/* Send-tab page-activity strip — one line, muted, non-blocking. */\n.page-strip {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--mfb-space-3);\n margin: var(--mfb-space-3) 0 0;\n padding: var(--mfb-space-2) var(--mfb-space-3);\n border-radius: var(--mfb-radius);\n background: var(--mfb-surface-2);\n color: var(--mfb-text-muted);\n font-size: var(--mfb-text-sm);\n}\n.page-strip-view {\n border: none;\n background: none;\n padding: 0;\n color: var(--mfb-accent);\n font-size: var(--mfb-text-sm);\n font-weight: 600;\n cursor: pointer;\n}\n.page-strip-view:hover {\n text-decoration: underline;\n}\n\n/* Master/detail layout — two-column at >=900px, stacked below. */\n.board-body {\n display: grid;\n grid-template-columns: minmax(280px, 360px) 1fr;\n gap: var(--mfb-space-4);\n flex: 1 1 auto;\n min-height: 0;\n}\n@media (max-width: 900px) {\n .board-body { grid-template-columns: 1fr; }\n .board-detail-wrap:not(.has-selection) { display: none; }\n}\n\n.board-list-wrap {\n display: flex;\n flex-direction: column;\n gap: 6px;\n min-height: 0;\n overflow: hidden;\n}\n.board-list-count {\n font-size: var(--mfb-text-xs);\n color: var(--mfb-text-muted);\n padding: 0 4px;\n}\n.board-list {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 8px;\n overflow-y: auto;\n flex: 1 1 auto;\n scrollbar-width: thin;\n scrollbar-gutter: stable;\n}\n\n.board-row {\n display: flex;\n flex-direction: column;\n gap: 6px;\n width: 100%;\n text-align: left;\n padding: 12px 14px;\n border-radius: var(--mfb-radius);\n background: var(--mfb-bg);\n border: 1px solid var(--mfb-border);\n cursor: pointer;\n font: inherit;\n color: inherit;\n /* Stagger reveal on first paint. */\n animation: mfb-row-in 280ms ease-out both;\n transition: border-color 140ms ease, background 140ms ease, transform 80ms ease;\n}\n.board-row:hover {\n border-color: var(--mfb-border-strong);\n background: var(--mfb-surface);\n}\n.board-row:active { transform: scale(0.997); }\n.board-row.is-selected {\n border-color: var(--mfb-accent);\n background: color-mix(in srgb, var(--mfb-accent) 6%, var(--mfb-bg));\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--mfb-accent) 18%, transparent);\n}\n@keyframes mfb-row-in {\n from { opacity: 0; transform: translateY(4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n@media (prefers-reduced-motion: reduce) {\n .board-row { animation: none; }\n}\n/* Stagger first 6 rows so the list reveals top-down. */\n.board-row:nth-child(1) { animation-delay: 0ms; }\n.board-row:nth-child(2) { animation-delay: 40ms; }\n.board-row:nth-child(3) { animation-delay: 80ms; }\n.board-row:nth-child(4) { animation-delay: 120ms; }\n.board-row:nth-child(5) { animation-delay: 160ms; }\n.board-row:nth-child(6) { animation-delay: 200ms; }\n\n.board-row-badges {\n display: flex;\n gap: 6px;\n align-items: center;\n flex-wrap: wrap;\n}\n.board-row-description {\n font-size: var(--mfb-text-sm);\n color: var(--mfb-text);\n line-height: 1.4;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n.board-row-meta {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: var(--mfb-text-xs);\n color: var(--mfb-text-muted);\n flex-wrap: wrap;\n}\n.board-row-author { font-weight: 500; color: var(--mfb-text); }\n.board-row-comments {\n display: inline-flex;\n align-items: center;\n gap: 3px;\n font-variant-numeric: tabular-nums;\n}\n.board-row-time { margin-left: auto; font-variant-numeric: tabular-nums; }\n\n/* Status / severity badges shared with the detail panel. The tones\n * borrow PNR's badge palette — calm pastels that don't fight content. */\n.board-row-badges .badge {\n font-size: 11px;\n font-weight: 500;\n letter-spacing: 0.02em;\n padding: 2px 8px;\n border-radius: 999px;\n background: #f3f4f6;\n color: #374151;\n text-transform: lowercase;\n}\n.badge.status-new { background: #dbeafe; color: #1e40af; }\n.badge.status-in_progress { background: #fef3c7; color: #92400e; }\n.badge.status-awaiting_validation { background: #ede9fe; color: #6b21a8; }\n.badge.status-closed { background: #d1fae5; color: #065f46; }\n.badge.status-rejected { background: #fee2e2; color: #991b1b; }\n.badge.status-duplicate { background: #f3f4f6; color: #4b5563; }\n.badge.status-wontfix { background: #f3f4f6; color: #4b5563; }\n.badge.severity-blocker { background: #fee2e2; color: #991b1b; }\n.badge.severity-high { background: #fee2e2; color: #b91c1c; }\n.badge.severity-medium { background: #fef3c7; color: #92400e; }\n.badge.severity-low { background: #e0f2fe; color: #075985; }\n\n/* Detail pane (within Board). Inherits from .report-detail.variant-board */\n.board-detail-wrap {\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n background: var(--mfb-bg);\n overflow-y: auto;\n min-height: 0;\n display: flex;\n flex-direction: column;\n}\n.board-detail-empty {\n flex: 1 1 auto;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n color: var(--mfb-text-muted);\n padding: var(--mfb-space-5);\n text-align: center;\n font-size: var(--mfb-text-sm);\n}\n.board-detail-empty svg { opacity: 0.6; }\n\n.report-detail.variant-board { padding: 16px 18px; }\n.report-detail-header--board {\n align-items: center;\n}\n.report-detail-back {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n height: 28px;\n padding: 0 10px;\n}\n\n/* Empty + loading + error states */\n.board-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n text-align: center;\n padding: var(--mfb-space-5);\n color: var(--mfb-text-muted);\n flex: 1 1 auto;\n}\n.board-empty h3 {\n margin: 0;\n font-size: var(--mfb-text-base);\n color: var(--mfb-text);\n}\n.board-empty p {\n margin: 0;\n font-size: var(--mfb-text-sm);\n}\n.board-error {\n padding: 12px 14px;\n border-radius: var(--mfb-radius);\n background: #fef2f2;\n color: #991b1b;\n font-size: var(--mfb-text-sm);\n}\n\n/* Skeleton rows pulse subtly so the user knows something's coming. */\n.board-list-skeleton .skeleton-row {\n cursor: default;\n pointer-events: none;\n}\n.skeleton-line {\n height: 12px;\n background: linear-gradient(\n 90deg,\n var(--mfb-surface) 0%,\n color-mix(in srgb, var(--mfb-surface) 60%, var(--mfb-border)) 50%,\n var(--mfb-surface) 100%\n );\n background-size: 200% 100%;\n border-radius: 4px;\n animation: mfb-skeleton 1400ms ease-in-out infinite;\n}\n.skeleton-badges { width: 60%; height: 14px; }\n.skeleton-text { width: 90%; }\n.skeleton-text.short { width: 60%; }\n@keyframes mfb-skeleton {\n from { background-position: 100% 0; }\n to { background-position: -100% 0; }\n}\n@media (prefers-reduced-motion: reduce) {\n .skeleton-line { animation: none; }\n}\n\n/* Hover peek panel — desktop-only ambient surface listing this page's\n open reports above the FAB (spec §3.2). Non-modal; never renders while\n the modal itself is open. */\n.page-peek {\n position: absolute;\n right: 0;\n bottom: 60px;\n width: 300px;\n padding: var(--mfb-space-3);\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius-lg);\n background: var(--mfb-surface);\n color: var(--mfb-text);\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);\n font-size: var(--mfb-text-sm);\n}\n.page-peek-title {\n font-weight: 700;\n margin-bottom: var(--mfb-space-2);\n color: var(--mfb-text-muted);\n text-transform: uppercase;\n font-size: var(--mfb-text-xs);\n letter-spacing: 0.04em;\n}\n.page-peek-row {\n display: flex;\n align-items: center;\n gap: var(--mfb-space-2);\n width: 100%;\n padding: var(--mfb-space-2);\n border: none;\n border-radius: var(--mfb-radius);\n background: none;\n color: var(--mfb-text);\n text-align: left;\n cursor: pointer;\n}\n.page-peek-row:hover {\n background: var(--mfb-surface-2);\n}\n.page-peek-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n flex: none;\n}\n.page-peek-dot.status-new { background: #1e40af; }\n.page-peek-dot.status-in_progress { background: #92400e; }\n.page-peek-dot.status-awaiting_validation { background: #6b21a8; }\n.page-peek-dot.status-closed { background: #065f46; }\n.page-peek-dot.status-rejected { background: #991b1b; }\n.page-peek-desc {\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n.page-peek-footer {\n display: flex;\n justify-content: space-between;\n margin-top: var(--mfb-space-2);\n padding-top: var(--mfb-space-2);\n border-top: 1px solid var(--mfb-border);\n}\n.page-peek-viewall,\n.page-peek-send {\n border: none;\n background: none;\n padding: 0;\n cursor: pointer;\n color: var(--mfb-accent);\n font-weight: 600;\n font-size: var(--mfb-text-sm);\n}\n.page-peek-skeleton div {\n height: 14px;\n border-radius: 4px;\n background: var(--mfb-surface-2);\n margin: var(--mfb-space-2) 0;\n}\n`\n","import { createApiClient } from './api/client'\nimport { installCapture } from './capture'\nimport { resolveStrings } from './widget/i18n'\nimport { mountWidget } from './widget/mount'\nimport { currentPagePath } from './widget/currentPage'\nimport type { FeedbackApi, FeedbackConfig, ReportPayload, ReportTransformer, UserIdentity } from './types'\n\nexport interface InternalConfig extends FeedbackConfig {\n fetchImpl?: typeof fetch\n}\n\ninterface WindowWithGlobal extends Window {\n mhosaicFeedback?: FeedbackApi\n}\n\n/** Default offset for the secondary QA FAB: center the 24px QA dot above the\n * 48px feedback FAB (bottom:24 right:24). → { right: 36, bottom: 84 }. */\nfunction defaultQaPosition(): { right: number; bottom: number } {\n return { right: 24 + (48 - 24) / 2, bottom: 24 + 48 + 12 }\n}\n\nexport function createFeedback(config: InternalConfig): FeedbackApi & { _registerTransformer(fn: ReportTransformer): void } {\n const env = config.env ?? 'prod'\n const locale =\n config.locale ?? (typeof navigator !== 'undefined' ? navigator.language : undefined)\n const strings = resolveStrings(\n config.translations ?? {},\n locale !== undefined ? { locale } : {},\n )\n const capture = installCapture({\n ...(config.sanitizeUrl !== undefined && { sanitizeUrl: config.sanitizeUrl }),\n })\n let user: UserIdentity | undefined = config.user\n let metadata: Record<string, unknown> = config.metadata ?? {}\n\n const api = createApiClient({\n apiKey: config.apiKey,\n endpoint: config.endpoint,\n ...(config.fetchImpl !== undefined && { fetch: config.fetchImpl }),\n ...(config.beforeSend !== undefined && { beforeSend: config.beforeSend }),\n // v0.13: the API client reads this on every request so a late\n // `identify({userHash, exp, ...})` lights up signed headers\n // immediately on subsequent calls. Returns null when the host\n // hasn't supplied a signature (legacy unsigned path).\n getSignedIdentity: () => {\n if (!user || !user.userHash || !user.exp || !user.email) return null\n return {\n userHash: user.userHash,\n exp: user.exp,\n email: user.email,\n }\n },\n })\n const transformers: ReportTransformer[] = []\n\n const host = document.createElement('div')\n host.className = 'mhosaic-feedback'\n if (config.attachTo) {\n const attach = typeof config.attachTo === 'string' ? document.querySelector(config.attachTo) : config.attachTo\n attach?.appendChild(host)\n } else {\n document.body.appendChild(host)\n }\n\n async function buildAndSubmit(values: {\n description: string\n feedback_type?: string\n severity?: string\n synthetic?: boolean\n /** Screenshot(s) the user explicitly attached — via dropzone, paste,\n * file picker, or the annotator. `screenshots` (attach order) is what\n * the form emits; the single `screenshot` stays accepted for the\n * public fb.submit() API. v0.12 dropped html2canvas-on-submit;\n * v0.7.1 dropped getDisplayMedia. If the user didn't attach anything,\n * the report ships without a screenshot. */\n screenshot?: Blob\n screenshots?: Blob[]\n /** Capture-method label for the payload. Always \"manual\" now (v0.7.1);\n * the union type stays open against the backend schema, which still\n * recognizes legacy values on existing reports. */\n capture_method?: 'manual'\n }) {\n // Auto-error reports never carry a screenshot — the captured ring\n // buffer (errors, console, network) carries the signal, and the DOM\n // is typically in an inconsistent state when a JS error fires anyway.\n const attached = values.screenshots?.length\n ? values.screenshots\n : values.screenshot\n ? [values.screenshot]\n : undefined\n const manualScreenshots = values.synthetic ? undefined : attached\n const technical_context = capture.snapshot()\n // Surface identify()/setMetadata() values on the report. Without this\n // the host-supplied user identity and metadata sit in closure forever\n // and never reach the operator — a stack trace from a logged-in user\n // looks anonymous on the dashboard.\n //\n // SECURITY: identify() may carry `userHash` + `exp` (the host's HMAC\n // envelope used to attest the user's identity to the backend). That\n // envelope is a bearer-style auth token until it expires; leaking it\n // into technical_context surfaces it on the operator dashboard and to\n // anyone who can read the report. Strip it before embedding.\n if (user) {\n const { userHash: _hash, exp: _exp, ...safeUser } = user\n technical_context.user = safeUser\n }\n if (metadata && Object.keys(metadata).length > 0) {\n technical_context.metadata = { ...metadata }\n }\n const captureMethod: ReportPayload['capture_method'] = manualScreenshots?.length\n ? (values.capture_method ?? 'manual')\n : 'none'\n const payload: ReportPayload = {\n description: values.description,\n feedback_type: (values.feedback_type ?? 'bug') as ReportPayload['feedback_type'],\n severity: (values.severity ?? 'medium') as ReportPayload['severity'],\n env,\n page_url: window.location.href,\n user_agent: navigator.userAgent,\n capture_method: captureMethod,\n technical_context,\n }\n // Build-time stamp; tsup's `define` substitutes the package.json\n // version into __MFB_VERSION__. Conditionally assigned so\n // exactOptionalPropertyTypes stays happy under vitest (no tsup\n // pass) where the constant is undefined.\n if (typeof __MFB_VERSION__ !== 'undefined' && __MFB_VERSION__) {\n payload.widget_version = __MFB_VERSION__\n }\n if (manualScreenshots?.length) {\n payload.screenshots = manualScreenshots\n // Mirror the first into the legacy field for beforeSend hooks that\n // still read payload.screenshot.\n payload.screenshot = manualScreenshots[0]!\n }\n if (values.synthetic) payload.synthetic = true\n // current-page feedback: resolve path via host override or pathname.\n const pagePath = currentPagePath(config)\n if (pagePath) payload.page_path = pagePath\n // v0.7: lift the host-provided identity to the top level so the\n // backend can upsert a WidgetUser FK. technical_context.user still\n // carries it for backward-compat with the operator's tech-context\n // viewer (and so an audit-log entry knows who was identified at\n // submission time).\n if (user?.id !== undefined && user.id !== null && user.id !== '') {\n payload.user = {\n // The host can pass `id` as a string or number; the backend\n // stores it as an opaque string. Coerce here to a stable shape.\n id: String(user.id),\n ...(user.email !== undefined && { email: user.email }),\n ...(user.name !== undefined && { name: user.name }),\n }\n }\n let finalPayload: ReportPayload = payload\n for (const t of transformers) finalPayload = await t(finalPayload)\n try {\n const result = await api.submitReport(finalPayload)\n config.onSubmitSuccess?.(result)\n capture.clear()\n return result\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err))\n config.onError?.(error)\n throw error\n }\n }\n\n const handle = mountWidget({\n host,\n strings,\n showFAB: config.showFAB ?? true,\n // Return the created report so the modal can acknowledge it by #seq (#82).\n onSubmit: (values) => buildAndSubmit(values),\n api,\n // Keep this a callback (not a snapshot) so the mount picks up identity\n // changes that happen after createFeedback() — `notifyIdentityChanged()`\n // is the trigger for a re-render.\n getExternalId: () => (user?.id !== undefined && user.id !== null && user.id !== '' ? String(user.id) : undefined),\n // Phase 4: the manifest tells the loader whether this project gates the\n // widget per end-user; the loader forwards it as `requiresVisibilityCheck`.\n requiresVisibilityCheck: config.requiresVisibilityCheck ?? false,\n // Send the identified email alongside the id so an email-based allowlist\n // can match (the backend matches external_id OR email). Read from `user`\n // live so identity set after createFeedback() is reflected.\n checkVisibility: (externalId) => api.checkVisibility(externalId, user?.email),\n // Thread the host's getCurrentPage override so the Board chip resolves\n // the page identically to what the submit side records (§ currentPage).\n ...(config.getCurrentPage !== undefined && { getCurrentPage: config.getCurrentPage }),\n // Opt-in: FAB opens to the page-scoped Board (default off → Send-first).\n ...(config.openToCurrentPageFeedback !== undefined && {\n openToCurrentPageFeedback: config.openToCurrentPageFeedback,\n }),\n ...(config.showPageActivity !== undefined && {\n showPageActivity: config.showPageActivity,\n }),\n })\n\n // Opt-in QA Meter second FAB. Lazily imported so the QA bytes stay out of\n // the main bundle for consumers who don't enable it. The host serves its\n // own artifact via qaMeter.source.\n let qaHandle: { dispose(): void } | undefined\n let qaDisposed = false\n if (config.qaMeter?.source) {\n const qa = config.qaMeter\n const qaLocale: 'fr' | 'en' =\n qa.locale ?? (String(locale ?? '').toLowerCase().startsWith('fr') ? 'fr' : 'en')\n void import('./qa-meter').then(({ createQaMeter }) => {\n if (qaDisposed) return // shutdown() ran before the import resolved\n qaHandle = createQaMeter({\n source: qa.source,\n size: qa.size ?? 'sm',\n position: qa.position ?? defaultQaPosition(),\n locale: qaLocale,\n ...(qa.getCurrentPage && { getCurrentPage: qa.getCurrentPage }),\n })\n })\n }\n\n const instance: FeedbackApi & { _registerTransformer(fn: ReportTransformer): void } = {\n show() { handle.open() },\n hide() { handle.close() },\n open(opts) { handle.open(); void opts },\n async submit(partial) {\n return buildAndSubmit({\n description: partial.description,\n ...(partial.feedback_type !== undefined && { feedback_type: partial.feedback_type }),\n ...(partial.severity !== undefined && { severity: partial.severity }),\n ...(partial.synthetic !== undefined && { synthetic: partial.synthetic }),\n ...(partial.screenshot !== undefined && { screenshot: partial.screenshot }),\n ...(partial.screenshots !== undefined && { screenshots: partial.screenshots }),\n })\n },\n identify(u) {\n user = u\n // Tell the mount to re-evaluate FAB visibility / Mine tab gating.\n handle.notifyIdentityChanged()\n },\n setMetadata(kv) { metadata = { ...metadata, ...kv } },\n shutdown() {\n qaDisposed = true\n qaHandle?.dispose()\n handle.dispose()\n capture.dispose()\n host.remove()\n // Only release the global if it still points at *us* — otherwise a\n // newer instance has taken ownership and we mustn't blow it away.\n const w = window as unknown as WindowWithGlobal\n if (w.mhosaicFeedback === instance) {\n delete w.mhosaicFeedback\n }\n },\n _registerTransformer(fn: ReportTransformer) { transformers.push(fn) },\n }\n\n // Expose the instance globally for ad-hoc callers (DevTools, docs pages,\n // help-widget integrations). The most-recently-created instance wins.\n ;(window as unknown as WindowWithGlobal).mhosaicFeedback = instance\n\n return instance\n}\n","/**\n * Always-on debugger module. Hooks `window.error` and `unhandledrejection`,\n * then ships each fresh runtime error as a synthetic FeedbackReport via\n * the existing widget submit pipeline.\n *\n * Same opt-in pattern as `withReplay` / `withWebVitals` — host apps that\n * want curated-feedback-only simply don't import it.\n *\n * Design choices (see PR description):\n * - Per-fingerprint cooldown (default 5 min) absorbs render loops without\n * flooding the operator dashboard or burning the per-key write throttle.\n * - Server-side fingerprinting groups across sessions; the client-side\n * fingerprint exists only to dedup within a single page lifetime, so\n * it doesn't need to match the server's hash.\n * - Recursion guard: an error thrown *inside* the submit pipeline must\n * not re-enter the handler.\n * - Description is the error message truncated at 200 chars — the full\n * stack lives in technical_context.errors[].\n * - No screenshot is taken (buildAndSubmit honors `synthetic: true`):\n * html2canvas is slow + the DOM is unreliable mid-error.\n */\n\nimport type { FeedbackApi } from '../types'\n\nexport interface ErrorTrackingOptions {\n /** Opt-out kill switch without removing the import. Default `true`. */\n enabled?: boolean\n /**\n * Drop repeats of the same fingerprint within this window.\n * Default 300_000 ms (5 minutes). Set to 0 to disable dedup\n * (every fired error becomes a report — useful only for tests).\n */\n perFingerprintCooldownMs?: number\n /**\n * Probabilistic head sampling: 1.0 captures everything, 0.5 drops half.\n * Default 1.0. Sampled-out errors don't count toward the cooldown — if\n * you sample at 0.1, you'll still see ~10% of the volume per fingerprint.\n */\n sampleRate?: number\n /** Max characters in the auto-generated description. Default 200. */\n maxDescriptionLen?: number\n /**\n * Global ceiling on synthetic submissions per minute, regardless of\n * fingerprint. Defends against a hostile / buggy page that throws\n * errors with attacker-varying `message` strings (counter, nonce) —\n * each unique message creates a fresh fingerprint, bypassing the\n * per-fingerprint cooldown, and would otherwise burn the project's\n * backend write quota.\n * Default 30. Set to 0 to disable the global cap (not recommended).\n */\n globalRatePerMinute?: number\n /**\n * Max number of distinct fingerprints emitted per page lifetime. Once\n * exceeded, NEW fingerprints are dropped (the cooldown still applies\n * to already-seen ones, so legitimate repeating errors keep cycling).\n * Defense against a page that synthesizes an unbounded number of\n * unique error shapes.\n * Default 200. Set to 0 to disable.\n */\n maxDistinctFingerprints?: number\n}\n\ninterface InternalApi extends FeedbackApi {\n _registerTransformer?: (fn: unknown) => void\n /** Set once withErrorTracking has armed this instance (idempotency guard). */\n _errorTrackingArmed?: boolean\n}\n\ninterface NormalizedError {\n name: string\n message: string\n stack?: string\n}\n\nconst DEFAULTS = {\n enabled: true,\n perFingerprintCooldownMs: 5 * 60 * 1000,\n sampleRate: 1,\n maxDescriptionLen: 200,\n globalRatePerMinute: 30,\n maxDistinctFingerprints: 200,\n} as const satisfies Required<ErrorTrackingOptions>\n\n/**\n * Pull a normalized {name, message, stack} from whatever the runtime\n * handed us. Browsers throw genuine Error subclasses for uncaught\n * exceptions but `unhandledrejection.reason` is sometimes a string,\n * a plain object, or undefined.\n */\nfunction normalize(thrown: unknown): NormalizedError {\n if (thrown instanceof Error) {\n return {\n name: thrown.name || 'Error',\n message: String(thrown.message ?? ''),\n ...(thrown.stack && { stack: String(thrown.stack) }),\n }\n }\n if (typeof thrown === 'string') return { name: 'Error', message: thrown }\n if (thrown && typeof thrown === 'object') {\n const o = thrown as Record<string, unknown>\n return {\n name: (typeof o.name === 'string' && o.name) || 'Error',\n message:\n (typeof o.message === 'string' && o.message) ||\n (() => {\n try { return JSON.stringify(thrown) } catch { return String(thrown) }\n })(),\n ...(typeof o.stack === 'string' && { stack: o.stack }),\n }\n }\n return { name: 'Error', message: String(thrown) }\n}\n\n/**\n * Stable per-session signature. Server still computes its own fingerprint\n * across sessions — this only needs to dedup within one page lifetime.\n * Stack frames are the strongest discriminator; pathname keeps two distinct\n * routes from collapsing onto the same key.\n *\n * SECURITY: we include only a 30-char message prefix, not the full message.\n * A hostile or buggy page that throws errors with counter-suffixed messages\n * (e.g. `Error 1`, `Error 2`, …) would otherwise produce a fresh fingerprint\n * per throw and bypass the per-fingerprint cooldown. The stack frames are\n * the same across those counter variants, so collapsing the message into a\n * prefix preserves real-error distinction while killing the counter bypass.\n */\nfunction clientFingerprint(err: NormalizedError, pathname: string): string {\n const firstFrames = (err.stack ?? '').split('\\n').slice(0, 4).join('\\n')\n const messagePrefix = err.message.slice(0, 30)\n return `${err.name}:${messagePrefix}|${pathname}|${firstFrames}`\n}\n\nfunction truncate(s: string, max: number): string {\n if (s.length <= max) return s\n return s.slice(0, Math.max(0, max - 1)) + '…'\n}\n\nexport function withErrorTracking(\n fb: FeedbackApi,\n options: ErrorTrackingOptions = {},\n): FeedbackApi {\n const opts = { ...DEFAULTS, ...options }\n if (!opts.enabled) return fb\n if (typeof window === 'undefined') return fb\n\n const internal = fb as InternalApi\n\n // Idempotency guard. The same instance can now be armed from two places:\n // the host-side <FeedbackProvider> (opt-out default) AND the runtime\n // widget bundle (which self-arms on the loader path). A client who both\n // upgrades the provider and receives the self-arming bundle would\n // otherwise double-register the window listeners and file every error\n // twice. First arm wins; later calls are no-ops.\n if (internal._errorTrackingArmed) return fb\n internal._errorTrackingArmed = true\n\n // Recursion guard, scoped per-fingerprint: while we're posting a synthetic\n // report for fingerprint X, a re-entry for the SAME X (e.g. an error\n // raised by the submit pipeline itself) is dropped. Distinct errors that\n // fire concurrently still both go through.\n const inFlight = new Set<string>()\n\n // fingerprint -> last-sent epoch ms. Periodically pruned in shouldSend().\n const lastSent = new Map<string, number>()\n // Sliding-window of recent send timestamps for the global rate cap.\n // Trimmed in shouldSend(); never grows beyond globalRatePerMinute entries.\n const recentSendTimes: number[] = []\n\n function shouldSend(fp: string, now: number): boolean {\n // (1) Global rate ceiling — runs first because it doesn't depend on\n // fingerprint state. Defends against the \"counter-suffixed error\n // messages\" attack that would otherwise produce one unique\n // fingerprint per throw.\n if (opts.globalRatePerMinute > 0) {\n const windowStart = now - 60_000\n // Drop expired entries from the front of the sliding window.\n while (recentSendTimes.length > 0 && recentSendTimes[0]! < windowStart) {\n recentSendTimes.shift()\n }\n if (recentSendTimes.length >= opts.globalRatePerMinute) {\n return false\n }\n }\n // (2) Per-page cap on the number of distinct fingerprints. A hostile\n // page that synthesizes an unbounded number of unique error\n // shapes runs out of new slots after this many — already-seen\n // fingerprints keep cycling under the per-fp cooldown.\n if (\n opts.maxDistinctFingerprints > 0 &&\n !lastSent.has(fp) &&\n lastSent.size >= opts.maxDistinctFingerprints\n ) {\n return false\n }\n if (opts.perFingerprintCooldownMs <= 0) {\n recentSendTimes.push(now)\n return true\n }\n const prev = lastSent.get(fp)\n if (prev !== undefined && now - prev < opts.perFingerprintCooldownMs) {\n return false\n }\n // Cheap stale-entry sweep so the Map doesn't grow unboundedly on a\n // long-lived session with many distinct errors. First pass: drop\n // entries past `cooldown * 2`. If we're still above a hard cap of\n // 256 — i.e. an SPA that's been live for hours and keeps emitting\n // *fresh* fingerprints faster than they go stale — drop the oldest\n // entries until we're back to a comfortable ceiling. Map iteration\n // order is insertion order so the first N keys are the oldest.\n const HARD_CAP = 256\n const TARGET_AFTER_TRIM = 192\n if (lastSent.size > HARD_CAP) {\n const cutoff = now - opts.perFingerprintCooldownMs * 2\n for (const [k, v] of lastSent) {\n if (v < cutoff) lastSent.delete(k)\n }\n if (lastSent.size > HARD_CAP) {\n const toDrop = lastSent.size - TARGET_AFTER_TRIM\n let dropped = 0\n for (const k of lastSent.keys()) {\n if (dropped >= toDrop) break\n lastSent.delete(k)\n dropped++\n }\n }\n }\n recentSendTimes.push(now)\n return true\n }\n\n async function report(err: NormalizedError) {\n if (opts.sampleRate < 1 && Math.random() >= opts.sampleRate) return\n\n const fp = clientFingerprint(err, window.location.pathname)\n if (inFlight.has(fp)) return\n const now = Date.now()\n if (!shouldSend(fp, now)) return\n lastSent.set(fp, now)\n\n const description = truncate(\n err.message ? `${err.name}: ${err.message}` : err.name,\n opts.maxDescriptionLen,\n )\n\n inFlight.add(fp)\n try {\n await internal.submit({\n description,\n feedback_type: 'bug',\n severity: 'high',\n synthetic: true,\n } as Parameters<FeedbackApi['submit']>[0] & { synthetic: boolean })\n } catch {\n // Swallow submit errors — propagating them would just trigger another\n // unhandledrejection and feed the loop. The host's `onError` callback\n // (configured on createFeedback) still fires for visibility.\n } finally {\n inFlight.delete(fp)\n }\n }\n\n function onError(event: ErrorEvent) {\n const candidate: unknown = event.error ?? {\n name: 'Error',\n message: event.message,\n stack: `at ${event.filename}:${event.lineno}:${event.colno}`,\n }\n void report(normalize(candidate))\n }\n\n function onUnhandledRejection(event: PromiseRejectionEvent) {\n void report(normalize(event.reason))\n }\n\n window.addEventListener('error', onError)\n window.addEventListener('unhandledrejection', onUnhandledRejection)\n\n // Wrap shutdown so listeners come down with the widget. Without this, a\n // host that re-mounts the widget under React StrictMode would double the\n // listeners and emit each error twice.\n const originalShutdown = internal.shutdown.bind(internal)\n internal.shutdown = () => {\n window.removeEventListener('error', onError)\n window.removeEventListener('unhandledrejection', onUnhandledRejection)\n lastSent.clear()\n originalShutdown()\n }\n\n return fb\n}\n"],"mappings":"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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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","names":["assign","obj","props","i","removeNode","node","parentNode","removeChild","createElement","type","children","key","ref","normalizedProps","arguments","length","slice","call","defaultProps","createVNode","original","vnode","__k","__","__b","__e","__c","constructor","__v","vnodeId","__i","__u","options","Fragment","BaseComponent","context","this","getDomSibling","childIndex","sibling","renderComponent","component","__P","__d","oldVNode","oldDom","commitQueue","refQueue","newVNode","diff","__n","namespaceURI","commitRoot","updateParentDomPointers","base","some","child","enqueueRender","c","rerenderQueue","push","process","__r","prevDebounce","debounceRendering","defer","l","sort","depthSort","shift","diffChildren","parentDom","renderResult","newParentVNode","oldParentVNode","globalContext","namespace","excessDomChildren","isHydrating","childVNode","newDom","firstChildDom","result","shouldPlace","oldChildren","EMPTY_ARR","newChildrenLength","constructNewChildrenArray","EMPTY_OBJ","applyRef","insert","nextSibling","skewedIndex","matchingIndex","oldChildrenLength","remainingOldChildren","skew","Array","String","isArray","findMatchingIndex","unmount","parentVNode","insertBefore","nodeType","x","y","matched","setStyle","style","value","setProperty","IS_NON_DIMENSIONAL","test","dom","name","oldValue","useCapture","lowerCaseName","o","cssText","replace","CAPTURE_REGEX","toLowerCase","EVENT_ATTACHED","eventClock","addEventListener","eventProxyCapture","eventProxy","removeEventListener","e","removeAttribute","setAttribute","createEventProxy","eventHandler","EVENT_DISPATCHED","event","tmp","isNew","oldProps","oldState","snapshot","clearProcessingException","newProps","isClassComponent","provider","componentContext","renderHook","count","newType","outer","prototype","render","contextType","__E","doRender","sub","state","__h","_sb","__s","getDerivedStateFromProps","componentWillMount","componentDidMount","componentWillReceiveProps","shouldComponentUpdate","apply","componentWillUpdate","componentDidUpdate","getChildContext","getSnapshotBeforeUpdate","cloneNode","then","MODE_HYDRATE","indexOf","markAsForce","diffElementNodes","diffed","root","cb","map","newHtml","oldHtml","newChildren","inputValue","checked","localName","document","createTextNode","createElementNS","is","__m","data","childNodes","attributes","__html","innerHTML","content","undefined","hasRefUnmount","current","skipRemove","r","componentWillUnmount","replaceNode","documentElement","firstChild","isValidElement","_id","init_preact","__esmMin","error","errorInfo","ctor","handled","getDerivedStateFromError","setState","componentDidCatch","update","callback","s","forceUpdate","Promise","bind","resolve","setTimeout","a","b","Math","random","toString","getHookState","index","type","options","__h","currentComponent","currentHook","hooks","__H","__","length","push","useState","initialState","useReducer","invokeOrReturn","reducer","init","hookState","currentIndex","_reducer","__c","action","currentValue","__N","nextValue","setState","__f","updateHookState","p","s","c","stateHooks","filter","x","every","prevScu","call","this","shouldUpdate","props","some","hookItem","shouldComponentUpdate","prevCWU","componentWillUpdate","__e","tmp","useEffect","callback","args","state","__s","argsChanged","_pendingArgs","useLayoutEffect","useRef","initialValue","useMemo","current","factory","useCallback","flushAfterPaintEffects","component","afterPaintEffects","shift","__P","invokeCleanup","invokeEffect","e","__v","afterNextFrame","raf","done","clearTimeout","timeout","HAS_RAF","cancelAnimationFrame","setTimeout","requestAnimationFrame","hook","comp","cleanup","oldArgs","newArgs","arg","f","previousComponent","prevRaf","oldBeforeDiff","oldBeforeRender","oldAfterDiff","oldCommit","oldBeforeUnmount","oldRoot","_options","__b","__r","diffed","unmount","vnode","parentDom","__k","__m","commitQueue","cb","hasErrored","resolveStrings","locale","QA_STRINGS_EN","QA_STRINGS_FR","init_i18n","__esmMin","normalizePattern","pattern","n","matchesPattern","pathSegs","patternSegs","required","last","seg","i","pat","resolvePage","pathname","patterns","segments","best","bestScore","patSegs","score","s","init_resolver","__esmMin","p","QA_METER_STYLES","init_styles","__esmMin","botTriColor","lane","humanTriColor","aggregateTri","colors","c","formatQaDate","iso","date","shortVersion","version","QA_COLOR_MODIFIER","QA_COLOR_LABEL_KEY","QA_PRIORITY_MODIFIER","QA_CASE_STATUS_LABEL_KEY","QA_GROUNDING_LABEL_KEY","init_ui","__esmMin","Pastille","color","state","strings","onOpenModal","onHover","base","detail","QA_COLOR_LABEL_KEY","label","k","QA_COLOR_MODIFIER","init_Pastille","__esmMin","init_preact","init_ui","LaneIcons","dev","bot","human","strings","k","TRI_MODIFIER","init_LaneIcons","__esmMin","init_preact","laneTriColor","scenario","humanTriColor","botTriColor","groupScenarios","scenarios","_a","_b","_c","map","key","bucket","a","b","interpolate","template","params","_","k","ScenarioRow","strings","lanes","provenance","QA_PRIORITY_MODIFIER","QA_CASE_STATUS_LABEL_KEY","QA_GROUNDING_LABEL_KEY","LaneIcons","TestPanel","page","state","meta","expanded","setExpanded","d","toggleGroup","prev","next","freshness","formatQaDate","shortVersion","body","hasGroups","s","groups","group","isOpen","agg","aggregateTri","init_TestPanel","__esmMin","init_preact","init_hooks","init_LaneIcons","init_ui","buildTree","nodes","_a","childrenByParent","node","bucket","a","b","out","visit","parentId","depth","_b","children","dedupeScenarios","artifact","_c","byKey","page","scenario","key","entry","ref","rank","p","_d","ga","gb","interpolate","template","params","_","k","formatPct","pct","SitemapModal","currentPattern","strings","onClose","activeTab","setActiveTab","d","search","setSearch","y","onKeydown","event","here","normalizePattern","tree","T","cases","titleByPattern","map","patternTitle","pattern","query","filteredCases","c","freshness","formatQaDate","shortVersion","e","row","isHere","QA_COLOR_MODIFIER","lanes","humanTriColor","botTriColor","traversed","LaneIcons","init_SitemapModal","__esmMin","init_preact","init_hooks","init_LaneIcons","init_resolver","init_ui","patchHistory","historyRefCount","historyPatched","originalPushState","originalReplaceState","fire","LOCATION_CHANGE_EVENT","args","ret","unpatchHistory","isValidArtifact","value","QA_ARTIFACT_FORMAT","warnBadFormatOnce","warnedBadFormat","patternsFromArtifact","artifact","set","page","node","resolveCurrent","pattern","UNKNOWN","p","createQaMeter","options","_a","_b","_c","_d","source","strings","resolveStrings","inlineArtifact","NOOP_HANDLE","loadStarted","loadPromise","load","r","forceRender","host","shadow","style","QA_METER_STYLES","mountPoint","getCurrentPattern","resolvePage","Container","tick","setTick","d","bump","q","n","resolved","meta","onHover","onOpenModal","uiOpen","closeModal","y","onNav","k","cancelHoverClose","scheduleHoverClose","uiHover","TestPanel","Pastille","SitemapModal","hoverCloseTimer","HOVER_CLOSE_MS","disposed","mountTree","R","init_mount","__esmMin","init_preact","init_hooks","init_i18n","init_resolver","init_styles","init_Pastille","init_TestPanel","init_SitemapModal","qa_meter_exports","__export","createQaMeter","options","init_qa_meter","__esmMin","init_mount","widget_exports","__export","createFeedback","SCALAR_FIELDS","assertSafeEndpoint","endpoint","parsed","e","RateLimitError","retryAfterSeconds","__publicField","parseRetryAfter","response","raw","n","ensureReadable","label","text","readJsonArray","data","readJsonObject","createApiClient","options","_a","_b","fetcher","submitReport","input","_c","_d","payload","form","field","blob","i","headers","signed","widgetHeaders","externalId","listMine","listChangelog","getReport","reportId","getReportBySeq","seq","addComment","body","clientNonce","attachments","init","editDescription","description","closeAsResolved","reopenUnresolved","boardQueryString","filters","params","s","t","qs","listBoard","page","fetchBoardKpis","checkVisibility","email","SENSITIVE_NAME","SENSITIVE_VALUE_PATTERNS","looksLikeCredential","value","re","sanitizeUrl","url","isAbsolute","isRootRelative","base","u","clean","name","e","SENSITIVE_TOKEN_PATTERNS","scrubCredentials","text","out","re","collectDevice","_a","nav","connection","deviceMemory","rawReferrer","referrer","sanitizeUrl","sanitizedHere","pathname","parsed","e","scrubCredentials","safeStringify","arg","_k","v","e","installConsolePatch","buffer","levels","originals","level","original","args","rawMessage","message","scrubCredentials","entry","stack","fn","installFetchPatch","buffer","sanitize","original","input","init","start","url","method","response","err","rawErr","scrubCredentials","installXhrPatch","Original","originalOpen","originalSend","body","ctx","e","installErrorHandlers","buffer","onError","e","rawStack","stack","scrubCredentials","onRejection","reason","rawMessage","createPerformanceCollector","slowResourceMs","longTasks","slowResources","observer","list","entry","e","sanitizeUrl","nav","navigation","RingBuffer","max","__publicField","item","installCapture","options","_a","maxConsole","maxNetwork","maxErrors","sanitize","sanitizeUrl","consoleBuf","RingBuffer","networkBuf","errorBuf","uninstallConsole","installConsolePatch","uninstallFetch","installFetchPatch","uninstallXhr","installXhrPatch","uninstallErrors","installErrorHandlers","perf","createPerformanceCollector","collectDevice","DEFAULT_STRINGS","FRENCH_STRINGS","LOCALE_PACKS","packFor","locale","_a","tag","resolveStrings","overrides","options","localePack","init_preact","init_hooks","currentPagePath","opts","_a","override","LOCATION_CHANGE","patched","patchHistory","m","orig","args","r","onLocationChange","cb","init_hooks","PREFIX","loadBoardView","externalId","raw","parsed","e","saveBoardView","view","startPoll","tick","intervalMs","stopped","parked","failures","timer","doc","delayMs","run","ok","e","onVisibilityChange","rateLimitMessage","err","strings","RateLimitError","init_hooks","vnodeId","createVNode","type","props","key","isStaticChildren","__source","__self","ref","i","normalizedProps","vnode","__k","__","__b","__e","__c","constructor","__v","vnodeId","__i","__u","defaultProps","options","SEQ_RE","linkifySeq","text","onOpenSeq","value","parts","last","m","seq","u","CommentBubble","comment","strings","onOpenSeq","_a","isMine","isAgent","isSystem","variant","labelKey","label","images","a","u","linkifySeq","formatTime","iso","e","ALLOWED_IMAGE_TYPES","validateScreenshotFile","file","ALLOWED_IMAGE_TYPES","truncateUrl","url","maxLength","keepStart","keepEnd","safeExternalHref","parsed","e","POLL_MS","ReportDetailView","api","externalId","reportId","strings","onBack","canModerate","variant","onOpenSeq","buildPermalink","_a","_b","_c","_d","_e","detail","setDetail","d","error","setError","copied","setCopied","editing","setEditing","editBody","setEditBody","savingEdit","setSavingEdit","editError","setEditError","composeBody","setComposeBody","sending","setSending","closing","setClosing","reopening","setReopening","composeShots","setComposeShots","attachError","setAttachError","fileInputRef","A","mountedRef","shotsRef","y","s","acceptComposeFiles","files","remaining","batch","file","err","validateScreenshotFile","incoming","blob","prev","handleAttachChange","e","input","removeComposeShot","index","shot","_","i","fetchDetail","next","rl","rateLimitMessage","poll","startPoll","handleSend","nonce","attachments","created","appendComment","handleCopyLink","startEdit","cancelEdit","handleSaveEdit","updated","handleClose","handleReopen","u","isMine","canAct","showCloseCta","showComposeBox","ContextBlock","linkifySeq","url","safeHref","safeExternalHref","c","CommentBubble","StatusHistorySection","TechnicalContextDrawer","current","captureKey","captureLabel","formatSubmittedAt","truncateUrl","iso","TECH_CONSOLE_LIMIT","TECH_NETWORK_LIMIT","ctx","errors","consoleLogs","network","device","errorCount","summary","DeviceSection","ErrorsSection","ConsoleSection","NetworkSection","parts","dpr","p","S","truncateStack","logs","l","rows","n","failed","stack","lines","from","to","sourceKey","POLL_MS","SEARCH_DEBOUNCE_MS","SORT_OPTIONS","STATUSES","TYPES","SEVERITIES","BoardView","api","externalId","strings","getCurrentPage","initialSelectedId","_a","_b","filters","setFilters","d","loadBoardView","thisPage","setThisPage","y","saveBoardView","filtersHash","page","setPage","kpis","setKpis","loading","setLoading","error","setError","selectedId","setSelectedId","detailKey","setDetailKey","reloadTick","setReloadTick","qDraft","setQDraft","debouncedQ","useDebouncedValue","f","_drop","rest","activeFilterCount","T","_c","_d","_e","_f","onLocationChange","t","fetchFilters","searching","currentPagePath","cancelled","poll","startPoll","list","k","e","rateLimitMessage","selectedRow","r","onPickRow","row","u","BoardHeader","BoardFilters","v","BoardListSkeleton","BoardEmpty","BoardList","ReportDetailView","seq","DetailEmptyIllustration","scopeLabel","BoardKpiStrip","cells","onChange","searchDraft","onSearchDraftChange","activeCount","onToggleThisPage","_g","setStatus","value","setType","setSeverity","setOrdering","toggleMine","clear","o","s","CloseIcon","rows","onPick","total","S","BoardRowCard","selected","author","CommentIcon","formatRelative","onAllPages","EmptyIllustration","_","i","ms","debounced","setDebounced","iso","then","delta","m","h","init_hooks","statusClassName","status","severityClassName","severity","typeClassName","formatRelative","iso","then","seconds","minutes","hours","repliesLabel","count","strings","ReportRow","row","onClick","_a","preview","u","POLL_MS","isoWeekKey","iso","d","day","groupRowsByWeek","rows","buckets","row","key","existing","a","b","weekKey","weekRows","formatWeekLabel","e","ChangelogList","api","externalId","strings","onSelect","setRows","error","setError","refreshing","setRefreshing","mountedRef","A","fetchRows","next","err","y","poll","startPoll","groups","T","isLoading","isEmpty","u","g","ReportRow","BugIcon","u","Fab","label","onClick","count","init_hooks","init_hooks","COLORS","HIGHLIGHT_ALPHA","drawShape","ctx","shape","sourceImage","drawArrow","i","metrics","padding","w","hh","normalizeStart","drawBlur","start","size","x1","y1","x2","y2","angle","x","y","h","tile","yy","xx","tw","th","Icon","u","Annotator","imageBlob","strings","onSave","onCancel","canvasRef","A","containerRef","imageRef","tool","setTool","d","color","setColor","shapes","setShapes","past","setPast","future","setFuture","isDrawingRef","draftShape","setDraftShape","imageLoaded","setImageLoaded","saving","setSaving","scaleRef","addShape","s","p","clearShapes","undo","prev","f","redo","next","_","onKey","e","k","url","img","maxW","maxH","fitScale","canvas","redraw","getCanvasCoords","rect","handleMouseDown","canvasW","lineWidth","blurTile","text","handleMouseMove","current","handleMouseUp","handleSave","blob","resolve","tools","t","c","TYPES","SEVERITIES","Form","strings","onSubmit","onCancel","status","errorMessage","submittedSeq","onDirtyChange","description","setDescription","d","feedbackType","setFeedbackType","severity","setSeverity","localError","setLocalError","shots","setShots","isDragOver","setIsDragOver","annotatingIndex","setAnnotatingIndex","fileInputRef","A","dropZoneRef","submitting","submitLabel","pageUrl","isDirty","y","shotsRef","s","acceptFiles","files","remaining","batch","file","err","validateScreenshotFile","incoming","blob","prev","removeShot","index","shot","_","i","handleFileInputChange","e","_a","input","handleDragOver","handleDragLeave","handleDrop","_b","zone","onPaste","items","item","handleAnnotated","annotated","replacement","u","values","t","truncateUrl","Annotator","init_hooks","computeKpiCounts","rows","counts","row","computeResolutionRate","FILTER_FOR_STATUS","rowsMatchingFilter","filter","KpiStrip","onFilter","strings","resolutionRate","cells","u","c","active","toggleTo","POLL_MS","MineList","api","externalId","strings","onSelect","onOpenSeq","_a","rows","setRows","d","error","setError","refreshing","setRefreshing","filter","setFilter","jumpValue","setJumpValue","jumpError","setJumpError","jumping","setJumping","mountedRef","A","handleJump","e","seq","found","fetchRows","next","err","rateLimitMessage","y","poll","startPoll","isEmpty","isLoading","visibleRows","rowsMatchingFilter","visibleEmpty","u","KpiStrip","row","ReportRow","init_hooks","Modal","onDismiss","children","closeLabel","expanded","modalRef","A","previouslyFocused","y","_a","onKey","e","root","first","prev","u","PageActivityStrip","open","strings","onView","text","u","init_hooks","PagePeekPanel","api","externalId","strings","getCurrentPage","open","onViewAll","onPickRow","onSend","rows","setRows","d","failed","setFailed","y","cancelled","pagePath","currentPagePath","page","u","row","init_hooks","TTL_MS","computeOpenCount","kpis","_a","_b","_c","s","cache","inflight","invalidatePageSignal","fetchOpen","api","externalId","path","key","hit","cache","TTL_MS","pending","inflight","p","kpis","open","computeOpenCount","usePageOpenCount","opts","getCurrentPage","enabled","setOpen","d","tick","setTick","refresh","q","invalidatePageSignal","t","y","cancelled","load","currentPagePath","n","unsub","onLocationChange","WIDGET_STYLES","computeFabVisible","opts","externalId","visibilityAllowed","mountWidget","options","shadow","style","WIDGET_STYLES","mountPoint","currentState","postSubmitTimer","formDirty","rerender","state","R","k","Root","clearSelected","s","_drop","_drop2","rest","openWidget","currentPagePath","kpis","_a","handleSubmit","q","values","result","pageSignal","err","openSeq","seq","r","buildPermalink","id","param","u","e","pageActivityEnabled","usePageOpenCount","fabLabel","setVisibilityAllowed","d","y","cancelled","show","fabVisible","showMineTab","peekOpen","setPeekOpen","peekTimers","A","S","_b","PagePeekPanel","reportId","Fab","Modal","reason","PageActivityStrip","Form","MineList","row","ReportDetailView","ChangelogList","BoardView","openReportRef","ref","api","value","defaultQaPosition","createFeedback","config","_a","_b","_c","_d","_e","_f","_g","_h","env","locale","strings","resolveStrings","capture","installCapture","user","metadata","api","createApiClient","transformers","host","attach","buildAndSubmit","values","attached","manualScreenshots","technical_context","_hash","_exp","safeUser","captureMethod","payload","pagePath","currentPagePath","finalPayload","t","result","err","error","handle","mountWidget","externalId","qaHandle","qaDisposed","qa","qaLocale","createQaMeter","instance","opts","partial","u","kv","w","fn","DEFAULTS","normalize","thrown","_a","o","e","clientFingerprint","err","pathname","firstFrames","messagePrefix","truncate","s","max","withErrorTracking","fb","options","opts","internal","inFlight","lastSent","recentSendTimes","shouldSend","fp","now","windowStart","prev","HARD_CAP","TARGET_AFTER_TRIM","cutoff","k","v","toDrop","dropped","report","description","onError","event","candidate","onUnhandledRejection","originalShutdown","createFeedback","config","errorTracking","base","fb","withErrorTracking"]}
|
|
1
|
+
{"version":3,"sources":["../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/src/constants.js","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/src/util.js","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/src/options.js","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/src/create-element.js","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/src/component.js","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/src/diff/props.js","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/src/create-context.js","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/src/diff/children.js","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/src/diff/index.js","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/src/render.js","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/src/clone-element.js","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/src/diff/catch-error.js","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/hooks/src/index.js","../src/qa-meter/i18n.ts","../src/qa-meter/resolver.ts","../src/qa-meter/styles.ts","../src/qa-meter/ui.ts","../src/qa-meter/Pastille.tsx","../src/qa-meter/LaneIcons.tsx","../src/qa-meter/TestPanel.tsx","../src/qa-meter/SitemapModal.tsx","../src/qa-meter/mount.tsx","../src/qa-meter/index.ts","../src/widget.ts","../src/api/client.ts","../src/capture/urlSanitizer.ts","../src/capture/scrub.ts","../src/capture/device.ts","../src/capture/console.ts","../src/capture/network.ts","../src/capture/errors.ts","../src/capture/performance.ts","../src/capture/ringBuffer.ts","../src/capture/index.ts","../src/widget/i18n.ts","../src/widget/mount.tsx","../src/widget/currentPage.ts","../src/widget/BoardView.tsx","../src/widget/boardCache.ts","../src/widget/boardViewStore.ts","../src/widget/poll.ts","../src/widget/rateLimit.ts","../src/widget/ReportDetailView.tsx","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/jsx-runtime/src/utils.js","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/src/constants.js","../../../node_modules/.pnpm/preact@10.29.1/node_modules/preact/jsx-runtime/src/index.js","../src/widget/linkifySeq.tsx","../src/widget/CommentBubble.tsx","../src/widget/screenshot-utils.ts","../src/widget/ChangelogList.tsx","../src/widget/ReportRow.tsx","../src/widget/Fab.tsx","../src/widget/Form.tsx","../src/widget/Annotator.tsx","../src/widget/MineList.tsx","../src/widget/KpiStrip.tsx","../src/widget/Modal.tsx","../src/widget/PageActivityStrip.tsx","../src/widget/PagePeekPanel.tsx","../src/widget/pageSignal.ts","../src/widget/styles.ts","../src/core.ts","../src/modules/error-tracking.ts"],"sourcesContent":["/** Normal hydration that attaches to a DOM tree but does not diff it. */\nexport const MODE_HYDRATE = 1 << 5;\n/** Signifies this VNode suspended on the previous render */\nexport const MODE_SUSPENDED = 1 << 7;\n/** Indicates that this node needs to be inserted while patching children */\nexport const INSERT_VNODE = 1 << 2;\n/** Indicates a VNode has been matched with another VNode in the diff */\nexport const MATCHED = 1 << 1;\n\n/** Reset all mode flags */\nexport const RESET_MODE = ~(MODE_HYDRATE | MODE_SUSPENDED);\n\nexport const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';\nexport const XHTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';\nexport const MATH_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';\n\nexport const NULL = null;\nexport const UNDEFINED = undefined;\nexport const EMPTY_OBJ = /** @type {any} */ ({});\nexport const EMPTY_ARR = [];\nexport const IS_NON_DIMENSIONAL =\n\t/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;\n","import { EMPTY_ARR } from './constants';\n\nexport const isArray = Array.isArray;\n\n/**\n * Assign properties from `props` to `obj`\n * @template O, P The obj and props types\n * @param {O} obj The object to copy properties to\n * @param {P} props The object to copy properties from\n * @returns {O & P}\n */\nexport function assign(obj, props) {\n\t// @ts-expect-error We change the type of `obj` to be `O & P`\n\tfor (let i in props) obj[i] = props[i];\n\treturn /** @type {O & P} */ (obj);\n}\n\n/**\n * Remove a child node from its parent if attached. This is a workaround for\n * IE11 which doesn't support `Element.prototype.remove()`. Using this function\n * is smaller than including a dedicated polyfill.\n * @param {import('./index').ContainerNode} node The node to remove\n */\nexport function removeNode(node) {\n\tif (node && node.parentNode) node.parentNode.removeChild(node);\n}\n\nexport const slice = EMPTY_ARR.slice;\n","import { _catchError } from './diff/catch-error';\n\n/**\n * The `option` object can potentially contain callback functions\n * that are called during various stages of our renderer. This is the\n * foundation on which all our addons like `preact/debug`, `preact/compat`,\n * and `preact/hooks` are based on. See the `Options` type in `internal.d.ts`\n * for a full list of available option hooks (most editors/IDEs allow you to\n * ctrl+click or cmd+click on mac the type definition below).\n * @type {import('./internal').Options}\n */\nconst options = {\n\t_catchError\n};\n\nexport default options;\n","import { slice } from './util';\nimport options from './options';\nimport { NULL, UNDEFINED } from './constants';\n\nlet vnodeId = 0;\n\n/**\n * Create an virtual node (used for JSX)\n * @param {import('./internal').VNode[\"type\"]} type The node name or Component constructor for this\n * virtual node\n * @param {object | null | undefined} [props] The properties of the virtual node\n * @param {Array<import('.').ComponentChildren>} [children] The children of the\n * virtual node\n * @returns {import('./internal').VNode}\n */\nexport function createElement(type, props, children) {\n\tlet normalizedProps = {},\n\t\tkey,\n\t\tref,\n\t\ti;\n\tfor (i in props) {\n\t\tif (i == 'key') key = props[i];\n\t\telse if (i == 'ref') ref = props[i];\n\t\telse normalizedProps[i] = props[i];\n\t}\n\n\tif (arguments.length > 2) {\n\t\tnormalizedProps.children =\n\t\t\targuments.length > 3 ? slice.call(arguments, 2) : children;\n\t}\n\n\t// If a Component VNode, check for and apply defaultProps\n\t// Note: type may be undefined in development, must never error here.\n\tif (typeof type == 'function' && type.defaultProps != NULL) {\n\t\tfor (i in type.defaultProps) {\n\t\t\tif (normalizedProps[i] === UNDEFINED) {\n\t\t\t\tnormalizedProps[i] = type.defaultProps[i];\n\t\t\t}\n\t\t}\n\t}\n\n\treturn createVNode(type, normalizedProps, key, ref, NULL);\n}\n\n/**\n * Create a VNode (used internally by Preact)\n * @param {import('./internal').VNode[\"type\"]} type The node name or Component\n * Constructor for this virtual node\n * @param {object | string | number | null} props The properties of this virtual node.\n * If this virtual node represents a text node, this is the text of the node (string or number).\n * @param {string | number | null} key The key for this virtual node, used when\n * diffing it against its children\n * @param {import('./internal').VNode[\"ref\"]} ref The ref property that will\n * receive a reference to its created child\n * @returns {import('./internal').VNode}\n */\nexport function createVNode(type, props, key, ref, original) {\n\t// V8 seems to be better at detecting type shapes if the object is allocated from the same call site\n\t// Do not inline into createElement and coerceToVNode!\n\t/** @type {import('./internal').VNode} */\n\tconst vnode = {\n\t\ttype,\n\t\tprops,\n\t\tkey,\n\t\tref,\n\t\t_children: NULL,\n\t\t_parent: NULL,\n\t\t_depth: 0,\n\t\t_dom: NULL,\n\t\t_component: NULL,\n\t\tconstructor: UNDEFINED,\n\t\t_original: original == NULL ? ++vnodeId : original,\n\t\t_index: -1,\n\t\t_flags: 0\n\t};\n\n\t// Only invoke the vnode hook if this was *not* a direct copy:\n\tif (original == NULL && options.vnode != NULL) options.vnode(vnode);\n\n\treturn vnode;\n}\n\nexport function createRef() {\n\treturn { current: NULL };\n}\n\nexport function Fragment(props) {\n\treturn props.children;\n}\n\n/**\n * Check if a the argument is a valid Preact VNode.\n * @param {*} vnode\n * @returns {vnode is VNode}\n */\nexport const isValidElement = vnode =>\n\tvnode != NULL && vnode.constructor === UNDEFINED;\n","import { assign } from './util';\nimport { diff, commitRoot } from './diff/index';\nimport options from './options';\nimport { Fragment } from './create-element';\nimport { MODE_HYDRATE, NULL } from './constants';\n\n/**\n * Base Component class. Provides `setState()` and `forceUpdate()`, which\n * trigger rendering\n * @param {object} props The initial component props\n * @param {object} context The initial context from parent components'\n * getChildContext\n */\nexport function BaseComponent(props, context) {\n\tthis.props = props;\n\tthis.context = context;\n}\n\n/**\n * Update component state and schedule a re-render.\n * @this {import('./internal').Component}\n * @param {object | ((s: object, p: object) => object)} update A hash of state\n * properties to update with new values or a function that given the current\n * state and props returns a new partial state\n * @param {() => void} [callback] A function to be called once component state is\n * updated\n */\nBaseComponent.prototype.setState = function (update, callback) {\n\t// only clone state when copying to nextState the first time.\n\tlet s;\n\tif (this._nextState != NULL && this._nextState != this.state) {\n\t\ts = this._nextState;\n\t} else {\n\t\ts = this._nextState = assign({}, this.state);\n\t}\n\n\tif (typeof update == 'function') {\n\t\t// Some libraries like `immer` mark the current state as readonly,\n\t\t// preventing us from mutating it, so we need to clone it. See #2716\n\t\tupdate = update(assign({}, s), this.props);\n\t}\n\n\tif (update) {\n\t\tassign(s, update);\n\t}\n\n\t// Skip update if updater function returned null\n\tif (update == NULL) return;\n\n\tif (this._vnode) {\n\t\tif (callback) {\n\t\t\tthis._stateCallbacks.push(callback);\n\t\t}\n\t\tenqueueRender(this);\n\t}\n};\n\n/**\n * Immediately perform a synchronous re-render of the component\n * @this {import('./internal').Component}\n * @param {() => void} [callback] A function to be called after component is\n * re-rendered\n */\nBaseComponent.prototype.forceUpdate = function (callback) {\n\tif (this._vnode) {\n\t\t// Set render mode so that we can differentiate where the render request\n\t\t// is coming from. We need this because forceUpdate should never call\n\t\t// shouldComponentUpdate\n\t\tthis._force = true;\n\t\tif (callback) this._renderCallbacks.push(callback);\n\t\tenqueueRender(this);\n\t}\n};\n\n/**\n * Accepts `props` and `state`, and returns a new Virtual DOM tree to build.\n * Virtual DOM is generally constructed via [JSX](https://jasonformat.com/wtf-is-jsx).\n * @param {object} props Props (eg: JSX attributes) received from parent\n * element/component\n * @param {object} state The component's current state\n * @param {object} context Context object, as returned by the nearest\n * ancestor's `getChildContext()`\n * @returns {ComponentChildren | void}\n */\nBaseComponent.prototype.render = Fragment;\n\n/**\n * @param {import('./internal').VNode} vnode\n * @param {number | null} [childIndex]\n */\nexport function getDomSibling(vnode, childIndex) {\n\tif (childIndex == NULL) {\n\t\t// Use childIndex==null as a signal to resume the search from the vnode's sibling\n\t\treturn vnode._parent\n\t\t\t? getDomSibling(vnode._parent, vnode._index + 1)\n\t\t\t: NULL;\n\t}\n\n\tlet sibling;\n\tfor (; childIndex < vnode._children.length; childIndex++) {\n\t\tsibling = vnode._children[childIndex];\n\n\t\tif (sibling != NULL && sibling._dom != NULL) {\n\t\t\t// Since updateParentDomPointers keeps _dom pointer correct,\n\t\t\t// we can rely on _dom to tell us if this subtree contains a\n\t\t\t// rendered DOM node, and what the first rendered DOM node is\n\t\t\treturn sibling._dom;\n\t\t}\n\t}\n\n\t// If we get here, we have not found a DOM node in this vnode's children.\n\t// We must resume from this vnode's sibling (in it's parent _children array)\n\t// Only climb up and search the parent if we aren't searching through a DOM\n\t// VNode (meaning we reached the DOM parent of the original vnode that began\n\t// the search)\n\treturn typeof vnode.type == 'function' ? getDomSibling(vnode) : NULL;\n}\n\n/**\n * Trigger in-place re-rendering of a component.\n * @param {import('./internal').Component} component The component to rerender\n */\nfunction renderComponent(component) {\n\tif (component._parentDom && component._dirty) {\n\t\tlet oldVNode = component._vnode,\n\t\t\toldDom = oldVNode._dom,\n\t\t\tcommitQueue = [],\n\t\t\trefQueue = [],\n\t\t\tnewVNode = assign({}, oldVNode);\n\t\tnewVNode._original = oldVNode._original + 1;\n\t\tif (options.vnode) options.vnode(newVNode);\n\n\t\tdiff(\n\t\t\tcomponent._parentDom,\n\t\t\tnewVNode,\n\t\t\toldVNode,\n\t\t\tcomponent._globalContext,\n\t\t\tcomponent._parentDom.namespaceURI,\n\t\t\toldVNode._flags & MODE_HYDRATE ? [oldDom] : NULL,\n\t\t\tcommitQueue,\n\t\t\toldDom == NULL ? getDomSibling(oldVNode) : oldDom,\n\t\t\t!!(oldVNode._flags & MODE_HYDRATE),\n\t\t\trefQueue\n\t\t);\n\n\t\tnewVNode._original = oldVNode._original;\n\t\tnewVNode._parent._children[newVNode._index] = newVNode;\n\t\tcommitRoot(commitQueue, newVNode, refQueue);\n\t\toldVNode._dom = oldVNode._parent = null;\n\n\t\tif (newVNode._dom != oldDom) {\n\t\t\tupdateParentDomPointers(newVNode);\n\t\t}\n\t}\n}\n\n/**\n * @param {import('./internal').VNode} vnode\n */\nfunction updateParentDomPointers(vnode) {\n\tif ((vnode = vnode._parent) != NULL && vnode._component != NULL) {\n\t\tvnode._dom = vnode._component.base = NULL;\n\t\tvnode._children.some(child => {\n\t\t\tif (child != NULL && child._dom != NULL) {\n\t\t\t\treturn (vnode._dom = vnode._component.base = child._dom);\n\t\t\t}\n\t\t});\n\n\t\treturn updateParentDomPointers(vnode);\n\t}\n}\n\n/**\n * The render queue\n * @type {Array<import('./internal').Component>}\n */\nlet rerenderQueue = [];\n\n/*\n * The value of `Component.debounce` must asynchronously invoke the passed in callback. It is\n * important that contributors to Preact can consistently reason about what calls to `setState`, etc.\n * do, and when their effects will be applied. See the links below for some further reading on designing\n * asynchronous APIs.\n * * [Designing APIs for Asynchrony](https://blog.izs.me/2013/08/designing-apis-for-asynchrony)\n * * [Callbacks synchronous and asynchronous](https://blog.ometer.com/2011/07/24/callbacks-synchronous-and-asynchronous/)\n */\n\nlet prevDebounce;\n\nconst defer =\n\ttypeof Promise == 'function'\n\t\t? Promise.prototype.then.bind(Promise.resolve())\n\t\t: setTimeout;\n\n/**\n * Enqueue a rerender of a component\n * @param {import('./internal').Component} c The component to rerender\n */\nexport function enqueueRender(c) {\n\tif (\n\t\t(!c._dirty &&\n\t\t\t(c._dirty = true) &&\n\t\t\trerenderQueue.push(c) &&\n\t\t\t!process._rerenderCount++) ||\n\t\tprevDebounce != options.debounceRendering\n\t) {\n\t\tprevDebounce = options.debounceRendering;\n\t\t(prevDebounce || defer)(process);\n\t}\n}\n\n/**\n * @param {import('./internal').Component} a\n * @param {import('./internal').Component} b\n */\nconst depthSort = (a, b) => a._vnode._depth - b._vnode._depth;\n\n/** Flush the render queue by rerendering all queued components */\nfunction process() {\n\ttry {\n\t\tlet c,\n\t\t\tl = 1;\n\n\t\t// Don't update `renderCount` yet. Keep its value non-zero to prevent unnecessary\n\t\t// process() calls from getting scheduled while `queue` is still being consumed.\n\t\twhile (rerenderQueue.length) {\n\t\t\t// Keep the rerender queue sorted by (depth, insertion order). The queue\n\t\t\t// will initially be sorted on the first iteration only if it has more than 1 item.\n\t\t\t//\n\t\t\t// New items can be added to the queue e.g. when rerendering a provider, so we want to\n\t\t\t// keep the order from top to bottom with those new items so we can handle them in a\n\t\t\t// single pass\n\t\t\tif (rerenderQueue.length > l) {\n\t\t\t\trerenderQueue.sort(depthSort);\n\t\t\t}\n\n\t\t\tc = rerenderQueue.shift();\n\t\t\tl = rerenderQueue.length;\n\n\t\t\trenderComponent(c);\n\t\t}\n\t} finally {\n\t\trerenderQueue.length = process._rerenderCount = 0;\n\t}\n}\n\nprocess._rerenderCount = 0;\n","import { IS_NON_DIMENSIONAL, NULL, SVG_NAMESPACE } from '../constants';\nimport options from '../options';\n\n// Per-instance unique key for event clock stamps. Each Preact copy on the page\n// gets its own random suffix so that `_dispatched` / `_attached` properties on\n// shared event objects and handler functions cannot collide across instances.\n// ~1 in 60M collision odds - if you have that many praect versions on the page,\n// you deserve some weird bugs.\n// In 11 we can replace this with a\n// Symbol\nlet _id = Math.random().toString(8),\n\tEVENT_DISPATCHED = '__d' + _id,\n\tEVENT_ATTACHED = '__a' + _id;\n\nfunction setStyle(style, key, value) {\n\tif (key[0] == '-') {\n\t\tstyle.setProperty(key, value == NULL ? '' : value);\n\t} else if (value == NULL) {\n\t\tstyle[key] = '';\n\t} else if (typeof value != 'number' || IS_NON_DIMENSIONAL.test(key)) {\n\t\tstyle[key] = value;\n\t} else {\n\t\tstyle[key] = value + 'px';\n\t}\n}\n\nconst CAPTURE_REGEX = /(PointerCapture)$|Capture$/i;\n\n// A logical clock to solve issues like https://github.com/preactjs/preact/issues/3927.\n// When the DOM performs an event it leaves micro-ticks in between bubbling up which means that\n// an event can trigger on a newly reated DOM-node while the event bubbles up.\n//\n// Originally inspired by Vue\n// (https://github.com/vuejs/core/blob/caeb8a68811a1b0f79/packages/runtime-dom/src/modules/events.ts#L90-L101),\n// but modified to use a logical clock instead of Date.now() in case event handlers get attached\n// and events get dispatched during the same millisecond.\n//\n// The clock is incremented after each new event dispatch. This allows 1 000 000 new events\n// per second for over 280 years before the value reaches Number.MAX_SAFE_INTEGER (2**53 - 1).\nlet eventClock = 0;\n\n/**\n * Set a property value on a DOM node\n * @param {import('../internal').PreactElement} dom The DOM node to modify\n * @param {string} name The name of the property to set\n * @param {*} value The value to set the property to\n * @param {*} oldValue The old value the property had\n * @param {string} namespace Whether or not this DOM node is an SVG node or not\n */\nexport function setProperty(dom, name, value, oldValue, namespace) {\n\tlet useCapture;\n\n\to: if (name == 'style') {\n\t\tif (typeof value == 'string') {\n\t\t\tdom.style.cssText = value;\n\t\t} else {\n\t\t\tif (typeof oldValue == 'string') {\n\t\t\t\tdom.style.cssText = oldValue = '';\n\t\t\t}\n\n\t\t\tif (oldValue) {\n\t\t\t\tfor (name in oldValue) {\n\t\t\t\t\tif (!(value && name in value)) {\n\t\t\t\t\t\tsetStyle(dom.style, name, '');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (value) {\n\t\t\t\tfor (name in value) {\n\t\t\t\t\tif (!oldValue || value[name] != oldValue[name]) {\n\t\t\t\t\t\tsetStyle(dom.style, name, value[name]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t// Benchmark for comparison: https://esbench.com/bench/574c954bdb965b9a00965ac6\n\telse if (name[0] == 'o' && name[1] == 'n') {\n\t\tuseCapture = name != (name = name.replace(CAPTURE_REGEX, '$1'));\n\t\tconst lowerCaseName = name.toLowerCase();\n\n\t\t// Infer correct casing for DOM built-in events:\n\t\tif (lowerCaseName in dom || name == 'onFocusOut' || name == 'onFocusIn')\n\t\t\tname = lowerCaseName.slice(2);\n\t\telse name = name.slice(2);\n\n\t\tif (!dom._listeners) dom._listeners = {};\n\t\tdom._listeners[name + useCapture] = value;\n\n\t\tif (value) {\n\t\t\tif (!oldValue) {\n\t\t\t\tvalue[EVENT_ATTACHED] = eventClock;\n\t\t\t\tdom.addEventListener(\n\t\t\t\t\tname,\n\t\t\t\t\tuseCapture ? eventProxyCapture : eventProxy,\n\t\t\t\t\tuseCapture\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tvalue[EVENT_ATTACHED] = oldValue[EVENT_ATTACHED];\n\t\t\t}\n\t\t} else {\n\t\t\tdom.removeEventListener(\n\t\t\t\tname,\n\t\t\t\tuseCapture ? eventProxyCapture : eventProxy,\n\t\t\t\tuseCapture\n\t\t\t);\n\t\t}\n\t} else {\n\t\tif (namespace == SVG_NAMESPACE) {\n\t\t\t// Normalize incorrect prop usage for SVG:\n\t\t\t// - xlink:href / xlinkHref --> href (xlink:href was removed from SVG and isn't needed)\n\t\t\t// - className --> class\n\t\t\tname = name.replace(/xlink(H|:h)/, 'h').replace(/sName$/, 's');\n\t\t} else if (\n\t\t\tname != 'width' &&\n\t\t\tname != 'height' &&\n\t\t\tname != 'href' &&\n\t\t\tname != 'list' &&\n\t\t\tname != 'form' &&\n\t\t\t// Default value in browsers is `-1` and an empty string is\n\t\t\t// cast to `0` instead\n\t\t\tname != 'tabIndex' &&\n\t\t\tname != 'download' &&\n\t\t\tname != 'rowSpan' &&\n\t\t\tname != 'colSpan' &&\n\t\t\tname != 'role' &&\n\t\t\tname != 'popover' &&\n\t\t\tname in dom\n\t\t) {\n\t\t\ttry {\n\t\t\t\tdom[name] = value == NULL ? '' : value;\n\t\t\t\t// labelled break is 1b smaller here than a return statement (sorry)\n\t\t\t\tbreak o;\n\t\t\t} catch (e) {}\n\t\t}\n\n\t\t// aria- and data- attributes have no boolean representation.\n\t\t// A `false` value is different from the attribute not being\n\t\t// present, so we can't remove it. For non-boolean aria\n\t\t// attributes we could treat false as a removal, but the\n\t\t// amount of exceptions would cost too many bytes. On top of\n\t\t// that other frameworks generally stringify `false`.\n\n\t\tif (typeof value == 'function') {\n\t\t\t// never serialize functions as attribute values\n\t\t} else if (value != NULL && (value !== false || name[4] == '-')) {\n\t\t\tdom.setAttribute(name, name == 'popover' && value == true ? '' : value);\n\t\t} else {\n\t\t\tdom.removeAttribute(name);\n\t\t}\n\t}\n}\n\n/**\n * Create an event proxy function.\n * @param {boolean} useCapture Is the event handler for the capture phase.\n * @private\n */\nfunction createEventProxy(useCapture) {\n\t/**\n\t * Proxy an event to hooked event handlers\n\t * @param {import('../internal').PreactEvent} e The event object from the browser\n\t * @private\n\t */\n\treturn function (e) {\n\t\tif (this._listeners) {\n\t\t\tconst eventHandler = this._listeners[e.type + useCapture];\n\t\t\tif (e[EVENT_DISPATCHED] == NULL) {\n\t\t\t\te[EVENT_DISPATCHED] = eventClock++;\n\n\t\t\t\t// When `e[EVENT_DISPATCHED]` is smaller than the time when the targeted event\n\t\t\t\t// handler was attached we know we have bubbled up to an element that was added\n\t\t\t\t// during patching the DOM.\n\t\t\t} else if (e[EVENT_DISPATCHED] < eventHandler[EVENT_ATTACHED]) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\treturn eventHandler(options.event ? options.event(e) : e);\n\t\t}\n\t};\n}\n\nconst eventProxy = createEventProxy(false);\nconst eventProxyCapture = createEventProxy(true);\n","import { enqueueRender } from './component';\nimport { NULL } from './constants';\n\nexport let i = 0;\n\nexport function createContext(defaultValue) {\n\tfunction Context(props) {\n\t\tif (!this.getChildContext) {\n\t\t\t/** @type {Set<import('./internal').Component> | null} */\n\t\t\tlet subs = new Set();\n\t\t\tlet ctx = {};\n\t\t\tctx[Context._id] = this;\n\n\t\t\tthis.getChildContext = () => ctx;\n\n\t\t\tthis.componentWillUnmount = () => {\n\t\t\t\tsubs = NULL;\n\t\t\t};\n\n\t\t\tthis.shouldComponentUpdate = function (_props) {\n\t\t\t\t// @ts-expect-error even\n\t\t\t\tif (this.props.value != _props.value) {\n\t\t\t\t\tsubs.forEach(c => {\n\t\t\t\t\t\tc._force = true;\n\t\t\t\t\t\tenqueueRender(c);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tthis.sub = c => {\n\t\t\t\tsubs.add(c);\n\t\t\t\tlet old = c.componentWillUnmount;\n\t\t\t\tc.componentWillUnmount = () => {\n\t\t\t\t\tif (subs) {\n\t\t\t\t\t\tsubs.delete(c);\n\t\t\t\t\t}\n\t\t\t\t\tif (old) old.call(c);\n\t\t\t\t};\n\t\t\t};\n\t\t}\n\n\t\treturn props.children;\n\t}\n\n\tContext._id = '__cC' + i++;\n\tContext._defaultValue = defaultValue;\n\n\t/** @type {import('./internal').FunctionComponent} */\n\tContext.Consumer = (props, contextValue) => {\n\t\treturn props.children(contextValue);\n\t};\n\n\t// we could also get rid of _contextRef entirely\n\tContext.Provider =\n\t\tContext._contextRef =\n\t\tContext.Consumer.contextType =\n\t\t\tContext;\n\n\treturn Context;\n}\n","import { diff, unmount, applyRef } from './index';\nimport { createVNode, Fragment } from '../create-element';\nimport {\n\tEMPTY_OBJ,\n\tEMPTY_ARR,\n\tINSERT_VNODE,\n\tMATCHED,\n\tUNDEFINED,\n\tNULL\n} from '../constants';\nimport { isArray } from '../util';\nimport { getDomSibling } from '../component';\n\n/**\n * @typedef {import('../internal').ComponentChildren} ComponentChildren\n * @typedef {import('../internal').Component} Component\n * @typedef {import('../internal').PreactElement} PreactElement\n * @typedef {import('../internal').VNode} VNode\n */\n\n/**\n * Diff the children of a virtual node\n * @param {PreactElement} parentDom The DOM element whose children are being\n * diffed\n * @param {ComponentChildren[]} renderResult\n * @param {VNode} newParentVNode The new virtual node whose children should be\n * diff'ed against oldParentVNode\n * @param {VNode} oldParentVNode The old virtual node whose children should be\n * diff'ed against newParentVNode\n * @param {object} globalContext The current context object - modified by\n * getChildContext\n * @param {string} namespace Current namespace of the DOM node (HTML, SVG, or MathML)\n * @param {Array<PreactElement>} excessDomChildren\n * @param {Array<Component>} commitQueue List of components which have callbacks\n * to invoke in commitRoot\n * @param {PreactElement} oldDom The current attached DOM element any new dom\n * elements should be placed around. Likely `null` on first render (except when\n * hydrating). Can be a sibling DOM element when diffing Fragments that have\n * siblings. In most cases, it starts out as `oldChildren[0]._dom`.\n * @param {boolean} isHydrating Whether or not we are in hydration\n * @param {any[]} refQueue an array of elements needed to invoke refs\n */\nexport function diffChildren(\n\tparentDom,\n\trenderResult,\n\tnewParentVNode,\n\toldParentVNode,\n\tglobalContext,\n\tnamespace,\n\texcessDomChildren,\n\tcommitQueue,\n\toldDom,\n\tisHydrating,\n\trefQueue\n) {\n\tlet i,\n\t\t/** @type {VNode} */\n\t\toldVNode,\n\t\t/** @type {VNode} */\n\t\tchildVNode,\n\t\t/** @type {PreactElement} */\n\t\tnewDom,\n\t\t/** @type {PreactElement} */\n\t\tfirstChildDom;\n\n\t// This is a compression of oldParentVNode!=null && oldParentVNode != EMPTY_OBJ && oldParentVNode._children || EMPTY_ARR\n\t// as EMPTY_OBJ._children should be `undefined`.\n\t/** @type {VNode[]} */\n\tlet oldChildren = (oldParentVNode && oldParentVNode._children) || EMPTY_ARR;\n\n\tlet newChildrenLength = renderResult.length;\n\n\toldDom = constructNewChildrenArray(\n\t\tnewParentVNode,\n\t\trenderResult,\n\t\toldChildren,\n\t\toldDom,\n\t\tnewChildrenLength\n\t);\n\n\tfor (i = 0; i < newChildrenLength; i++) {\n\t\tchildVNode = newParentVNode._children[i];\n\t\tif (childVNode == NULL) continue;\n\n\t\t// At this point, constructNewChildrenArray has assigned _index to be the\n\t\t// matchingIndex for this VNode's oldVNode (or -1 if there is no oldVNode).\n\t\toldVNode =\n\t\t\t(childVNode._index != -1 && oldChildren[childVNode._index]) || EMPTY_OBJ;\n\n\t\t// Update childVNode._index to its final index\n\t\tchildVNode._index = i;\n\n\t\t// Morph the old element into the new one, but don't append it to the dom yet\n\t\tlet result = diff(\n\t\t\tparentDom,\n\t\t\tchildVNode,\n\t\t\toldVNode,\n\t\t\tglobalContext,\n\t\t\tnamespace,\n\t\t\texcessDomChildren,\n\t\t\tcommitQueue,\n\t\t\toldDom,\n\t\t\tisHydrating,\n\t\t\trefQueue\n\t\t);\n\n\t\t// Adjust DOM nodes\n\t\tnewDom = childVNode._dom;\n\t\tif (childVNode.ref && oldVNode.ref != childVNode.ref) {\n\t\t\tif (oldVNode.ref) {\n\t\t\t\tapplyRef(oldVNode.ref, NULL, childVNode);\n\t\t\t}\n\t\t\trefQueue.push(\n\t\t\t\tchildVNode.ref,\n\t\t\t\tchildVNode._component || newDom,\n\t\t\t\tchildVNode\n\t\t\t);\n\t\t}\n\n\t\tif (firstChildDom == NULL && newDom != NULL) {\n\t\t\tfirstChildDom = newDom;\n\t\t}\n\n\t\tlet shouldPlace = !!(childVNode._flags & INSERT_VNODE);\n\t\tif (shouldPlace || oldVNode._children === childVNode._children) {\n\t\t\toldDom = insert(childVNode, oldDom, parentDom, shouldPlace);\n\n\t\t\t// When a matched VNode is physically moved via INSERT_VNODE, its old\n\t\t\t// _dom pointer becomes a stale positional reference. Clear it so that\n\t\t\t// getDomSibling (called from nested diffs) won't return this stale\n\t\t\t// reference and mis-place subsequent DOM nodes. See #5065.\n\t\t\tif (shouldPlace && oldVNode._dom) {\n\t\t\t\toldVNode._dom = NULL;\n\t\t\t}\n\t\t} else if (typeof childVNode.type == 'function' && result !== UNDEFINED) {\n\t\t\toldDom = result;\n\t\t} else if (newDom) {\n\t\t\toldDom = newDom.nextSibling;\n\t\t}\n\n\t\t// Unset diffing flags\n\t\tchildVNode._flags &= ~(INSERT_VNODE | MATCHED);\n\t}\n\n\tnewParentVNode._dom = firstChildDom;\n\n\treturn oldDom;\n}\n\n/**\n * @param {VNode} newParentVNode\n * @param {ComponentChildren[]} renderResult\n * @param {VNode[]} oldChildren\n */\nfunction constructNewChildrenArray(\n\tnewParentVNode,\n\trenderResult,\n\toldChildren,\n\toldDom,\n\tnewChildrenLength\n) {\n\t/** @type {number} */\n\tlet i;\n\t/** @type {VNode} */\n\tlet childVNode;\n\t/** @type {VNode} */\n\tlet oldVNode;\n\n\tlet oldChildrenLength = oldChildren.length,\n\t\tremainingOldChildren = oldChildrenLength;\n\n\tlet skew = 0;\n\n\tnewParentVNode._children = new Array(newChildrenLength);\n\tfor (i = 0; i < newChildrenLength; i++) {\n\t\t// @ts-expect-error We are reusing the childVNode variable to hold both the\n\t\t// pre and post normalized childVNode\n\t\tchildVNode = renderResult[i];\n\n\t\tif (\n\t\t\tchildVNode == NULL ||\n\t\t\ttypeof childVNode == 'boolean' ||\n\t\t\ttypeof childVNode == 'function'\n\t\t) {\n\t\t\tnewParentVNode._children[i] = NULL;\n\t\t\tcontinue;\n\t\t}\n\t\t// If this newVNode is being reused (e.g. <div>{reuse}{reuse}</div>) in the same diff,\n\t\t// or we are rendering a component (e.g. setState) copy the oldVNodes so it can have\n\t\t// it's own DOM & etc. pointers\n\t\telse if (\n\t\t\ttypeof childVNode == 'string' ||\n\t\t\ttypeof childVNode == 'number' ||\n\t\t\t// eslint-disable-next-line valid-typeof\n\t\t\ttypeof childVNode == 'bigint' ||\n\t\t\tchildVNode.constructor == String\n\t\t) {\n\t\t\tchildVNode = newParentVNode._children[i] = createVNode(\n\t\t\t\tNULL,\n\t\t\t\tchildVNode,\n\t\t\t\tNULL,\n\t\t\t\tNULL,\n\t\t\t\tNULL\n\t\t\t);\n\t\t} else if (isArray(childVNode)) {\n\t\t\tchildVNode = newParentVNode._children[i] = createVNode(\n\t\t\t\tFragment,\n\t\t\t\t{ children: childVNode },\n\t\t\t\tNULL,\n\t\t\t\tNULL,\n\t\t\t\tNULL\n\t\t\t);\n\t\t} else if (childVNode.constructor === UNDEFINED && childVNode._depth > 0) {\n\t\t\t// VNode is already in use, clone it. This can happen in the following\n\t\t\t// scenario:\n\t\t\t// const reuse = <div />\n\t\t\t// <div>{reuse}<span />{reuse}</div>\n\t\t\tchildVNode = newParentVNode._children[i] = createVNode(\n\t\t\t\tchildVNode.type,\n\t\t\t\tchildVNode.props,\n\t\t\t\tchildVNode.key,\n\t\t\t\tchildVNode.ref ? childVNode.ref : NULL,\n\t\t\t\tchildVNode._original\n\t\t\t);\n\t\t} else {\n\t\t\tnewParentVNode._children[i] = childVNode;\n\t\t}\n\n\t\tconst skewedIndex = i + skew;\n\t\tchildVNode._parent = newParentVNode;\n\t\tchildVNode._depth = newParentVNode._depth + 1;\n\n\t\t// Temporarily store the matchingIndex on the _index property so we can pull\n\t\t// out the oldVNode in diffChildren. We'll override this to the VNode's\n\t\t// final index after using this property to get the oldVNode\n\t\tconst matchingIndex = (childVNode._index = findMatchingIndex(\n\t\t\tchildVNode,\n\t\t\toldChildren,\n\t\t\tskewedIndex,\n\t\t\tremainingOldChildren\n\t\t));\n\n\t\toldVNode = NULL;\n\t\tif (matchingIndex != -1) {\n\t\t\toldVNode = oldChildren[matchingIndex];\n\t\t\tremainingOldChildren--;\n\t\t\tif (oldVNode) {\n\t\t\t\toldVNode._flags |= MATCHED;\n\t\t\t}\n\t\t}\n\n\t\t// Here, we define isMounting for the purposes of the skew diffing\n\t\t// algorithm. Nodes that are unsuspending are considered mounting and we detect\n\t\t// this by checking if oldVNode._original == null\n\t\tconst isMounting = oldVNode == NULL || oldVNode._original == NULL;\n\n\t\tif (isMounting) {\n\t\t\tif (matchingIndex == -1) {\n\t\t\t\t// When the array of children is growing we need to decrease the skew\n\t\t\t\t// as we are adding a new element to the array.\n\t\t\t\t// Example:\n\t\t\t\t// [1, 2, 3] --> [0, 1, 2, 3]\n\t\t\t\t// oldChildren newChildren\n\t\t\t\t//\n\t\t\t\t// The new element is at index 0, so our skew is 0,\n\t\t\t\t// we need to decrease the skew as we are adding a new element.\n\t\t\t\t// The decrease will cause us to compare the element at position 1\n\t\t\t\t// with value 1 with the element at position 0 with value 0.\n\t\t\t\t//\n\t\t\t\t// A linear concept is applied when the array is shrinking,\n\t\t\t\t// if the length is unchanged we can assume that no skew\n\t\t\t\t// changes are needed.\n\t\t\t\tif (newChildrenLength > oldChildrenLength) {\n\t\t\t\t\tskew--;\n\t\t\t\t} else if (newChildrenLength < oldChildrenLength) {\n\t\t\t\t\tskew++;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If we are mounting a DOM VNode, mark it for insertion\n\t\t\tif (typeof childVNode.type != 'function') {\n\t\t\t\tchildVNode._flags |= INSERT_VNODE;\n\t\t\t}\n\t\t} else if (matchingIndex != skewedIndex) {\n\t\t\t// When we move elements around i.e. [0, 1, 2] --> [1, 0, 2]\n\t\t\t// --> we diff 1, we find it at position 1 while our skewed index is 0 and our skew is 0\n\t\t\t// we set the skew to 1 as we found an offset.\n\t\t\t// --> we diff 0, we find it at position 0 while our skewed index is at 2 and our skew is 1\n\t\t\t// this makes us increase the skew again.\n\t\t\t// --> we diff 2, we find it at position 2 while our skewed index is at 4 and our skew is 2\n\t\t\t//\n\t\t\t// this becomes an optimization question where currently we see a 1 element offset as an insertion\n\t\t\t// or deletion i.e. we optimize for [0, 1, 2] --> [9, 0, 1, 2]\n\t\t\t// while a more than 1 offset we see as a swap.\n\t\t\t// We could probably build heuristics for having an optimized course of action here as well, but\n\t\t\t// might go at the cost of some bytes.\n\t\t\t//\n\t\t\t// If we wanted to optimize for i.e. only swaps we'd just do the last two code-branches and have\n\t\t\t// only the first item be a re-scouting and all the others fall in their skewed counter-part.\n\t\t\t// We could also further optimize for swaps\n\t\t\tif (matchingIndex == skewedIndex - 1) {\n\t\t\t\tskew--;\n\t\t\t} else if (matchingIndex == skewedIndex + 1) {\n\t\t\t\tskew++;\n\t\t\t} else {\n\t\t\t\tif (matchingIndex > skewedIndex) {\n\t\t\t\t\tskew--;\n\t\t\t\t} else {\n\t\t\t\t\tskew++;\n\t\t\t\t}\n\n\t\t\t\t// Move this VNode's DOM if the original index (matchingIndex) doesn't\n\t\t\t\t// match the new skew index (i + new skew)\n\t\t\t\t// In the former two branches we know that it matches after skewing\n\t\t\t\tchildVNode._flags |= INSERT_VNODE;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Remove remaining oldChildren if there are any. Loop forwards so that as we\n\t// unmount DOM from the beginning of the oldChildren, we can adjust oldDom to\n\t// point to the next child, which needs to be the first DOM node that won't be\n\t// unmounted.\n\tif (remainingOldChildren) {\n\t\tfor (i = 0; i < oldChildrenLength; i++) {\n\t\t\toldVNode = oldChildren[i];\n\t\t\tif (oldVNode != NULL && (oldVNode._flags & MATCHED) == 0) {\n\t\t\t\tif (oldVNode._dom == oldDom) {\n\t\t\t\t\toldDom = getDomSibling(oldVNode);\n\t\t\t\t}\n\n\t\t\t\tunmount(oldVNode, oldVNode);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn oldDom;\n}\n\n/**\n * @param {VNode} parentVNode\n * @param {PreactElement} oldDom\n * @param {PreactElement} parentDom\n * @param {boolean} shouldPlace\n * @returns {PreactElement}\n */\nfunction insert(parentVNode, oldDom, parentDom, shouldPlace) {\n\t// Note: VNodes in nested suspended trees may be missing _children.\n\n\tif (typeof parentVNode.type == 'function') {\n\t\tlet children = parentVNode._children;\n\t\tfor (let i = 0; children && i < children.length; i++) {\n\t\t\tif (children[i]) {\n\t\t\t\t// If we enter this code path on sCU bailout, where we copy\n\t\t\t\t// oldVNode._children to newVNode._children, we need to update the old\n\t\t\t\t// children's _parent pointer to point to the newVNode (parentVNode\n\t\t\t\t// here).\n\t\t\t\tchildren[i]._parent = parentVNode;\n\t\t\t\toldDom = insert(children[i], oldDom, parentDom, shouldPlace);\n\t\t\t}\n\t\t}\n\n\t\treturn oldDom;\n\t} else if (parentVNode._dom != oldDom) {\n\t\tif (shouldPlace) {\n\t\t\tif (oldDom && parentVNode.type && !oldDom.parentNode) {\n\t\t\t\toldDom = getDomSibling(parentVNode);\n\t\t\t}\n\t\t\tparentDom.insertBefore(parentVNode._dom, oldDom || NULL);\n\t\t}\n\t\toldDom = parentVNode._dom;\n\t}\n\n\tdo {\n\t\toldDom = oldDom && oldDom.nextSibling;\n\t} while (oldDom != NULL && oldDom.nodeType == 8);\n\n\treturn oldDom;\n}\n\n/**\n * Flatten and loop through the children of a virtual node\n * @param {ComponentChildren} children The unflattened children of a virtual\n * node\n * @returns {VNode[]}\n */\nexport function toChildArray(children, out) {\n\tout = out || [];\n\tif (children == NULL || typeof children == 'boolean') {\n\t} else if (isArray(children)) {\n\t\tchildren.some(child => {\n\t\t\ttoChildArray(child, out);\n\t\t});\n\t} else {\n\t\tout.push(children);\n\t}\n\treturn out;\n}\n\n/**\n * @param {VNode} childVNode\n * @param {VNode[]} oldChildren\n * @param {number} skewedIndex\n * @param {number} remainingOldChildren\n * @returns {number}\n */\nfunction findMatchingIndex(\n\tchildVNode,\n\toldChildren,\n\tskewedIndex,\n\tremainingOldChildren\n) {\n\tconst key = childVNode.key;\n\tconst type = childVNode.type;\n\tlet oldVNode = oldChildren[skewedIndex];\n\tconst matched = oldVNode != NULL && (oldVNode._flags & MATCHED) == 0;\n\n\t// We only need to perform a search if there are more children\n\t// (remainingOldChildren) to search. However, if the oldVNode we just looked\n\t// at skewedIndex was not already used in this diff, then there must be at\n\t// least 1 other (so greater than 1) remainingOldChildren to attempt to match\n\t// against. So the following condition checks that ensuring\n\t// remainingOldChildren > 1 if the oldVNode is not already used/matched. Else\n\t// if the oldVNode was null or matched, then there could needs to be at least\n\t// 1 (aka `remainingOldChildren > 0`) children to find and compare against.\n\t//\n\t// If there is an unkeyed functional VNode, that isn't a built-in like our Fragment,\n\t// we should not search as we risk re-using state of an unrelated VNode. (reverted for now)\n\tlet shouldSearch =\n\t\t// (typeof type != 'function' || type === Fragment || key) &&\n\t\tremainingOldChildren > (matched ? 1 : 0);\n\n\tif (\n\t\t(oldVNode === NULL && key == null) ||\n\t\t(matched && key == oldVNode.key && type == oldVNode.type)\n\t) {\n\t\treturn skewedIndex;\n\t} else if (shouldSearch) {\n\t\tlet x = skewedIndex - 1;\n\t\tlet y = skewedIndex + 1;\n\t\twhile (x >= 0 || y < oldChildren.length) {\n\t\t\tconst childIndex = x >= 0 ? x-- : y++;\n\t\t\toldVNode = oldChildren[childIndex];\n\t\t\tif (\n\t\t\t\toldVNode != NULL &&\n\t\t\t\t(oldVNode._flags & MATCHED) == 0 &&\n\t\t\t\tkey == oldVNode.key &&\n\t\t\t\ttype == oldVNode.type\n\t\t\t) {\n\t\t\t\treturn childIndex;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn -1;\n}\n","import {\n\tEMPTY_ARR,\n\tEMPTY_OBJ,\n\tMATH_NAMESPACE,\n\tMODE_HYDRATE,\n\tMODE_SUSPENDED,\n\tNULL,\n\tRESET_MODE,\n\tSVG_NAMESPACE,\n\tUNDEFINED,\n\tXHTML_NAMESPACE\n} from '../constants';\nimport { BaseComponent, getDomSibling } from '../component';\nimport { Fragment } from '../create-element';\nimport { diffChildren } from './children';\nimport { setProperty } from './props';\nimport { assign, isArray, removeNode, slice } from '../util';\nimport options from '../options';\n\n/**\n * @typedef {import('../internal').ComponentChildren} ComponentChildren\n * @typedef {import('../internal').Component} Component\n * @typedef {import('../internal').PreactElement} PreactElement\n * @typedef {import('../internal').VNode} VNode\n */\n\n/**\n * @template {any} T\n * @typedef {import('../internal').Ref<T>} Ref<T>\n */\n\n/**\n * Diff two virtual nodes and apply proper changes to the DOM\n * @param {PreactElement} parentDom The parent of the DOM element\n * @param {VNode} newVNode The new virtual node\n * @param {VNode} oldVNode The old virtual node\n * @param {object} globalContext The current context object. Modified by\n * getChildContext\n * @param {string} namespace Current namespace of the DOM node (HTML, SVG, or MathML)\n * @param {Array<PreactElement>} excessDomChildren\n * @param {Array<Component>} commitQueue List of components which have callbacks\n * to invoke in commitRoot\n * @param {PreactElement} oldDom The current attached DOM element any new dom\n * elements should be placed around. Likely `null` on first render (except when\n * hydrating). Can be a sibling DOM element when diffing Fragments that have\n * siblings. In most cases, it starts out as `oldChildren[0]._dom`.\n * @param {boolean} isHydrating Whether or not we are in hydration\n * @param {any[]} refQueue an array of elements needed to invoke refs\n */\nexport function diff(\n\tparentDom,\n\tnewVNode,\n\toldVNode,\n\tglobalContext,\n\tnamespace,\n\texcessDomChildren,\n\tcommitQueue,\n\toldDom,\n\tisHydrating,\n\trefQueue\n) {\n\t/** @type {any} */\n\tlet tmp,\n\t\tnewType = newVNode.type;\n\n\t// When passing through createElement it assigns the object\n\t// constructor as undefined. This to prevent JSON-injection.\n\tif (newVNode.constructor !== UNDEFINED) return NULL;\n\n\t// If the previous diff bailed out, resume creating/hydrating.\n\tif (oldVNode._flags & MODE_SUSPENDED) {\n\t\tisHydrating = !!(oldVNode._flags & MODE_HYDRATE);\n\t\toldDom = newVNode._dom = oldVNode._dom;\n\t\texcessDomChildren = [oldDom];\n\t}\n\n\tif ((tmp = options._diff)) tmp(newVNode);\n\n\touter: if (typeof newType == 'function') {\n\t\ttry {\n\t\t\tlet c, isNew, oldProps, oldState, snapshot, clearProcessingException;\n\t\t\tlet newProps = newVNode.props;\n\t\t\tconst isClassComponent = newType.prototype && newType.prototype.render;\n\n\t\t\t// Necessary for createContext api. Setting this property will pass\n\t\t\t// the context value as `this.context` just for this component.\n\t\t\ttmp = newType.contextType;\n\t\t\tlet provider = tmp && globalContext[tmp._id];\n\t\t\tlet componentContext = tmp\n\t\t\t\t? provider\n\t\t\t\t\t? provider.props.value\n\t\t\t\t\t: tmp._defaultValue\n\t\t\t\t: globalContext;\n\n\t\t\t// Get component and set it to `c`\n\t\t\tif (oldVNode._component) {\n\t\t\t\tc = newVNode._component = oldVNode._component;\n\t\t\t\tclearProcessingException = c._processingException = c._pendingError;\n\t\t\t} else {\n\t\t\t\t// Instantiate the new component\n\t\t\t\tif (isClassComponent) {\n\t\t\t\t\t// @ts-expect-error The check above verifies that newType is suppose to be constructed\n\t\t\t\t\tnewVNode._component = c = new newType(newProps, componentContext); // eslint-disable-line new-cap\n\t\t\t\t} else {\n\t\t\t\t\t// @ts-expect-error Trust me, Component implements the interface we want\n\t\t\t\t\tnewVNode._component = c = new BaseComponent(\n\t\t\t\t\t\tnewProps,\n\t\t\t\t\t\tcomponentContext\n\t\t\t\t\t);\n\t\t\t\t\tc.constructor = newType;\n\t\t\t\t\tc.render = doRender;\n\t\t\t\t}\n\t\t\t\tif (provider) provider.sub(c);\n\n\t\t\t\tif (!c.state) c.state = {};\n\t\t\t\tc._globalContext = globalContext;\n\t\t\t\tisNew = c._dirty = true;\n\t\t\t\tc._renderCallbacks = [];\n\t\t\t\tc._stateCallbacks = [];\n\t\t\t}\n\n\t\t\t// Invoke getDerivedStateFromProps\n\t\t\tif (isClassComponent && c._nextState == NULL) {\n\t\t\t\tc._nextState = c.state;\n\t\t\t}\n\n\t\t\tif (isClassComponent && newType.getDerivedStateFromProps != NULL) {\n\t\t\t\tif (c._nextState == c.state) {\n\t\t\t\t\tc._nextState = assign({}, c._nextState);\n\t\t\t\t}\n\n\t\t\t\tassign(\n\t\t\t\t\tc._nextState,\n\t\t\t\t\tnewType.getDerivedStateFromProps(newProps, c._nextState)\n\t\t\t\t);\n\t\t\t}\n\n\t\t\toldProps = c.props;\n\t\t\toldState = c.state;\n\t\t\tc._vnode = newVNode;\n\n\t\t\t// Invoke pre-render lifecycle methods\n\t\t\tif (isNew) {\n\t\t\t\tif (\n\t\t\t\t\tisClassComponent &&\n\t\t\t\t\tnewType.getDerivedStateFromProps == NULL &&\n\t\t\t\t\tc.componentWillMount != NULL\n\t\t\t\t) {\n\t\t\t\t\tc.componentWillMount();\n\t\t\t\t}\n\n\t\t\t\tif (isClassComponent && c.componentDidMount != NULL) {\n\t\t\t\t\tc._renderCallbacks.push(c.componentDidMount);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (\n\t\t\t\t\tisClassComponent &&\n\t\t\t\t\tnewType.getDerivedStateFromProps == NULL &&\n\t\t\t\t\tnewProps !== oldProps &&\n\t\t\t\t\tc.componentWillReceiveProps != NULL\n\t\t\t\t) {\n\t\t\t\t\tc.componentWillReceiveProps(newProps, componentContext);\n\t\t\t\t}\n\n\t\t\t\tif (\n\t\t\t\t\tnewVNode._original == oldVNode._original ||\n\t\t\t\t\t(!c._force &&\n\t\t\t\t\t\tc.shouldComponentUpdate != NULL &&\n\t\t\t\t\t\tc.shouldComponentUpdate(\n\t\t\t\t\t\t\tnewProps,\n\t\t\t\t\t\t\tc._nextState,\n\t\t\t\t\t\t\tcomponentContext\n\t\t\t\t\t\t) === false)\n\t\t\t\t) {\n\t\t\t\t\t// More info about this here: https://gist.github.com/JoviDeCroock/bec5f2ce93544d2e6070ef8e0036e4e8\n\t\t\t\t\tif (newVNode._original != oldVNode._original) {\n\t\t\t\t\t\t// When we are dealing with a bail because of sCU we have to update\n\t\t\t\t\t\t// the props, state and dirty-state.\n\t\t\t\t\t\t// when we are dealing with strict-equality we don't as the child could still\n\t\t\t\t\t\t// be dirtied see #3883\n\t\t\t\t\t\tc.props = newProps;\n\t\t\t\t\t\tc.state = c._nextState;\n\t\t\t\t\t\tc._dirty = false;\n\t\t\t\t\t}\n\n\t\t\t\t\tnewVNode._dom = oldVNode._dom;\n\t\t\t\t\tnewVNode._children = oldVNode._children;\n\t\t\t\t\tnewVNode._children.some(vnode => {\n\t\t\t\t\t\tif (vnode) vnode._parent = newVNode;\n\t\t\t\t\t});\n\n\t\t\t\t\tEMPTY_ARR.push.apply(c._renderCallbacks, c._stateCallbacks);\n\t\t\t\t\tc._stateCallbacks = [];\n\n\t\t\t\t\tif (c._renderCallbacks.length) {\n\t\t\t\t\t\tcommitQueue.push(c);\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak outer;\n\t\t\t\t}\n\n\t\t\t\tif (c.componentWillUpdate != NULL) {\n\t\t\t\t\tc.componentWillUpdate(newProps, c._nextState, componentContext);\n\t\t\t\t}\n\n\t\t\t\tif (isClassComponent && c.componentDidUpdate != NULL) {\n\t\t\t\t\tc._renderCallbacks.push(() => {\n\t\t\t\t\t\tc.componentDidUpdate(oldProps, oldState, snapshot);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tc.context = componentContext;\n\t\t\tc.props = newProps;\n\t\t\tc._parentDom = parentDom;\n\t\t\tc._force = false;\n\n\t\t\tlet renderHook = options._render,\n\t\t\t\tcount = 0;\n\t\t\tif (isClassComponent) {\n\t\t\t\tc.state = c._nextState;\n\t\t\t\tc._dirty = false;\n\n\t\t\t\tif (renderHook) renderHook(newVNode);\n\n\t\t\t\ttmp = c.render(c.props, c.state, c.context);\n\n\t\t\t\tEMPTY_ARR.push.apply(c._renderCallbacks, c._stateCallbacks);\n\t\t\t\tc._stateCallbacks = [];\n\t\t\t} else {\n\t\t\t\tdo {\n\t\t\t\t\tc._dirty = false;\n\t\t\t\t\tif (renderHook) renderHook(newVNode);\n\n\t\t\t\t\ttmp = c.render(c.props, c.state, c.context);\n\n\t\t\t\t\t// Handle setState called in render, see #2553\n\t\t\t\t\tc.state = c._nextState;\n\t\t\t\t} while (c._dirty && ++count < 25);\n\t\t\t}\n\n\t\t\t// Handle setState called in render, see #2553\n\t\t\tc.state = c._nextState;\n\n\t\t\tif (c.getChildContext != NULL) {\n\t\t\t\tglobalContext = assign(assign({}, globalContext), c.getChildContext());\n\t\t\t}\n\n\t\t\tif (isClassComponent && !isNew && c.getSnapshotBeforeUpdate != NULL) {\n\t\t\t\tsnapshot = c.getSnapshotBeforeUpdate(oldProps, oldState);\n\t\t\t}\n\n\t\t\tlet renderResult =\n\t\t\t\ttmp != NULL && tmp.type === Fragment && tmp.key == NULL\n\t\t\t\t\t? cloneNode(tmp.props.children)\n\t\t\t\t\t: tmp;\n\n\t\t\toldDom = diffChildren(\n\t\t\t\tparentDom,\n\t\t\t\tisArray(renderResult) ? renderResult : [renderResult],\n\t\t\t\tnewVNode,\n\t\t\t\toldVNode,\n\t\t\t\tglobalContext,\n\t\t\t\tnamespace,\n\t\t\t\texcessDomChildren,\n\t\t\t\tcommitQueue,\n\t\t\t\toldDom,\n\t\t\t\tisHydrating,\n\t\t\t\trefQueue\n\t\t\t);\n\n\t\t\tc.base = newVNode._dom;\n\n\t\t\t// We successfully rendered this VNode, unset any stored hydration/bailout state:\n\t\t\tnewVNode._flags &= RESET_MODE;\n\n\t\t\tif (c._renderCallbacks.length) {\n\t\t\t\tcommitQueue.push(c);\n\t\t\t}\n\n\t\t\tif (clearProcessingException) {\n\t\t\t\tc._pendingError = c._processingException = NULL;\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tnewVNode._original = NULL;\n\t\t\t// if hydrating or creating initial tree, bailout preserves DOM:\n\t\t\tif (isHydrating || excessDomChildren != NULL) {\n\t\t\t\tif (e.then) {\n\t\t\t\t\tnewVNode._flags |= isHydrating\n\t\t\t\t\t\t? MODE_HYDRATE | MODE_SUSPENDED\n\t\t\t\t\t\t: MODE_SUSPENDED;\n\n\t\t\t\t\twhile (oldDom && oldDom.nodeType == 8 && oldDom.nextSibling) {\n\t\t\t\t\t\toldDom = oldDom.nextSibling;\n\t\t\t\t\t}\n\n\t\t\t\t\texcessDomChildren[excessDomChildren.indexOf(oldDom)] = NULL;\n\t\t\t\t\tnewVNode._dom = oldDom;\n\t\t\t\t} else {\n\t\t\t\t\tfor (let i = excessDomChildren.length; i--; ) {\n\t\t\t\t\t\tremoveNode(excessDomChildren[i]);\n\t\t\t\t\t}\n\t\t\t\t\tmarkAsForce(newVNode);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tnewVNode._dom = oldVNode._dom;\n\t\t\t\tnewVNode._children = oldVNode._children;\n\t\t\t\tif (!e.then) markAsForce(newVNode);\n\t\t\t}\n\t\t\toptions._catchError(e, newVNode, oldVNode);\n\t\t}\n\t} else if (\n\t\texcessDomChildren == NULL &&\n\t\tnewVNode._original == oldVNode._original\n\t) {\n\t\tnewVNode._children = oldVNode._children;\n\t\tnewVNode._dom = oldVNode._dom;\n\t} else {\n\t\toldDom = newVNode._dom = diffElementNodes(\n\t\t\toldVNode._dom,\n\t\t\tnewVNode,\n\t\t\toldVNode,\n\t\t\tglobalContext,\n\t\t\tnamespace,\n\t\t\texcessDomChildren,\n\t\t\tcommitQueue,\n\t\t\tisHydrating,\n\t\t\trefQueue\n\t\t);\n\t}\n\n\tif ((tmp = options.diffed)) tmp(newVNode);\n\n\treturn newVNode._flags & MODE_SUSPENDED ? undefined : oldDom;\n}\n\nfunction markAsForce(vnode) {\n\tif (vnode) {\n\t\tif (vnode._component) vnode._component._force = true;\n\t\tif (vnode._children) vnode._children.some(markAsForce);\n\t}\n}\n\n/**\n * @param {Array<Component>} commitQueue List of components\n * which have callbacks to invoke in commitRoot\n * @param {VNode} root\n */\nexport function commitRoot(commitQueue, root, refQueue) {\n\tfor (let i = 0; i < refQueue.length; i++) {\n\t\tapplyRef(refQueue[i], refQueue[++i], refQueue[++i]);\n\t}\n\n\tif (options._commit) options._commit(root, commitQueue);\n\n\tcommitQueue.some(c => {\n\t\ttry {\n\t\t\t// @ts-expect-error Reuse the commitQueue variable here so the type changes\n\t\t\tcommitQueue = c._renderCallbacks;\n\t\t\tc._renderCallbacks = [];\n\t\t\tcommitQueue.some(cb => {\n\t\t\t\t// @ts-expect-error See above comment on commitQueue\n\t\t\t\tcb.call(c);\n\t\t\t});\n\t\t} catch (e) {\n\t\t\toptions._catchError(e, c._vnode);\n\t\t}\n\t});\n}\n\nfunction cloneNode(node) {\n\tif (typeof node != 'object' || node == NULL || node._depth > 0) {\n\t\treturn node;\n\t}\n\n\tif (isArray(node)) {\n\t\treturn node.map(cloneNode);\n\t}\n\n\treturn assign({}, node);\n}\n\n/**\n * Diff two virtual nodes representing DOM element\n * @param {PreactElement} dom The DOM element representing the virtual nodes\n * being diffed\n * @param {VNode} newVNode The new virtual node\n * @param {VNode} oldVNode The old virtual node\n * @param {object} globalContext The current context object\n * @param {string} namespace Current namespace of the DOM node (HTML, SVG, or MathML)\n * @param {Array<PreactElement>} excessDomChildren\n * @param {Array<Component>} commitQueue List of components which have callbacks\n * to invoke in commitRoot\n * @param {boolean} isHydrating Whether or not we are in hydration\n * @param {any[]} refQueue an array of elements needed to invoke refs\n * @returns {PreactElement}\n */\nfunction diffElementNodes(\n\tdom,\n\tnewVNode,\n\toldVNode,\n\tglobalContext,\n\tnamespace,\n\texcessDomChildren,\n\tcommitQueue,\n\tisHydrating,\n\trefQueue\n) {\n\tlet oldProps = oldVNode.props || EMPTY_OBJ;\n\tlet newProps = newVNode.props;\n\tlet nodeType = /** @type {string} */ (newVNode.type);\n\t/** @type {any} */\n\tlet i;\n\t/** @type {{ __html?: string }} */\n\tlet newHtml;\n\t/** @type {{ __html?: string }} */\n\tlet oldHtml;\n\t/** @type {ComponentChildren} */\n\tlet newChildren;\n\tlet value;\n\tlet inputValue;\n\tlet checked;\n\n\t// Tracks entering and exiting namespaces when descending through the tree.\n\tif (nodeType == 'svg') namespace = SVG_NAMESPACE;\n\telse if (nodeType == 'math') namespace = MATH_NAMESPACE;\n\telse if (!namespace) namespace = XHTML_NAMESPACE;\n\n\tif (excessDomChildren != NULL) {\n\t\tfor (i = 0; i < excessDomChildren.length; i++) {\n\t\t\tvalue = excessDomChildren[i];\n\n\t\t\t// if newVNode matches an element in excessDomChildren or the `dom`\n\t\t\t// argument matches an element in excessDomChildren, remove it from\n\t\t\t// excessDomChildren so it isn't later removed in diffChildren\n\t\t\tif (\n\t\t\t\tvalue &&\n\t\t\t\t'setAttribute' in value == !!nodeType &&\n\t\t\t\t(nodeType ? value.localName == nodeType : value.nodeType == 3)\n\t\t\t) {\n\t\t\t\tdom = value;\n\t\t\t\texcessDomChildren[i] = NULL;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (dom == NULL) {\n\t\tif (nodeType == NULL) {\n\t\t\treturn document.createTextNode(newProps);\n\t\t}\n\n\t\tdom = document.createElementNS(\n\t\t\tnamespace,\n\t\t\tnodeType,\n\t\t\tnewProps.is && newProps\n\t\t);\n\n\t\t// we are creating a new node, so we can assume this is a new subtree (in\n\t\t// case we are hydrating), this deopts the hydrate\n\t\tif (isHydrating) {\n\t\t\tif (options._hydrationMismatch)\n\t\t\t\toptions._hydrationMismatch(newVNode, excessDomChildren);\n\t\t\tisHydrating = false;\n\t\t}\n\t\t// we created a new parent, so none of the previously attached children can be reused:\n\t\texcessDomChildren = NULL;\n\t}\n\n\tif (nodeType == NULL) {\n\t\t// During hydration, we still have to split merged text from SSR'd HTML.\n\t\tif (oldProps !== newProps && (!isHydrating || dom.data != newProps)) {\n\t\t\tdom.data = newProps;\n\t\t}\n\t} else {\n\t\t// If excessDomChildren was not null, repopulate it with the current element's children:\n\t\texcessDomChildren = excessDomChildren && slice.call(dom.childNodes);\n\n\t\t// If we are in a situation where we are not hydrating but are using\n\t\t// existing DOM (e.g. replaceNode) we should read the existing DOM\n\t\t// attributes to diff them\n\t\tif (!isHydrating && excessDomChildren != NULL) {\n\t\t\toldProps = {};\n\t\t\tfor (i = 0; i < dom.attributes.length; i++) {\n\t\t\t\tvalue = dom.attributes[i];\n\t\t\t\toldProps[value.name] = value.value;\n\t\t\t}\n\t\t}\n\n\t\tfor (i in oldProps) {\n\t\t\tvalue = oldProps[i];\n\t\t\tif (i == 'dangerouslySetInnerHTML') {\n\t\t\t\toldHtml = value;\n\t\t\t} else if (\n\t\t\t\ti != 'children' &&\n\t\t\t\t!(i in newProps) &&\n\t\t\t\t!(i == 'value' && 'defaultValue' in newProps) &&\n\t\t\t\t!(i == 'checked' && 'defaultChecked' in newProps)\n\t\t\t) {\n\t\t\t\tsetProperty(dom, i, NULL, value, namespace);\n\t\t\t}\n\t\t}\n\n\t\t// During hydration, props are not diffed at all (including dangerouslySetInnerHTML)\n\t\t// @TODO we should warn in debug mode when props don't match here.\n\t\tfor (i in newProps) {\n\t\t\tvalue = newProps[i];\n\t\t\tif (i == 'children') {\n\t\t\t\tnewChildren = value;\n\t\t\t} else if (i == 'dangerouslySetInnerHTML') {\n\t\t\t\tnewHtml = value;\n\t\t\t} else if (i == 'value') {\n\t\t\t\tinputValue = value;\n\t\t\t} else if (i == 'checked') {\n\t\t\t\tchecked = value;\n\t\t\t} else if (\n\t\t\t\t(!isHydrating || typeof value == 'function') &&\n\t\t\t\toldProps[i] !== value\n\t\t\t) {\n\t\t\t\tsetProperty(dom, i, value, oldProps[i], namespace);\n\t\t\t}\n\t\t}\n\n\t\t// If the new vnode didn't have dangerouslySetInnerHTML, diff its children\n\t\tif (newHtml) {\n\t\t\t// Avoid re-applying the same '__html' if it did not changed between re-render\n\t\t\tif (\n\t\t\t\t!isHydrating &&\n\t\t\t\t(!oldHtml ||\n\t\t\t\t\t(newHtml.__html != oldHtml.__html && newHtml.__html != dom.innerHTML))\n\t\t\t) {\n\t\t\t\tdom.innerHTML = newHtml.__html;\n\t\t\t}\n\n\t\t\tnewVNode._children = [];\n\t\t} else {\n\t\t\tif (oldHtml) dom.innerHTML = '';\n\n\t\t\tdiffChildren(\n\t\t\t\t// @ts-expect-error\n\t\t\t\tnewVNode.type == 'template' ? dom.content : dom,\n\t\t\t\tisArray(newChildren) ? newChildren : [newChildren],\n\t\t\t\tnewVNode,\n\t\t\t\toldVNode,\n\t\t\t\tglobalContext,\n\t\t\t\tnodeType == 'foreignObject' ? XHTML_NAMESPACE : namespace,\n\t\t\t\texcessDomChildren,\n\t\t\t\tcommitQueue,\n\t\t\t\texcessDomChildren\n\t\t\t\t\t? excessDomChildren[0]\n\t\t\t\t\t: oldVNode._children && getDomSibling(oldVNode, 0),\n\t\t\t\tisHydrating,\n\t\t\t\trefQueue\n\t\t\t);\n\n\t\t\t// Remove children that are not part of any vnode.\n\t\t\tif (excessDomChildren != NULL) {\n\t\t\t\tfor (i = excessDomChildren.length; i--; ) {\n\t\t\t\t\tremoveNode(excessDomChildren[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// As above, don't diff props during hydration\n\t\tif (!isHydrating) {\n\t\t\ti = 'value';\n\t\t\tif (nodeType == 'progress' && inputValue == NULL) {\n\t\t\t\tdom.removeAttribute('value');\n\t\t\t} else if (\n\t\t\t\tinputValue != UNDEFINED &&\n\t\t\t\t// #2756 For the <progress>-element the initial value is 0,\n\t\t\t\t// despite the attribute not being present. When the attribute\n\t\t\t\t// is missing the progress bar is treated as indeterminate.\n\t\t\t\t// To fix that we'll always update it when it is 0 for progress elements\n\t\t\t\t(inputValue !== dom[i] ||\n\t\t\t\t\t(nodeType == 'progress' && !inputValue) ||\n\t\t\t\t\t// This is only for IE 11 to fix <select> value not being updated.\n\t\t\t\t\t// To avoid a stale select value we need to set the option.value\n\t\t\t\t\t// again, which triggers IE11 to re-evaluate the select value\n\t\t\t\t\t(nodeType == 'option' && inputValue != oldProps[i]))\n\t\t\t) {\n\t\t\t\tsetProperty(dom, i, inputValue, oldProps[i], namespace);\n\t\t\t}\n\n\t\t\ti = 'checked';\n\t\t\tif (checked != UNDEFINED && checked != dom[i]) {\n\t\t\t\tsetProperty(dom, i, checked, oldProps[i], namespace);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn dom;\n}\n\n/**\n * Invoke or update a ref, depending on whether it is a function or object ref.\n * @param {Ref<any> & { _unmount?: unknown }} ref\n * @param {any} value\n * @param {VNode} vnode\n */\nexport function applyRef(ref, value, vnode) {\n\ttry {\n\t\tif (typeof ref == 'function') {\n\t\t\tlet hasRefUnmount = typeof ref._unmount == 'function';\n\t\t\tif (hasRefUnmount) {\n\t\t\t\t// @ts-ignore TS doesn't like moving narrowing checks into variables\n\t\t\t\tref._unmount();\n\t\t\t}\n\n\t\t\tif (!hasRefUnmount || value != NULL) {\n\t\t\t\t// Store the cleanup function on the function\n\t\t\t\t// instance object itself to avoid shape\n\t\t\t\t// transitioning vnode\n\t\t\t\tref._unmount = ref(value);\n\t\t\t}\n\t\t} else ref.current = value;\n\t} catch (e) {\n\t\toptions._catchError(e, vnode);\n\t}\n}\n\n/**\n * Unmount a virtual node from the tree and apply DOM changes\n * @param {VNode} vnode The virtual node to unmount\n * @param {VNode} parentVNode The parent of the VNode that initiated the unmount\n * @param {boolean} [skipRemove] Flag that indicates that a parent node of the\n * current element is already detached from the DOM.\n */\nexport function unmount(vnode, parentVNode, skipRemove) {\n\tlet r;\n\tif (options.unmount) options.unmount(vnode);\n\n\tif ((r = vnode.ref)) {\n\t\tif (!r.current || r.current == vnode._dom) {\n\t\t\tapplyRef(r, NULL, parentVNode);\n\t\t}\n\t}\n\n\tif ((r = vnode._component) != NULL) {\n\t\tif (r.componentWillUnmount) {\n\t\t\ttry {\n\t\t\t\tr.componentWillUnmount();\n\t\t\t} catch (e) {\n\t\t\t\toptions._catchError(e, parentVNode);\n\t\t\t}\n\t\t}\n\n\t\tr.base = r._parentDom = NULL;\n\t}\n\n\tif ((r = vnode._children)) {\n\t\tfor (let i = 0; i < r.length; i++) {\n\t\t\tif (r[i]) {\n\t\t\t\tunmount(\n\t\t\t\t\tr[i],\n\t\t\t\t\tparentVNode,\n\t\t\t\t\tskipRemove || typeof vnode.type != 'function'\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!skipRemove) {\n\t\tremoveNode(vnode._dom);\n\t}\n\n\tvnode._component = vnode._parent = vnode._dom = UNDEFINED;\n}\n\n/** The `.render()` method for a PFC backing instance. */\nfunction doRender(props, state, context) {\n\treturn this.constructor(props, context);\n}\n","import { EMPTY_OBJ, NULL } from './constants';\nimport { commitRoot, diff } from './diff/index';\nimport { createElement, Fragment } from './create-element';\nimport options from './options';\nimport { slice } from './util';\n\n/**\n * Render a Preact virtual node into a DOM element\n * @param {import('./internal').ComponentChild} vnode The virtual node to render\n * @param {import('./internal').PreactElement} parentDom The DOM element to render into\n * @param {import('./internal').PreactElement | object} [replaceNode] Optional: Attempt to re-use an\n * existing DOM tree rooted at `replaceNode`\n */\nexport function render(vnode, parentDom, replaceNode) {\n\t// https://github.com/preactjs/preact/issues/3794\n\tif (parentDom == document) {\n\t\tparentDom = document.documentElement;\n\t}\n\n\tif (options._root) options._root(vnode, parentDom);\n\n\t// We abuse the `replaceNode` parameter in `hydrate()` to signal if we are in\n\t// hydration mode or not by passing the `hydrate` function instead of a DOM\n\t// element..\n\tlet isHydrating = typeof replaceNode == 'function';\n\n\t// To be able to support calling `render()` multiple times on the same\n\t// DOM node, we need to obtain a reference to the previous tree. We do\n\t// this by assigning a new `_children` property to DOM nodes which points\n\t// to the last rendered tree. By default this property is not present, which\n\t// means that we are mounting a new tree for the first time.\n\tlet oldVNode = isHydrating\n\t\t? NULL\n\t\t: (replaceNode && replaceNode._children) || parentDom._children;\n\n\tvnode = ((!isHydrating && replaceNode) || parentDom)._children =\n\t\tcreateElement(Fragment, NULL, [vnode]);\n\n\t// List of effects that need to be called after diffing.\n\tlet commitQueue = [],\n\t\trefQueue = [];\n\tdiff(\n\t\tparentDom,\n\t\t// Determine the new vnode tree and store it on the DOM element on\n\t\t// our custom `_children` property.\n\t\tvnode,\n\t\toldVNode || EMPTY_OBJ,\n\t\tEMPTY_OBJ,\n\t\tparentDom.namespaceURI,\n\t\t!isHydrating && replaceNode\n\t\t\t? [replaceNode]\n\t\t\t: oldVNode\n\t\t\t\t? NULL\n\t\t\t\t: parentDom.firstChild\n\t\t\t\t\t? slice.call(parentDom.childNodes)\n\t\t\t\t\t: NULL,\n\t\tcommitQueue,\n\t\t!isHydrating && replaceNode\n\t\t\t? replaceNode\n\t\t\t: oldVNode\n\t\t\t\t? oldVNode._dom\n\t\t\t\t: parentDom.firstChild,\n\t\tisHydrating,\n\t\trefQueue\n\t);\n\n\t// Flush all queued effects\n\tcommitRoot(commitQueue, vnode, refQueue);\n}\n\n/**\n * Update an existing DOM element with data from a Preact virtual node\n * @param {import('./internal').ComponentChild} vnode The virtual node to render\n * @param {import('./internal').PreactElement} parentDom The DOM element to update\n */\nexport function hydrate(vnode, parentDom) {\n\trender(vnode, parentDom, hydrate);\n}\n","import { assign, slice } from './util';\nimport { createVNode } from './create-element';\nimport { NULL, UNDEFINED } from './constants';\n\n/**\n * Clones the given VNode, optionally adding attributes/props and replacing its\n * children.\n * @param {import('./internal').VNode} vnode The virtual DOM element to clone\n * @param {object} props Attributes/props to add when cloning\n * @param {Array<import('./internal').ComponentChildren>} rest Any additional arguments will be used\n * as replacement children.\n * @returns {import('./internal').VNode}\n */\nexport function cloneElement(vnode, props, children) {\n\tlet normalizedProps = assign({}, vnode.props),\n\t\tkey,\n\t\tref,\n\t\ti;\n\n\tlet defaultProps;\n\n\tif (vnode.type && vnode.type.defaultProps) {\n\t\tdefaultProps = vnode.type.defaultProps;\n\t}\n\n\tfor (i in props) {\n\t\tif (i == 'key') key = props[i];\n\t\telse if (i == 'ref') ref = props[i];\n\t\telse if (props[i] === UNDEFINED && defaultProps != UNDEFINED) {\n\t\t\tnormalizedProps[i] = defaultProps[i];\n\t\t} else {\n\t\t\tnormalizedProps[i] = props[i];\n\t\t}\n\t}\n\n\tif (arguments.length > 2) {\n\t\tnormalizedProps.children =\n\t\t\targuments.length > 3 ? slice.call(arguments, 2) : children;\n\t}\n\n\treturn createVNode(\n\t\tvnode.type,\n\t\tnormalizedProps,\n\t\tkey || vnode.key,\n\t\tref || vnode.ref,\n\t\tNULL\n\t);\n}\n","import { NULL } from '../constants';\n\n/**\n * Find the closest error boundary to a thrown error and call it\n * @param {object} error The thrown value\n * @param {import('../internal').VNode} vnode The vnode that threw the error that was caught (except\n * for unmounting when this parameter is the highest parent that was being\n * unmounted)\n * @param {import('../internal').VNode} [oldVNode]\n * @param {import('../internal').ErrorInfo} [errorInfo]\n */\nexport function _catchError(error, vnode, oldVNode, errorInfo) {\n\t/** @type {import('../internal').Component} */\n\tlet component,\n\t\t/** @type {import('../internal').ComponentType} */\n\t\tctor,\n\t\t/** @type {boolean} */\n\t\thandled;\n\n\tfor (; (vnode = vnode._parent); ) {\n\t\tif ((component = vnode._component) && !component._processingException) {\n\t\t\ttry {\n\t\t\t\tctor = component.constructor;\n\n\t\t\t\tif (ctor && ctor.getDerivedStateFromError != NULL) {\n\t\t\t\t\tcomponent.setState(ctor.getDerivedStateFromError(error));\n\t\t\t\t\thandled = component._dirty;\n\t\t\t\t}\n\n\t\t\t\tif (component.componentDidCatch != NULL) {\n\t\t\t\t\tcomponent.componentDidCatch(error, errorInfo || {});\n\t\t\t\t\thandled = component._dirty;\n\t\t\t\t}\n\n\t\t\t\t// This is an error boundary. Mark it as having bailed out, and whether it was mid-hydration.\n\t\t\t\tif (handled) {\n\t\t\t\t\treturn (component._pendingError = component);\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\terror = e;\n\t\t\t}\n\t\t}\n\t}\n\n\tthrow error;\n}\n","import { options as _options } from 'preact';\n\n/** @type {number} */\nlet currentIndex;\n\n/** @type {import('./internal').Component} */\nlet currentComponent;\n\n/** @type {import('./internal').Component} */\nlet previousComponent;\n\n/** @type {number} */\nlet currentHook = 0;\n\n/** @type {Array<import('./internal').Component>} */\nlet afterPaintEffects = [];\n\n// Cast to use internal Options type\nconst options = /** @type {import('./internal').Options} */ (_options);\n\nlet oldBeforeDiff = options._diff;\nlet oldBeforeRender = options._render;\nlet oldAfterDiff = options.diffed;\nlet oldCommit = options._commit;\nlet oldBeforeUnmount = options.unmount;\nlet oldRoot = options._root;\n\n// We take the minimum timeout for requestAnimationFrame to ensure that\n// the callback is invoked after the next frame. 35ms is based on a 30hz\n// refresh rate, which is the minimum rate for a smooth user experience.\nconst RAF_TIMEOUT = 35;\nlet prevRaf;\n\n/** @type {(vnode: import('./internal').VNode) => void} */\noptions._diff = vnode => {\n\tcurrentComponent = null;\n\tif (oldBeforeDiff) oldBeforeDiff(vnode);\n};\n\noptions._root = (vnode, parentDom) => {\n\tif (vnode && parentDom._children && parentDom._children._mask) {\n\t\tvnode._mask = parentDom._children._mask;\n\t}\n\n\tif (oldRoot) oldRoot(vnode, parentDom);\n};\n\n/** @type {(vnode: import('./internal').VNode) => void} */\noptions._render = vnode => {\n\tif (oldBeforeRender) oldBeforeRender(vnode);\n\n\tcurrentComponent = vnode._component;\n\tcurrentIndex = 0;\n\n\tconst hooks = currentComponent.__hooks;\n\tif (hooks) {\n\t\tif (previousComponent === currentComponent) {\n\t\t\thooks._pendingEffects = [];\n\t\t\tcurrentComponent._renderCallbacks = [];\n\t\t\thooks._list.some(hookItem => {\n\t\t\t\tif (hookItem._nextValue) {\n\t\t\t\t\thookItem._value = hookItem._nextValue;\n\t\t\t\t}\n\t\t\t\thookItem._pendingArgs = hookItem._nextValue = undefined;\n\t\t\t});\n\t\t} else {\n\t\t\thooks._pendingEffects.some(invokeCleanup);\n\t\t\thooks._pendingEffects.some(invokeEffect);\n\t\t\thooks._pendingEffects = [];\n\t\t\tcurrentIndex = 0;\n\t\t}\n\t}\n\tpreviousComponent = currentComponent;\n};\n\n/** @type {(vnode: import('./internal').VNode) => void} */\noptions.diffed = vnode => {\n\tif (oldAfterDiff) oldAfterDiff(vnode);\n\n\tconst c = vnode._component;\n\tif (c && c.__hooks) {\n\t\tif (c.__hooks._pendingEffects.length) afterPaint(afterPaintEffects.push(c));\n\t\tc.__hooks._list.some(hookItem => {\n\t\t\tif (hookItem._pendingArgs) {\n\t\t\t\thookItem._args = hookItem._pendingArgs;\n\t\t\t}\n\t\t\thookItem._pendingArgs = undefined;\n\t\t});\n\t}\n\tpreviousComponent = currentComponent = null;\n};\n\n// TODO: Improve typing of commitQueue parameter\n/** @type {(vnode: import('./internal').VNode, commitQueue: any) => void} */\noptions._commit = (vnode, commitQueue) => {\n\tcommitQueue.some(component => {\n\t\ttry {\n\t\t\tcomponent._renderCallbacks.some(invokeCleanup);\n\t\t\tcomponent._renderCallbacks = component._renderCallbacks.filter(cb =>\n\t\t\t\tcb._value ? invokeEffect(cb) : true\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tcommitQueue.some(c => {\n\t\t\t\tif (c._renderCallbacks) c._renderCallbacks = [];\n\t\t\t});\n\t\t\tcommitQueue = [];\n\t\t\toptions._catchError(e, component._vnode);\n\t\t}\n\t});\n\n\tif (oldCommit) oldCommit(vnode, commitQueue);\n};\n\n/** @type {(vnode: import('./internal').VNode) => void} */\noptions.unmount = vnode => {\n\tif (oldBeforeUnmount) oldBeforeUnmount(vnode);\n\n\tconst c = vnode._component;\n\tif (c && c.__hooks) {\n\t\tlet hasErrored;\n\t\tc.__hooks._list.some(s => {\n\t\t\ttry {\n\t\t\t\tinvokeCleanup(s);\n\t\t\t} catch (e) {\n\t\t\t\thasErrored = e;\n\t\t\t}\n\t\t});\n\t\tc.__hooks = undefined;\n\t\tif (hasErrored) options._catchError(hasErrored, c._vnode);\n\t}\n};\n\n/**\n * Get a hook's state from the currentComponent\n * @param {number} index The index of the hook to get\n * @param {number} type The index of the hook to get\n * @returns {any}\n */\nfunction getHookState(index, type) {\n\tif (options._hook) {\n\t\toptions._hook(currentComponent, index, currentHook || type);\n\t}\n\tcurrentHook = 0;\n\n\t// Largely inspired by:\n\t// * https://github.com/michael-klein/funcy.js/blob/f6be73468e6ec46b0ff5aa3cc4c9baf72a29025a/src/hooks/core_hooks.mjs\n\t// * https://github.com/michael-klein/funcy.js/blob/650beaa58c43c33a74820a3c98b3c7079cf2e333/src/renderer.mjs\n\t// Other implementations to look at:\n\t// * https://codesandbox.io/s/mnox05qp8\n\tconst hooks =\n\t\tcurrentComponent.__hooks ||\n\t\t(currentComponent.__hooks = {\n\t\t\t_list: [],\n\t\t\t_pendingEffects: []\n\t\t});\n\n\tif (index >= hooks._list.length) {\n\t\thooks._list.push({});\n\t}\n\n\treturn hooks._list[index];\n}\n\n/**\n * @template {unknown} S\n * @param {import('./index').Dispatch<import('./index').StateUpdater<S>>} [initialState]\n * @returns {[S, (state: S) => void]}\n */\nexport function useState(initialState) {\n\tcurrentHook = 1;\n\treturn useReducer(invokeOrReturn, initialState);\n}\n\n/**\n * @template {unknown} S\n * @template {unknown} A\n * @param {import('./index').Reducer<S, A>} reducer\n * @param {import('./index').Dispatch<import('./index').StateUpdater<S>>} initialState\n * @param {(initialState: any) => void} [init]\n * @returns {[ S, (state: S) => void ]}\n */\nexport function useReducer(reducer, initialState, init) {\n\t/** @type {import('./internal').ReducerHookState} */\n\tconst hookState = getHookState(currentIndex++, 2);\n\thookState._reducer = reducer;\n\tif (!hookState._component) {\n\t\thookState._value = [\n\t\t\t!init ? invokeOrReturn(undefined, initialState) : init(initialState),\n\n\t\t\taction => {\n\t\t\t\tconst currentValue = hookState._nextValue\n\t\t\t\t\t? hookState._nextValue[0]\n\t\t\t\t\t: hookState._value[0];\n\t\t\t\tconst nextValue = hookState._reducer(currentValue, action);\n\n\t\t\t\tif (currentValue !== nextValue) {\n\t\t\t\t\thookState._nextValue = [nextValue, hookState._value[1]];\n\t\t\t\t\thookState._component.setState({});\n\t\t\t\t}\n\t\t\t}\n\t\t];\n\n\t\thookState._component = currentComponent;\n\n\t\tif (!currentComponent._hasScuFromHooks) {\n\t\t\tcurrentComponent._hasScuFromHooks = true;\n\t\t\tlet prevScu = currentComponent.shouldComponentUpdate;\n\t\t\tconst prevCWU = currentComponent.componentWillUpdate;\n\n\t\t\t// If we're dealing with a forced update `shouldComponentUpdate` will\n\t\t\t// not be called. But we use that to update the hook values, so we\n\t\t\t// need to call it.\n\t\t\tcurrentComponent.componentWillUpdate = function (p, s, c) {\n\t\t\t\tif (this._force) {\n\t\t\t\t\tlet tmp = prevScu;\n\t\t\t\t\t// Clear to avoid other sCU hooks from being called\n\t\t\t\t\tprevScu = undefined;\n\t\t\t\t\tupdateHookState(p, s, c);\n\t\t\t\t\tprevScu = tmp;\n\t\t\t\t}\n\n\t\t\t\tif (prevCWU) prevCWU.call(this, p, s, c);\n\t\t\t};\n\n\t\t\t// This SCU has the purpose of bailing out after repeated updates\n\t\t\t// to stateful hooks.\n\t\t\t// we store the next value in _nextValue[0] and keep doing that for all\n\t\t\t// state setters, if we have next states and\n\t\t\t// all next states within a component end up being equal to their original state\n\t\t\t// we are safe to bail out for this specific component.\n\t\t\t/**\n\t\t\t *\n\t\t\t * @type {import('./internal').Component[\"shouldComponentUpdate\"]}\n\t\t\t */\n\t\t\t// @ts-ignore - We don't use TS to downtranspile\n\t\t\t// eslint-disable-next-line no-inner-declarations\n\t\t\tfunction updateHookState(p, s, c) {\n\t\t\t\tif (!hookState._component.__hooks) return true;\n\n\t\t\t\tconst stateHooks = hookState._component.__hooks._list.filter(\n\t\t\t\t\tx => x._component\n\t\t\t\t);\n\n\t\t\t\tconst allHooksEmpty = stateHooks.every(x => !x._nextValue);\n\t\t\t\t// When we have no updated hooks in the component we invoke the previous SCU or\n\t\t\t\t// traverse the VDOM tree further.\n\t\t\t\tif (allHooksEmpty) {\n\t\t\t\t\treturn prevScu ? prevScu.call(this, p, s, c) : true;\n\t\t\t\t}\n\n\t\t\t\t// We check whether we have components with a nextValue set that\n\t\t\t\t// have values that aren't equal to one another this pushes\n\t\t\t\t// us to update further down the tree\n\t\t\t\tlet shouldUpdate = hookState._component.props !== p;\n\t\t\t\tstateHooks.some(hookItem => {\n\t\t\t\t\tif (hookItem._nextValue) {\n\t\t\t\t\t\tconst currentValue = hookItem._value[0];\n\t\t\t\t\t\thookItem._value = hookItem._nextValue;\n\t\t\t\t\t\thookItem._nextValue = undefined;\n\t\t\t\t\t\tif (currentValue !== hookItem._value[0]) shouldUpdate = true;\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\treturn prevScu\n\t\t\t\t\t? prevScu.call(this, p, s, c) || shouldUpdate\n\t\t\t\t\t: shouldUpdate;\n\t\t\t}\n\n\t\t\tcurrentComponent.shouldComponentUpdate = updateHookState;\n\t\t}\n\t}\n\n\treturn hookState._nextValue || hookState._value;\n}\n\n/**\n * @param {import('./internal').Effect} callback\n * @param {unknown[]} args\n * @returns {void}\n */\nexport function useEffect(callback, args) {\n\t/** @type {import('./internal').EffectHookState} */\n\tconst state = getHookState(currentIndex++, 3);\n\tif (!options._skipEffects && argsChanged(state._args, args)) {\n\t\tstate._value = callback;\n\t\tstate._pendingArgs = args;\n\n\t\tcurrentComponent.__hooks._pendingEffects.push(state);\n\t}\n}\n\n/**\n * @param {import('./internal').Effect} callback\n * @param {unknown[]} args\n * @returns {void}\n */\nexport function useLayoutEffect(callback, args) {\n\t/** @type {import('./internal').EffectHookState} */\n\tconst state = getHookState(currentIndex++, 4);\n\tif (!options._skipEffects && argsChanged(state._args, args)) {\n\t\tstate._value = callback;\n\t\tstate._pendingArgs = args;\n\n\t\tcurrentComponent._renderCallbacks.push(state);\n\t}\n}\n\n/** @type {(initialValue: unknown) => unknown} */\nexport function useRef(initialValue) {\n\tcurrentHook = 5;\n\treturn useMemo(() => ({ current: initialValue }), []);\n}\n\n/**\n * @param {object} ref\n * @param {() => object} createHandle\n * @param {unknown[]} args\n * @returns {void}\n */\nexport function useImperativeHandle(ref, createHandle, args) {\n\tcurrentHook = 6;\n\tuseLayoutEffect(\n\t\t() => {\n\t\t\tif (typeof ref == 'function') {\n\t\t\t\tconst result = ref(createHandle());\n\t\t\t\treturn () => {\n\t\t\t\t\tref(null);\n\t\t\t\t\tif (result && typeof result == 'function') result();\n\t\t\t\t};\n\t\t\t} else if (ref) {\n\t\t\t\tref.current = createHandle();\n\t\t\t\treturn () => (ref.current = null);\n\t\t\t}\n\t\t},\n\t\targs == null ? args : args.concat(ref)\n\t);\n}\n\n/**\n * @template {unknown} T\n * @param {() => T} factory\n * @param {unknown[]} args\n * @returns {T}\n */\nexport function useMemo(factory, args) {\n\t/** @type {import('./internal').MemoHookState<T>} */\n\tconst state = getHookState(currentIndex++, 7);\n\tif (argsChanged(state._args, args)) {\n\t\tstate._value = factory();\n\t\tstate._args = args;\n\t\tstate._factory = factory;\n\t}\n\n\treturn state._value;\n}\n\n/**\n * @param {() => void} callback\n * @param {unknown[]} args\n * @returns {() => void}\n */\nexport function useCallback(callback, args) {\n\tcurrentHook = 8;\n\treturn useMemo(() => callback, args);\n}\n\n/**\n * @param {import('./internal').PreactContext} context\n */\nexport function useContext(context) {\n\tconst provider = currentComponent.context[context._id];\n\t// We could skip this call here, but than we'd not call\n\t// `options._hook`. We need to do that in order to make\n\t// the devtools aware of this hook.\n\t/** @type {import('./internal').ContextHookState} */\n\tconst state = getHookState(currentIndex++, 9);\n\t// The devtools needs access to the context object to\n\t// be able to pull of the default value when no provider\n\t// is present in the tree.\n\tstate._context = context;\n\tif (!provider) return context._defaultValue;\n\t// This is probably not safe to convert to \"!\"\n\tif (state._value == null) {\n\t\tstate._value = true;\n\t\tprovider.sub(currentComponent);\n\t}\n\treturn provider.props.value;\n}\n\n/**\n * Display a custom label for a custom hook for the devtools panel\n * @type {<T>(value: T, cb?: (value: T) => string | number) => void}\n */\nexport function useDebugValue(value, formatter) {\n\tif (options.useDebugValue) {\n\t\toptions.useDebugValue(\n\t\t\tformatter ? formatter(value) : /** @type {any}*/ (value)\n\t\t);\n\t}\n}\n\n/**\n * @param {(error: unknown, errorInfo: import('preact').ErrorInfo) => void} cb\n * @returns {[unknown, () => void]}\n */\nexport function useErrorBoundary(cb) {\n\t/** @type {import('./internal').ErrorBoundaryHookState} */\n\tconst state = getHookState(currentIndex++, 10);\n\tconst errState = useState();\n\tstate._value = cb;\n\tif (!currentComponent.componentDidCatch) {\n\t\tcurrentComponent.componentDidCatch = (err, errorInfo) => {\n\t\t\tif (state._value) state._value(err, errorInfo);\n\t\t\terrState[1](err);\n\t\t};\n\t}\n\treturn [\n\t\terrState[0],\n\t\t() => {\n\t\t\terrState[1](undefined);\n\t\t}\n\t];\n}\n\n/** @type {() => string} */\nexport function useId() {\n\t/** @type {import('./internal').IdHookState} */\n\tconst state = getHookState(currentIndex++, 11);\n\tif (!state._value) {\n\t\t// Grab either the root node or the nearest async boundary node.\n\t\t/** @type {import('./internal').VNode} */\n\t\tlet root = currentComponent._vnode;\n\t\twhile (root !== null && !root._mask && root._parent !== null) {\n\t\t\troot = root._parent;\n\t\t}\n\n\t\tlet mask = root._mask || (root._mask = [0, 0]);\n\t\tstate._value = 'P' + mask[0] + '-' + mask[1]++;\n\t}\n\n\treturn state._value;\n}\n\n/**\n * After paint effects consumer.\n */\nfunction flushAfterPaintEffects() {\n\tlet component;\n\twhile ((component = afterPaintEffects.shift())) {\n\t\tconst hooks = component.__hooks;\n\t\tif (!component._parentDom || !hooks) continue;\n\t\ttry {\n\t\t\thooks._pendingEffects.some(invokeCleanup);\n\t\t\thooks._pendingEffects.some(invokeEffect);\n\t\t\thooks._pendingEffects = [];\n\t\t} catch (e) {\n\t\t\thooks._pendingEffects = [];\n\t\t\toptions._catchError(e, component._vnode);\n\t\t}\n\t}\n}\n\nlet HAS_RAF = typeof requestAnimationFrame == 'function';\n\n/**\n * Schedule a callback to be invoked after the browser has a chance to paint a new frame.\n * Do this by combining requestAnimationFrame (rAF) + setTimeout to invoke a callback after\n * the next browser frame.\n *\n * Also, schedule a timeout in parallel to the the rAF to ensure the callback is invoked\n * even if RAF doesn't fire (for example if the browser tab is not visible)\n *\n * @param {() => void} callback\n */\nfunction afterNextFrame(callback) {\n\tconst done = () => {\n\t\tclearTimeout(timeout);\n\t\tif (HAS_RAF) cancelAnimationFrame(raf);\n\t\tsetTimeout(callback);\n\t};\n\tconst timeout = setTimeout(done, RAF_TIMEOUT);\n\n\tlet raf;\n\tif (HAS_RAF) {\n\t\traf = requestAnimationFrame(done);\n\t}\n}\n\n// Note: if someone used options.debounceRendering = requestAnimationFrame,\n// then effects will ALWAYS run on the NEXT frame instead of the current one, incurring a ~16ms delay.\n// Perhaps this is not such a big deal.\n/**\n * Schedule afterPaintEffects flush after the browser paints\n * @param {number} newQueueLength\n * @returns {void}\n */\nfunction afterPaint(newQueueLength) {\n\tif (newQueueLength === 1 || prevRaf !== options.requestAnimationFrame) {\n\t\tprevRaf = options.requestAnimationFrame;\n\t\t(prevRaf || afterNextFrame)(flushAfterPaintEffects);\n\t}\n}\n\n/**\n * @param {import('./internal').HookState} hook\n * @returns {void}\n */\nfunction invokeCleanup(hook) {\n\t// A hook cleanup can introduce a call to render which creates a new root, this will call options.vnode\n\t// and move the currentComponent away.\n\tconst comp = currentComponent;\n\tlet cleanup = hook._cleanup;\n\tif (typeof cleanup == 'function') {\n\t\thook._cleanup = undefined;\n\t\tcleanup();\n\t}\n\n\tcurrentComponent = comp;\n}\n\n/**\n * Invoke a Hook's effect\n * @param {import('./internal').EffectHookState} hook\n * @returns {void}\n */\nfunction invokeEffect(hook) {\n\t// A hook call can introduce a call to render which creates a new root, this will call options.vnode\n\t// and move the currentComponent away.\n\tconst comp = currentComponent;\n\thook._cleanup = hook._value();\n\tcurrentComponent = comp;\n}\n\n/**\n * @param {unknown[]} oldArgs\n * @param {unknown[]} newArgs\n * @returns {boolean}\n */\nfunction argsChanged(oldArgs, newArgs) {\n\treturn (\n\t\t!oldArgs ||\n\t\toldArgs.length !== newArgs.length ||\n\t\tnewArgs.some((arg, index) => arg !== oldArgs[index])\n\t);\n}\n\n/**\n * @template Arg\n * @param {Arg} arg\n * @param {(arg: Arg) => any} f\n * @returns {any}\n */\nfunction invokeOrReturn(arg, f) {\n\treturn typeof f == 'function' ? f(arg) : f;\n}\n","/**\n * Bundled FR/EN strings for the QA Meter widget.\n *\n * Keys are the donor's `qaMeter.*` keys with the `qaMeter.` prefix stripped.\n * Values are copied verbatim from the donor locale files.\n *\n * Two new keys not present in the donor are appended at the end of each record:\n * - `panel.noTests`\n * - `panel.unknownPage`\n */\n\nexport const QA_STRINGS_FR = {\n 'pastille.label': 'Suivi des tests',\n 'pastille.loading': 'Chargement…',\n 'pastille.unavailable': 'Statuts indisponibles',\n 'pastille.unknownPage': 'Page inconnue',\n 'pastille.unknownPageHint': \"Cette route n’est pas encore au plan du site des tests.\",\n 'panel.freshness': 'Statuts g\\xe9n\\xe9r\\xe9s le {date} \\xb7 version {version} \\xb7 env {env}',\n 'panel.noScenarios': 'Aucun sc\\xe9nario li\\xe9 \\xe0 cette page.',\n 'panel.openSitemap': 'Voir le plan du site',\n 'panel.sitemapLink': 'Plan du site',\n 'panel.scenarioCountOne': '{count} sc\\xe9nario',\n 'panel.scenarioCountMany': '{count} sc\\xe9narios',\n 'panel.laneAutomated': 'Automatis\\xe9',\n 'panel.laneDev': 'D\\xe9v',\n 'panel.laneClient': 'Client',\n 'lane.notTested': 'Non test\\xe9',\n 'lane.validatedBy': 'Valid\\xe9 par {by}',\n 'lane.rejectedBy': 'Rejet\\xe9 par {by}',\n 'lane.stale': 'Valid\\xe9 en v{version}, version actuelle v{current}',\n 'lane.clientPending': 'Non test\\xe9 — plateforme requise',\n 'lane.bot': 'Automatis\\xe9 (bot)',\n 'lane.code': 'D\\xe9veloppeur (code)',\n 'lane.human': 'Humain',\n 'laneState.ok': '\\xc0 jour',\n 'laneState.issue': 'Probl\\xe8me',\n 'laneState.none': 'Non fait / p\\xe9rim\\xe9',\n 'color.green': 'Valid\\xe9',\n 'color.yellow': 'En attente',\n 'color.red': 'En \\xe9chec',\n 'color.gray': 'Non couvert',\n 'automated.passing': 'R\\xe9ussi',\n 'automated.failing': 'En \\xe9chec',\n 'automated.flaky': 'Instable',\n 'automated.neverRun': 'Jamais ex\\xe9cut\\xe9',\n 'source.testgen': 'g\\xe9n\\xe9r\\xe9',\n 'source.testgenHint': 'Sc\\xe9nario g\\xe9n\\xe9r\\xe9 automatiquement (testgen)',\n 'suite.cases': '{count} cas',\n 'case.covered': 'auto',\n 'case.manual': 'manuel',\n 'case.blocked': 'bloqu\\xe9',\n 'case.openQuestion': 'question produit',\n 'grounding.verified': 'attendu v\\xe9rifi\\xe9',\n 'grounding.confirmLive': '\\xe0 confirmer en live',\n 'grounding.productQuestion': 'd\\xe9cision produit',\n 'modal.title': 'Plan du site — suivi des tests',\n 'modal.close': 'Fermer',\n 'modal.tabPages': 'Pages',\n 'modal.tabScenarios': 'Sc\\xe9narios',\n 'modal.youAreHere': 'vous \\xeates ici',\n 'modal.search': 'Filtrer les sc\\xe9narios…',\n 'modal.noMatch': 'Aucun sc\\xe9nario ne correspond au filtre.',\n 'modal.traversed': 'Travers\\xe9es',\n 'modal.appliesTo': '{count} pages',\n 'modal.kpiPagesCovered': 'Pages avec ≥ 1 sc\\xe9nario',\n 'modal.kpiScenariosPassing': 'Sc\\xe9narios passants',\n // New keys (not in donor)\n 'panel.noTests': 'Aucun test ne couvre encore cette page.',\n 'panel.unknownPage': 'Page inconnue du registre QA.',\n} as const\n\nexport const QA_STRINGS_EN = {\n 'pastille.label': 'Test tracking',\n 'pastille.loading': 'Loading…',\n 'pastille.unavailable': 'Statuses unavailable',\n 'pastille.unknownPage': 'Unknown page',\n 'pastille.unknownPageHint': \"This route isn’t in the test sitemap yet.\",\n 'panel.freshness': 'Statuses generated on {date} \\xb7 version {version} \\xb7 env {env}',\n 'panel.noScenarios': 'No scenario linked to this page.',\n 'panel.openSitemap': 'View sitemap',\n 'panel.sitemapLink': 'Sitemap',\n 'panel.scenarioCountOne': '{count} scenario',\n 'panel.scenarioCountMany': '{count} scenarios',\n 'panel.laneAutomated': 'Automated',\n 'panel.laneDev': 'Dev',\n 'panel.laneClient': 'Client',\n 'lane.notTested': 'Not tested',\n 'lane.validatedBy': 'Validated by {by}',\n 'lane.rejectedBy': 'Rejected by {by}',\n 'lane.stale': 'Validated in v{version}, current version v{current}',\n 'lane.clientPending': 'Not tested — platform required',\n 'lane.bot': 'Automated (bot)',\n 'lane.code': 'Developer (code)',\n 'lane.human': 'Human',\n 'laneState.ok': 'Up to date',\n 'laneState.issue': 'Issue',\n 'laneState.none': 'Not done / stale',\n 'color.green': 'Validated',\n 'color.yellow': 'Pending',\n 'color.red': 'Failing',\n 'color.gray': 'Not covered',\n 'automated.passing': 'Passing',\n 'automated.failing': 'Failing',\n 'automated.flaky': 'Flaky',\n 'automated.neverRun': 'Never run',\n 'source.testgen': 'generated',\n 'source.testgenHint': 'Auto-generated scenario (testgen)',\n 'suite.cases': '{count} cases',\n 'case.covered': 'auto',\n 'case.manual': 'manual',\n 'case.blocked': 'blocked',\n 'case.openQuestion': 'product question',\n 'grounding.verified': 'expected verified',\n 'grounding.confirmLive': 'confirm live',\n 'grounding.productQuestion': 'product decision',\n 'modal.title': 'Sitemap — test tracking',\n 'modal.close': 'Close',\n 'modal.tabPages': 'Pages',\n 'modal.tabScenarios': 'Scenarios',\n 'modal.youAreHere': 'you are here',\n 'modal.search': 'Filter scenarios…',\n 'modal.noMatch': 'No scenario matches the filter.',\n 'modal.traversed': 'Traversed',\n 'modal.appliesTo': '{count} pages',\n 'modal.kpiPagesCovered': 'Pages with ≥ 1 scenario',\n 'modal.kpiScenariosPassing': 'Passing scenarios',\n // New keys (not in donor)\n 'panel.noTests': 'No tests cover this page yet.',\n 'panel.unknownPage': 'Page unknown to the QA registry.',\n} as const\n\nexport type QaStringKey = keyof typeof QA_STRINGS_FR\nexport type QaStrings = Record<QaStringKey, string>\n\nexport function resolveStrings(locale: 'fr' | 'en'): QaStrings {\n return locale === 'en' ? QA_STRINGS_EN : QA_STRINGS_FR\n}\n","/** Routerless page resolution: match window.location.pathname against the\n * artifact's path_patterns. Most-specific match wins (static segments beat\n * params; longer beats shorter). The host can override with a router-aware\n * getCurrentPage — this is only the default. */\n\nexport function normalizePattern(pattern: string | undefined | null): string {\n if (!pattern) return '/'\n let n = pattern.startsWith('/') ? pattern : `/${pattern}`\n n = n.replace(/\\/{2,}/g, '/')\n if (n.length > 1) n = n.replace(/\\/+$/, '')\n return n.length > 0 ? n : '/'\n}\n\nconst segments = (p: string): string[] => p.split('/').filter(Boolean)\n\nfunction matchesPattern(pathSegs: string[], patternSegs: string[]): boolean {\n let required = patternSegs.length\n while (required > 0) {\n const last = patternSegs[required - 1]\n if (last !== undefined && last.startsWith(':') && last.endsWith('?')) required--\n else break\n }\n if (pathSegs.length < required || pathSegs.length > patternSegs.length) return false\n return pathSegs.every((seg, i) => {\n const pat = patternSegs[i]\n return pat !== undefined && (pat.startsWith(':') || pat === seg)\n })\n}\n\nexport function resolvePage(pathname: string, patterns: ReadonlyArray<string>): string | null {\n const pathSegs = segments(normalizePattern(pathname))\n let best: string | null = null\n let bestScore = -1\n for (const pattern of patterns) {\n const patSegs = segments(pattern)\n if (!matchesPattern(pathSegs, patSegs)) continue\n const statics = patSegs.filter((s) => !s.startsWith(':')).length\n const score = statics * 100 + patSegs.length\n if (score > bestScore) { best = pattern; bestScore = score }\n }\n return best\n}\n","/**\n * QA Meter CSS styles — a `:host` block with CSS variables prefixed `--mqa-`.\n *\n * The QA Meter z-index is set one below the feedback widget's `2147483640`\n * so the feedback FAB wins overlaps.\n *\n * Color tokens (hardcoded to remove dependency on DaisyUI HSL variables):\n * green #16a34a\n * yellow #eab308\n * red #dc2626\n * gray #9ca3af\n *\n * Selectors ported from qa-pastille.vue, qa-test-panel.vue, qa-lane-icons.vue,\n * and qa-sitemap-modal.vue — adapted for standalone Shadow DOM use.\n */\nexport const QA_METER_STYLES = `\n:host {\n --mqa-z-index: 2147483639;\n --mqa-z-panel: 2147483638;\n\n /* Color tokens */\n --mqa-green: #16a34a;\n --mqa-yellow: #eab308;\n --mqa-red: #dc2626;\n --mqa-gray: #9ca3af;\n\n /* Surface */\n --mqa-bg: #ffffff;\n --mqa-surface: #f9fafb;\n --mqa-border: #e5e7eb;\n --mqa-text: #111827;\n --mqa-text-muted: #6b7280;\n --mqa-primary: #3b82f6;\n --mqa-shadow: 0 4px 12px rgba(0,0,0,0.08), 0 2px 4px rgba(0,0,0,0.12);\n --mqa-shadow-xl: 0 24px 56px rgba(0,0,0,0.18), 0 8px 20px rgba(0,0,0,0.10);\n\n /* Font */\n --mqa-font: system-ui, -apple-system, sans-serif;\n --mqa-radius: 8px;\n\n all: initial;\n font-family: var(--mqa-font);\n color: var(--mqa-text);\n position: fixed;\n z-index: var(--mqa-z-index);\n}\n\n@media (prefers-color-scheme: dark) {\n :host {\n --mqa-bg: #1e293b;\n --mqa-surface: #0f172a;\n --mqa-border: #334155;\n --mqa-text: #f8fafc;\n --mqa-text-muted: #94a3b8;\n --mqa-shadow-xl: 0 24px 56px rgba(0,0,0,0.55), 0 8px 20px rgba(0,0,0,0.40);\n }\n}\n\n/* ── Semaphore modifier classes ─────────────────────────────────────────── */\n\n.qa--green { background: var(--mqa-green); }\n.qa--yellow { background: var(--mqa-yellow); }\n.qa--red { background: var(--mqa-red); }\n.qa--gray { background: var(--mqa-gray); }\n\n/* Priority modifiers (color only — components set sizing). */\n.qa-prio--p0 { color: var(--mqa-red); }\n.qa-prio--p1 { color: var(--mqa-yellow); }\n.qa-prio--p2 { color: var(--mqa-green); }\n.qa-prio--p3 { color: var(--mqa-gray); }\n\n/* ── Pastille (FAB circle) ──────────────────────────────────────────────── */\n\n/* The wrap is the fixed, bottom-right anchor for the whole pastille cluster.\n It MUST be a positioning context: the hover panel .qa-panel is\n position:absolute and is a SIBLING of the pastille, so without a positioned\n wrap it would anchor to :host (fixed, no offsets — i.e. the top-left of the\n screen) instead of sitting above the FAB. The --mqa-right/--mqa-bottom knobs\n (set on the host, inherited here) keep driving the on-screen position. The\n fixed sitemap modal inside the wrap anchors to the viewport regardless, so\n it's unaffected. */\n.qa-pastille-wrap {\n position: fixed;\n right: var(--mqa-right, 1.5rem);\n bottom: var(--mqa-bottom, 1.5rem);\n z-index: var(--mqa-z-index);\n}\n\n.qa-pastille {\n position: relative;\n}\n\n.qa-pastille__dot {\n width: 48px;\n height: 48px;\n border-radius: 9999px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n box-shadow: var(--mqa-shadow);\n color: rgba(0, 0, 0, 0.5);\n /* No idle animation — the pastille stays static until interacted with, like\n the feedback FAB. (Previously pulsed on a perpetual keyframe loop.) Only\n the hover transition below animates. */\n transition: transform 0.15s ease, box-shadow 0.15s ease;\n border: none;\n}\n\n.qa-pastille__dot:hover {\n transform: scale(1.12);\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.3);\n}\n\n.qa-pastille__ecg {\n width: 30px;\n height: 15px;\n}\n\n/* Secondary size: half-scale pastille for the integrated second-FAB use. */\n:host([data-mqa-size=\"sm\"]) .qa-pastille__dot { width: 24px; height: 24px; }\n:host([data-mqa-size=\"sm\"]) .qa-pastille__ecg { width: 15px; height: 8px; }\n\n/* Color overrides: on colored backgrounds the ECG trace needs a dark stroke. */\n.qa-pastille__dot.qa--green,\n.qa-pastille__dot.qa--yellow,\n.qa-pastille__dot.qa--red {\n color: rgba(0, 0, 0, 0.6);\n}\n\n.qa-pastille__dot.qa--gray {\n background: rgba(156, 163, 175, 0.35);\n color: rgba(0, 0, 0, 0.45);\n}\n\n.qa-fade-enter-active,\n.qa-fade-leave-active {\n transition: opacity 0.15s ease, transform 0.15s ease;\n}\n.qa-fade-enter-from,\n.qa-fade-leave-to {\n opacity: 0;\n transform: translateY(4px);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .qa-pastille__dot { transition: none; }\n}\n\n/* ── Test panel ─────────────────────────────────────────────────────────── */\n\n/* The hover panel anchors above the pastille inside the fixed\n .qa-pastille-wrap. The ANCHOR (not the panel) is what's taken out of flow:\n it MUST be absolute so the wrap shrink-wraps to the pastille dot — an in-flow\n panel would stretch the wrap to 340px and either push the dot aside or (on a\n tall, scrolled host page) drive a scrollbar-reflow feedback loop that makes\n the FAB jump. (The donor's positioning lived on an orphaned\n .qa-pastille__panel selector that matched nothing after the Vue→Preact port\n renamed the root to .qa-panel.)\n\n The anchor's padding-bottom renders the 0.5rem visual gap between the dot\n and the panel AS PART of the hover region, so sweeping the cursor from the\n dot up onto the panel never crosses dead space → never fires the wrap's\n mouseleave → no flicker. (Paired with a small close-delay in mount.tsx.) */\n.qa-panel-anchor {\n position: absolute;\n bottom: 100%;\n right: 0;\n padding-bottom: 0.5rem;\n z-index: var(--mqa-z-panel);\n}\n\n.qa-panel {\n background: var(--mqa-bg);\n border-radius: var(--mqa-radius);\n border: 1px solid var(--mqa-border);\n box-shadow: var(--mqa-shadow-xl);\n width: 340px;\n max-height: 60vh;\n overflow: hidden auto;\n font-size: 0.8125rem;\n color: var(--mqa-text);\n}\n\n.qa-panel__empty {\n padding: 0.75rem;\n opacity: 0.7;\n}\n\n.qa-panel__list,\n.qa-panel__groups {\n margin: 0;\n padding: 0.25rem 0;\n list-style: none;\n}\n\n.qa-panel__foot {\n border-top: 1px solid var(--mqa-border);\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.5rem;\n padding: 0.5rem 0.75rem;\n position: sticky;\n bottom: 0;\n background: var(--mqa-bg);\n}\n\n.qa-panel__count {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n font-size: 0.6875rem;\n opacity: 0.65;\n}\n\n.qa-panel__link {\n flex: none;\n font-weight: 600;\n cursor: pointer;\n white-space: nowrap;\n color: var(--mqa-primary);\n background: none;\n border: none;\n font: inherit;\n}\n\n.qa-panel__link:hover { text-decoration: underline; }\n\n/* ── Suite groups ───────────────────────────────────────────────────────── */\n\n.qa-suite + .qa-suite { border-top: 1px solid var(--mqa-border); }\n\n.qa-suite__head {\n width: 100%;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.5rem 0.75rem;\n cursor: pointer;\n text-align: left;\n background: none;\n border: none;\n font: inherit;\n color: inherit;\n}\n\n.qa-suite__head:hover { background: var(--mqa-surface); }\n\n.qa-suite__chev {\n flex: none;\n font-size: 0.625rem;\n opacity: 0.6;\n transition: transform 0.12s ease;\n}\n\n.qa-suite__chev.is-open { transform: rotate(90deg); }\n\n.qa-suite__key {\n flex: none;\n width: 1.25rem;\n height: 1.25rem;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-size: 0.6875rem;\n font-weight: 700;\n background: var(--mqa-surface);\n border-radius: 4px;\n}\n\n.qa-suite__label {\n flex: 1;\n min-width: 0;\n font-weight: 600;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.qa-suite__count {\n flex: none;\n font-size: 0.6875rem;\n opacity: 0.55;\n}\n\n.qa-suite__list {\n background: var(--mqa-surface);\n margin: 0;\n padding: 0 0 0.25rem;\n list-style: none;\n}\n\n/* ── Scenario rows ──────────────────────────────────────────────────────── */\n\n.qa-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.75rem;\n padding: 0.5rem 0.75rem;\n}\n\n.qa-row + .qa-row { border-top: 1px solid var(--mqa-border); }\n\n.qa-row__title {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n min-width: 0;\n}\n\n.qa-row__name {\n flex: 1;\n min-width: 0;\n font-weight: 600;\n line-height: 1.25;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.qa-suite__list .qa-row { padding-left: 1.5rem; }\n\n.qa-tag {\n flex: none;\n font-size: 0.625rem;\n padding: 0 0.25rem;\n text-transform: uppercase;\n letter-spacing: 0.02em;\n opacity: 0.8;\n background: var(--mqa-surface);\n border-radius: 9999px;\n}\n\n/* ── Lane icons ─────────────────────────────────────────────────────────── */\n\n.qa-lanes {\n display: inline-flex;\n align-items: center;\n gap: 0.3125rem;\n flex: none;\n padding: 0.1875rem 0.4375rem;\n border-radius: 9999px;\n background: rgba(17, 24, 39, 0.06);\n}\n\n.qa-lanes--sm { font-size: 0.875rem; }\n.qa-lanes--md { font-size: 1rem; }\n\n.qa-lanes__icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: rgba(17, 24, 39, 0.4);\n}\n\n.qa-lanes__svg {\n width: 1em;\n height: 1em;\n display: block;\n}\n\n.qa-lanes__icon.qa--green { color: var(--mqa-green); background: none; }\n.qa-lanes__icon.qa--yellow { color: var(--mqa-yellow); background: none; }\n.qa-lanes__icon.qa--gray { color: rgba(17, 24, 39, 0.4); background: none; }\n\n/* ── Sitemap modal backdrop ─────────────────────────────────────────────── */\n\n.qa-modal__backdrop {\n background-color: rgba(17, 24, 39, 0.4);\n backdrop-filter: blur(1px);\n position: fixed;\n inset: 0;\n z-index: var(--mqa-z-index);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 1rem;\n}\n\n/* ── Sitemap modal ──────────────────────────────────────────────────────── */\n\n.qa-modal {\n background: var(--mqa-bg);\n border-radius: var(--mqa-radius);\n box-shadow: var(--mqa-shadow-xl);\n border: 1px solid var(--mqa-border);\n width: min(720px, 92vw);\n max-height: 86vh;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n color: var(--mqa-text);\n}\n\n.qa-modal__head {\n background: var(--mqa-primary);\n color: #ffffff;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.5rem 0.75rem;\n}\n\n.qa-modal__head h3 {\n font-weight: 700;\n font-size: 1rem;\n margin: 0;\n}\n\n.qa-modal__close {\n border: 1px solid rgba(255, 255, 255, 0.3);\n background: transparent;\n color: #ffffff;\n width: 24px;\n height: 24px;\n padding: 0;\n border-radius: var(--mqa-radius);\n cursor: pointer;\n display: grid;\n place-items: center;\n}\n\n.qa-modal__close:hover { background: var(--mqa-red); }\n\n.qa-modal__freshness {\n background: var(--mqa-surface);\n padding: 0.375rem 0.75rem;\n font-size: 0.6875rem;\n opacity: 0.8;\n}\n\n.qa-modal__body {\n overflow: auto;\n padding: 0.5rem 0;\n}\n\n/* ── KPI strip ──────────────────────────────────────────────────────────── */\n\n.qa-kpi {\n display: flex;\n gap: 1.5rem;\n padding: 0.625rem 0.75rem;\n}\n\n.qa-kpi__item {\n display: flex;\n flex-direction: column;\n}\n\n.qa-kpi__value {\n font-size: 1.25rem;\n font-weight: 700;\n line-height: 1;\n}\n\n.qa-kpi__label {\n font-size: 0.6875rem;\n opacity: 0.7;\n}\n\n/* ── Tabs ───────────────────────────────────────────────────────────────── */\n\n.qa-tabs {\n border-bottom: 1px solid var(--mqa-border);\n display: flex;\n gap: 0.25rem;\n padding: 0 0.5rem;\n}\n\n.qa-tabs__tab {\n padding: 0.375rem 0.75rem;\n font-weight: 600;\n font-size: 0.8125rem;\n border-bottom: 2px solid transparent;\n cursor: pointer;\n opacity: 0.6;\n background: none;\n border-left: none;\n border-right: none;\n border-top: none;\n font: inherit;\n color: inherit;\n}\n\n.qa-tabs__tab--active {\n color: var(--mqa-primary);\n border-bottom-color: var(--mqa-primary);\n opacity: 1;\n}\n\n/* ── Sitemap page tree ──────────────────────────────────────────────────── */\n\n.qa-tree {\n margin: 0;\n padding: 0;\n list-style: none;\n font-size: 0.8125rem;\n}\n\n.qa-tree__row {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n padding-top: 0.25rem;\n padding-bottom: 0.25rem;\n padding-right: 0.75rem;\n}\n\n.qa-tree__row--here { background: var(--mqa-surface); }\n\n.qa-tree__caret {\n width: 1rem;\n text-align: center;\n cursor: pointer;\n opacity: 0.6;\n font-size: 0.6875rem;\n background: none;\n border: none;\n font: inherit;\n color: inherit;\n}\n\n.qa-tree__caret--leaf { cursor: default; }\n\n.qa-tree__title { flex: 1; }\n.qa-tree__title--structural { font-style: italic; opacity: 0.75; }\n\n.qa-tree__here {\n color: var(--mqa-primary);\n font-size: 0.625rem;\n font-weight: 700;\n text-transform: uppercase;\n}\n\n.qa-tree__count {\n background: var(--mqa-surface);\n border-radius: 9999px;\n font-size: 0.6875rem;\n padding: 0 0.375rem;\n min-width: 1.25rem;\n text-align: center;\n}\n\n/* ── Scenarios tab ──────────────────────────────────────────────────────── */\n\n.qa-scenarios { padding: 0 0.75rem; }\n\n.qa-scenarios__search {\n border: 1px solid var(--mqa-border);\n border-radius: var(--mqa-radius);\n width: 100%;\n height: 2rem;\n padding: 0 0.75rem;\n font-size: 0.8125rem;\n margin-bottom: 0.5rem;\n background: var(--mqa-bg);\n color: var(--mqa-text);\n font-family: inherit;\n box-sizing: border-box;\n}\n\n.qa-scenarios__empty {\n opacity: 0.7;\n padding: 0.5rem 0;\n}\n\n.qa-scenarios__list {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.qa-scenarios__item {\n border-top: 1px solid var(--mqa-border);\n padding: 0.5rem 0;\n}\n\n.qa-scenarios__line {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n}\n\n.qa-scenarios__title {\n flex: 1;\n font-weight: 600;\n font-size: 0.8125rem;\n}\n\n.qa-scenarios__pages {\n display: flex;\n flex-wrap: wrap;\n gap: 0.75rem;\n font-size: 0.6875rem;\n opacity: 0.7;\n margin-top: 0.125rem;\n padding-left: 1rem;\n}\n\n/* ── Scenario dot (color indicator) ────────────────────────────────────── */\n\n.qa-dot {\n width: 0.625rem;\n height: 0.625rem;\n border-radius: 9999px;\n flex: none;\n background: rgba(17, 24, 39, 0.6);\n}\n\n/* Override modifier background for dot — dot uses background not text color. */\n.qa-dot.qa--green { background: var(--mqa-green); }\n.qa-dot.qa--yellow { background: var(--mqa-yellow); }\n.qa-dot.qa--red { background: var(--mqa-red); }\n.qa-dot.qa--gray { background: var(--mqa-gray); }\n\n/* ── Suite key badge ────────────────────────────────────────────────────── */\n\n.qa-suitekey {\n flex: none;\n font-size: 0.625rem;\n font-weight: 700;\n padding: 0 0.3125rem;\n line-height: 1.4;\n background: var(--mqa-surface);\n border-radius: 4px;\n}\n`\n","/**\n * Helpers de PRÉSENTATION du QA Meter (aucune logique de statut).\n *\n * La couleur d'un scénario / d'une page est calculée à la construction de\n * l'artefact (doc 03 §9.3) et arrive déjà décidée. Ici on ne fait que la\n * traduire en classe CSS + clé i18n, et formater les dates/versions.\n *\n * Ported from the donor `qa-meter.ui.ts`; type imports point to ./types,\n * i18n keys are unprefixed (no `qaMeter.` prefix), Vue-specific imports dropped.\n */\nimport type {\n QaAutomatedLane,\n QaAutomatedStatus,\n QaCaseStatus,\n QaColor,\n QaGrounding,\n QaHumanLane,\n QaPriority,\n QaTriColor,\n} from './types';\nimport type { QaStringKey } from './i18n';\n\n/** Classe modificatrice BEM appliquée aux pastilles/puces (cf. styles des composants). */\nexport const QA_COLOR_MODIFIER: Record<QaColor, string> = {\n green: 'qa--green',\n yellow: 'qa--yellow',\n red: 'qa--red',\n gray: 'qa--gray',\n};\n\n/** Clé i18n du libellé d'une couleur (référencée dynamiquement → présente dans les deux locales). */\nexport const QA_COLOR_LABEL_KEY: Record<QaColor, QaStringKey> = {\n green: 'color.green',\n yellow: 'color.yellow',\n red: 'color.red',\n gray: 'color.gray',\n};\n\n/** Clé i18n du libellé d'un statut automated. */\nexport const QA_AUTOMATED_LABEL_KEY: Record<QaAutomatedStatus, QaStringKey> = {\n passing: 'automated.passing',\n failing: 'automated.failing',\n flaky: 'automated.flaky',\n never_run: 'automated.neverRun',\n};\n\n/** Modificateur sémaphore d'un volet automated (le volet, pas le scénario). */\nexport const QA_AUTOMATED_MODIFIER: Record<QaAutomatedStatus, string> = {\n passing: 'qa--green',\n failing: 'qa--red',\n flaky: 'qa--yellow',\n never_run: 'qa--gray',\n};\n\n/**\n * Modificateur sémaphore d'un volet humain (dev/client) :\n * - `rejected` → rouge ; non validé → gris ; validé mais périmé/contredit → jaune ; validé → vert.\n */\nexport function humanLaneModifier(lane: { validated: boolean; rejected?: boolean; stale: boolean }): string {\n if (lane.rejected) {\n return 'qa--red';\n }\n if (!lane.validated) {\n return 'qa--gray';\n }\n return lane.stale ? 'qa--yellow' : 'qa--green';\n}\n\n/** Modificateur d'une puce de priorité (P0 = critique → rouge ; décroît vers neutre). */\nexport const QA_PRIORITY_MODIFIER: Record<QaPriority, string> = {\n P0: 'qa-prio--p0',\n P1: 'qa-prio--p1',\n P2: 'qa-prio--p2',\n P3: 'qa-prio--p3',\n};\n\n/** Clé i18n du tag de statut d'un cas (couvert / manuel / bloqué / question produit). */\nexport const QA_CASE_STATUS_LABEL_KEY: Record<QaCaseStatus, QaStringKey> = {\n covered: 'case.covered',\n manual: 'case.manual',\n blocked: 'case.blocked',\n 'open-question': 'case.openQuestion',\n};\n\n/** Clé i18n de l'ancrage de l'attendu (vérifié / à confirmer / décision produit). */\nexport const QA_GROUNDING_LABEL_KEY: Record<QaGrounding, QaStringKey> = {\n verified: 'grounding.verified',\n 'confirm-live': 'grounding.confirmLive',\n 'product-question': 'grounding.productQuestion',\n};\n\n// ── Voies code/bot/human (3 icônes indépendantes, couleur 3-états) ───────────\n\n/** Voie « bot » (exécution automatisée) : passe → vert ; échec/instable → jaune ; jamais exécuté → gris. */\nexport function botTriColor(lane: QaAutomatedLane): QaTriColor {\n if (lane.status === 'passing') {\n return 'green';\n }\n if (lane.status === 'failing' || lane.status === 'flaky') {\n return 'yellow';\n }\n return 'gray';\n}\n\n/** Voie humaine (« code » = dev, « human » = client) : validé & frais → vert ; rejeté → jaune ; non validé/périmé → gris. */\nexport function humanTriColor(lane: QaHumanLane): QaTriColor {\n if (lane.rejected) {\n return 'yellow';\n }\n if (lane.validated) {\n return lane.stale ? 'gray' : 'green';\n }\n return 'gray';\n}\n\n/** Agrège une voie sur un ensemble de cas : un jaune → jaune ; sinon tout vert → vert ; sinon gris. */\nexport function aggregateTri(colors: ReadonlyArray<QaTriColor>): QaTriColor {\n if (colors.some((c) => c === 'yellow')) {\n return 'yellow';\n }\n if (colors.length > 0 && colors.every((c) => c === 'green')) {\n return 'green';\n }\n return 'gray';\n}\n\n/** Rang de gravité d'une couleur, pour résumer un groupe par son « pire » état. */\nconst QA_COLOR_RANK: Record<QaColor, number> = { red: 3, yellow: 2, green: 1, gray: 0 };\n\n/** Couleur résumé d'un groupe : rouge domine ; sinon jaune ; sinon vert ; sinon gris. */\nexport function worstColor(colors: ReadonlyArray<QaColor>): QaColor {\n return colors.reduce<QaColor>((acc, c) => (QA_COLOR_RANK[c] > QA_COLOR_RANK[acc] ? c : acc), 'gray');\n}\n\n/** Priorité la plus haute (P0 < P1 < P2 < P3) parmi une liste, ou `undefined`. */\nexport function topPriority(priorities: ReadonlyArray<QaPriority | undefined>): QaPriority | undefined {\n return priorities.filter((p): p is QaPriority => Boolean(p)).sort()[0];\n}\n\n/**\n * Formate une estampille ISO en date+heure locale courte (fr-CA → AAAA-MM-JJ HH:MM).\n * Retourne `''` pour une valeur absente/invalide (l'appelant masque alors le segment).\n */\nexport function formatQaDate(iso: string | undefined | null): string {\n if (!iso) {\n return '';\n }\n const date = new Date(iso);\n if (Number.isNaN(date.getTime())) {\n return '';\n }\n return new Intl.DateTimeFormat('fr-CA', {\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n }).format(date);\n}\n\n/**\n * Version d'app = chaîne opaque (doc 03 §1.1, jamais parsée). Tronquée pour\n * l'affichage : un timestamp de build complet est illisible dans une puce.\n */\nexport function shortVersion(version: string | undefined | null): string {\n if (!version) {\n return '';\n }\n return version.length > 19 ? `${version.slice(0, 19)}…` : version;\n}\n","import { h } from 'preact'\n\nimport type { QaColor } from './types'\nimport type { QaStrings } from './i18n'\nimport { QA_COLOR_LABEL_KEY, QA_COLOR_MODIFIER } from './ui'\n\n/**\n * The heartbeat/ECG pastille — purely presentational. It renders the page color\n * as a semaphore modifier and emits hover/open callbacks; it owns NO state, NO\n * router, NO store. State (current page, fetched artifact) lives in the mount\n * container task that wraps this.\n *\n * Heartbeat/ECG SVG copied verbatim from the donor `qa-pastille.vue`.\n */\nexport interface PastilleProps {\n color: QaColor\n state: 'known' | 'no-tests' | 'unknown-page'\n strings: QaStrings\n onOpenModal: () => void\n onHover: () => void\n}\n\nexport function Pastille({ color, state, strings, onOpenModal, onHover }: PastilleProps) {\n // The color → class mapping is the same for every state; only the title text\n // differs slightly so a hover hint can explain the no-tests / unknown-page cases.\n const base = strings['pastille.label']\n let detail = strings[QA_COLOR_LABEL_KEY[color]]\n if (state === 'no-tests') {\n detail = strings['panel.noTests']\n } else if (state === 'unknown-page') {\n detail = strings['pastille.unknownPage']\n }\n const label = `${base} — ${detail}`\n\n // Mirror the donor's two-element structure: `.qa-pastille` is the fixed-position\n // CONTAINER; `.qa-pastille__dot` carries the size/background/animation (the\n // `.qa-pastille__dot.qa--*` rules in styles.ts are where color resolves).\n return h(\n 'div',\n { class: 'qa-pastille' },\n h(\n 'button',\n {\n type: 'button',\n 'data-testid': 'qa-pastille',\n class: `qa-pastille__dot ${QA_COLOR_MODIFIER[color]}`,\n 'aria-label': label,\n title: label,\n onClick: onOpenModal,\n onMouseEnter: onHover,\n onFocus: onHover,\n },\n h(\n 'svg',\n { class: 'qa-pastille__ecg', viewBox: '0 0 24 12', 'aria-hidden': 'true' },\n h('polyline', {\n points: '1,6 7,6 9,2 12,10 15,4 17,6 23,6',\n fill: 'none',\n stroke: 'currentColor',\n 'stroke-width': '1.5',\n 'stroke-linejoin': 'round',\n 'stroke-linecap': 'round',\n }),\n ),\n ),\n )\n}\n","import { h } from 'preact'\n\nimport type { QaTriColor } from './types'\nimport type { QaStrings } from './i18n'\n\n/**\n * A row of three independent lane chips for a scenario (or a suite aggregate):\n * - dev (`code`) = developer validation (the `</>` chevrons);\n * - bot (`bot`) = automated execution (Playwright);\n * - client (`human`) = human/client validation.\n *\n * Each chip carries its OWN tri-color (green/yellow/gray) — they are independent:\n * a human may have validated while the bot never ran, etc. Pure presentational:\n * no hooks, no state, no fetching.\n *\n * SVG path data copied verbatim from the donor `qa-lane-icons.vue`.\n */\nexport interface LaneIconsProps {\n dev: QaTriColor\n bot: QaTriColor\n human: QaTriColor\n strings: QaStrings\n}\n\n/** Tri-color → BEM modifier (donor mapping: `qa--green|qa--yellow|qa--gray`). */\nconst TRI_MODIFIER: Record<QaTriColor, string> = {\n green: 'qa--green',\n yellow: 'qa--yellow',\n gray: 'qa--gray',\n}\n\nexport function LaneIcons({ dev, bot, human, strings }: LaneIconsProps) {\n return h(\n 'span',\n { class: 'qa-lanes', 'data-testid': 'qa-lane-icons' },\n // dev — chevrons </>\n h(\n 'span',\n {\n class: `qa-lanes__icon qa-lane ${TRI_MODIFIER[dev]}`,\n title: strings['lane.code'],\n 'aria-label': strings['lane.code'],\n 'data-testid': 'qa-lane-dev',\n 'data-state': dev,\n },\n h(\n 'svg',\n { class: 'qa-lanes__svg', viewBox: '0 0 24 24', 'aria-hidden': 'true' },\n h('path', {\n d: 'M8 7l-5 5 5 5M16 7l5 5-5 5',\n fill: 'none',\n stroke: 'currentColor',\n 'stroke-width': '2.2',\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n }),\n ),\n ),\n // bot — robot head\n h(\n 'span',\n {\n class: `qa-lanes__icon qa-lane ${TRI_MODIFIER[bot]}`,\n title: strings['lane.bot'],\n 'aria-label': strings['lane.bot'],\n 'data-testid': 'qa-lane-bot',\n 'data-state': bot,\n },\n h(\n 'svg',\n {\n class: 'qa-lanes__svg',\n viewBox: '0 0 24 24',\n 'aria-hidden': 'true',\n fill: 'none',\n stroke: 'currentColor',\n 'stroke-width': '2',\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n },\n h('rect', { x: '4.5', y: '8.5', width: '15', height: '10.5', rx: '2.5' }),\n h('path', { d: 'M12 4.5v4' }),\n h('circle', { cx: '12', cy: '3.4', r: '1.3', fill: 'currentColor', stroke: 'none' }),\n h('path', { d: 'M2.5 12.5v3M21.5 12.5v3' }),\n h('circle', { cx: '9', cy: '13.5', r: '1.2', fill: 'currentColor', stroke: 'none' }),\n h('circle', { cx: '15', cy: '13.5', r: '1.2', fill: 'currentColor', stroke: 'none' }),\n ),\n ),\n // client — person\n h(\n 'span',\n {\n class: `qa-lanes__icon qa-lane ${TRI_MODIFIER[human]}`,\n title: strings['lane.human'],\n 'aria-label': strings['lane.human'],\n 'data-testid': 'qa-lane-client',\n 'data-state': human,\n },\n h(\n 'svg',\n {\n class: 'qa-lanes__svg',\n viewBox: '0 0 24 24',\n 'aria-hidden': 'true',\n fill: 'none',\n stroke: 'currentColor',\n 'stroke-width': '2',\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n },\n h('circle', { cx: '12', cy: '8', r: '3.6' }),\n h('path', { d: 'M5 20c0-3.6 3.1-6 7-6s7 2.4 7 6' }),\n ),\n ),\n )\n}\n","import { h } from 'preact'\nimport { useState } from 'preact/hooks'\n\nimport type { QaPageStatus, QaScenario, QaTriColor } from './types'\nimport type { QaStrings } from './i18n'\nimport { LaneIcons } from './LaneIcons'\nimport {\n aggregateTri,\n botTriColor,\n formatQaDate,\n humanTriColor,\n QA_CASE_STATUS_LABEL_KEY,\n QA_GROUNDING_LABEL_KEY,\n QA_PRIORITY_MODIFIER,\n shortVersion,\n} from './ui'\n\n/**\n * Preact port of the donor `qa-test-panel.vue`.\n *\n * Presentational (no data-fetching, router, or store). The container task feeds\n * it the resolved page-status, a `state` discriminator (so the two new empty\n * states can render even when `page` is null), and the run metadata for the\n * freshness banner. Local UI state (which suite groups are expanded) lives in a\n * `useState` hook — matching the donor's `expanded`/`toggleGroup` interaction.\n *\n * The donor's `<script setup>` computed properties (lane tri-color derivation and\n * suite grouping) are extracted here as the pure exported helpers `laneTriColor`\n * and `groupScenarios` so they're unit-testable and reusable by the mount task.\n */\nexport interface TestPanelProps {\n page: QaPageStatus | null\n state: 'known' | 'no-tests' | 'unknown-page'\n meta: { generated_at: string; app_version: string; environment: string }\n strings: QaStrings\n}\n\n/** The three independent lane tri-colors of a scenario — single source of the\n * dev/bot/human derivation (donor `lanesFor`). The client lane with no event is\n * gray (never green), because `humanTriColor` returns gray for an unvalidated lane. */\nexport interface ScenarioLanes {\n dev: QaTriColor\n bot: QaTriColor\n human: QaTriColor\n}\n\nexport function laneTriColor(scenario: QaScenario): ScenarioLanes {\n return {\n dev: humanTriColor(scenario.dev),\n bot: botTriColor(scenario.automated),\n human: humanTriColor(scenario.client),\n }\n}\n\nexport interface ScenarioGroupView {\n key: string\n label: string\n scenarios: QaScenario[]\n}\n\n/** Group scenarios by `scenario.group.key` (donor `grouped`). Scenarios with no\n * group fall into a `'·'` bucket. Keys are alphabetical with the inter-module\n * `X` suite forced last, matching the donor ordering. */\nexport function groupScenarios(scenarios: ReadonlyArray<QaScenario>): ScenarioGroupView[] {\n const map = new Map<string, QaScenario[]>()\n for (const scenario of scenarios) {\n const key = scenario.group?.key ?? '·'\n const bucket = map.get(key) ?? []\n bucket.push(scenario)\n map.set(key, bucket)\n }\n const keys = [...map.keys()].sort((a, b) => (a === 'X' ? 1 : b === 'X' ? -1 : a.localeCompare(b)))\n return keys.map((key) => {\n const bucket = map.get(key)!\n return { key, label: bucket[0]?.group?.label ?? '', scenarios: bucket }\n })\n}\n\nfunction interpolate(template: string, params: Record<string, string | number>): string {\n return template.replace(/\\{(\\w+)\\}/g, (_, k: string) => String(params[k] ?? ''))\n}\n\n/** One scenario row: title, priority chip, provenance + tags, and the three lanes. */\nfunction ScenarioRow({ scenario, strings }: { scenario: QaScenario; strings: QaStrings }) {\n const lanes = laneTriColor(scenario)\n const provenance = scenario.automated.provenance\n return h(\n 'li',\n { class: 'qa-row', 'data-testid': 'qa-scenario-row' },\n h(\n 'span',\n { class: 'qa-row__title' },\n scenario.priority\n ? h(\n 'span',\n { class: `qa-tag ${QA_PRIORITY_MODIFIER[scenario.priority]}`, 'data-testid': 'qa-priority' },\n scenario.priority,\n )\n : null,\n h('span', { class: 'qa-row__name' }, scenario.title),\n scenario.source === 'testgen'\n ? h('span', { class: 'qa-tag', title: strings['source.testgenHint'] }, strings['source.testgen'])\n : null,\n scenario.caseStatus\n ? h('span', { class: 'qa-tag' }, strings[QA_CASE_STATUS_LABEL_KEY[scenario.caseStatus]])\n : null,\n scenario.grounding\n ? h('span', { class: 'qa-tag' }, strings[QA_GROUNDING_LABEL_KEY[scenario.grounding]])\n : null,\n provenance ? h('span', { class: 'qa-tag', 'data-testid': 'qa-provenance' }, provenance) : null,\n ),\n h(LaneIcons, { dev: lanes.dev, bot: lanes.bot, human: lanes.human, strings }),\n )\n}\n\nexport function TestPanel({ page, state, meta, strings }: TestPanelProps) {\n // Local UI state: which suite-group keys are expanded. Matches the donor's\n // `expanded` ref, which starts empty → all groups COLLAPSED by default (so a\n // heavily-loaded page folds into a few theme headers, per QaScenarioGroup).\n const [expanded, setExpanded] = useState<ReadonlySet<string>>(new Set())\n const toggleGroup = (key: string): void => {\n setExpanded((prev) => {\n const next = new Set(prev)\n if (next.has(key)) {\n next.delete(key)\n } else {\n next.add(key)\n }\n return next\n })\n }\n\n const freshness = interpolate(strings['panel.freshness'], {\n date: formatQaDate(meta.generated_at),\n version: shortVersion(meta.app_version),\n env: meta.environment,\n })\n\n let body\n if (state === 'no-tests') {\n body = h('p', { class: 'qa-panel__empty' }, strings['panel.noTests'])\n } else if (state === 'unknown-page') {\n body = h('p', { class: 'qa-panel__empty' }, strings['panel.unknownPage'])\n } else {\n const scenarios = page?.scenarios ?? []\n const hasGroups = scenarios.some((s) => s.group)\n if (scenarios.length === 0) {\n body = h('p', { class: 'qa-panel__empty' }, strings['panel.noScenarios'])\n } else if (!hasGroups) {\n // Synthetic / ungrouped page (donor `hasGroups === false`): flat list, no\n // suite header — avoids a spurious empty-label group wrapper.\n body = h(\n 'ul',\n { class: 'qa-panel__list' },\n scenarios.map((scenario) => h(ScenarioRow, { key: scenario.id, scenario, strings })),\n )\n } else {\n const groups = groupScenarios(scenarios)\n body = h(\n 'ul',\n { class: 'qa-panel__groups', 'data-testid': 'qa-suite-groups' },\n groups.map((group) => {\n const isOpen = expanded.has(group.key)\n const agg = {\n dev: aggregateTri(group.scenarios.map((s) => humanTriColor(s.dev))),\n bot: aggregateTri(group.scenarios.map((s) => botTriColor(s.automated))),\n human: aggregateTri(group.scenarios.map((s) => humanTriColor(s.client))),\n }\n return h(\n 'li',\n { key: group.key, class: 'qa-suite', 'data-testid': 'qa-panel-group' },\n h(\n 'button',\n {\n type: 'button',\n class: 'qa-suite__head',\n 'aria-expanded': isOpen,\n 'data-testid': 'qa-suite-group',\n onClick: () => toggleGroup(group.key),\n },\n h(\n 'span',\n { class: `qa-suite__chev ${isOpen ? 'is-open' : ''}`, 'aria-hidden': 'true' },\n '▸',\n ),\n h('span', { class: 'qa-suite__key' }, group.key),\n h('span', { class: 'qa-suite__label' }, group.label),\n h(\n 'span',\n { class: 'qa-suite__count' },\n interpolate(strings['suite.cases'], { count: group.scenarios.length }),\n ),\n h(LaneIcons, { dev: agg.dev, bot: agg.bot, human: agg.human, strings }),\n ),\n isOpen\n ? h(\n 'ul',\n { class: 'qa-suite__list' },\n group.scenarios.map((scenario) =>\n h(ScenarioRow, { key: scenario.id, scenario, strings }),\n ),\n )\n : null,\n )\n }),\n )\n }\n }\n\n return h(\n 'section',\n { class: 'qa-panel', 'data-testid': 'qa-panel' },\n body,\n h(\n 'footer',\n { class: 'qa-panel__foot' },\n h(\n 'span',\n { class: 'qa-panel__count', title: freshness, 'data-testid': 'qa-panel-freshness' },\n freshness,\n ),\n ),\n )\n}\n","import { h } from 'preact'\nimport { useEffect, useMemo, useState } from 'preact/hooks'\n\nimport type { QaScenario, QaSitemapNode, QaStatusArtifact } from './types'\nimport type { QaStrings } from './i18n'\nimport { LaneIcons } from './LaneIcons'\nimport { normalizePattern } from './resolver'\nimport { botTriColor, formatQaDate, humanTriColor, QA_COLOR_MODIFIER, shortVersion } from './ui'\n\n/**\n * Preact port of the donor `qa-sitemap-modal.vue`.\n *\n * Presentational apart from the Escape-key listener (the one place a hook is\n * warranted — the donor used a `watch` + `window.addEventListener`). Data comes\n * in as the already-fetched artifact; this component owns no store or fetching.\n *\n * The donor's `<script setup>` tree reconstruction is extracted as the pure\n * exported `buildTree` helper so it's unit-testable and reusable by the mount\n * task.\n */\nexport interface SitemapModalProps {\n artifact: QaStatusArtifact\n currentPattern: string | null\n strings: QaStrings\n onClose: () => void\n}\n\n/** A node of the rebuilt sitemap tree, flattened depth-first for rendering. The\n * flat `artifact.sitemap` links children to parents via `parent_id`; we rebuild\n * the hierarchy and emit `depth` + `hasChildren` (donor `flatTree`). */\nexport interface TreeRow {\n node: QaSitemapNode\n depth: number\n hasChildren: boolean\n}\n\nexport function buildTree(nodes: ReadonlyArray<QaSitemapNode>): TreeRow[] {\n const childrenByParent = new Map<string | null, QaSitemapNode[]>()\n for (const node of nodes) {\n const bucket = childrenByParent.get(node.parent_id) ?? []\n bucket.push(node)\n childrenByParent.set(node.parent_id, bucket)\n }\n for (const bucket of childrenByParent.values()) {\n bucket.sort((a, b) => a.order - b.order)\n }\n const out: TreeRow[] = []\n const visit = (parentId: string | null, depth: number): void => {\n for (const node of childrenByParent.get(parentId) ?? []) {\n const children = childrenByParent.get(node.id) ?? []\n out.push({ node, depth, hasChildren: children.length > 0 })\n if (children.length > 0) {\n visit(node.id, depth + 1)\n }\n }\n }\n visit(null, 0)\n return out\n}\n\ninterface CaseEntry {\n scenario: QaScenario\n primaries: Set<string>\n traversed: Set<string>\n}\n\n/** Dedupe scenarios across pages by `external_key ?? id` (donor `dedupedCases`):\n * a cross-module/traversed case that appears on several pages lists ONCE, with\n * its primary/traversed pattern sets merged. */\nfunction dedupeScenarios(artifact: QaStatusArtifact): CaseEntry[] {\n const byKey = new Map<string, CaseEntry>()\n for (const page of artifact.pages) {\n for (const scenario of page.scenarios) {\n const key = scenario.external_key ?? scenario.id\n const entry = byKey.get(key) ?? { scenario, primaries: new Set<string>(), traversed: new Set<string>() }\n for (const ref of scenario.pages ?? []) {\n ;(ref.role === 'primary' ? entry.primaries : entry.traversed).add(ref.pattern)\n }\n byKey.set(key, entry)\n }\n }\n const rank = (p?: string): number => (p ? Number(p.slice(1)) : 9)\n return [...byKey.values()].sort((a, b) => {\n const ga = a.scenario.group?.key ?? '~'\n const gb = b.scenario.group?.key ?? '~'\n if (ga !== gb) {\n return ga.localeCompare(gb)\n }\n return rank(a.scenario.priority) - rank(b.scenario.priority) || a.scenario.title.localeCompare(b.scenario.title)\n })\n}\n\nfunction interpolate(template: string, params: Record<string, string | number>): string {\n return template.replace(/\\{(\\w+)\\}/g, (_, k: string) => String(params[k] ?? ''))\n}\n\n/** KPI formatting: the metric values are already whole percentages (0–100), as\n * emitted by `qa build` (`cli/src/qa/build.ts` rounds `100*num/den`). Render\n * them directly — do NOT multiply by 100, or a CLI artifact's `25` becomes\n * \"2500%\". See the QaMetric contract in `types.ts`. */\nfunction formatPct(pct: number): string {\n return `${Math.round(pct)}%`\n}\n\nexport function SitemapModal({ artifact, currentPattern, strings, onClose }: SitemapModalProps) {\n const [activeTab, setActiveTab] = useState<'pages' | 'scenarios'>('pages')\n const [search, setSearch] = useState('')\n\n // The one place a hook is warranted: close on Escape, cleaned up on unmount.\n useEffect(() => {\n const onKeydown = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') {\n onClose()\n }\n }\n window.addEventListener('keydown', onKeydown)\n return () => window.removeEventListener('keydown', onKeydown)\n }, [onClose])\n\n const here = currentPattern ? normalizePattern(currentPattern) : null\n const tree = useMemo(() => buildTree(artifact.sitemap), [artifact])\n const cases = useMemo(() => dedupeScenarios(artifact), [artifact])\n\n const titleByPattern = useMemo(() => {\n const map = new Map<string, string>()\n for (const node of artifact.sitemap) {\n map.set(node.path_pattern, node.title)\n }\n return map\n }, [artifact])\n const patternTitle = (pattern: string): string => titleByPattern.get(pattern) ?? pattern\n\n const query = search.trim().toLowerCase()\n const filteredCases = query\n ? cases.filter(\n (c) =>\n c.scenario.title.toLowerCase().includes(query) ||\n (c.scenario.group?.label ?? '').toLowerCase().includes(query),\n )\n : cases\n\n const freshness = interpolate(strings['panel.freshness'], {\n date: formatQaDate(artifact.generated_at),\n version: shortVersion(artifact.app_version),\n env: artifact.environment,\n })\n\n return h(\n 'div',\n {\n class: 'qa-modal__backdrop',\n 'data-testid': 'qa-sitemap-modal',\n onClick: (e: MouseEvent) => {\n if (e.target === e.currentTarget) onClose()\n },\n },\n h(\n 'div',\n { class: 'qa-modal', role: 'dialog', 'aria-modal': 'true' },\n h(\n 'header',\n { class: 'qa-modal__head' },\n h('h3', null, strings['modal.title']),\n h(\n 'button',\n {\n type: 'button',\n class: 'qa-modal__close',\n title: strings['modal.close'],\n 'aria-label': strings['modal.close'],\n 'data-testid': 'qa-modal-close',\n onClick: onClose,\n },\n '×',\n ),\n ),\n h('div', { class: 'qa-modal__freshness' }, freshness),\n // Dual metric (doc 01 D12) — rendered as rounded percentages.\n h(\n 'div',\n { class: 'qa-kpi', 'data-testid': 'qa-kpi' },\n h(\n 'div',\n { class: 'qa-kpi__item' },\n h('span', { class: 'qa-kpi__value' }, formatPct(artifact.metric.pages_with_scenarios_pct)),\n h('span', { class: 'qa-kpi__label' }, strings['modal.kpiPagesCovered']),\n ),\n h(\n 'div',\n { class: 'qa-kpi__item' },\n h('span', { class: 'qa-kpi__value' }, formatPct(artifact.metric.scenarios_passing_pct)),\n h('span', { class: 'qa-kpi__label' }, strings['modal.kpiScenariosPassing']),\n ),\n ),\n h(\n 'nav',\n { class: 'qa-tabs' },\n h(\n 'button',\n {\n type: 'button',\n class: `qa-tabs__tab ${activeTab === 'pages' ? 'qa-tabs__tab--active' : ''}`,\n 'data-testid': 'qa-tab-pages',\n onClick: () => setActiveTab('pages'),\n },\n strings['modal.tabPages'],\n ),\n h(\n 'button',\n {\n type: 'button',\n class: `qa-tabs__tab ${activeTab === 'scenarios' ? 'qa-tabs__tab--active' : ''}`,\n 'data-testid': 'qa-tab-scenarios',\n onClick: () => setActiveTab('scenarios'),\n },\n strings['modal.tabScenarios'],\n ),\n ),\n h(\n 'div',\n { class: 'qa-modal__body' },\n activeTab === 'pages'\n ? h(\n 'ul',\n { class: 'qa-tree' },\n tree.map((row) => {\n const isHere = here !== null && row.node.path_pattern === here\n return h(\n 'li',\n {\n key: row.node.id,\n class: `qa-tree__row ${isHere ? 'qa-tree__row--here' : ''}`,\n 'data-testid': isHere ? 'qa-tree-current' : 'qa-sitemap-node',\n style: { paddingLeft: `${0.5 + row.depth * 1.1}rem` },\n },\n h(\n 'span',\n {\n class: `qa-dot ${QA_COLOR_MODIFIER[row.node.color]}`,\n 'aria-hidden': 'true',\n },\n ),\n h(\n 'span',\n {\n class: `qa-tree__title ${row.node.structural ? 'qa-tree__title--structural' : ''}`,\n },\n row.node.title || row.node.path_pattern,\n ),\n isHere ? h('span', { class: 'qa-tree__here' }, ` ${row.node.path_pattern} `) : null,\n isHere ? h('span', { class: 'qa-tree__here' }, strings['modal.youAreHere']) : null,\n h('span', { class: 'qa-tree__count' }, String(row.node.counts.scenarios)),\n )\n }),\n )\n : h(\n 'div',\n { class: 'qa-scenarios' },\n h('input', {\n type: 'search',\n class: 'qa-scenarios__search',\n placeholder: strings['modal.search'],\n 'data-testid': 'qa-scenarios-search',\n value: search,\n onInput: (e: Event) => setSearch((e.target as HTMLInputElement).value),\n }),\n filteredCases.length === 0\n ? h('p', { class: 'qa-scenarios__empty' }, strings['modal.noMatch'])\n : h(\n 'ul',\n { class: 'qa-scenarios__list' },\n filteredCases.map((entry) => {\n const scenario = entry.scenario\n const lanes = {\n dev: humanTriColor(scenario.dev),\n bot: botTriColor(scenario.automated),\n human: humanTriColor(scenario.client),\n }\n const traversed = [...entry.traversed].map(patternTitle)\n return h(\n 'li',\n {\n key: scenario.external_key ?? scenario.id,\n class: 'qa-scenarios__item',\n 'data-testid': 'qa-scenario-row',\n },\n h(\n 'div',\n { class: 'qa-scenarios__line' },\n h('span', { class: `qa-dot ${QA_COLOR_MODIFIER[scenario.color]}`, 'aria-hidden': 'true' }),\n scenario.group?.key\n ? h('span', { class: 'qa-suitekey' }, scenario.group.key)\n : null,\n h('span', { class: 'qa-scenarios__title' }, scenario.title),\n scenario.source === 'testgen'\n ? h('span', { class: 'qa-tag' }, strings['source.testgen'])\n : null,\n h(LaneIcons, { dev: lanes.dev, bot: lanes.bot, human: lanes.human, strings }),\n ),\n h(\n 'div',\n { class: 'qa-scenarios__pages' },\n entry.primaries.size\n ? h(\n 'span',\n { class: 'qa-scenarios__primary' },\n interpolate(strings['modal.appliesTo'], { count: entry.primaries.size }),\n )\n : null,\n traversed.length\n ? h(\n 'span',\n { class: 'qa-scenarios__traversed' },\n `${strings['modal.traversed']}: ${traversed.join(', ')}`,\n )\n : null,\n ),\n )\n }),\n ),\n ),\n ),\n ),\n )\n}\n","import { h, render } from 'preact'\nimport { useCallback, useEffect, useState } from 'preact/hooks'\n\nimport type { QaColor, QaPageStatus, QaStatusArtifact } from './types'\nimport type { QaMeterHandle, QaMeterOptions } from './index'\nimport { resolveStrings } from './i18n'\nimport { resolvePage } from './resolver'\nimport { QA_METER_STYLES } from './styles'\nimport { Pastille } from './Pastille'\nimport { TestPanel } from './TestPanel'\nimport { SitemapModal } from './SitemapModal'\n\n/** The custom event the patched History API dispatches so the meter can\n * re-resolve the current page on SPA navigations (pushState/replaceState\n * don't fire `popstate`). Module-scoped name so every instance listens to\n * the same channel. */\nconst LOCATION_CHANGE_EVENT = 'mqa:locationchange'\n\n// ── History API patch (module-level, ref-counted) ──────────────────────────\n// We monkey-patch pushState/replaceState ONCE across all instances so a\n// `mqa:locationchange` CustomEvent fires on SPA navigations. A ref-count lets\n// the LAST disposed instance restore the originals; guarding on `patched`\n// prevents a second instance from double-wrapping (which would dispatch twice\n// and, worse, capture an already-wrapped \"original\").\nlet historyPatched = false\nlet historyRefCount = 0\nlet originalPushState: History['pushState'] | null = null\nlet originalReplaceState: History['replaceState'] | null = null\n\nfunction patchHistory(): void {\n historyRefCount++\n if (historyPatched) return\n historyPatched = true\n originalPushState = history.pushState\n originalReplaceState = history.replaceState\n const fire = (): void => {\n window.dispatchEvent(new CustomEvent(LOCATION_CHANGE_EVENT))\n }\n history.pushState = function patchedPushState(\n this: History,\n ...args: Parameters<History['pushState']>\n ) {\n const ret = originalPushState!.apply(this, args)\n fire()\n return ret\n }\n history.replaceState = function patchedReplaceState(\n this: History,\n ...args: Parameters<History['replaceState']>\n ) {\n const ret = originalReplaceState!.apply(this, args)\n fire()\n return ret\n }\n}\n\nfunction unpatchHistory(): void {\n if (historyRefCount > 0) historyRefCount--\n if (historyRefCount > 0 || !historyPatched) return\n historyPatched = false\n if (originalPushState) history.pushState = originalPushState\n if (originalReplaceState) history.replaceState = originalReplaceState\n originalPushState = null\n originalReplaceState = null\n}\n\n/** Default offset, matching the donor `qa-pastille.vue` (sits near the feedback\n * FAB). `.qa-pastille` anchors bottom-right via the `--mqa-right`/`--mqa-bottom`\n * custom properties, which default to `1.5rem` in the stylesheet. CSS custom\n * properties inherit from the host element THROUGH the shadow boundary, so a\n * host can nudge the pastille by setting those vars on the host (done below\n * when `options.position` is provided) — setting `host.style.right/bottom`\n * directly would NOT work, since `.qa-pastille` is itself `position: fixed`\n * and anchors to the viewport, not the host. */\n\n// ── Format validation ───────────────────────────────────────────────────────\nconst QA_ARTIFACT_FORMAT = 1\n\nfunction isValidArtifact(value: unknown): value is QaStatusArtifact {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { format?: unknown }).format === QA_ARTIFACT_FORMAT\n )\n}\n\nlet warnedBadFormat = false\nfunction warnBadFormatOnce(): void {\n if (warnedBadFormat) return\n warnedBadFormat = true\n if (typeof console !== 'undefined') {\n console.warn('[mqa] QA Meter artifact has an unsupported `format` — refusing to mount.')\n }\n}\n\nconst NOOP_HANDLE: QaMeterHandle = {\n open() {},\n close() {},\n dispose() {},\n}\n\n/** Every path_pattern an artifact knows — `pages[].page?.path_pattern` ∪\n * `sitemap[].path_pattern`. Feeds the default routerless resolver. */\nfunction patternsFromArtifact(artifact: QaStatusArtifact): string[] {\n const set = new Set<string>()\n for (const page of artifact.pages) {\n if (page.page) set.add(page.page.path_pattern)\n }\n for (const node of artifact.sitemap) {\n set.add(node.path_pattern)\n }\n return [...set]\n}\n\ninterface Resolved {\n state: 'known' | 'no-tests' | 'unknown-page'\n color: QaColor\n pattern: string | null\n page: QaPageStatus | null\n}\n\nconst UNKNOWN: Resolved = { state: 'unknown-page', color: 'gray', pattern: null, page: null }\n\n/** State machine (task spec): a matched `pages[]` entry → known/its color;\n * a pattern present only in the sitemap → no-tests/gray; no match (or no\n * artifact yet) → unknown-page/gray. Never throws. */\nfunction resolveCurrent(artifact: QaStatusArtifact | null, pattern: string | null): Resolved {\n if (!artifact || !pattern) return UNKNOWN\n const page = artifact.pages.find((p) => p.page && p.page.path_pattern === pattern)\n if (page && page.page) {\n return { state: 'known', color: page.color, pattern, page }\n }\n const inSitemap = artifact.sitemap.some((n) => n.path_pattern === pattern)\n if (inSitemap) {\n return { state: 'no-tests', color: 'gray', pattern, page: null }\n }\n return { state: 'unknown-page', color: 'gray', pattern, page: null }\n}\n\nexport function createQaMeter(options: QaMeterOptions): QaMeterHandle {\n const { source } = options\n const strings = resolveStrings(options.locale ?? 'fr')\n\n // For an INLINE artifact we must validate synchronously at create time, so a\n // wrong-format object never produces a host (the test asserts this after a\n // single microtask).\n const inlineArtifact = typeof source === 'object' ? source : null\n if (inlineArtifact && !isValidArtifact(inlineArtifact)) {\n warnBadFormatOnce()\n return NOOP_HANDLE\n }\n\n // Loaded artifact cache + lazy loader state. Inline artifacts are ready now;\n // string/function sources load lazily on first open/hover, exactly once.\n let artifact: QaStatusArtifact | null = inlineArtifact\n let loadStarted = false\n let loadPromise: Promise<void> | null = null\n\n function load(): void {\n if (artifact || loadStarted) return\n loadStarted = true\n const got: Promise<unknown> =\n typeof source === 'function'\n ? source()\n : typeof source === 'string'\n ? fetch(source).then((r) => r.json())\n : Promise.resolve(source)\n loadPromise = got\n .then((value) => {\n if (isValidArtifact(value)) {\n artifact = value\n forceRender()\n } else {\n warnBadFormatOnce()\n }\n })\n .catch(() => {\n // Never throw to the host: stay gray.\n })\n }\n\n // ── Host + shadow root + styles (mirrors widget/mount.tsx) ────────────────\n const host = document.createElement('div')\n host.setAttribute('data-mqa-host', '')\n host.setAttribute('data-mqa-size', options.size ?? 'md')\n // Offset knobs as CSS custom properties: they inherit through the shadow\n // boundary down to `.qa-pastille`, which reads them with a 1.5rem fallback.\n // Only set each when provided so the stylesheet default applies otherwise.\n if (options.position?.right !== undefined) {\n host.style.setProperty('--mqa-right', `${options.position.right}px`)\n }\n if (options.position?.bottom !== undefined) {\n host.style.setProperty('--mqa-bottom', `${options.position.bottom}px`)\n }\n document.body.appendChild(host)\n\n const shadow = host.attachShadow({ mode: 'open' })\n const style = document.createElement('style')\n style.textContent = QA_METER_STYLES\n shadow.appendChild(style)\n const mountPoint = document.createElement('div')\n shadow.appendChild(mountPoint)\n\n // Imperative re-render hook into the Preact tree. The container component\n // registers its dispatcher here so the loader/navigation callbacks can poke\n // it without React-ish prop threading.\n let forceRender: () => void = () => {}\n\n function getCurrentPattern(): string | null {\n if (options.getCurrentPage) return options.getCurrentPage()\n if (!artifact) return null\n return resolvePage(window.location.pathname, patternsFromArtifact(artifact))\n }\n\n function Container() {\n // A bump-counter is the simplest \"force re-render on external change\"\n // primitive (loader resolves, navigation, open/close).\n const [tick, setTick] = useState(0)\n void tick\n const bump = useCallback(() => setTick((n) => n + 1), [])\n forceRender = bump\n\n // open/close lives in module scope (so the handle can drive it) but we\n // read it via a closure variable bumped through `forceRender`.\n const resolved = resolveCurrent(artifact, getCurrentPattern())\n\n const meta = artifact\n ? {\n generated_at: artifact.generated_at,\n app_version: artifact.app_version,\n environment: artifact.environment,\n }\n : { generated_at: '', app_version: '', environment: '' }\n\n const onHover = useCallback(() => {\n // Lazy-load on first hover too (not just open), so the panel shows real\n // data before the user clicks.\n load()\n }, [])\n\n const onOpenModal = useCallback(() => {\n load()\n uiOpen = true\n bump()\n }, [])\n\n const closeModal = useCallback(() => {\n uiOpen = false\n bump()\n }, [])\n\n // Re-resolve current page on navigation. popstate covers back/forward;\n // the patched History API covers SPA pushState/replaceState.\n useEffect(() => {\n const onNav = (): void => bump()\n window.addEventListener('popstate', onNav)\n window.addEventListener(LOCATION_CHANGE_EVENT, onNav)\n return () => {\n window.removeEventListener('popstate', onNav)\n window.removeEventListener(LOCATION_CHANGE_EVENT, onNav)\n }\n }, [])\n\n return h(\n 'div',\n {\n class: 'qa-pastille-wrap',\n // Re-entering the wrap (the dot OR the panel above it — both live\n // inside) cancels a pending close; leaving schedules a debounced close.\n onMouseEnter: cancelHoverClose,\n onMouseLeave: scheduleHoverClose,\n },\n uiHover\n ? h(\n 'div',\n { class: 'qa-panel-anchor' },\n h(TestPanel, { page: resolved.page, state: resolved.state, meta, strings }),\n )\n : null,\n h(Pastille, {\n color: resolved.color,\n state: resolved.state,\n strings,\n onHover: () => {\n cancelHoverClose()\n uiHover = true\n onHover()\n bump()\n },\n onOpenModal,\n }),\n uiOpen && artifact\n ? h(SitemapModal, {\n artifact,\n currentPattern: resolved.pattern,\n strings,\n onClose: closeModal,\n })\n : null,\n )\n }\n\n // UI-local flags kept in closure scope so the imperative handle (open/close)\n // can flip them and `forceRender()` reflects the change. The container reads\n // them on each render.\n let uiOpen = false\n let uiHover = false\n\n // Closing the hover panel is debounced (rather than firing on the raw\n // mouseleave) so sweeping the cursor across the small gap between the dot and\n // the panel — or a jittery mouse at the dot's edge — doesn't unmount it\n // mid-sweep. Any mouseenter back into the wrap cancels the pending close.\n let hoverCloseTimer: ReturnType<typeof setTimeout> | null = null\n const HOVER_CLOSE_MS = 140\n function cancelHoverClose(): void {\n if (hoverCloseTimer !== null) {\n clearTimeout(hoverCloseTimer)\n hoverCloseTimer = null\n }\n }\n function scheduleHoverClose(): void {\n cancelHoverClose()\n hoverCloseTimer = setTimeout(() => {\n hoverCloseTimer = null\n if (disposed) return\n uiHover = false\n forceRender()\n }, HOVER_CLOSE_MS)\n }\n\n function mountTree(): void {\n render(h(Container, {}), mountPoint)\n }\n\n // Initial mount.\n mountTree()\n\n patchHistory()\n\n let disposed = false\n\n return {\n open() {\n if (disposed) return\n load()\n uiOpen = true\n forceRender()\n },\n close() {\n if (disposed) return\n uiOpen = false\n forceRender()\n },\n dispose() {\n if (disposed) return\n disposed = true\n cancelHoverClose()\n render(null, mountPoint)\n host.remove()\n unpatchHistory()\n void loadPromise\n },\n }\n}\n","import type { QaStatusArtifact } from './types'\nimport { createQaMeter as createQaMeterImpl } from './mount'\n\nexport interface QaMeterOptions {\n /** Where the QA status artifact comes from. An inline object renders\n * immediately; a URL is `fetch`ed lazily on first open/hover; a function is\n * invoked lazily likewise. */\n source: string | QaStatusArtifact | (() => Promise<QaStatusArtifact>)\n /** Router-aware override. Defaults to a routerless resolver over the\n * artifact's path_patterns + `window.location.pathname`. */\n getCurrentPage?: () => string | null\n locale?: 'fr' | 'en'\n /** Pastille size. 'md' (48px, default) for standalone; the feedback-widget\n * integration passes 'sm' (24px) so the QA FAB reads as secondary. */\n size?: 'sm' | 'md'\n position?: { right?: number; bottom?: number }\n}\n\nexport interface QaMeterHandle {\n open(): void\n close(): void\n dispose(): void\n}\n\nexport function createQaMeter(options: QaMeterOptions): QaMeterHandle {\n return createQaMeterImpl(options)\n}\n\nexport type {\n QaStatusArtifact,\n QaColor,\n QaScenario,\n QaPageStatus,\n QaSitemapNode,\n} from './types'\n","/**\n * The widget bundle the loader fetches at runtime.\n *\n * Built as an IIFE with `globalName: MhosaicFeedback`, so once the bundle\n * is parsed by the browser, `window.MhosaicFeedback = { createFeedback }`\n * is set. The loader (`src/loader/`) injects this script with SRI and\n * picks up the global.\n *\n * Distinct from `src/embed.ts` (which is the legacy CDN script-tag entry\n * that auto-initializes from `data-key` attributes). This entry deliberately\n * does NOT auto-init — it just exposes the API for the loader to call.\n *\n * Self-arming error tracking: this bundle wraps the instance with\n * `withErrorTracking` BY DEFAULT. The bundle is the only part of a\n * loader-path install that auto-updates (the host's pinned npm shim never\n * changes), so arming here is what lets already-deployed clients start\n * filing synthetic frontend reports on their next page load — no host\n * redeploy. The loader forwards the per-project `error_tracking` manifest\n * flag as `config.errorTracking`; pass `false` to opt out (default on, the\n * platform's chosen posture). `withErrorTracking` is idempotent, so a host\n * that ALSO wraps via <FeedbackProvider> won't double-register.\n */\n\nimport { createFeedback as baseCreateFeedback, type InternalConfig } from './core'\nimport { withErrorTracking, type ErrorTrackingOptions } from './modules/error-tracking'\n\nexport interface WidgetConfig extends InternalConfig {\n /** Off-switch for the bundle's default error capture. Default on. */\n errorTracking?: boolean | ErrorTrackingOptions\n}\n\nexport function createFeedback(config: WidgetConfig) {\n const { errorTracking, ...base } = config\n const fb = baseCreateFeedback(base)\n if (errorTracking !== false) {\n withErrorTracking(fb, typeof errorTracking === 'object' ? errorTracking : undefined)\n }\n return fb\n}\n\nexport type {\n FeedbackApi,\n FeedbackConfig,\n FeedbackEnv,\n FeedbackSeverity,\n FeedbackType,\n ReportPayload,\n SubmittedReport,\n UserIdentity,\n} from './types'\n","import type {\n BoardFilters,\n ReportPayload,\n SubmittedReport,\n WidgetBoardKpis,\n WidgetBoardRow,\n WidgetChangelogRow,\n WidgetCommentRow,\n WidgetReportDetail,\n WidgetReportRow,\n} from '../types'\n\n/** Page envelope DRF returns for paginated list endpoints. */\nexport interface BoardListPage {\n count: number\n next: string | null\n previous: string | null\n results: WidgetBoardRow[]\n /** Phase-3 backends embed the KPI-strip payload in the list response so\n * a board poll is one request; absent on older backends (the widget\n * falls back to the standalone /board/kpis/ endpoint). */\n kpis?: WidgetBoardKpis\n}\n\n/** Per-call options for reader requests. `signal` lets callers cancel a\n * superseded fetch (filter/search changed, component unmounted) so the\n * backend stops paying for a response nobody will render — a fast typist\n * could otherwise stack overlapping project-wide `q=` scans (the\n * pre-0.30.0 OOM shape; the debounce caps rate, not concurrency). */\nexport interface ReadOpts {\n signal?: AbortSignal\n}\n\nexport interface ApiClientOptions {\n apiKey: string\n endpoint: string\n fetch?: typeof fetch\n beforeSend?: (payload: ReportPayload) => ReportPayload | false | Promise<ReportPayload | false>\n /**\n * v0.13 — signed-identity callback. Returns the current\n * `(userHash, exp, email)` triple if the host has called\n * `identify({userHash, exp, ...})`. The API client reads this fresh\n * on every request so a delayed `identify()` lights up signed\n * headers on subsequent calls. Return `null` for the legacy\n * unsigned path (the default until the project issues a\n * `WidgetSigningSecret`).\n */\n getSignedIdentity?: () => { userHash: string; exp: number; email: string } | null\n}\n\nexport interface ApiClient {\n submitReport(payload: ReportPayload): Promise<SubmittedReport>\n /** POST /v1/widget/visibility/ — whether this end-user may see the widget\n * (Phase 4 allowlist). `email`, when the host identified one, lets an\n * email-based allowlist match (the backend matches external_id OR email).\n * Throws on error so the caller can fail closed. */\n checkVisibility(externalId: string, email?: string): Promise<boolean>\n /** GET /v1/reports/widget/mine/ — caller's own reports on this project. */\n listMine(externalId: string): Promise<WidgetReportRow[]>\n /** GET /v1/reports/widget/changelog/ — caller's resolved reports for the\n * \"This week\" tab; client groups by ISO week of `resolved_at`. */\n listChangelog(externalId: string): Promise<WidgetChangelogRow[]>\n /** GET /v1/reports/widget/<id>/ — single report + thread. */\n getReport(reportId: string, externalId: string, opts?: ReadOpts): Promise<WidgetReportDetail>\n /** Resolve a report by its per-project #seq — powers clickable \"#NN\"\n * references and jump-by-number (#85). */\n getReportBySeq(seq: number, externalId: string): Promise<WidgetReportDetail>\n /** POST /v1/reports/widget/<id>/comments/ — submitter follow-up.\n * When `attachments` are supplied the request switches to multipart so\n * images ride along (#91); a text-only comment stays a JSON POST. */\n addComment(\n reportId: string,\n externalId: string,\n body: string,\n clientNonce?: string,\n attachments?: readonly Blob[],\n ): Promise<WidgetCommentRow>\n /** PATCH /v1/reports/widget/<id>/ — the author edits their report's\n * description after submitting (F4). Author-only server-side. */\n editDescription(\n reportId: string,\n externalId: string,\n description: string,\n ): Promise<WidgetReportDetail>\n /** PATCH /v1/reports/widget/<id>/ — close-as-resolved by the submitter. */\n closeAsResolved(\n reportId: string,\n externalId: string,\n ): Promise<WidgetReportDetail>\n /** PATCH /v1/reports/widget/<id>/ — reopen (→ in_progress) when the\n * submitter says the shipped fix didn't actually resolve the issue. */\n reopenUnresolved(\n reportId: string,\n externalId: string,\n ): Promise<WidgetReportDetail>\n /** GET /v1/reports/widget/board/ — paginated, filtered list backing the\n * v0.12 Board tab. Scope follows the project's\n * `share_reports_with_widget` flag (submitter-only when off, project-\n * wide when on); `filters.mine = true` narrows even when on. */\n listBoard(externalId: string, filters?: BoardFilters, opts?: ReadOpts): Promise<BoardListPage>\n /** GET /v1/reports/widget/board/kpis/ — total + per-status counts +\n * resolution rate. Cheap; safe to poll alongside the list. */\n fetchBoardKpis(\n externalId: string,\n filters?: BoardFilters,\n opts?: ReadOpts,\n ): Promise<WidgetBoardKpis>\n}\n\nconst SCALAR_FIELDS: Array<keyof ReportPayload> = [\n 'description', 'feedback_type', 'severity', 'env', 'page_url', 'user_agent', 'capture_method',\n]\n\nexport function assertSafeEndpoint(endpoint: string): void {\n let parsed: URL\n try {\n parsed = new URL(endpoint)\n } catch {\n throw new Error(\n `[mhosaic-feedback] \\`endpoint\\` is not a valid URL: ${endpoint}`,\n )\n }\n if (parsed.protocol === 'https:') return\n if (\n parsed.protocol === 'http:' &&\n (parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1' || parsed.hostname === '[::1]')\n ) {\n return\n }\n throw new Error(\n `[mhosaic-feedback] \\`endpoint\\` must use https:// (got ${parsed.protocol}//${parsed.hostname}). ` +\n 'http:// is only allowed for localhost in dev.',\n )\n}\n\n/**\n * A widget reader endpoint answered 429. Carried as a typed error so the UI\n * can show an actionable \"too many requests — retry in Xs\" message + a retry\n * button, instead of a generic failure or an infinite \"Loading…\" (#80/#81).\n */\nexport class RateLimitError extends Error {\n readonly retryAfterSeconds: number\n constructor(retryAfterSeconds: number) {\n super(`rate limited; retry after ${retryAfterSeconds}s`)\n this.name = 'RateLimitError'\n this.retryAfterSeconds = retryAfterSeconds\n }\n}\n\nfunction parseRetryAfter(response: Response): number {\n const raw = response.headers.get('Retry-After')\n const n = raw ? Number.parseInt(raw, 10) : NaN\n return Number.isFinite(n) && n >= 0 ? n : 0\n}\n\n// Turn a reader response into a throw on any non-ok status: 429 → typed\n// RateLimitError; everything else → a labelled Error. Callers must handle\n// 404 (empty-thread) BEFORE calling this.\nasync function ensureReadable(response: Response, label: string): Promise<void> {\n if (response.status === 429) throw new RateLimitError(parseRetryAfter(response))\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n throw new Error(`${label} failed: ${response.status} ${text}`)\n }\n}\n\n// A malformed-but-200 body used to be handed straight to the list views,\n// which set their rows to null/undefined and hung on \"Loading…\" forever.\n// Validating the shape here means a 200 always resolves the loading state —\n// to real data, an empty list, or (on a bad shape) an actionable error (#80).\nasync function readJsonArray<T>(response: Response, label: string): Promise<T[]> {\n await ensureReadable(response, label)\n const data = await response.json().catch(() => undefined)\n if (!Array.isArray(data)) {\n throw new Error(`${label}: unexpected response shape (expected an array)`)\n }\n return data as T[]\n}\n\nasync function readJsonObject<T>(response: Response, label: string): Promise<T> {\n await ensureReadable(response, label)\n const data = await response.json().catch(() => undefined)\n if (data === null || typeof data !== 'object' || Array.isArray(data)) {\n throw new Error(`${label}: unexpected response shape (expected an object)`)\n }\n return data as T\n}\n\nexport function createApiClient(options: ApiClientOptions): ApiClient {\n // No silent fallback to a hardcoded host — a misconfigured deploy used\n // to leak reports to https://core.mhosaic.com (which doesn't even\n // resolve). Failing fast at construction makes the misconfig obvious.\n const endpoint = (options.endpoint ?? '').replace(/\\/+$/, '')\n if (!endpoint) {\n throw new Error(\n '[mhosaic-feedback] `endpoint` is required (e.g. \"https://feedback.example.com\").',\n )\n }\n // SECURITY: endpoint is host-controlled config. Without a scheme guard a\n // misconfigured host (or a host XSS that injects a `<script\n // data-endpoint=\"http://attacker\">` embed tag) can steer the widget at\n // any URL and exfiltrate every captured report — including the\n // pk_proj_ key in the Authorization header — to attacker-controlled\n // infra. Only allow https:; permit http:// for localhost/127.0.0.1 so\n // dev sandboxes keep working.\n assertSafeEndpoint(endpoint)\n const fetcher = options.fetch ?? globalThis.fetch\n\n async function submitReport(input: ReportPayload): Promise<SubmittedReport> {\n let payload: ReportPayload | false = input\n if (options.beforeSend) payload = await options.beforeSend(input)\n if (payload === false) throw new Error('Submission cancelled by beforeSend')\n\n const form = new FormData()\n for (const field of SCALAR_FIELDS) {\n form.append(field, String(payload[field]))\n }\n form.append('technical_context', JSON.stringify(payload.technical_context))\n // Repeated `screenshot` parts — the backend reads FILES.getlist. The\n // first part keeps the legacy filename so single-shot requests stay\n // byte-identical to pre-multi widget builds.\n const screenshots = payload.screenshots?.length\n ? payload.screenshots\n : payload.screenshot\n ? [payload.screenshot]\n : []\n screenshots.forEach((blob, i) => {\n form.append('screenshot', blob, i === 0 ? 'screenshot.png' : `screenshot-${i + 1}.png`)\n })\n // Only emit `synthetic` when truthy — a `false` value would still trigger\n // the BooleanField parser on the backend, which is fine, but omitting it\n // keeps the request shape byte-identical for existing curated submissions.\n if (payload.synthetic) form.append('synthetic', 'true')\n // v0.7: nest the identity payload so DRF's WidgetUserIdentitySerializer\n // sees a real object. FormData can't carry nested objects directly —\n // use a JSON-encoded string under the `user` key; DRF parses it via\n // the JSONField semantics on the WidgetUser fields.\n if (payload.user?.id) {\n form.append('user', JSON.stringify(payload.user))\n }\n // v0.7.3: widget_version is build-time-stamped by tsup. Lets the\n // backend show \"currently running v0.7.2\" per project on the\n // operator Companies page (per-customer version observability).\n if (payload.widget_version) {\n form.append('widget_version', payload.widget_version)\n }\n if (payload.page_path) {\n form.append('page_path', payload.page_path)\n }\n\n // The backend honors `synthetic=true` only when this header is set,\n // so a hand-crafted curl with the public key can't smuggle a curated\n // report into the auto-error bucket (where the default operator view\n // hides it). The bundled debugger always sets payload.synthetic; the\n // user-facing widget never does.\n const headers: Record<string, string> = {\n Authorization: `Bearer ${options.apiKey}`,\n }\n if (payload.synthetic) {\n headers['X-Mhosaic-Capture-Source'] = 'error-tracking'\n }\n // Signed-identity headers on the SUBMIT path too: when the host has\n // wired up identify({userHash, exp, email}) and the backend project\n // has a WidgetSigningSecret, the request must carry the HMAC. We\n // also re-stamp `X-Mhosaic-User` so the verifier sees the\n // signed-over external_id directly (the body `user` block is still\n // sent — the backend prefers the header pair when signing is in\n // effect; see views.py:CreateFeedbackReportView.post).\n const signed = options.getSignedIdentity?.()\n if (signed && signed.userHash && signed.exp && payload.user?.id) {\n headers['X-Mhosaic-User'] = String(payload.user.id)\n headers['X-Mhosaic-User-Hmac'] = signed.userHash\n headers['X-Mhosaic-User-Exp'] = String(signed.exp)\n if (signed.email) headers['X-Mhosaic-User-Email'] = signed.email\n }\n const response = await fetcher(`${endpoint}/api/feedback/v1/reports/`, {\n method: 'POST',\n headers,\n body: form,\n })\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n throw new Error(`Feedback submit failed: ${response.status} ${text}`)\n }\n return response.json() as Promise<SubmittedReport>\n }\n\n function widgetHeaders(externalId: string): Record<string, string> {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${options.apiKey}`,\n 'X-Mhosaic-User': externalId,\n }\n // Signed-identity Phase 1 (v0.13): forward the HMAC envelope when the\n // host has supplied one via `identify({userHash, exp, email})`. The\n // backend gates on the project's `WidgetSigningSecret` row — projects\n // that haven't issued a secret continue to accept the bare header.\n const signed = options.getSignedIdentity?.()\n if (signed && signed.userHash && signed.exp) {\n headers['X-Mhosaic-User-Hmac'] = signed.userHash\n headers['X-Mhosaic-User-Exp'] = String(signed.exp)\n if (signed.email) headers['X-Mhosaic-User-Email'] = signed.email\n }\n return headers\n }\n\n async function listMine(externalId: string): Promise<WidgetReportRow[]> {\n const response = await fetcher(`${endpoint}/api/feedback/v1/reports/widget/mine/`, {\n method: 'GET',\n headers: widgetHeaders(externalId),\n })\n if (response.status === 404) return [] // submitter has no thread yet\n return readJsonArray<WidgetReportRow>(response, 'listMine')\n }\n\n async function listChangelog(externalId: string): Promise<WidgetChangelogRow[]> {\n const response = await fetcher(\n `${endpoint}/api/feedback/v1/reports/widget/changelog/`,\n { method: 'GET', headers: widgetHeaders(externalId) },\n )\n if (response.status === 404) return []\n return readJsonArray<WidgetChangelogRow>(response, 'listChangelog')\n }\n\n async function getReport(\n reportId: string,\n externalId: string,\n opts?: ReadOpts,\n ): Promise<WidgetReportDetail> {\n const response = await fetcher(\n `${endpoint}/api/feedback/v1/reports/widget/${reportId}/`,\n {\n method: 'GET',\n headers: widgetHeaders(externalId),\n ...(opts?.signal ? { signal: opts.signal } : {}),\n },\n )\n return readJsonObject<WidgetReportDetail>(response, 'getReport')\n }\n\n async function getReportBySeq(\n seq: number,\n externalId: string,\n ): Promise<WidgetReportDetail> {\n const response = await fetcher(\n `${endpoint}/api/feedback/v1/reports/widget/by-seq/${seq}/`,\n { method: 'GET', headers: widgetHeaders(externalId) },\n )\n return readJsonObject<WidgetReportDetail>(response, 'getReportBySeq')\n }\n\n async function addComment(\n reportId: string,\n externalId: string,\n body: string,\n clientNonce?: string,\n attachments?: readonly Blob[],\n ): Promise<WidgetCommentRow> {\n // Multipart only when images are attached — a plain follow-up stays a\n // JSON POST so its request shape is byte-identical to pre-#91 builds.\n // With FormData we must NOT set Content-Type ourselves: the browser\n // appends the multipart boundary, and a hardcoded value would lose it.\n let init: RequestInit\n if (attachments && attachments.length > 0) {\n const form = new FormData()\n form.append('body', body)\n if (clientNonce !== undefined) form.append('client_nonce', clientNonce)\n attachments.forEach((blob, i) =>\n form.append('attachment', blob, `attachment-${i + 1}.png`),\n )\n init = { method: 'POST', headers: widgetHeaders(externalId), body: form }\n } else {\n init = {\n method: 'POST',\n headers: {\n ...widgetHeaders(externalId),\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n body,\n ...(clientNonce !== undefined && { client_nonce: clientNonce }),\n }),\n }\n }\n const response = await fetcher(\n `${endpoint}/api/feedback/v1/reports/widget/${reportId}/comments/`,\n init,\n )\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n throw new Error(`addComment failed: ${response.status} ${text}`)\n }\n return response.json() as Promise<WidgetCommentRow>\n }\n\n async function editDescription(\n reportId: string,\n externalId: string,\n description: string,\n ): Promise<WidgetReportDetail> {\n const response = await fetcher(\n `${endpoint}/api/feedback/v1/reports/widget/${reportId}/`,\n {\n method: 'PATCH',\n headers: {\n ...widgetHeaders(externalId),\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ description }),\n },\n )\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n throw new Error(`editDescription failed: ${response.status} ${text}`)\n }\n return response.json() as Promise<WidgetReportDetail>\n }\n\n async function closeAsResolved(\n reportId: string,\n externalId: string,\n ): Promise<WidgetReportDetail> {\n const response = await fetcher(\n `${endpoint}/api/feedback/v1/reports/widget/${reportId}/`,\n {\n method: 'PATCH',\n headers: {\n ...widgetHeaders(externalId),\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ status: 'closed' }),\n },\n )\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n throw new Error(`closeAsResolved failed: ${response.status} ${text}`)\n }\n return response.json() as Promise<WidgetReportDetail>\n }\n\n async function reopenUnresolved(\n reportId: string,\n externalId: string,\n ): Promise<WidgetReportDetail> {\n const response = await fetcher(\n `${endpoint}/api/feedback/v1/reports/widget/${reportId}/`,\n {\n method: 'PATCH',\n headers: {\n ...widgetHeaders(externalId),\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ status: 'in_progress' }),\n },\n )\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n throw new Error(`reopenUnresolved failed: ${response.status} ${text}`)\n }\n return response.json() as Promise<WidgetReportDetail>\n }\n\n function boardQueryString(filters?: BoardFilters): string {\n if (!filters) return ''\n const params = new URLSearchParams()\n // Multi-value fields use append() so repeated keys stack the way DRF's\n // `request.query_params.getlist()` reads them on the server.\n filters.status?.forEach((s) => params.append('status', s))\n filters.type?.forEach((t) => params.append('type', t))\n filters.severity?.forEach((s) => params.append('severity', s))\n if (filters.q) params.set('q', filters.q)\n if (filters.mine) params.set('mine', '1')\n if (filters.ordering) params.set('ordering', filters.ordering)\n if (filters.page && filters.page > 1) params.set('page', String(filters.page))\n if (filters.pagePath) params.set('page_path', filters.pagePath)\n const qs = params.toString()\n return qs ? `?${qs}` : ''\n }\n\n async function listBoard(\n externalId: string,\n filters?: BoardFilters,\n opts?: ReadOpts,\n ): Promise<BoardListPage> {\n const response = await fetcher(\n `${endpoint}/api/feedback/v1/reports/widget/board/${boardQueryString(filters)}`,\n {\n method: 'GET',\n headers: widgetHeaders(externalId),\n ...(opts?.signal ? { signal: opts.signal } : {}),\n },\n )\n if (response.status === 404) {\n // Caller has no WidgetUser row yet — render an empty Board rather\n // than a hard error so the first-time experience is a clean slate.\n return { count: 0, next: null, previous: null, results: [] }\n }\n const page = await readJsonObject<BoardListPage>(response, 'listBoard')\n if (!Array.isArray(page.results)) {\n throw new Error('listBoard: unexpected response shape (missing results[])')\n }\n return page\n }\n\n async function fetchBoardKpis(\n externalId: string,\n filters?: BoardFilters,\n opts?: ReadOpts,\n ): Promise<WidgetBoardKpis> {\n const response = await fetcher(\n `${endpoint}/api/feedback/v1/reports/widget/board/kpis/${boardQueryString(filters)}`,\n {\n method: 'GET',\n headers: widgetHeaders(externalId),\n ...(opts?.signal ? { signal: opts.signal } : {}),\n },\n )\n if (response.status === 404) {\n return { total: 0, by_status: {}, resolution_rate: 0, scope: 'mine' }\n }\n return readJsonObject<WidgetBoardKpis>(response, 'fetchBoardKpis')\n }\n\n async function checkVisibility(\n externalId: string,\n email?: string,\n ): Promise<boolean> {\n const response = await fetcher(\n `${endpoint}/api/feedback/v1/widget/visibility/`,\n {\n method: 'POST',\n headers: {\n ...widgetHeaders(externalId),\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n external_id: externalId,\n ...(email ? { email } : {}),\n }),\n },\n )\n if (!response.ok) {\n throw new Error(`checkVisibility failed: ${response.status}`)\n }\n const data = (await response.json()) as { show?: boolean }\n return Boolean(data.show)\n }\n\n return {\n submitReport,\n checkVisibility,\n listMine,\n listChangelog,\n getReport,\n getReportBySeq,\n addComment,\n editDescription,\n closeAsResolved,\n reopenUnresolved,\n listBoard,\n fetchBoardKpis,\n }\n}\n","/**\n * URL sanitizer for captured network request URLs.\n *\n * Two layers of defense:\n *\n * 1) Param NAME match — anything whose name suggests a credential is\n * redacted unconditionally (token, key, password, secret, auth, etc.).\n *\n * 2) Param VALUE shape match — even when the name is innocent (\"id\",\n * \"redirect_uri\", \"context\"), if the VALUE looks like a JWT, a\n * Bearer token, an mhosaic-feedback API key, or a long opaque hex/b64\n * string we redact it. This catches the realistic case where a backend\n * routes credentials through generic-named params or where a careless\n * developer puts a token in a URL fragment for \"convenience.\"\n *\n * The path portion is preserved as-is — its purpose is operator-side\n * \"which page was this report filed from\", and stripping it would\n * destroy that signal. Hosts that route secrets through path segments\n * (uncommon) should mark those routes for the widget to skip via the\n * sanitizeUrl hook in createFeedback().\n */\n\nconst SENSITIVE_NAME = /token|key|password|secret|auth|session|sig|credential|bearer|cookie/i\n\nconst SENSITIVE_VALUE_PATTERNS: RegExp[] = [\n // JWT: three base64url segments separated by dots, header begins with eyJ.\n /^eyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$/,\n // Mhosaic feedback keys (the project's own format).\n /^(?:sk|pk)_proj_[A-Za-z0-9_-]{16,}$/,\n // GitHub PATs (ghp/gho/ghu/ghs/ghr prefixes, 36+ chars).\n /^gh[pousr]_[A-Za-z0-9]{30,}$/,\n // Stripe live/test secret keys.\n /^(?:sk|pk|rk|whsec)_(?:live|test)_[A-Za-z0-9]{16,}$/,\n // AWS access key id.\n /^AKIA[0-9A-Z]{12,}$/,\n // Slack bot tokens.\n /^xox[abprso]-[A-Za-z0-9-]{12,}$/,\n // Generic high-entropy: hex 40+ chars (SHA-1, SHA-256) or long alnum 32+.\n /^[a-f0-9]{40,}$/i,\n // Long opaque base64-ish string (common for OAuth codes & session ids).\n /^[A-Za-z0-9_-]{40,}$/,\n]\n\nfunction looksLikeCredential(value: string): boolean {\n return SENSITIVE_VALUE_PATTERNS.some((re) => re.test(value))\n}\n\nexport function sanitizeUrl(url: string): string {\n try {\n // Accept absolute URLs or root-relative paths; reject everything else\n const isAbsolute = /^https?:\\/\\//i.test(url)\n const isRootRelative = url.startsWith('/')\n if (!isAbsolute && !isRootRelative) return url\n\n const base = typeof window !== 'undefined' ? window.location.origin : 'http://localhost'\n const u = new URL(url, base)\n const clean = new URLSearchParams()\n u.searchParams.forEach((value, name) => {\n if (SENSITIVE_NAME.test(name) || looksLikeCredential(value)) {\n clean.set(name, '[redacted]')\n } else {\n clean.set(name, value)\n }\n })\n u.search = clean.toString()\n // Hash fragments occasionally carry credentials (OAuth implicit flow):\n // never log them.\n u.hash = u.hash ? '#[redacted]' : ''\n return u.toString()\n } catch {\n return url\n }\n}\n","/**\n * Shared credential-shaped-string scrubber.\n *\n * Used by every capture module (console, error, performance, network) so a\n * single regression in any one path can't leak credentials a sibling path\n * already cleans. Patterns mirror the ones in the URL sanitizer's\n * value-shape detector — keep them in sync.\n */\n\nexport const SENSITIVE_TOKEN_PATTERNS: RegExp[] = [\n // Mhosaic feedback keys. Match ANY tail length — a backend error echo can\n // truncate the suffix (e.g. `sk_proj_***abc`) and the redaction must still\n // catch the prefix.\n /\\b(?:sk|pk)_proj_[A-Za-z0-9_*-]+/g,\n // JWT shape (header.payload.signature).\n /\\beyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\b/g,\n // \"Bearer <token>\" and \"Authorization: …\" headers.\n /\\bBearer\\s+[A-Za-z0-9._~+/=-]{16,}\\b/g,\n /\\bAuthorization\\s*[:=]\\s*[\"']?[A-Za-z0-9._~+/=-]{16,}[\"']?/gi,\n // GitHub PATs.\n /\\bgh[pousr]_[A-Za-z0-9]{30,}\\b/g,\n // Stripe live/test secret keys + webhook secrets.\n /\\b(?:sk|pk|rk|whsec)_(?:live|test)_[A-Za-z0-9]{16,}\\b/g,\n // AWS access key id.\n /\\bAKIA[0-9A-Z]{12,}\\b/g,\n // Slack tokens.\n /\\bxox[abprso]-[A-Za-z0-9-]{12,}\\b/g,\n // Google API keys (Maps, GCP, Firebase, etc.).\n /\\bAIza[0-9A-Za-z_-]{35}\\b/g,\n // OpenAI API keys (legacy, project-scoped, service-account).\n /\\bsk-(?:proj-|svcacct-)?[A-Za-z0-9_-]{20,}\\b/g,\n // Anthropic API keys.\n /\\bsk-ant-(?:api03-)?[A-Za-z0-9_-]{40,}\\b/g,\n // Twilio account / API SIDs (paired secret would also be redacted by the\n // Stripe-shape regex if it's prefixed sk_).\n /\\bAC[a-f0-9]{32}\\b/g,\n /\\bSK[a-f0-9]{32}\\b/g,\n]\n\nexport function scrubCredentials(text: string): string {\n let out = text\n for (const re of SENSITIVE_TOKEN_PATTERNS) {\n out = out.replace(re, '[redacted-token]')\n }\n return out\n}\n","import type { DeviceContext } from '../types'\nimport { scrubCredentials } from './scrub'\nimport { sanitizeUrl } from './urlSanitizer'\n\nexport function collectDevice(): DeviceContext {\n const nav = navigator as Navigator & { connection?: { effectiveType?: string }; deviceMemory?: number }\n const connection = nav.connection?.effectiveType\n const deviceMemory = nav.deviceMemory\n // Auth flows commonly hand off via query-string tokens (`?token=…`,\n // `?code=…`); when the user clicks through to the host page, both the\n // raw referrer AND the host's own pathname can carry those values.\n // Run both through the same sanitizer used for fetch/XHR; collapse the\n // result back to a path-only string so we don't accidentally upload the\n // operator-side host's origin as if it were the captured page's URL.\n const rawReferrer = document.referrer || undefined\n const referrer = rawReferrer !== undefined ? sanitizeUrl(rawReferrer) : undefined\n const sanitizedHere = sanitizeUrl(window.location.pathname + window.location.search)\n let pathname: string\n try {\n const parsed = new URL(sanitizedHere, window.location.origin)\n pathname = parsed.pathname + parsed.search\n } catch {\n pathname = window.location.pathname\n }\n return {\n viewport: { w: window.innerWidth, h: window.innerHeight, dpr: window.devicePixelRatio || 1 },\n screen: { w: window.screen.width, h: window.screen.height },\n platform: nav.platform,\n language: nav.language,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n timezoneOffset: new Date().getTimezoneOffset(),\n ...(connection !== undefined && { connection }),\n online: nav.onLine,\n ...(deviceMemory !== undefined && { deviceMemory }),\n hardwareConcurrency: nav.hardwareConcurrency,\n ...(referrer !== undefined && { referrer }),\n // Hosts commonly stuff credentials / PII into page titles\n // (\"Inbox (3) — bob@x.com\", \"Reset password — token=abc\"). Cap to a\n // sane length and run through the same scrubber the console/error\n // paths use.\n title: scrubCredentials((document.title || '')).slice(0, 200),\n pathname,\n }\n}\n","import type { RingBuffer } from './ringBuffer'\nimport type { ConsoleEntry } from '../types'\nimport { scrubCredentials } from './scrub'\n\ntype ConsoleLevel = 'log' | 'info' | 'warn' | 'error' | 'debug'\n\nfunction safeStringify(arg: unknown): string {\n if (arg == null) return String(arg)\n if (typeof arg === 'string') return arg\n if (typeof arg === 'number' || typeof arg === 'boolean') return String(arg)\n if (arg instanceof Error) return `${arg.name}: ${arg.message}`\n if (arg instanceof Element) return `<${arg.tagName.toLowerCase()}>`\n try {\n return JSON.stringify(arg, (_k, v) => (typeof v === 'bigint' ? v.toString() : v))\n } catch {\n try { return String(arg) } catch { return '[unserializable]' }\n }\n}\n\nexport function installConsolePatch(buffer: RingBuffer<ConsoleEntry>): () => void {\n const levels: ConsoleLevel[] = ['log', 'info', 'warn', 'error', 'debug']\n const originals: Partial<Record<ConsoleLevel, (...args: unknown[]) => void>> = {}\n for (const level of levels) {\n const original = console[level] as ((...args: unknown[]) => void) | undefined\n if (typeof original !== 'function') continue\n originals[level] = original\n console[level] = function patched(...args: unknown[]) {\n try {\n const rawMessage = args.map(safeStringify).join(' ')\n const message = scrubCredentials(rawMessage).slice(0, 2000)\n const entry: ConsoleEntry = { level, message, ts: Date.now() }\n if (level === 'error') {\n const stack = new Error().stack\n if (stack) entry.stack = stack.split('\\n').slice(2, 8).join('\\n')\n }\n buffer.push(entry)\n } catch { /* never break console */ }\n original.apply(console, args)\n }\n }\n return () => {\n for (const [level, fn] of Object.entries(originals)) {\n (console as unknown as Record<string, (...args: unknown[]) => void>)[level] = fn\n }\n }\n}\n","import type { RingBuffer } from './ringBuffer'\nimport type { NetworkEntry } from '../types'\nimport { scrubCredentials } from './scrub'\n\nexport function installFetchPatch(buffer: RingBuffer<NetworkEntry>, sanitize: (url: string) => string): () => void {\n if (typeof window === 'undefined' || typeof window.fetch !== 'function') return () => {}\n const original = window.fetch.bind(window)\n window.fetch = async function patched(input: RequestInfo | URL, init?: RequestInit) {\n const start = performance.now()\n const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url\n const method = (init?.method || (input instanceof Request ? input.method : 'GET')).toUpperCase()\n try {\n const response = await original(input, init)\n buffer.push({ url: sanitize(url), method, status: response.status, durationMs: Math.round(performance.now() - start), ts: Date.now() })\n return response\n } catch (err) {\n // Error messages from fetch (CORS rejection, abort, etc.) sometimes\n // echo the requested URL back including its full query string. Scrub\n // for the same patterns the console path does.\n const rawErr = err instanceof Error ? err.message : String(err)\n buffer.push({\n url: sanitize(url), method, status: 0,\n durationMs: Math.round(performance.now() - start),\n ts: Date.now(),\n error: scrubCredentials(rawErr),\n })\n throw err\n }\n }\n return () => { window.fetch = original }\n}\n\nexport function installXhrPatch(buffer: RingBuffer<NetworkEntry>, sanitize: (url: string) => string): () => void {\n if (typeof window === 'undefined' || typeof window.XMLHttpRequest !== 'function') return () => {}\n const Original = window.XMLHttpRequest\n const originalOpen = Original.prototype.open\n const originalSend = Original.prototype.send\n\n Original.prototype.open = function patchedOpen(this: XMLHttpRequest & { __mfb?: { method: string; url: string; start: number } }, method: string, url: string | URL) {\n this.__mfb = { method: method.toUpperCase(), url: typeof url === 'string' ? url : url.toString(), start: performance.now() }\n return originalOpen.apply(this, arguments as unknown as Parameters<typeof originalOpen>)\n }\n\n Original.prototype.send = function patchedSend(this: XMLHttpRequest & { __mfb?: { method: string; url: string; start: number } }, body?: Document | XMLHttpRequestBodyInit | null) {\n this.addEventListener('loadend', () => {\n try {\n const ctx = this.__mfb\n if (!ctx) return\n buffer.push({\n url: sanitize(ctx.url),\n method: ctx.method,\n status: this.status,\n durationMs: Math.round(performance.now() - ctx.start),\n ts: Date.now(),\n })\n } catch { /* noop */ }\n })\n return originalSend.call(this, body ?? null)\n }\n\n return () => {\n Original.prototype.open = originalOpen\n Original.prototype.send = originalSend\n }\n}\n","import type { RingBuffer } from './ringBuffer'\nimport type { ErrorEntry } from '../types'\nimport { scrubCredentials } from './scrub'\n\n// Errors that bubble up to `window.error` or `unhandledrejection` often\n// carry credentials in plain text — e.g. an SDK throws `new Error(\"Bearer\n// eyJ... rejected (401)\")`, or a promise rejects with `{apiKey: \"sk_live_…\"}`.\n// Both ship to the central backend unless we scrub here.\n\nexport function installErrorHandlers(buffer: RingBuffer<ErrorEntry>): () => void {\n if (typeof window === 'undefined') return () => {}\n const onError = (e: ErrorEvent) => {\n const rawStack = e.error instanceof Error ? e.error.stack : undefined\n const stack = rawStack !== undefined ? scrubCredentials(rawStack) : undefined\n buffer.push({\n message: scrubCredentials(e.message || 'Unknown error'),\n ...(stack !== undefined && { stack }),\n ts: Date.now(),\n source: 'window.error',\n })\n }\n const onRejection = (e: PromiseRejectionEvent) => {\n const reason = e.reason\n const rawMessage =\n reason instanceof Error ? reason.message :\n typeof reason === 'string' ? reason :\n (() => { try { return JSON.stringify(reason) } catch { return String(reason) } })()\n const rawStack = reason instanceof Error ? reason.stack : undefined\n const stack = rawStack !== undefined ? scrubCredentials(rawStack) : undefined\n buffer.push({\n message: scrubCredentials(rawMessage),\n ...(stack !== undefined && { stack }),\n ts: Date.now(),\n source: 'unhandledrejection',\n })\n }\n window.addEventListener('error', onError)\n window.addEventListener('unhandledrejection', onRejection)\n return () => {\n window.removeEventListener('error', onError)\n window.removeEventListener('unhandledrejection', onRejection)\n }\n}\n","import { sanitizeUrl } from './urlSanitizer'\n\nexport interface PerformanceSnapshot {\n navigation?: { type: string; duration: number }\n longTasks: { duration: number; startTime: number }[]\n slowResources: { name: string; duration: number; initiatorType: string }[]\n}\n\nexport function createPerformanceCollector(slowResourceMs = 1000) {\n const longTasks: PerformanceSnapshot['longTasks'] = []\n const slowResources: PerformanceSnapshot['slowResources'] = []\n let observer: PerformanceObserver | null = null\n\n if (typeof PerformanceObserver !== 'undefined') {\n try {\n observer = new PerformanceObserver((list) => {\n for (const entry of list.getEntries()) {\n if (entry.entryType === 'longtask') {\n longTasks.push({ duration: entry.duration, startTime: entry.startTime })\n while (longTasks.length > 20) longTasks.shift()\n } else if (entry.entryType === 'resource') {\n const e = entry as PerformanceResourceTiming\n if (e.duration > slowResourceMs) {\n // sanitizeUrl strips credential-shaped query params + the hash\n // — the resource URL is otherwise raw input from any third-party\n // request the host page makes (analytics pixels, OAuth\n // callbacks with tokens in the query, etc.).\n slowResources.push({ name: sanitizeUrl(e.name), duration: e.duration, initiatorType: e.initiatorType })\n while (slowResources.length > 20) slowResources.shift()\n }\n }\n }\n })\n observer.observe({ entryTypes: ['longtask', 'resource'] })\n } catch { /* unsupported */ }\n }\n\n return {\n snapshot(): PerformanceSnapshot {\n const nav = typeof performance !== 'undefined' ? performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming | undefined : undefined\n const navigation = nav ? { type: nav.type, duration: nav.duration } : undefined\n return {\n ...(navigation !== undefined && { navigation }),\n longTasks: longTasks.slice(),\n slowResources: slowResources.slice(),\n }\n },\n dispose() { observer?.disconnect() },\n }\n}\n","export class RingBuffer<T> {\n private items: T[] = []\n constructor(private readonly max: number) {}\n\n push(item: T): void {\n this.items.push(item)\n while (this.items.length > this.max) this.items.shift()\n }\n\n snapshot(): T[] {\n return this.items.slice()\n }\n\n clear(): void {\n this.items.length = 0\n }\n}\n","import { sanitizeUrl } from './urlSanitizer'\nimport { collectDevice } from './device'\nimport { installConsolePatch } from './console'\nimport { installFetchPatch, installXhrPatch } from './network'\nimport { installErrorHandlers } from './errors'\nimport { createPerformanceCollector } from './performance'\nimport { RingBuffer } from './ringBuffer'\nimport type { CapturedContext, ConsoleEntry, ErrorEntry, NetworkEntry } from '../types'\n\nexport interface CaptureOptions {\n sanitizeUrl?: (url: string) => string\n maxConsole?: number\n maxNetwork?: number\n maxErrors?: number\n}\n\nexport interface CaptureHandle {\n snapshot(): CapturedContext\n clear(): void\n dispose(): void\n}\n\nexport function installCapture(options: CaptureOptions = {}): CaptureHandle {\n const { maxConsole = 50, maxNetwork = 50, maxErrors = 20 } = options\n const sanitize = options.sanitizeUrl ?? sanitizeUrl\n\n const consoleBuf = new RingBuffer<ConsoleEntry>(maxConsole)\n const networkBuf = new RingBuffer<NetworkEntry>(maxNetwork)\n const errorBuf = new RingBuffer<ErrorEntry>(maxErrors)\n\n const uninstallConsole = installConsolePatch(consoleBuf)\n const uninstallFetch = installFetchPatch(networkBuf, sanitize)\n const uninstallXhr = installXhrPatch(networkBuf, sanitize)\n const uninstallErrors = installErrorHandlers(errorBuf)\n const perf = createPerformanceCollector()\n\n return {\n snapshot(): CapturedContext {\n return {\n consoleLogs: consoleBuf.snapshot(),\n networkRequests: networkBuf.snapshot(),\n errors: errorBuf.snapshot(),\n device: collectDevice(),\n capturedAt: Date.now(),\n }\n },\n clear() {\n consoleBuf.clear(); networkBuf.clear(); errorBuf.clear()\n },\n dispose() {\n uninstallConsole(); uninstallFetch(); uninstallXhr(); uninstallErrors()\n perf.dispose()\n },\n }\n}\n","export const DEFAULT_STRINGS = {\n 'fab.label': 'Send feedback',\n 'page.badge.aria': 'Feedback — {n} open on this page',\n 'page.badge.aria.one': 'Feedback — 1 open on this page',\n 'page.strip.text': '{n} open reports on this page',\n 'page.strip.text.one': '1 open report on this page',\n 'page.strip.view': 'View',\n 'page.peek.title': 'On this page',\n 'page.peek.viewAll': 'View all {n}',\n 'page.peek.viewAll.one': 'View 1 open report',\n 'form.title': 'Send feedback',\n 'form.description.label': 'What happened?',\n 'form.description.placeholder': 'Describe the issue or idea in one or two sentences.',\n 'form.type.label': 'Type',\n 'form.severity.label': 'Severity',\n 'form.submit': 'Send',\n 'form.cancel': 'Cancel',\n 'form.close': 'Close',\n 'form.submitting': 'Sending…',\n 'form.success': 'Thanks — your feedback was sent.',\n 'form.success.seq': 'Thanks — report #{seq} was sent. ✓',\n 'form.discard.title': 'Unsaved changes',\n 'form.discard.body': 'Discard your feedback?',\n 'form.discard.keep': 'Keep editing',\n 'form.discard.confirm': 'Discard',\n 'form.error': 'Could not send. Please try again.',\n 'form.description.required': 'Please describe the issue before sending.',\n 'form.screenshot.label': 'Screenshot',\n 'form.screenshot.cta_click': 'Click',\n 'form.screenshot.cta_rest': 'drop, or paste an image',\n 'form.screenshot.formats': 'PNG, JPEG or WebP — up to 10 MB',\n 'form.screenshot.remove': 'Remove screenshot',\n 'form.screenshot.annotate': 'Annotate',\n 'form.screenshot.error_type': 'Unsupported file type. Use PNG, JPEG or WebP.',\n 'form.screenshot.error_size': 'File too large (max {max} MB).',\n 'form.screenshot.error_count': 'Too many screenshots (max {max}).',\n 'form.context.label': 'Page',\n 'form.capture.notice':\n 'Console, network activity and errors are captured automatically to help us debug.',\n 'type.bug': 'Bug',\n 'type.feature': 'Feature request',\n 'type.question': 'Question',\n 'type.praise': 'Praise',\n 'type.typo': 'Typo',\n 'severity.blocker': 'Blocker',\n 'severity.high': 'High',\n 'severity.medium': 'Medium',\n 'severity.low': 'Low',\n 'annotator.title': 'Annotate screenshot',\n 'annotator.tool.rectangle': 'Rectangle',\n 'annotator.tool.arrow': 'Arrow',\n 'annotator.tool.freehand': 'Freehand',\n 'annotator.tool.text': 'Text',\n 'annotator.tool.highlight': 'Highlight',\n 'annotator.tool.blur': 'Blur (hide sensitive data)',\n 'annotator.text_prompt': 'Enter text:',\n 'annotator.color_picker': 'Custom color',\n 'annotator.undo': 'Undo',\n 'annotator.redo': 'Redo',\n 'annotator.clear': 'Clear all',\n 'annotator.count_suffix': 'annotations',\n 'annotator.loading': 'Loading…',\n 'annotator.apply': 'Apply',\n 'annotator.applying': 'Applying…',\n 'tab.send': 'Send',\n 'tab.mine': 'My reports',\n 'tab.changelog': 'This week',\n 'tab.board': 'Board',\n 'board.kpi.total': 'Total',\n 'board.kpi.new': 'New',\n 'board.kpi.in_progress': 'In progress',\n 'board.kpi.awaiting_validation': 'Awaiting validation',\n 'board.kpi.closed': 'Closed',\n 'board.kpi.rejected': 'Rejected',\n 'board.kpi.resolution_rate': 'Resolution rate',\n 'board.sort': 'Sort',\n 'board.sort.recent': 'Newest first',\n 'board.sort.oldest': 'Oldest first',\n 'board.sort.updated': 'Recently active',\n 'board.sort.severity': 'Severity',\n 'board.sort.status': 'Status',\n 'board.filter.status': 'Status',\n 'board.filter.type': 'Type',\n 'board.filter.severity': 'Severity',\n 'board.filter.search.placeholder': 'Search…',\n 'board.filter.mine': 'Mine only',\n 'board.filter.clear': 'Clear filters',\n 'board.list.empty.title': 'Nothing here yet',\n 'board.list.empty.description': 'When reports land, they’ll show up here.',\n 'board.list.loading': 'Loading…',\n 'board.list.error': 'Couldn’t load reports.',\n 'board.retry': 'Try again',\n 'board.list.you': 'you',\n 'board.list.count': '{n} of {total}',\n 'board.list.loadMore': 'Show more',\n 'board.detail.empty': 'Pick a report on the left to see its full thread.',\n 'board.detail.by': 'By',\n 'board.detail.confirm_resolution': 'Confirm resolution',\n 'board.detail.status_history': 'History',\n 'board.scope.project': 'Project reports',\n 'board.scope.mine': 'Your reports',\n 'board.scope.thisPage': 'This page',\n 'board.scope.allPages': 'All pages',\n 'board.scope.onThisPage': 'on this page',\n 'board.list.count.page': '{n} of {total} · this page',\n 'board.list.empty.page.title': 'No feedback on this page yet.',\n 'board.list.empty.page.description':\n 'Be the first to report something here — or browse the whole project.',\n 'board.back': 'Back',\n 'changelog.empty.title': 'Nothing resolved yet',\n 'changelog.empty.body': 'Once a report you sent is fixed it will appear here, grouped by week.',\n 'changelog.week_of': 'Week of {date}',\n 'changelog.resolved_one': '{count} resolved',\n 'changelog.resolved_many': '{count} resolved',\n 'mine.empty.title': 'No reports yet',\n 'mine.empty.body': 'Once you send feedback you can follow the thread here.',\n 'mine.refresh': 'Refresh',\n 'mine.loading': 'Loading…',\n 'mine.error': 'Could not load your reports.',\n 'mine.jump.placeholder': '#',\n 'mine.jump.go': 'Open',\n 'mine.jump.aria': 'Open a report by number',\n 'mine.jump.not_found': 'No report with that number.',\n 'error.rate_limited': 'Too many requests — retry in {seconds}s.',\n 'error.rate_limited_generic': 'Too many requests — try again in a moment.',\n 'mine.replies_one': '1 reply',\n 'mine.replies_many': '{count} replies',\n 'mine.filter.empty': 'No reports match this filter.',\n 'kpi.new': 'New',\n 'kpi.in_progress': 'In progress',\n 'kpi.awaiting_validation': 'Awaiting you',\n 'kpi.resolution_rate': 'Resolution rate',\n 'detail.back': 'Back',\n 'detail.thread': 'Conversation',\n 'detail.no_replies': 'No replies yet — we’ll let you know when an operator responds.',\n 'detail.compose_placeholder': 'Add a follow-up reply…',\n 'detail.compose_send': 'Reply',\n 'detail.compose_sending': 'Sending…',\n 'detail.attach_add': 'Add image',\n 'detail.attach_remove': 'Remove',\n 'detail.attachment_alt': 'Attached image',\n 'detail.copy_link': 'Copy link',\n 'detail.copied': 'Link copied',\n 'detail.edit': 'Edit',\n 'detail.edit_save': 'Save',\n 'detail.edit_cancel': 'Cancel',\n 'detail.edit_saving': 'Saving…',\n 'detail.edit_failed': 'Could not save your changes.',\n 'detail.close_cta': 'Mark as resolved',\n 'detail.close_busy': 'Marking…',\n 'detail.reopen_cta': 'Still not fixed',\n 'detail.reopen_busy': 'Reopening…',\n 'detail.teammate_notice':\n 'Viewing a teammate’s report — replies are private to the submitter.',\n 'detail.load_failed.title': 'Report not available',\n 'detail.load_failed.body':\n 'It may have been deleted, or you no longer have access to it.',\n 'detail.load_failed.cta': 'Back',\n 'detail.send_failed': 'Couldn’t send your reply. Try again.',\n 'detail.close_failed': 'Couldn’t mark as resolved. Try again.',\n 'detail.reopen_failed': 'Couldn’t reopen the report. Try again.',\n 'detail.history': 'Status history',\n 'detail.context.submitted_at': 'Submitted',\n 'detail.context.page': 'Page',\n 'detail.context.capture.manual': 'Manual capture',\n 'detail.context.capture.html2canvas': 'Auto capture',\n 'detail.context.capture.display_media': 'Screen share',\n 'detail.context.capture.none': 'No screenshot',\n 'detail.author.staff': 'Operator',\n 'detail.author.mcp': 'Mhosaic Team',\n 'detail.author.system': 'System',\n 'detail.tech.title': 'What we received',\n 'detail.tech.errors_one': 'error',\n 'detail.tech.errors_many': 'errors',\n 'detail.tech.device': 'Device',\n 'detail.tech.device.viewport': 'Viewport',\n 'detail.tech.device.platform': 'Platform',\n 'detail.tech.device.language': 'Language',\n 'detail.tech.device.timezone': 'Timezone',\n 'detail.tech.device.connection': 'Connection',\n 'detail.tech.errors': 'Runtime errors',\n 'detail.tech.console': 'Console (last 20)',\n 'detail.tech.network': 'Network (last 15)',\n 'status.new': 'New',\n 'status.in_progress': 'In progress',\n 'status.awaiting_validation': 'Awaiting your validation',\n 'status.closed': 'Closed',\n 'status.rejected': 'Rejected',\n 'status.duplicate': 'Duplicate',\n 'status.wontfix': 'Won’t fix',\n}\n\nexport type StringKey = keyof typeof DEFAULT_STRINGS\n\nconst FRENCH_STRINGS: Record<StringKey, string> = {\n 'fab.label': 'Envoyer un commentaire',\n 'page.badge.aria': 'Retours — {n} ouverts sur cette page',\n 'page.badge.aria.one': 'Retours — 1 ouvert sur cette page',\n 'page.strip.text': '{n} retours ouverts sur cette page',\n 'page.strip.text.one': '1 retour ouvert sur cette page',\n 'page.strip.view': 'Voir',\n 'page.peek.title': 'Sur cette page',\n 'page.peek.viewAll': 'Voir les {n}',\n 'page.peek.viewAll.one': 'Voir 1 retour ouvert',\n 'form.title': 'Envoyer un commentaire',\n 'form.description.label': 'Qu’est-il arrivé ?',\n 'form.description.placeholder': 'Décrivez le problème ou l’idée en une ou deux phrases.',\n 'form.type.label': 'Type',\n 'form.severity.label': 'Sévérité',\n 'form.submit': 'Envoyer',\n 'form.cancel': 'Annuler',\n 'form.close': 'Fermer',\n 'form.submitting': 'Envoi…',\n 'form.success': 'Merci — votre commentaire a été envoyé.',\n 'form.success.seq': 'Merci — rapport n°{seq} envoyé. ✓',\n 'form.discard.title': 'Modifications non enregistrées',\n 'form.discard.body': 'Abandonner votre commentaire ?',\n 'form.discard.keep': 'Continuer la saisie',\n 'form.discard.confirm': 'Abandonner',\n 'form.error': 'Échec d’envoi. Veuillez réessayer.',\n 'form.description.required': 'Décrivez le problème avant d’envoyer.',\n 'form.screenshot.label': 'Capture d’écran',\n 'form.screenshot.cta_click': 'Cliquez',\n 'form.screenshot.cta_rest': 'déposez ou collez une image',\n 'form.screenshot.formats': 'PNG, JPEG ou WebP — jusqu’à 10 Mo',\n 'form.screenshot.remove': 'Retirer la capture',\n 'form.screenshot.annotate': 'Annoter',\n 'form.screenshot.error_type': 'Format non supporté. Utilisez PNG, JPEG ou WebP.',\n 'form.screenshot.error_size': 'Fichier trop volumineux (max {max} Mo).',\n 'form.screenshot.error_count': 'Trop de captures d’écran (max {max}).',\n 'form.context.label': 'Page',\n 'form.capture.notice':\n 'La console, l’activité réseau et les erreurs sont capturées automatiquement pour faciliter le diagnostic.',\n 'type.bug': 'Bogue',\n 'type.feature': 'Suggestion',\n 'type.question': 'Question',\n 'type.praise': 'Compliment',\n 'type.typo': 'Coquille',\n 'severity.blocker': 'Bloquant',\n 'severity.high': 'Élevée',\n 'severity.medium': 'Moyenne',\n 'severity.low': 'Faible',\n 'annotator.title': 'Annoter la capture',\n 'annotator.tool.rectangle': 'Rectangle',\n 'annotator.tool.arrow': 'Flèche',\n 'annotator.tool.freehand': 'Dessin libre',\n 'annotator.tool.text': 'Texte',\n 'annotator.tool.highlight': 'Surligner',\n 'annotator.tool.blur': 'Flouter (données sensibles)',\n 'annotator.text_prompt': 'Entrez le texte :',\n 'annotator.color_picker': 'Couleur personnalisée',\n 'annotator.undo': 'Annuler',\n 'annotator.redo': 'Refaire',\n 'annotator.clear': 'Tout effacer',\n 'annotator.count_suffix': 'annotations',\n 'annotator.loading': 'Chargement…',\n 'annotator.apply': 'Appliquer',\n 'annotator.applying': 'Application…',\n 'tab.send': 'Envoyer',\n 'tab.mine': 'Mes rapports',\n 'tab.changelog': 'Cette semaine',\n 'tab.board': 'Tableau',\n 'board.kpi.total': 'Total',\n 'board.kpi.new': 'Nouveau',\n 'board.kpi.in_progress': 'En cours',\n 'board.kpi.awaiting_validation': 'À valider',\n 'board.kpi.closed': 'Fermé',\n 'board.kpi.rejected': 'Refusé',\n 'board.kpi.resolution_rate': 'Taux de résolution',\n 'board.sort': 'Trier',\n 'board.sort.recent': 'Plus récents',\n 'board.sort.oldest': 'Plus anciens',\n 'board.sort.updated': 'Activité récente',\n 'board.sort.severity': 'Sévérité',\n 'board.sort.status': 'Statut',\n 'board.filter.status': 'Statut',\n 'board.filter.type': 'Type',\n 'board.filter.severity': 'Sévérité',\n 'board.filter.search.placeholder': 'Rechercher…',\n 'board.filter.mine': 'Les miens',\n 'board.filter.clear': 'Effacer les filtres',\n 'board.list.empty.title': 'Rien à voir ici',\n 'board.list.empty.description': 'Les rapports apparaîtront ici dès qu’ils arrivent.',\n 'board.list.loading': 'Chargement…',\n 'board.list.error': 'Impossible de charger les rapports.',\n 'board.retry': 'Réessayer',\n 'board.list.you': 'vous',\n 'board.list.count': '{n} sur {total}',\n 'board.list.loadMore': 'Afficher plus',\n 'board.detail.empty': 'Sélectionnez un rapport à gauche pour voir le fil complet.',\n 'board.detail.by': 'Par',\n 'board.detail.confirm_resolution': 'Confirmer la résolution',\n 'board.detail.status_history': 'Historique',\n 'board.scope.project': 'Rapports du projet',\n 'board.scope.mine': 'Vos rapports',\n 'board.scope.thisPage': 'Cette page',\n 'board.scope.allPages': 'Toutes les pages',\n 'board.scope.onThisPage': 'sur cette page',\n 'board.list.count.page': '{n} sur {total} · cette page',\n 'board.list.empty.page.title': 'Aucun retour sur cette page pour l’instant.',\n 'board.list.empty.page.description':\n 'Soyez le premier à signaler quelque chose ici — ou parcourez tout le projet.',\n 'board.back': 'Retour',\n 'changelog.empty.title': 'Rien de résolu pour l’instant',\n 'changelog.empty.body': 'Quand un rapport que vous avez envoyé est corrigé, il apparaîtra ici, regroupé par semaine.',\n 'changelog.week_of': 'Semaine du {date}',\n 'changelog.resolved_one': '{count} résolu',\n 'changelog.resolved_many': '{count} résolus',\n 'mine.empty.title': 'Aucun rapport',\n 'mine.empty.body': 'Après votre premier envoi vous pourrez suivre la conversation ici.',\n 'mine.refresh': 'Actualiser',\n 'mine.loading': 'Chargement…',\n 'mine.error': 'Impossible de charger vos rapports.',\n 'mine.jump.placeholder': '#',\n 'mine.jump.go': 'Ouvrir',\n 'mine.jump.aria': 'Ouvrir un rapport par numéro',\n 'mine.jump.not_found': 'Aucun rapport avec ce numéro.',\n 'error.rate_limited': 'Trop de requêtes — réessaie dans {seconds} s.',\n 'error.rate_limited_generic': 'Trop de requêtes — réessaie dans un instant.',\n 'mine.replies_one': '1 réponse',\n 'mine.replies_many': '{count} réponses',\n 'mine.filter.empty': 'Aucun rapport ne correspond à ce filtre.',\n 'kpi.new': 'Nouveau',\n 'kpi.in_progress': 'En cours',\n 'kpi.awaiting_validation': 'À valider',\n 'kpi.resolution_rate': 'Taux de résolution',\n 'detail.back': 'Retour',\n 'detail.thread': 'Conversation',\n 'detail.no_replies': 'Pas encore de réponse — vous serez notifié dès qu’un opérateur répondra.',\n 'detail.compose_placeholder': 'Ajouter une réponse…',\n 'detail.compose_send': 'Répondre',\n 'detail.compose_sending': 'Envoi…',\n 'detail.attach_add': 'Ajouter une image',\n 'detail.attach_remove': 'Retirer',\n 'detail.attachment_alt': 'Image jointe',\n 'detail.copy_link': 'Copier le lien',\n 'detail.copied': 'Lien copié',\n 'detail.edit': 'Modifier',\n 'detail.edit_save': 'Enregistrer',\n 'detail.edit_cancel': 'Annuler',\n 'detail.edit_saving': 'Enregistrement…',\n 'detail.edit_failed': 'Impossible d’enregistrer vos modifications.',\n 'detail.close_cta': 'Marquer comme résolu',\n 'detail.close_busy': 'Validation…',\n 'detail.reopen_cta': 'Toujours pas réglé',\n 'detail.reopen_busy': 'Réouverture…',\n 'detail.teammate_notice':\n 'Vous consultez le rapport d’un coéquipier — les réponses sont privées au soumetteur.',\n 'detail.load_failed.title': 'Rapport indisponible',\n 'detail.load_failed.body':\n 'Il a peut-être été supprimé, ou vous n’y avez plus accès.',\n 'detail.load_failed.cta': 'Retour',\n 'detail.send_failed': 'Impossible d’envoyer votre réponse. Réessayez.',\n 'detail.close_failed':\n 'Impossible de marquer comme résolu. Réessayez.',\n 'detail.reopen_failed':\n 'Impossible de rouvrir le rapport. Réessayez.',\n 'detail.history': 'Historique du statut',\n 'detail.context.submitted_at': 'Envoyé',\n 'detail.context.page': 'Page',\n 'detail.context.capture.manual': 'Capture manuelle',\n 'detail.context.capture.html2canvas': 'Capture automatique',\n 'detail.context.capture.display_media': 'Partage d’écran',\n 'detail.context.capture.none': 'Sans capture',\n 'detail.author.staff': 'Opérateur',\n 'detail.author.mcp': 'Équipe Mhosaic',\n 'detail.author.system': 'Système',\n 'detail.tech.title': 'Ce que nous avons reçu',\n 'detail.tech.errors_one': 'erreur',\n 'detail.tech.errors_many': 'erreurs',\n 'detail.tech.device': 'Appareil',\n 'detail.tech.device.viewport': 'Fenêtre',\n 'detail.tech.device.platform': 'Plateforme',\n 'detail.tech.device.language': 'Langue',\n 'detail.tech.device.timezone': 'Fuseau horaire',\n 'detail.tech.device.connection': 'Connexion',\n 'detail.tech.errors': 'Erreurs d’exécution',\n 'detail.tech.console': 'Console (20 derniers)',\n 'detail.tech.network': 'Réseau (15 derniers)',\n 'status.new': 'Nouveau',\n 'status.in_progress': 'En cours',\n 'status.awaiting_validation': 'En attente de validation',\n 'status.closed': 'Fermé',\n 'status.rejected': 'Rejeté',\n 'status.duplicate': 'Doublon',\n 'status.wontfix': 'Non corrigé',\n}\n\nconst LOCALE_PACKS: Record<string, Record<StringKey, string>> = {\n fr: FRENCH_STRINGS,\n}\n\ninterface ResolveOptions {\n locale?: string\n}\n\nfunction packFor(locale: string | undefined): Record<StringKey, string> | null {\n if (!locale) return null\n // Match the language subtag only (fr-CA, fr-FR → fr).\n const tag = locale.toLowerCase().split(/[-_]/)[0]!\n return LOCALE_PACKS[tag] ?? null\n}\n\nexport function resolveStrings(\n overrides: Record<string, string>,\n options: ResolveOptions = {},\n): Record<StringKey, string> {\n const localePack = packFor(options.locale) ?? {}\n return {\n ...DEFAULT_STRINGS,\n ...localePack,\n ...overrides,\n } as Record<StringKey, string>\n}\n","import { h, render } from 'preact'\nimport { useCallback, useEffect, useRef, useState } from 'preact/hooks'\n\nimport type { ApiClient } from '../api/client'\nimport type { SubmittedReport } from '../types'\nimport { currentPagePath } from './currentPage'\nimport { BoardView } from './BoardView'\nimport { ChangelogList } from './ChangelogList'\nimport { Fab } from './Fab'\nimport { Form, type FormValues } from './Form'\nimport { MineList } from './MineList'\nimport { Modal } from './Modal'\nimport { PageActivityStrip } from './PageActivityStrip'\nimport { PagePeekPanel } from './PagePeekPanel'\nimport { fetchPageKpis, usePageOpenCount } from './pageSignal'\nimport { ReportDetailView } from './ReportDetailView'\nimport { WIDGET_STYLES } from './styles'\nimport type { StringKey } from './i18n'\n\nexport interface MountOptions {\n host: HTMLElement\n strings: Record<StringKey, string>\n showFAB: boolean\n onSubmit: (values: FormValues) => Promise<SubmittedReport | void>\n /** v0.7: ApiClient for the conversation-loop reader UI. Optional so\n * the synthetic / auto-error path keeps working with no host changes. */\n api?: ApiClient\n /** v0.7: callback that returns the host's identified user id, or\n * undefined when no `identify()` has been called yet. The \"My\n * reports\" tab + FAB visibility both depend on this. */\n getExternalId?: () => string | undefined\n /** Phase 4: when true (driven by the manifest's requires_visibility_check),\n * the FAB renders only if `checkVisibility` confirms this end-user is on the\n * project allowlist. Off (default) → the legacy showFAB/identity path. */\n requiresVisibilityCheck?: boolean\n checkVisibility?: (externalId: string) => Promise<boolean>\n /** Host override for the current-page path — same contract as\n * FeedbackConfig.getCurrentPage. Threaded to BoardView so the \"Cette page\"\n * chip resolves identically to what the submit side records. */\n getCurrentPage?: () => string | null\n /** Opt-in: FAB opens to the page-scoped Board when the current page has\n * feedback (else Send). Default false — never hijack the submit flow. */\n openToCurrentPageFeedback?: boolean\n /** Ambient page-activity surfaces (badge/peek/strip). Default true. */\n showPageActivity?: boolean\n /** #85 — deep-link / permalink support. On mount the widget reads this\n * query param from the host URL and, if it holds a report id or #seq,\n * opens that report directly (read-only — the host URL is never mutated).\n * The detail view also offers a \"copy link\" button built from it.\n * Default `'mhfeedback'`; pass `false` to disable entirely. */\n deepLinkParam?: string | false\n}\n\n/** Pure FAB-visibility decision (Phase 4 adds the `visibilityAllowed` gate on\n * top of the showFAB + identity rules). Exported for unit testing. */\nexport function computeFabVisible(\n opts: Pick<MountOptions, 'showFAB' | 'getExternalId' | 'requiresVisibilityCheck'>,\n externalId: string | undefined,\n visibilityAllowed: boolean,\n): boolean {\n if (!opts.showFAB) return false\n if (opts.getExternalId !== undefined && !externalId) return false\n if (opts.requiresVisibilityCheck && !visibilityAllowed) return false\n return true\n}\n\nexport interface MountHandle {\n open(): void\n close(): void\n dispose(): void\n /** Force a re-render — e.g. after the host calls `identify()` so\n * the FAB becomes visible without a page reload. */\n notifyIdentityChanged(): void\n}\n\ntype Status = 'idle' | 'submitting' | 'error' | 'success'\ntype Tab = 'send' | 'mine' | 'changelog' | 'board'\ninterface State {\n open: boolean\n status: Status\n error?: string\n tab: Tab\n /** When set, MineList is replaced by the detail view for that report id. */\n selectedReportId?: string\n /** Set by a peek-panel row click — BoardView opens with this report\n * pre-selected (spec §3.2). */\n boardSelectId?: string\n /** Sequence number of the just-submitted report, shown in the success\n * acknowledgement (\"report #42 sent\") (#82). */\n submittedSeq?: number\n /** When true, the \"discard unsaved changes?\" confirmation is shown over\n * the form instead of closing on an accidental backdrop/Esc dismiss (#89). */\n confirmingDiscard?: boolean\n}\n\nexport function mountWidget(options: MountOptions): MountHandle {\n const shadow = options.host.attachShadow({ mode: 'open' })\n const style = document.createElement('style')\n style.textContent = WIDGET_STYLES\n shadow.appendChild(style)\n const mountPoint = document.createElement('div')\n shadow.appendChild(mountPoint)\n\n let currentState: State = { open: false, status: 'idle', tab: 'send' }\n let postSubmitTimer: ReturnType<typeof setTimeout> | null = null\n // Tracks whether the send Form currently holds unsaved input, so an\n // accidental backdrop/Esc dismiss can be guarded with a confirmation (#89).\n let formDirty = false\n\n function rerender(state: State) {\n currentState = state\n render(h(Root, { state }), mountPoint)\n }\n\n /** Strip selectedReportId rather than reassigning `undefined` — under\n * exactOptionalPropertyTypes the explicit `undefined` is not assignable\n * back into the optional slot. */\n function clearSelected(s: State): State {\n const { selectedReportId: _drop, boardSelectId: _drop2, ...rest } = s\n void _drop\n void _drop2\n return rest\n }\n\n /** Open the modal to `send` immediately (non-blocking), then switch to\n * `board` if the current page already has feedback (total > 0).\n * Errors from fetchBoardKpis are silently swallowed — a slow or failing\n * network must never delay or break the FAB open. */\n function openWidget(externalId: string | undefined): void {\n rerender({ ...currentState, open: true, tab: 'send' })\n if (options.openToCurrentPageFeedback && options.api && externalId) {\n // Through the pageSignal cache: the FAB badge fetched these exact\n // KPIs moments ago — don't fire a second identical request per open.\n fetchPageKpis(options.api, externalId, currentPagePath(options))\n .then((kpis) => {\n if (kpis.total > 0 && currentState.open && currentState.tab === 'send') {\n rerender({ ...currentState, tab: 'board' })\n }\n })\n .catch(() => {\n // best-effort; stay on send\n })\n }\n }\n\n function Root({ state }: { state: State }) {\n const handleSubmit = useCallback(async (values: FormValues) => {\n rerender({ ...currentState, status: 'submitting' })\n try {\n const result = await options.onSubmit(values)\n pageSignal.refresh()\n rerender({\n ...currentState,\n status: 'success',\n ...(result && result.seq !== undefined && { submittedSeq: result.seq }),\n })\n // Track the close-the-modal timer so dispose() can cancel it.\n // Without this, calling shutdown() mid-submit (e.g. SPA route\n // change right after the user hits send) lets the timer fire\n // after teardown and rerender into a stale shadow root.\n if (postSubmitTimer !== null) clearTimeout(postSubmitTimer)\n postSubmitTimer = setTimeout(() => {\n postSubmitTimer = null\n rerender({\n ...currentState,\n open: false,\n status: 'idle',\n // After a successful submit, jump the user to \"My reports\" so\n // they immediately see their just-filed report in the list\n // (and the conversation that's about to start).\n tab: options.api ? 'mine' : 'send',\n })\n }, 1200)\n } catch (err) {\n // Don't leak raw API errors to the form's error banner (e.g.\n // `Feedback submit failed: 429 Too Many Requests {…}` straight\n // into a `<div class=\"error\">`). The strings table already has\n // `form.error` — a friendly fallback. Raw error to console.\n if (typeof console !== 'undefined')\n console.warn('[mhosaic] submit:', err)\n rerender({\n ...currentState,\n status: 'error',\n error: options.strings['form.error'],\n })\n }\n }, [])\n\n const externalId = options.getExternalId?.()\n // Resolve a report by its per-project #seq and open its detail. Shared by\n // the jump-by-#seq box and clickable \"#NN\" references (#85). Resolves\n // false when the seq can't be opened (unknown / no access / throttled).\n const openSeq = (seq: number): Promise<boolean> => {\n if (!options.api || !externalId) return Promise.resolve(false)\n return options.api\n .getReportBySeq(seq, externalId)\n .then((r) => {\n rerender({ ...currentState, open: true, tab: 'mine', selectedReportId: r.id })\n return true\n })\n .catch(() => false)\n }\n // Build a shareable permalink for a report id (#85), unless deep-linking\n // is disabled. Sets/replaces the param on the current URL without mutating\n // the live location (the returned string is only ever copied to clipboard).\n const buildPermalink =\n options.deepLinkParam === false\n ? undefined\n : (id: string): string => {\n const param = options.deepLinkParam || 'mhfeedback'\n try {\n const u = new URL(window.location.href)\n u.searchParams.set(param, id)\n return u.toString()\n } catch {\n return ''\n }\n }\n const pageActivityEnabled = options.showPageActivity !== false\n const pageSignal = usePageOpenCount({\n ...(options.api !== undefined && { api: options.api }),\n ...(externalId !== undefined && { externalId }),\n ...(options.getCurrentPage !== undefined && { getCurrentPage: options.getCurrentPage }),\n enabled: pageActivityEnabled,\n })\n const fabLabel =\n pageSignal.open === 1\n ? options.strings['page.badge.aria.one']\n : pageSignal.open > 0\n ? options.strings['page.badge.aria'].replace('{n}', String(pageSignal.open))\n : options.strings['fab.label']\n // Phase 4: per-end-user server visibility. Default true unless the project\n // requires a check; then false until checkVisibility() confirms allowlist\n // membership. Re-runs whenever the resolved identity changes.\n const [visibilityAllowed, setVisibilityAllowed] = useState(\n !options.requiresVisibilityCheck,\n )\n useEffect(() => {\n if (!options.requiresVisibilityCheck) {\n setVisibilityAllowed(true)\n return\n }\n if (!externalId || !options.checkVisibility) {\n setVisibilityAllowed(false)\n return\n }\n let cancelled = false\n options\n .checkVisibility(externalId)\n .then((show) => {\n if (!cancelled) setVisibilityAllowed(show)\n })\n .catch(() => {\n if (!cancelled) setVisibilityAllowed(false)\n })\n return () => {\n cancelled = true\n }\n }, [externalId])\n\n // FAB-visibility gate: showFAB + (identity resolved when the host wired\n // identity) + (server visibility when the project gates it). Legacy hosts\n // that never wired identity stay on the old \"showFAB → visible\" path.\n const fabVisible = computeFabVisible(options, externalId, visibilityAllowed)\n const showMineTab = Boolean(options.api && externalId)\n\n const [peekOpen, setPeekOpen] = useState(false)\n // Hover intent: 250ms to open (no accidental flashes), 140ms debounce to\n // close (QA-Meter precedent) so moving FAB→panel doesn't flicker.\n const peekTimers = useRef<{\n open?: ReturnType<typeof setTimeout>\n close?: ReturnType<typeof setTimeout>\n }>({})\n const peekEnter = () => {\n if (peekTimers.current.close) clearTimeout(peekTimers.current.close)\n peekTimers.current.open = setTimeout(() => setPeekOpen(true), 250)\n }\n const peekLeave = () => {\n if (peekTimers.current.open) clearTimeout(peekTimers.current.open)\n peekTimers.current.close = setTimeout(() => setPeekOpen(false), 140)\n }\n\n return (\n <>\n {fabVisible && (\n <div\n class=\"fab-area\"\n onMouseEnter={peekEnter}\n onMouseLeave={peekLeave}\n onFocusIn={(e: FocusEvent) => {\n // Modal restores focus to the FAB on every dismissal (X,\n // backdrop, Escape, the post-submit auto-close) — that's a\n // programmatic re-focus, not deliberate intent, so only\n // open for keyboard-modality focus (:focus-visible). This\n // also clears any pending mouse-leave close timer so a\n // hover→tab handoff doesn't get closed out from under a\n // keyboard user by a stale 140ms timeout.\n if (peekTimers.current.close) clearTimeout(peekTimers.current.close)\n if ((e.target as HTMLElement).matches?.(':focus-visible')) setPeekOpen(true)\n }}\n onFocusOut={() => setPeekOpen(false)}\n onKeyDown={(e: KeyboardEvent) => e.key === 'Escape' && setPeekOpen(false)}\n >\n {peekOpen &&\n !state.open &&\n pageActivityEnabled &&\n pageSignal.open > 0 &&\n options.api &&\n externalId && (\n <PagePeekPanel\n api={options.api}\n externalId={externalId}\n strings={options.strings}\n {...(options.getCurrentPage !== undefined && {\n getCurrentPage: options.getCurrentPage,\n })}\n open={pageSignal.open}\n onViewAll={() => {\n setPeekOpen(false)\n rerender({ ...currentState, open: true, tab: 'board' })\n }}\n onPickRow={(reportId) => {\n setPeekOpen(false)\n rerender({ ...currentState, open: true, tab: 'board', boardSelectId: reportId })\n }}\n onSend={() => {\n setPeekOpen(false)\n openWidget(externalId)\n }}\n />\n )}\n <Fab\n label={fabLabel}\n onClick={() => openWidget(externalId)}\n {...(pageSignal.open > 0 && { count: pageSignal.open })}\n />\n </div>\n )}\n {state.open && (\n <Modal\n onDismiss={(reason) => {\n // Guard an accidental dismiss (backdrop/Esc) while the send\n // form holds unsaved input — show a discard confirmation\n // instead of closing. The explicit ✕ (reason 'button') and\n // the Cancel action always close (#89).\n if (\n reason !== 'button' &&\n formDirty &&\n currentState.tab === 'send' &&\n currentState.status !== 'submitting'\n ) {\n rerender({ ...currentState, confirmingDiscard: true })\n return\n }\n formDirty = false\n rerender(\n clearSelected({\n ...currentState,\n open: false,\n status: 'idle',\n confirmingDiscard: false,\n }),\n )\n }}\n closeLabel={options.strings['form.close']}\n expanded={state.tab === 'board'}\n >\n {state.confirmingDiscard && (\n <div class=\"discard-confirm\" role=\"alertdialog\" aria-modal=\"true\">\n <div class=\"discard-confirm-card\">\n <p class=\"discard-confirm-title\">\n {options.strings['form.discard.title']}\n </p>\n <p class=\"discard-confirm-body\">\n {options.strings['form.discard.body']}\n </p>\n <div class=\"discard-confirm-actions\">\n <button\n type=\"button\"\n class=\"btn\"\n onClick={() =>\n rerender({ ...currentState, confirmingDiscard: false })\n }\n >\n {options.strings['form.discard.keep']}\n </button>\n <button\n type=\"button\"\n class=\"btn btn--primary\"\n onClick={() => {\n formDirty = false\n rerender(\n clearSelected({\n ...currentState,\n open: false,\n status: 'idle',\n confirmingDiscard: false,\n }),\n )\n }}\n >\n {options.strings['form.discard.confirm']}\n </button>\n </div>\n </div>\n </div>\n )}\n {showMineTab && (\n <div class=\"tab-strip\" role=\"tablist\">\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={state.tab === 'send'}\n class={`tab-button ${state.tab === 'send' ? 'is-active' : ''}`}\n onClick={() =>\n rerender(clearSelected({ ...currentState, tab: 'send' }))\n }\n >\n {options.strings['tab.send']}\n </button>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={state.tab === 'mine'}\n class={`tab-button ${state.tab === 'mine' ? 'is-active' : ''}`}\n onClick={() =>\n rerender(clearSelected({ ...currentState, tab: 'mine' }))\n }\n >\n {options.strings['tab.mine']}\n </button>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={state.tab === 'changelog'}\n class={`tab-button ${state.tab === 'changelog' ? 'is-active' : ''}`}\n onClick={() =>\n rerender(clearSelected({ ...currentState, tab: 'changelog' }))\n }\n >\n {options.strings['tab.changelog']}\n </button>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={state.tab === 'board'}\n class={`tab-button tab-button--board ${state.tab === 'board' ? 'is-active' : ''}`}\n onClick={() =>\n rerender(clearSelected({ ...currentState, tab: 'board' }))\n }\n >\n {options.strings['tab.board']}\n </button>\n </div>\n )}\n {state.tab === 'send' && (\n <>\n {showMineTab && pageActivityEnabled && (\n <PageActivityStrip\n open={pageSignal.open}\n strings={options.strings}\n onView={() =>\n rerender(clearSelected({ ...currentState, tab: 'board' }))\n }\n />\n )}\n <Form\n strings={options.strings}\n onSubmit={handleSubmit}\n onCancel={() =>\n rerender({ ...currentState, open: false, status: 'idle' })\n }\n status={state.status}\n {...(state.error !== undefined && { errorMessage: state.error })}\n {...(state.submittedSeq !== undefined && {\n submittedSeq: state.submittedSeq,\n })}\n onDirtyChange={(d) => {\n formDirty = d\n }}\n />\n </>\n )}\n {state.tab === 'mine' && options.api && externalId && !state.selectedReportId && (\n <MineList\n api={options.api}\n externalId={externalId}\n strings={options.strings}\n onSelect={(row) =>\n rerender({ ...currentState, selectedReportId: row.id })\n }\n onOpenSeq={openSeq}\n />\n )}\n {(state.tab === 'mine' || state.tab === 'changelog') &&\n options.api &&\n externalId &&\n state.selectedReportId && (\n <ReportDetailView\n api={options.api}\n externalId={externalId}\n reportId={state.selectedReportId}\n strings={options.strings}\n onBack={() =>\n rerender(clearSelected({ ...currentState }))\n }\n onOpenSeq={openSeq}\n {...(buildPermalink && { buildPermalink })}\n />\n )}\n {state.tab === 'changelog' &&\n options.api &&\n externalId &&\n !state.selectedReportId && (\n <ChangelogList\n api={options.api}\n externalId={externalId}\n strings={options.strings}\n onSelect={(row) =>\n rerender({ ...currentState, selectedReportId: row.id })\n }\n />\n )}\n {state.tab === 'board' && options.api && externalId && (\n // BoardView owns its own master/detail navigation — no\n // selectedReportId routing through the modal-level state.\n <BoardView\n api={options.api}\n externalId={externalId}\n strings={options.strings}\n {...(options.getCurrentPage !== undefined && { getCurrentPage: options.getCurrentPage })}\n {...(state.boardSelectId !== undefined && { initialSelectedId: state.boardSelectId })}\n />\n )}\n </Modal>\n )}\n </>\n )\n }\n\n rerender(currentState)\n\n /** #85 — open a report by id or #seq. Numeric refs resolve via the\n * by-seq endpoint; anything else is treated as a report id. Best-effort:\n * a stale/inaccessible ref (or a missing identity) just no-ops. */\n function openReportRef(ref: string): void {\n const api = options.api\n const externalId = options.getExternalId?.()\n if (!api || !externalId || !ref) return\n const seq = /^\\d+$/.test(ref) ? parseInt(ref, 10) : null\n const resolve =\n seq !== null ? api.getReportBySeq(seq, externalId) : api.getReport(ref, externalId)\n void resolve\n .then((r) =>\n rerender({ ...currentState, open: true, tab: 'mine', selectedReportId: r.id }),\n )\n .catch(() => {\n /* unknown ref / no access / throttled — silent */\n })\n }\n\n // Deep-link on load: if the host URL carries the permalink param, open that\n // report. Read-only — we never write back to the host's URL.\n if (options.deepLinkParam !== false) {\n try {\n const param = options.deepLinkParam || 'mhfeedback'\n const value = new URLSearchParams(window.location.search).get(param)\n if (value) openReportRef(value.trim())\n } catch {\n /* no window / malformed URL — skip */\n }\n }\n\n return {\n open() {\n openWidget(options.getExternalId?.())\n },\n close() {\n rerender({ ...currentState, open: false, status: 'idle' })\n },\n dispose() {\n if (postSubmitTimer !== null) {\n clearTimeout(postSubmitTimer)\n postSubmitTimer = null\n }\n render(null, mountPoint)\n options.host.innerHTML = ''\n },\n notifyIdentityChanged() {\n rerender({ ...currentState })\n },\n }\n}\n","/** Current host-page path for \"feedback on this page\". A host getCurrentPage()\n * override (same contract as QaMeterConfig.getCurrentPage) wins; else the raw\n * pathname. The backend turns this into the canonical key (derive_page_key),\n * so the widget never computes the key itself. */\nexport function currentPagePath(opts: { getCurrentPage?: () => string | null }): string {\n const override = opts.getCurrentPage?.()\n if (override) return override\n return typeof window !== 'undefined' ? window.location.pathname : '/'\n}\n\nconst LOCATION_CHANGE = 'mfb:locationchange'\nlet patched = false\n\nfunction patchHistory(): void {\n if (patched || typeof history === 'undefined') return\n patched = true\n for (const m of ['pushState', 'replaceState'] as const) {\n const orig = history[m]\n history[m] = function (this: History, ...args: unknown[]) {\n const r = (orig as (...a: unknown[]) => unknown).apply(this, args)\n window.dispatchEvent(new Event(LOCATION_CHANGE))\n return r\n } as History[typeof m]\n }\n}\n\n/** Subscribe to client-side navigation (SPA + back/forward). Returns an\n * unsubscribe. Mirrors the QA Meter's history-patch approach. */\nexport function onLocationChange(cb: () => void): () => void {\n patchHistory()\n window.addEventListener('popstate', cb)\n window.addEventListener(LOCATION_CHANGE, cb)\n return () => {\n window.removeEventListener('popstate', cb)\n window.removeEventListener(LOCATION_CHANGE, cb)\n }\n}\n","/**\n * BoardView — the \"Voir tout\" surface (v0.12).\n *\n * Renders the project's reports in a master/detail layout with a KPI\n * strip on top and a filter row below it. Scope follows the project's\n * `share_reports_with_widget` flag server-side; the client just renders\n * what the endpoint returns and uses `is_mine` to permission-gate the\n * detail-pane status buttons.\n *\n * Architecture notes:\n * - The list polls every 30s while open (same cadence as MineList),\n * pausing in hidden tabs and backing off on failures — see poll.ts.\n * Filters trigger an immediate refetch.\n * - The KPI strip refetches alongside the list so the counts always\n * match what the user sees.\n * - Detail pane is the existing ReportDetailView used by My-reports +\n * This-week. Permission-gating happens via the `canModerate` prop\n * (false for project-scoped rows that aren't `is_mine`).\n * - The whole tab assumes the Modal it's rendered into has been\n * `expanded` — see Modal.tsx + .modal.is-expanded in styles.ts.\n */\n\nimport { h, type JSX } from 'preact'\nimport { useEffect, useMemo, useState } from 'preact/hooks'\n\nimport type { ApiClient, BoardListPage } from '../api/client'\nimport type {\n BoardFilters,\n BoardSortKey,\n FeedbackSeverity,\n FeedbackType,\n ReportStatus,\n WidgetBoardKpis,\n WidgetBoardRow,\n} from '../types'\nimport type { StringKey } from './i18n'\nimport { boardCacheKey, readBoardCache, writeBoardCache } from './boardCache'\nimport { loadBoardView, saveBoardView } from './boardViewStore'\nimport { currentPagePath, onLocationChange } from './currentPage'\nimport { startPoll } from './poll'\nimport { rateLimitMessage } from './rateLimit'\nimport { ReportDetailView } from './ReportDetailView'\n\nconst POLL_MS = 30_000\n\n// The search box debounces before it drives a fetch. Every keystroke used to\n// spin up an immediate project-wide listBoard + fetchBoardKpis pair, so a fast\n// typist fired a burst of expensive `q=` scans that OOM-restarted the shared\n// backend and blanked the board. 300ms matches the admin reports search (#284).\nconst SEARCH_DEBOUNCE_MS = 300\n\nconst SORT_OPTIONS: BoardSortKey[] = ['recent', 'oldest', 'updated', 'severity', 'status']\n\nconst STATUSES: ReportStatus[] = [\n 'new',\n 'in_progress',\n 'awaiting_validation',\n 'closed',\n 'rejected',\n]\nconst TYPES: FeedbackType[] = ['bug', 'feature', 'question', 'praise', 'typo']\nconst SEVERITIES: FeedbackSeverity[] = ['blocker', 'high', 'medium', 'low']\n\ninterface BoardViewProps {\n api: ApiClient\n externalId: string\n strings: Record<StringKey, string>\n getCurrentPage?: () => string | null\n /** Set by the hover peek panel's row click — Board opens with this\n * report pre-selected instead of the empty detail pane (spec §3.2). */\n initialSelectedId?: string\n}\n\nexport function BoardView({\n api,\n externalId,\n strings,\n getCurrentPage,\n initialSelectedId,\n}: BoardViewProps) {\n const [filters, setFilters] = useState<BoardFilters>(() => loadBoardView(externalId))\n // `thisPage` is transient (not persisted) — it tracks whether the board\n // is scoped to the current page path. Defaults on.\n const [thisPage, setThisPage] = useState(true)\n useEffect(() => {\n saveBoardView(externalId, filters)\n }, [externalId, filtersHash(filters)])\n const [page, setPage] = useState<BoardListPage | null>(null)\n const [kpis, setKpis] = useState<WidgetBoardKpis | null>(null)\n const [loading, setLoading] = useState(true)\n // True while the visible rows are the PREVIOUS query's data (locally\n // pre-filtered where possible) and the server round-trip for the new\n // query is still in flight — drives the dimmed \"refreshing\" look.\n const [stale, setStale] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const [selectedId, setSelectedId] = useState<string | null>(initialSelectedId ?? null)\n // Detail-pane mount tick — increments when the user picks a row so the\n // panel can re-mount and re-fetch without us caching old data.\n const [detailKey, setDetailKey] = useState(0)\n const [reloadTick, setReloadTick] = useState(0)\n // Pages 2+ accumulated via the \"Show more\" button (#93). Page 1 lives in\n // `page` and is what the 30s poll refreshes; extras are kept across polls\n // and deduped by id at render. Reset whenever the query changes.\n const [extra, setExtra] = useState<{\n rows: WidgetBoardRow[]\n nextPage: number\n hasMore: boolean\n } | null>(null)\n const [loadingMore, setLoadingMore] = useState(false)\n\n // Search text is kept as a local draft (updated per keystroke so typing stays\n // snappy) and debounced into `filters.q` — the value that actually drives the\n // list + KPI fetch. Committing per keystroke is what stormed the backend.\n const [qDraft, setQDraft] = useState<string>(filters.q ?? '')\n const debouncedQ = useDebouncedValue(qDraft, SEARCH_DEBOUNCE_MS)\n // Commit the settled query into the fetch-driving filters (drop the key when\n // empty, matching the exactOptionalPropertyTypes convention below).\n useEffect(() => {\n setFilters((f) => {\n if ((f.q ?? '') === debouncedQ) return f\n if (debouncedQ === '') {\n const { q: _drop, ...rest } = f\n void _drop\n return rest\n }\n return { ...f, q: debouncedQ }\n })\n }, [debouncedQ])\n // Keep the draft in sync when q is reset elsewhere (the Clear button).\n useEffect(() => {\n setQDraft(filters.q ?? '')\n }, [filters.q])\n\n const activeFilterCount = useMemo(() => {\n return (\n (filters.status?.length ?? 0) +\n (filters.type?.length ?? 0) +\n (filters.severity?.length ?? 0) +\n (filters.q ? 1 : 0) +\n (filters.mine ? 1 : 0)\n )\n }, [filters])\n\n // When thisPage is on and the host SPA navigates, bump reloadTick so the\n // fetch effect re-runs with the new currentPagePath. Clean up on unmount or\n // when thisPage is toggled off.\n useEffect(() => {\n if (!thisPage) return\n return onLocationChange(() => setReloadTick((t) => t + 1))\n }, [thisPage])\n\n // Merge the transient page-path scope into a fetch-only filters object.\n // `pagePath` is never written into the persisted boardViewStore blob.\n // A search query always searches the whole project — the \"Cette page\"\n // scope silently hid cross-page matches, so search looked broken (#92).\n const fetchFilters: BoardFilters = useMemo(() => {\n const searching = !!filters.q?.trim()\n return thisPage && !searching\n ? { ...filters, pagePath: currentPagePath(getCurrentPage !== undefined ? { getCurrentPage } : {}) }\n : filters\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [filtersHash(filters), thisPage, reloadTick])\n\n // Single source of truth for list + KPIs — they refetch together so the\n // counts never drift from the visible rows. Cadence, hidden-tab pause and\n // failure backoff live in startPoll.\n //\n // Query changes are stale-while-revalidate, NOT blank-and-spinner. The\n // #352 requirement stands — a toggle must never look inert when the\n // backend is slow — but it's met by responding instantly instead of by\n // clearing: rows carry status/type/severity/is_mine, so those filters\n // (and the sort) apply locally the moment the user clicks, while the\n // dimmed `is-stale` look says \"confirming\". Search/scope/nav changes\n // can't be computed locally — previous rows stay, dimmed. The skeleton\n // only ever shows on a true cold start (no previous data, no cache).\n // Background 30s polls run inside startPoll and never re-enter this\n // effect, so they keep their data — no flicker on refresh.\n useEffect(() => {\n let cancelled = false\n const controller = new AbortController()\n const cacheKey = boardCacheKey(externalId, JSON.stringify(fetchFilters))\n const cached = readBoardCache(cacheKey)\n if (cached) {\n // Instant paint (e.g. modal reopen, or back to a recently-viewed\n // filter combo) — the poll below still revalidates immediately.\n setPage(cached.page)\n setKpis(cached.kpis)\n setStale(false)\n setLoading(false)\n } else {\n setPage((prev) =>\n prev ? { ...prev, results: applyLocalFilters(prev.results, fetchFilters) } : null,\n )\n setStale(true)\n setLoading(true)\n }\n // Pages 2+ belong to the previous query — drop them.\n setExtra(null)\n setLoadingMore(false)\n const poll = startPoll(async () => {\n try {\n const list = await api.listBoard(externalId, fetchFilters, {\n signal: controller.signal,\n })\n // Phase-3 backends embed the KPI payload in the page envelope so\n // each poll is ONE request; fall back to the standalone endpoint\n // against older backends.\n const k =\n list.kpis ??\n (await api.fetchBoardKpis(externalId, fetchFilters, {\n signal: controller.signal,\n }))\n if (cancelled) return true\n writeBoardCache(cacheKey, list, k)\n setPage(list)\n setKpis(k)\n setStale(false)\n setError(null)\n return true\n } catch (e) {\n if (cancelled) return false\n // 429 → actionable \"retry in Xs\"; otherwise the generic friendly\n // copy. Never store the raw error text (it used to be shown) (#80).\n setError(rateLimitMessage(e, strings) ?? strings['board.list.error'])\n return false\n } finally {\n if (!cancelled) setLoading(false)\n }\n }, POLL_MS)\n return () => {\n cancelled = true\n poll.stop()\n // Cancel the in-flight request server-side too. `cancelled` is set\n // first, so the AbortError lands in a catch that ignores it.\n controller.abort()\n }\n // fetchFilters already encodes filters + thisPage + reloadTick.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [api, externalId, JSON.stringify(fetchFilters)])\n\n // Page 1 (poll-refreshed) + loaded-more extras, deduped by id — a poll\n // tick can shift a row that was on page 2 up into page 1.\n const visibleRows = useMemo(() => {\n if (!page) return []\n if (!extra || extra.rows.length === 0) return page.results\n const seen = new Set(page.results.map((r) => r.id))\n return [...page.results, ...extra.rows.filter((r) => !seen.has(r.id))]\n }, [page, extra])\n const hasMore = extra ? extra.hasMore : Boolean(page?.next)\n\n const loadMore = async () => {\n if (loadingMore || !page) return\n const pageNum = extra ? extra.nextPage : 2\n setLoadingMore(true)\n try {\n const res = await api.listBoard(externalId, { ...fetchFilters, page: pageNum })\n setExtra((prev) => ({\n rows: [...(prev?.rows ?? []), ...res.results],\n nextPage: pageNum + 1,\n hasMore: res.next !== null,\n }))\n } catch {\n // Leave state as-is — the button stays for a retry.\n } finally {\n setLoadingMore(false)\n }\n }\n\n const selectedRow = useMemo(() => {\n if (!selectedId) return null\n return visibleRows.find((r) => r.id === selectedId) ?? null\n }, [selectedId, visibleRows])\n\n const onPickRow = (row: WidgetBoardRow) => {\n setSelectedId(row.id)\n setDetailKey((k) => k + 1)\n }\n\n return (\n <div class={`board-view ${stale ? 'is-stale' : ''}`}>\n <BoardHeader strings={strings} kpis={kpis} thisPage={thisPage} />\n <BoardFilters\n filters={filters}\n onChange={setFilters}\n searchDraft={qDraft}\n onSearchDraftChange={setQDraft}\n activeCount={activeFilterCount}\n strings={strings}\n thisPage={thisPage}\n onToggleThisPage={() => setThisPage((v) => !v)}\n />\n <div class=\"board-body\">\n <div class=\"board-list-wrap\" aria-busy={loading}>\n {error && (\n <div class=\"board-error\" role=\"alert\">\n <span>{error}</span>\n <button\n type=\"button\"\n class=\"btn btn--ghost\"\n onClick={() => {\n setError(null)\n setLoading(true)\n setReloadTick((t) => t + 1)\n }}\n >\n {strings['board.retry']}\n </button>\n </div>\n )}\n {!error && loading && !page && (\n <BoardListSkeleton />\n )}\n {/* `stale` widens the condition: when the locally pre-filtered\n previous rows are already empty, say so immediately instead\n of showing dimmed nothing until the server confirms. */}\n {!error && page && visibleRows.length === 0 && (!loading || stale) && (\n <BoardEmpty\n strings={strings}\n thisPage={thisPage}\n onAllPages={() => setThisPage(false)}\n />\n )}\n {!error && page && visibleRows.length > 0 && (\n <>\n <BoardList\n rows={visibleRows}\n selectedId={selectedId}\n onPick={onPickRow}\n strings={strings}\n total={page.count}\n thisPage={thisPage}\n />\n {hasMore && !stale && (\n <button\n type=\"button\"\n class=\"btn btn--ghost board-load-more\"\n onClick={() => void loadMore()}\n disabled={loadingMore}\n >\n {strings['board.list.loadMore']}\n </button>\n )}\n </>\n )}\n </div>\n {/* Keyed on selectedId (not the row): a preselected report that\n isn't on the current list page — the hover-peek fetches a\n different scope than the persisted board filters — still\n renders via its own fetch instead of silently showing the\n empty pane. The row, when present, seeds the instant paint. */}\n <div class={`board-detail-wrap ${selectedId ? 'has-selection' : ''}`}>\n {selectedId ? (\n <ReportDetailView\n key={detailKey}\n api={api}\n externalId={externalId}\n reportId={selectedId}\n strings={strings}\n onBack={() => setSelectedId(null)}\n canModerate={\n selectedRow ? (selectedRow.can_moderate ?? selectedRow.is_mine) : false\n }\n {...(selectedRow ? { seed: selectedRow } : {})}\n variant=\"board\"\n onOpenSeq={(seq) => {\n // Resolve the #NN reference and open it in the detail pane.\n void api\n .getReportBySeq(seq, externalId)\n .then((r) => {\n setSelectedId(r.id)\n setDetailKey((k) => k + 1)\n })\n .catch(() => {\n // Best-effort — a stale/inaccessible #ref just no-ops.\n })\n }}\n />\n ) : (\n <div class=\"board-detail-empty\">\n <DetailEmptyIllustration />\n <p>{strings['board.detail.empty']}</p>\n </div>\n )}\n </div>\n </div>\n </div>\n )\n}\n\n// ---------- subcomponents -------------------------------------------------\n\nfunction BoardHeader({\n strings,\n kpis,\n thisPage,\n}: {\n strings: Record<StringKey, string>\n kpis: WidgetBoardKpis | null\n thisPage: boolean\n}) {\n const scopeLabel = thisPage\n ? strings['board.scope.onThisPage']\n : kpis?.scope === 'project'\n ? strings['board.scope.project']\n : strings['board.scope.mine']\n return (\n <header class=\"board-header\">\n <div class=\"board-header-title\">\n <span class=\"board-header-emoji\" aria-hidden=\"true\">📋</span>\n <div>\n <h2 class=\"board-header-h\">{strings['tab.board']}</h2>\n <p class=\"board-header-sub\">\n {kpis ? `${kpis.total} · ${scopeLabel}` : scopeLabel}\n </p>\n </div>\n </div>\n <BoardKpiStrip kpis={kpis} strings={strings} />\n </header>\n )\n}\n\nfunction BoardKpiStrip({\n kpis,\n strings,\n}: {\n kpis: WidgetBoardKpis | null\n strings: Record<StringKey, string>\n}) {\n // Always render 4 cards so the strip's height is stable while the\n // first fetch is in flight — eliminates the layout shift the PNR\n // page has on its first paint.\n const cells: { key: string; label: string; value: string; tone: string }[] = [\n {\n key: 'new',\n label: strings['board.kpi.new'],\n value: String(kpis?.by_status?.new ?? 0),\n tone: 'tone-new',\n },\n {\n key: 'in_progress',\n label: strings['board.kpi.in_progress'],\n value: String(kpis?.by_status?.in_progress ?? 0),\n tone: 'tone-progress',\n },\n {\n key: 'awaiting_validation',\n label: strings['board.kpi.awaiting_validation'],\n value: String(kpis?.by_status?.awaiting_validation ?? 0),\n tone: 'tone-validation',\n },\n {\n key: 'rate',\n label: strings['board.kpi.resolution_rate'],\n value: kpis ? `${Math.round((kpis.resolution_rate || 0) * 100)}%` : '—',\n tone: 'tone-rate',\n },\n ]\n return (\n <div class={`board-kpi-strip ${kpis ? 'is-ready' : 'is-loading'}`} aria-live=\"polite\">\n {cells.map((c) => (\n <div key={c.key} class={`board-kpi-card ${c.tone}`}>\n <div class=\"board-kpi-value\">{c.value}</div>\n <div class=\"board-kpi-label\">{c.label}</div>\n </div>\n ))}\n </div>\n )\n}\n\nfunction BoardFilters({\n filters,\n onChange,\n searchDraft,\n onSearchDraftChange,\n activeCount,\n strings,\n thisPage,\n onToggleThisPage,\n}: {\n filters: BoardFilters\n onChange: (f: BoardFilters) => void\n /** Live search text (debounced into `filters.q` by the parent). */\n searchDraft: string\n onSearchDraftChange: (q: string) => void\n activeCount: number\n strings: Record<StringKey, string>\n thisPage: boolean\n onToggleThisPage: () => void\n}) {\n // Under `exactOptionalPropertyTypes: true` we can't set keys to\n // `undefined` to clear them — the optional slot doesn't accept the\n // explicit undefined. Drop the key via destructuring instead.\n const setStatus = (value: ReportStatus | '') => {\n if (value === '') {\n const { status: _drop, ...rest } = filters\n void _drop\n onChange(rest)\n } else {\n onChange({ ...filters, status: [value] })\n }\n }\n const setType = (value: FeedbackType | '') => {\n if (value === '') {\n const { type: _drop, ...rest } = filters\n void _drop\n onChange(rest)\n } else {\n onChange({ ...filters, type: [value] })\n }\n }\n const setSeverity = (value: FeedbackSeverity | '') => {\n if (value === '') {\n const { severity: _drop, ...rest } = filters\n void _drop\n onChange(rest)\n } else {\n onChange({ ...filters, severity: [value] })\n }\n }\n const setOrdering = (value: BoardSortKey) => onChange({ ...filters, ordering: value })\n const toggleMine = () => {\n if (filters.mine) {\n const { mine: _drop, ...rest } = filters\n void _drop\n onChange(rest)\n } else {\n onChange({ ...filters, mine: true })\n }\n }\n const clear = () => onChange({})\n return (\n <div class=\"board-filters\">\n <div class=\"board-scope\" role=\"group\" aria-label={strings['board.scope.thisPage']}>\n <button\n type=\"button\"\n class={`board-scope-btn ${thisPage ? 'is-active' : ''}`}\n aria-pressed={thisPage}\n onClick={() => !thisPage && onToggleThisPage()}\n >\n {strings['board.scope.thisPage']}\n </button>\n <button\n type=\"button\"\n class={`board-scope-btn ${!thisPage ? 'is-active' : ''}`}\n aria-pressed={!thisPage}\n onClick={() => thisPage && onToggleThisPage()}\n >\n {strings['board.scope.allPages']}\n </button>\n </div>\n <select\n class=\"board-filter-select\"\n aria-label={strings['board.sort']}\n value={filters.ordering ?? 'recent'}\n onChange={(e) => setOrdering((e.target as HTMLSelectElement).value as BoardSortKey)}\n >\n {SORT_OPTIONS.map((o) => (\n <option key={o} value={o}>\n {strings[`board.sort.${o}` as StringKey]}\n </option>\n ))}\n </select>\n <select\n class=\"board-filter-select\"\n aria-label={strings['board.filter.status']}\n value={filters.status?.[0] ?? ''}\n onChange={(e) =>\n setStatus((e.target as HTMLSelectElement).value as ReportStatus | '')\n }\n >\n <option value=\"\">{strings['board.filter.status']}</option>\n {STATUSES.map((s) => (\n <option key={s} value={s}>\n {strings[`board.kpi.${s}` as StringKey] ?? s}\n </option>\n ))}\n </select>\n <select\n class=\"board-filter-select\"\n aria-label={strings['board.filter.type']}\n value={filters.type?.[0] ?? ''}\n onChange={(e) =>\n setType((e.target as HTMLSelectElement).value as FeedbackType | '')\n }\n >\n <option value=\"\">{strings['board.filter.type']}</option>\n {TYPES.map((t) => (\n <option key={t} value={t}>\n {strings[`type.${t}` as StringKey] ?? t}\n </option>\n ))}\n </select>\n <select\n class=\"board-filter-select\"\n aria-label={strings['board.filter.severity']}\n value={filters.severity?.[0] ?? ''}\n onChange={(e) =>\n setSeverity((e.target as HTMLSelectElement).value as FeedbackSeverity | '')\n }\n >\n <option value=\"\">{strings['board.filter.severity']}</option>\n {SEVERITIES.map((s) => (\n <option key={s} value={s}>\n {strings[`severity.${s}` as StringKey] ?? s}\n </option>\n ))}\n </select>\n <input\n type=\"search\"\n class=\"board-filter-search\"\n placeholder={strings['board.filter.search.placeholder']}\n value={searchDraft}\n onInput={(e) => onSearchDraftChange((e.target as HTMLInputElement).value)}\n />\n <label class=\"board-filter-toggle\">\n <input\n type=\"checkbox\"\n checked={Boolean(filters.mine)}\n onChange={toggleMine}\n />\n <span>{strings['board.filter.mine']}</span>\n </label>\n {activeCount > 0 && (\n <button type=\"button\" class=\"board-filter-clear\" onClick={clear}>\n <CloseIcon />\n {strings['board.filter.clear']}\n </button>\n )}\n </div>\n )\n}\n\nfunction BoardList({\n rows,\n selectedId,\n onPick,\n strings,\n total,\n thisPage,\n}: {\n rows: WidgetBoardRow[]\n selectedId: string | null\n onPick: (row: WidgetBoardRow) => void\n strings: Record<StringKey, string>\n total: number\n thisPage: boolean\n}) {\n return (\n <>\n <div class=\"board-list-count\">\n {strings[thisPage ? 'board.list.count.page' : 'board.list.count']\n .replace('{n}', String(rows.length))\n .replace('{total}', String(total))}\n </div>\n <ul class=\"board-list\" role=\"list\">\n {rows.map((row) => (\n <BoardRowCard\n key={row.id}\n row={row}\n selected={row.id === selectedId}\n onPick={onPick}\n strings={strings}\n />\n ))}\n </ul>\n </>\n )\n}\n\nfunction BoardRowCard({\n row,\n selected,\n onPick,\n strings,\n}: {\n row: WidgetBoardRow\n selected: boolean\n onPick: (row: WidgetBoardRow) => void\n strings: Record<StringKey, string>\n}) {\n const author = row.is_mine\n ? strings['board.list.you']\n : row.author_label || '—'\n return (\n <li>\n <button\n type=\"button\"\n class={`board-row ${selected ? 'is-selected' : ''}`}\n onClick={() => onPick(row)}\n aria-pressed={selected}\n >\n <div class=\"board-row-badges\">\n <span class={`badge status-${row.status}`}>\n {strings[`status.${row.status}` as StringKey] ?? row.status}\n </span>\n <span class={`badge severity-${row.severity}`}>\n {strings[`severity.${row.severity}` as StringKey] ?? row.severity}\n </span>\n </div>\n <div class=\"board-row-description\">{row.description}</div>\n <div class=\"board-row-meta\">\n <span class=\"board-row-author\">{author}</span>\n {row.comment_count > 0 && (\n <span class=\"board-row-comments\">\n <CommentIcon /> {row.comment_count}\n </span>\n )}\n <span class=\"board-row-time\">{formatRelative(row.created_at)}</span>\n </div>\n </button>\n </li>\n )\n}\n\nfunction BoardEmpty({\n strings,\n thisPage,\n onAllPages,\n}: {\n strings: Record<StringKey, string>\n thisPage: boolean\n onAllPages: () => void\n}) {\n return (\n <div class=\"board-empty\">\n <EmptyIllustration />\n <h3>{strings[thisPage ? 'board.list.empty.page.title' : 'board.list.empty.title']}</h3>\n <p>\n {strings[thisPage ? 'board.list.empty.page.description' : 'board.list.empty.description']}\n </p>\n {thisPage && (\n <button type=\"button\" class=\"btn btn--ghost\" onClick={onAllPages}>\n {strings['board.scope.allPages']}\n </button>\n )}\n </div>\n )\n}\n\nfunction BoardListSkeleton() {\n return (\n <ul class=\"board-list board-list-skeleton\" aria-hidden=\"true\">\n {Array.from({ length: 5 }).map((_, i) => (\n <li key={i}>\n <div class=\"board-row skeleton-row\">\n <div class=\"skeleton-line skeleton-badges\" />\n <div class=\"skeleton-line skeleton-text\" />\n <div class=\"skeleton-line skeleton-text short\" />\n </div>\n </li>\n ))}\n </ul>\n )\n}\n\n// ---------- icons ---------------------------------------------------------\n\nfunction CommentIcon(): JSX.Element {\n return (\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\" />\n </svg>\n )\n}\n\nfunction CloseIcon(): JSX.Element {\n return (\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n )\n}\n\nfunction EmptyIllustration(): JSX.Element {\n return (\n <svg width=\"64\" height=\"64\" viewBox=\"0 0 64 64\" fill=\"none\" aria-hidden=\"true\">\n <circle cx=\"32\" cy=\"32\" r=\"28\" stroke=\"currentColor\" stroke-opacity=\"0.18\" stroke-width=\"2\" />\n <path d=\"M22 32h20M32 22v20\" stroke=\"currentColor\" stroke-opacity=\"0.35\" stroke-width=\"2\" stroke-linecap=\"round\" />\n </svg>\n )\n}\n\nfunction DetailEmptyIllustration(): JSX.Element {\n return (\n <svg width=\"80\" height=\"80\" viewBox=\"0 0 64 64\" fill=\"none\" aria-hidden=\"true\">\n <rect x=\"10\" y=\"12\" width=\"44\" height=\"36\" rx=\"4\" stroke=\"currentColor\" stroke-opacity=\"0.22\" stroke-width=\"2\" />\n <line x1=\"18\" y1=\"22\" x2=\"46\" y2=\"22\" stroke=\"currentColor\" stroke-opacity=\"0.22\" stroke-width=\"2\" stroke-linecap=\"round\" />\n <line x1=\"18\" y1=\"30\" x2=\"38\" y2=\"30\" stroke=\"currentColor\" stroke-opacity=\"0.18\" stroke-width=\"2\" stroke-linecap=\"round\" />\n <line x1=\"18\" y1=\"38\" x2=\"32\" y2=\"38\" stroke=\"currentColor\" stroke-opacity=\"0.14\" stroke-width=\"2\" stroke-linecap=\"round\" />\n </svg>\n )\n}\n\n// ---------- helpers -------------------------------------------------------\n\n/** Returns `value` delayed by `ms`, re-timing on every change so a burst of\n * updates collapses to the last one. Keeps the board search from firing a\n * project-wide query on every keystroke. */\nfunction useDebouncedValue<T>(value: T, ms: number): T {\n const [debounced, setDebounced] = useState(value)\n useEffect(() => {\n const t = setTimeout(() => setDebounced(value), ms)\n return () => clearTimeout(t)\n }, [value, ms])\n return debounced\n}\n\nconst SEVERITY_RANK: Record<string, number> = { blocker: 0, high: 1, medium: 2, low: 3 }\n\n/** Best-effort local application of the new query to the PREVIOUS rows so\n * a filter/sort click responds in the same frame (#352: never inert).\n * Only predicates the row data can answer are applied — q / page scope\n * are server-side concepts and leave the rows as-is (dimmed instead).\n * The server response replaces all of this within the round-trip. */\nfunction applyLocalFilters(rows: WidgetBoardRow[], f: BoardFilters): WidgetBoardRow[] {\n let out = rows\n if (f.status?.length) out = out.filter((r) => f.status!.includes(r.status))\n if (f.type?.length) out = out.filter((r) => f.type!.includes(r.feedback_type))\n if (f.severity?.length) out = out.filter((r) => f.severity!.includes(r.severity))\n if (f.mine) out = out.filter((r) => r.is_mine)\n const sorted = out.slice()\n switch (f.ordering ?? 'recent') {\n case 'oldest':\n sorted.sort((a, b) => a.created_at.localeCompare(b.created_at))\n break\n case 'updated':\n sorted.sort((a, b) => b.updated_at.localeCompare(a.updated_at))\n break\n case 'severity':\n sorted.sort(\n (a, b) => (SEVERITY_RANK[a.severity] ?? 9) - (SEVERITY_RANK[b.severity] ?? 9),\n )\n break\n case 'status':\n // Server-defined ordering — leave the previous order until it lands.\n break\n default:\n sorted.sort((a, b) => b.created_at.localeCompare(a.created_at))\n }\n return sorted\n}\n\nfunction filtersHash(f: BoardFilters): string {\n // Stable serialization for the useEffect dep array — avoids re-running\n // when the BoardView re-renders with a new object identity but the\n // same filter values.\n return JSON.stringify({\n s: f.status?.slice().sort(),\n t: f.type?.slice().sort(),\n sv: f.severity?.slice().sort(),\n q: f.q ?? '',\n m: Boolean(f.mine),\n o: f.ordering ?? '',\n })\n}\n\nfunction formatRelative(iso: string): string {\n // Tiny \"5m / 3h / 2d / 5w\" formatter — same shape PNR uses. Doesn't\n // depend on date-fns; keeps the bundle lean.\n const then = new Date(iso).getTime()\n if (Number.isNaN(then)) return ''\n const delta = Math.max(0, Date.now() - then)\n const m = Math.floor(delta / 60_000)\n if (m < 1) return 'just now'\n if (m < 60) return `${m}m`\n const h = Math.floor(m / 60)\n if (h < 24) return `${h}h`\n const d = Math.floor(h / 24)\n if (d < 7) return `${d}d`\n const w = Math.floor(d / 7)\n return `${w}w`\n}\n","/** Module-level board data cache — the `pageSignal.ts` pattern applied to\n * the Board tab's list + KPIs.\n *\n * The modal unmounts BoardView on close, so before this cache every\n * reopen was a skeleton + a fresh listBoard/fetchBoardKpis pair even\n * when the user had looked at the exact same board seconds earlier.\n * Entries are keyed by (externalId, serialized fetch filters) and only\n * ever used as an instant first paint — BoardView always revalidates\n * immediately, so a stale entry can survive at most one poll tick. */\nimport type { BoardListPage } from '../api/client'\nimport type { WidgetBoardKpis } from '../types'\n\nconst TTL_MS = 60_000\n// Session-scoped safety cap: distinct filter combos accumulate over a long\n// session; evict oldest-written past this rather than growing unbounded.\nconst MAX_ENTRIES = 50\n\ninterface BoardCacheEntry {\n page: BoardListPage\n kpis: WidgetBoardKpis\n at: number\n}\n\nconst cache = new Map<string, BoardCacheEntry>()\n\nexport function boardCacheKey(externalId: string, fetchKey: string): string {\n return `${externalId}|${fetchKey}`\n}\n\nexport function readBoardCache(key: string): BoardCacheEntry | null {\n const hit = cache.get(key)\n return hit && Date.now() - hit.at < TTL_MS ? hit : null\n}\n\nexport function writeBoardCache(\n key: string,\n page: BoardListPage,\n kpis: WidgetBoardKpis,\n): void {\n if (!cache.has(key) && cache.size >= MAX_ENTRIES) {\n const oldest = cache.keys().next().value\n if (oldest !== undefined) cache.delete(oldest)\n }\n cache.delete(key) // re-insert so Map order stays write-ordered\n cache.set(key, { page, kpis, at: Date.now() })\n}\n\nexport function _resetBoardCache(): void {\n cache.clear()\n}\n","import type { BoardFilters } from '../types'\n\n// Per-user persisted Board view (sort + filters). A widget instance is\n// scoped to one project, so the external id is a sufficient key. Best-\n// effort: storage may be disabled or hold a stale/corrupt blob — every\n// path falls back to an empty view rather than throwing into render.\nconst PREFIX = 'mhosaic.boardView.'\n\nexport function loadBoardView(externalId: string): BoardFilters {\n try {\n const raw = localStorage.getItem(PREFIX + externalId)\n if (!raw) return {}\n const parsed = JSON.parse(raw)\n return parsed && typeof parsed === 'object' ? (parsed as BoardFilters) : {}\n } catch {\n return {}\n }\n}\n\nexport function saveBoardView(externalId: string, view: BoardFilters): void {\n try {\n localStorage.setItem(PREFIX + externalId, JSON.stringify(view))\n } catch {\n /* storage disabled / quota — non-fatal, just no persistence */\n }\n}\n","/**\n * startPoll — shared scheduler for the widget's 30s refresh loops\n * (BoardView, MineList, ChangelogList, ReportDetailView).\n *\n * Born from the 2026-07-07 OOM outage: board tabs left open overnight kept\n * polling at full cadence — even while every response was a 429 — and the\n * accumulated fleet-wide churn restart-looped the backend. Two rules fix\n * that class of failure at the source:\n *\n * - Hidden tabs don't poll. When a tick comes due while `document.hidden`,\n * the loop parks instead of fetching; a `visibilitychange` listener\n * fires an immediate refresh when the tab comes back, so a returning\n * user still sees fresh data without ever paying for a background tab.\n * - Failures back off. Any failed tick doubles the delay, capped at\n * MAX_BACKOFF_MULTIPLIER × interval — a throttled (429) or down backend\n * sees pressure decay instead of a steady hammer. One success resets\n * the cadence.\n *\n * The first tick runs immediately (every caller renders from it). `tick`\n * reports success via its return value instead of throwing — callers\n * already own their error state; a throw is treated as a failed tick so a\n * bug in a caller can never kill the loop.\n */\n\nexport interface PollHandle {\n stop(): void\n}\n\n/** Backoff ceiling: 30s cadence degrades no further than 5 min. */\nconst MAX_BACKOFF_MULTIPLIER = 10\n\nexport function startPoll(\n tick: () => Promise<boolean>,\n intervalMs: number,\n): PollHandle {\n let stopped = false\n let parked = false\n let failures = 0\n let timer: ReturnType<typeof setTimeout> | null = null\n const doc = typeof document !== 'undefined' ? document : null\n\n const delayMs = () =>\n Math.min(intervalMs * 2 ** failures, intervalMs * MAX_BACKOFF_MULTIPLIER)\n\n const run = async () => {\n if (stopped) return\n if (doc?.hidden) {\n parked = true\n return\n }\n let ok = false\n try {\n ok = await tick()\n } catch {\n ok = false\n }\n if (stopped) return\n failures = ok ? 0 : failures + 1\n timer = setTimeout(() => void run(), delayMs())\n }\n\n const onVisibilityChange = () => {\n if (stopped || !parked || doc?.hidden) return\n parked = false\n void run()\n }\n\n doc?.addEventListener('visibilitychange', onVisibilityChange)\n void run()\n\n return {\n stop() {\n stopped = true\n if (timer) clearTimeout(timer)\n doc?.removeEventListener('visibilitychange', onVisibilityChange)\n },\n }\n}\n","import { RateLimitError } from '../api/client'\nimport type { StringKey } from './i18n'\n\n/**\n * Map a caught reader error to an actionable \"too many requests\" message when\n * it's a 429 (#80/#81), else null so the caller can fall back to its generic\n * error copy. Prevents the widget from showing an infinite \"Loading…\" or a\n * false \"Report not available\" when the real cause is a rate limit.\n */\nexport function rateLimitMessage(\n err: unknown,\n strings: Record<StringKey, string>,\n): string | null {\n if (!(err instanceof RateLimitError)) return null\n return err.retryAfterSeconds > 0\n ? strings['error.rate_limited'].replace(\n '{seconds}',\n String(err.retryAfterSeconds),\n )\n : strings['error.rate_limited_generic']\n}\n","/**\n * ReportDetailView — selected report's full thread.\n *\n * Renders: status pill + description + screenshot + comment thread +\n * compose box + (when status=awaiting_validation) \"Mark as resolved\"\n * button. Polls every 30 s while open so the user sees an operator's\n * reply without manually refreshing.\n */\n\nimport { h } from 'preact'\nimport { useEffect, useRef, useState } from 'preact/hooks'\n\nimport { linkifySeq } from './linkifySeq'\nimport { rateLimitMessage } from './rateLimit'\n\nimport type { ApiClient } from '../api/client'\nimport type {\n CapturedContext,\n ConsoleEntry,\n ErrorEntry,\n NetworkEntry,\n WidgetBoardRow,\n WidgetCommentRow,\n WidgetReportDetail,\n WidgetStatusChangeRow,\n} from '../types'\nimport { CommentBubble } from './CommentBubble'\nimport type { StringKey } from './i18n'\nimport { startPoll } from './poll'\nimport {\n MAX_SCREENSHOTS,\n safeExternalHref,\n truncateUrl,\n validateScreenshotFile,\n} from './screenshot-utils'\n\ninterface ReportDetailViewProps {\n api: ApiClient\n externalId: string\n reportId: string\n strings: Record<StringKey, string>\n onBack: () => void\n /** When false, the self-service \"Confirm resolution\" button is hidden\n * even if the report is in awaiting_validation. The Board passes false\n * for project-scoped rows that don't belong to the current user. */\n canModerate?: boolean\n /** Layout flavor. \"modal\" (default) keeps the original My-reports look —\n * scrollable card centered in the modal. \"board\" tightens the padding\n * + drops the top \"← back\" because the Board renders the back action\n * in its own header. */\n variant?: 'modal' | 'board'\n /** Open another report from a clickable \"#NN\" reference in the description\n * or a comment (#85). Absent → references render as plain text. */\n onOpenSeq?: (seq: number) => void\n /** Build a shareable permalink for this report id (#85). Absent → the\n * \"copy link\" affordance is hidden. */\n buildPermalink?: (reportId: string) => string\n /** Board row already in memory for this report. When set, the pane paints\n * the row's status + description in the SAME frame as the click and only\n * the thread section waits on the network — clicking a report used to be\n * a guaranteed full-pane spinner even though these fields were already\n * loaded (perf program D6). */\n seed?: WidgetBoardRow\n}\n\nconst POLL_MS = 30_000\n\nexport function ReportDetailView({\n api,\n externalId,\n reportId,\n strings,\n onBack,\n canModerate = true,\n variant = 'modal',\n onOpenSeq,\n buildPermalink,\n seed,\n}: ReportDetailViewProps) {\n const [detail, setDetail] = useState<WidgetReportDetail | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [copied, setCopied] = useState(false)\n const [editing, setEditing] = useState(false)\n const [editBody, setEditBody] = useState('')\n const [savingEdit, setSavingEdit] = useState(false)\n const [editError, setEditError] = useState(false)\n const [composeBody, setComposeBody] = useState('')\n const [sending, setSending] = useState(false)\n const [closing, setClosing] = useState(false)\n const [reopening, setReopening] = useState(false)\n // Pending images to attach to the next comment (#91). Same {blob, preview}\n // shape and limits as the report compose form.\n const [composeShots, setComposeShots] = useState<\n Array<{ blob: Blob; preview: string }>\n >([])\n const [attachError, setAttachError] = useState('')\n const fileInputRef = useRef<HTMLInputElement>(null)\n const mountedRef = useRef(true)\n\n // Screenshot/attachment URLs are presigned per response — SigV4 stamps\n // the signing time, so every 30s poll returns DIFFERENT URLs for the\n // same immutable images. Swapping `src` each tick made the browser\n // re-download every image for as long as the pane stayed open. Pin the\n // first URL seen per stable identity (screenshot index / attachment id);\n // presigns live 3600s, so refresh a pin only after 45min or when the\n // image errors (expired early / revoked).\n const urlPins = useRef(new Map<string, { url: string; at: number }>())\n const [, bumpPinTick] = useState(0)\n const PIN_MAX_AGE_MS = 45 * 60_000\n const pinUrl = (key: string, url: string | null): string | null => {\n if (!url) return url\n const pin = urlPins.current.get(key)\n if (pin && Date.now() - pin.at < PIN_MAX_AGE_MS) return pin.url\n urlPins.current.set(key, { url, at: Date.now() })\n return url\n }\n const unpinUrl = (key: string) => {\n urlPins.current.delete(key)\n // Re-render so the next paint picks the freshest presigned URL from\n // the current `detail`.\n bumpPinTick((t) => t + 1)\n }\n\n // Revoke every pending preview URL on unmount (individual revokes happen\n // on remove/send).\n const shotsRef = useRef(composeShots)\n shotsRef.current = composeShots\n useEffect(() => {\n return () => shotsRef.current.forEach((s) => URL.revokeObjectURL(s.preview))\n }, [])\n\n const acceptComposeFiles = (files: File[]) => {\n setAttachError('')\n const remaining = MAX_SCREENSHOTS - composeShots.length\n const batch = files.slice(0, Math.max(0, remaining))\n for (const file of batch) {\n const err = validateScreenshotFile(file)\n if (err) {\n setAttachError(\n err.kind === 'type'\n ? strings['form.screenshot.error_type']\n : strings['form.screenshot.error_size'].replace('{max}', String(err.maxMb)),\n )\n return\n }\n }\n if (batch.length) {\n const incoming = batch.map((blob) => ({\n blob,\n preview: URL.createObjectURL(blob),\n }))\n setComposeShots((prev) => [...prev, ...incoming])\n }\n if (files.length > batch.length) {\n setAttachError(\n strings['form.screenshot.error_count'].replace('{max}', String(MAX_SCREENSHOTS)),\n )\n }\n }\n\n const handleAttachChange = (e: Event) => {\n const input = e.target as HTMLInputElement\n const files = Array.from(input.files ?? [])\n if (files.length) acceptComposeFiles(files)\n input.value = '' // re-picking the same file must re-fire change\n }\n\n const removeComposeShot = (index: number) => {\n const shot = composeShots[index]\n if (shot) URL.revokeObjectURL(shot.preview)\n setComposeShots((prev) => prev.filter((_, i) => i !== index))\n setAttachError('')\n }\n\n const fetchDetail = async () => {\n try {\n const next = await api.getReport(reportId, externalId)\n if (!mountedRef.current) return true\n setDetail(next)\n setError(null)\n return true\n } catch (err) {\n // The verbatim `err.message` (e.g. `getReport failed: 404 …`) leaked\n // straight into the panel pre-v0.15.3. Friendly UI via the\n // `!detail` empty-state branch below; raw error to console.\n if (typeof console !== 'undefined') console.warn('[mhosaic] getReport:', err)\n if (!mountedRef.current) return false\n // `load_failed` is a sentinel — the render branch swaps to the\n // friendly \"not available\" empty-state when `error` is that sentinel\n // and `detail` is null. A 429 must NOT claim the report was deleted\n // (it wasn't — it's just throttled), so we store the actionable\n // rate-limit message instead and the render shows it verbatim (#80/#81).\n const rl = rateLimitMessage(err, strings)\n setError(rl ?? 'load_failed')\n return false\n }\n }\n\n useEffect(() => {\n mountedRef.current = true\n const poll = startPoll(fetchDetail, POLL_MS)\n return () => {\n mountedRef.current = false\n poll.stop()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [reportId, externalId])\n\n const handleSend = async () => {\n // A comment may be text, images, or both — send if either is present.\n if ((!composeBody.trim() && composeShots.length === 0) || sending) return\n setSending(true)\n try {\n const nonce = `${reportId}:${Date.now()}`\n const attachments = composeShots.map((s) => s.blob)\n const created = await api.addComment(\n reportId,\n externalId,\n composeBody.trim(),\n nonce,\n attachments.length ? attachments : undefined,\n )\n if (!mountedRef.current) return\n setComposeBody('')\n composeShots.forEach((s) => URL.revokeObjectURL(s.preview))\n setComposeShots([])\n setAttachError('')\n // Optimistically append + then refetch so server-rendered timestamps\n // and any race with operator replies stay coherent.\n setDetail((prev) =>\n prev ? { ...prev, comments: appendComment(prev.comments, created) } : prev,\n )\n void fetchDetail()\n } catch (err) {\n // Don't surface raw API messages (e.g. `addComment failed: 403 …`).\n // Friendly fallback in the UI, raw error in the console.\n if (typeof console !== 'undefined') console.warn('[mhosaic] addComment:', err)\n if (!mountedRef.current) return\n setError(strings['detail.send_failed'])\n } finally {\n if (mountedRef.current) setSending(false)\n }\n }\n\n const handleCopyLink = async () => {\n if (!buildPermalink || !detail) return\n try {\n await navigator.clipboard.writeText(buildPermalink(detail.id))\n if (!mountedRef.current) return\n setCopied(true)\n setTimeout(() => {\n if (mountedRef.current) setCopied(false)\n }, 2000)\n } catch {\n /* clipboard blocked (permissions / insecure context) — no-op */\n }\n }\n\n const startEdit = () => {\n if (!detail) return\n setEditBody(detail.description)\n setEditError(false)\n setEditing(true)\n }\n const cancelEdit = () => {\n setEditing(false)\n setEditError(false)\n }\n const handleSaveEdit = async () => {\n if (!detail || savingEdit) return\n const next = editBody.trim()\n if (!next) return\n setSavingEdit(true)\n setEditError(false)\n try {\n const updated = await api.editDescription(reportId, externalId, next)\n if (!mountedRef.current) return\n setDetail(updated)\n setEditing(false)\n } catch (err) {\n if (typeof console !== 'undefined') console.warn('[mhosaic] editDescription:', err)\n if (!mountedRef.current) return\n setEditError(true)\n } finally {\n if (mountedRef.current) setSavingEdit(false)\n }\n }\n\n const handleClose = async () => {\n if (closing || reopening) return\n setClosing(true)\n try {\n const next = await api.closeAsResolved(reportId, externalId)\n if (!mountedRef.current) return\n setDetail(next)\n } catch (err) {\n if (typeof console !== 'undefined')\n console.warn('[mhosaic] closeAsResolved:', err)\n if (!mountedRef.current) return\n setError(strings['detail.close_failed'])\n } finally {\n if (mountedRef.current) setClosing(false)\n }\n }\n\n const handleReopen = async () => {\n if (closing || reopening) return\n setReopening(true)\n try {\n const next = await api.reopenUnresolved(reportId, externalId)\n if (!mountedRef.current) return\n setDetail(next)\n } catch (err) {\n if (typeof console !== 'undefined')\n console.warn('[mhosaic] reopenUnresolved:', err)\n if (!mountedRef.current) return\n setError(strings['detail.reopen_failed'])\n } finally {\n if (mountedRef.current) setReopening(false)\n }\n }\n\n if (!detail && !error) {\n if (seed) {\n // Optimistic first paint from the board row: real status pill + full\n // description immediately, spinner scoped to the thread only. The\n // fetched detail replaces this wholesale (including any fresher\n // status), so a stale seed survives at most one round-trip.\n const seedPill = (\n <span class={`pill pill-status pill-status--${seed.status}`}>\n {strings[`status.${seed.status}` as StringKey] ?? seed.status}\n </span>\n )\n return (\n <div class={`report-detail variant-${variant} is-seeded`}>\n {variant === 'modal' && (\n <div class=\"report-detail-header\">\n <button type=\"button\" class=\"btn\" onClick={onBack}>\n ← {strings['detail.back']}\n </button>\n {seedPill}\n </div>\n )}\n {variant === 'board' && (\n <div class=\"report-detail-header report-detail-header--board\">\n <button\n type=\"button\"\n class=\"btn btn--ghost report-detail-back\"\n onClick={onBack}\n aria-label={strings['board.back']}\n >\n ← {strings['board.back']}\n </button>\n {seedPill}\n </div>\n )}\n <div class=\"report-detail-body\">\n <div class=\"report-detail-description-row\">\n <p class=\"report-detail-description\">\n {linkifySeq(seed.description, onOpenSeq)}\n </p>\n </div>\n <h3 class=\"report-detail-section\">{strings['detail.thread']}</h3>\n <div class=\"mine-loading\">{strings['mine.loading']}</div>\n </div>\n </div>\n )\n }\n return <div class=\"mine-loading\">{strings['mine.loading']}</div>\n }\n\n if (!detail) {\n // A 429 stores an actionable rate-limit message rather than the\n // 'load_failed' sentinel — show it with a Retry instead of the (false)\n // \"may have been deleted\" copy (#80/#81).\n if (error && error !== 'load_failed') {\n return (\n <div class=\"report-detail-empty-state\" role=\"alert\">\n <p class=\"report-detail-empty-state-body\">{error}</p>\n <button type=\"button\" class=\"btn\" onClick={() => void fetchDetail()}>\n {strings['board.retry']}\n </button>\n <button type=\"button\" class=\"btn\" onClick={onBack}>\n ← {strings['detail.load_failed.cta']}\n </button>\n </div>\n )\n }\n // The raw `error.message` from the api client used to render here as\n // e.g. `getReport failed: 404 {\"detail\":\"Report not found.\"}` — a\n // verbatim leak of the backend error. The friendly variant + a Back\n // button is what end users (and external testers) expect to see.\n return (\n <div class=\"report-detail-empty-state\" role=\"alert\">\n <p class=\"report-detail-empty-state-title\">\n {strings['detail.load_failed.title']}\n </p>\n <p class=\"report-detail-empty-state-body\">\n {strings['detail.load_failed.body']}\n </p>\n <button type=\"button\" class=\"btn\" onClick={onBack}>\n ← {strings['detail.load_failed.cta']}\n </button>\n </div>\n )\n }\n\n // Permission gating. `canModerate` is passed by the parent (Board\n // passes `is_mine`; My-reports defaults to true). v0.15.3 also reads\n // `detail.is_mine` from the server when present, so a direct fetch\n // (no parent context) still gates correctly.\n const isMine = detail.is_mine ?? canModerate\n // `can_moderate` (server-computed) widens the action strip to\n // colleagues when the project opted into widget_peer_validation;\n // older backends omit it and we fall back to the submitter-only rule.\n const canAct = detail.can_moderate ?? isMine\n // \"Mark as resolved\" is the only self-service status transition\n // the widget allows (awaiting_validation → closed).\n const showCloseCta = canAct && detail.status === 'awaiting_validation'\n // Reply compose box: submitter, or a peer under peer validation.\n // Hidden (not just disabled) otherwise, since the action is genuinely\n // unavailable.\n const showComposeBox = canAct\n\n return (\n <div class={`report-detail variant-${variant}`}>\n {variant === 'modal' && (\n <div class=\"report-detail-header\">\n <button type=\"button\" class=\"btn\" onClick={onBack}>\n ← {strings['detail.back']}\n </button>\n <span class={`pill pill-status pill-status--${detail.status}`}>\n {strings[`status.${detail.status}` as StringKey] ?? detail.status}\n </span>\n </div>\n )}\n {variant === 'board' && (\n <div class=\"report-detail-header report-detail-header--board\">\n <button\n type=\"button\"\n class=\"btn btn--ghost report-detail-back\"\n onClick={onBack}\n aria-label={strings['board.back']}\n >\n ← {strings['board.back']}\n </button>\n <span class={`pill pill-status pill-status--${detail.status}`}>\n {strings[`status.${detail.status}` as StringKey] ?? detail.status}\n </span>\n </div>\n )}\n\n <div class=\"report-detail-body\">\n <ContextBlock detail={detail} strings={strings} />\n {editing ? (\n <div class=\"report-detail-edit\">\n <textarea\n class=\"report-detail-edit-input\"\n value={editBody}\n onInput={(e) => {\n setEditBody((e.target as HTMLTextAreaElement).value)\n setEditError(false)\n }}\n disabled={savingEdit}\n />\n {editError && (\n <p class=\"error\" role=\"alert\">\n {strings['detail.edit_failed']}\n </p>\n )}\n <div class=\"report-detail-edit-actions\">\n <button\n type=\"button\"\n class=\"btn btn--ghost\"\n onClick={cancelEdit}\n disabled={savingEdit}\n >\n {strings['detail.edit_cancel']}\n </button>\n <button\n type=\"button\"\n class=\"btn btn--primary\"\n onClick={handleSaveEdit}\n disabled={savingEdit || !editBody.trim()}\n >\n {savingEdit\n ? strings['detail.edit_saving']\n : strings['detail.edit_save']}\n </button>\n </div>\n </div>\n ) : (\n <div class=\"report-detail-description-row\">\n <p class=\"report-detail-description\">\n {linkifySeq(detail.description, onOpenSeq)}\n </p>\n {isMine && (\n <button\n type=\"button\"\n class=\"btn btn--ghost report-detail-edit\"\n onClick={startEdit}\n >\n {strings['detail.edit']}\n </button>\n )}\n </div>\n )}\n {buildPermalink && (\n <div class=\"report-detail-permalink\">\n <button\n type=\"button\"\n class=\"btn btn--ghost report-detail-copy-link\"\n onClick={handleCopyLink}\n >\n {copied ? strings['detail.copied'] : strings['detail.copy_link']}\n </button>\n </div>\n )}\n {(detail.screenshots?.length\n ? detail.screenshots.map((s) => s.url)\n : [detail.screenshot_url]\n )\n // Screenshots are immutable and in fixed submission order, so the\n // index is a stable identity to pin the presigned URL on.\n .map((url, i) => ({ url: pinUrl(`shot:${i}`, url ?? null), pinKey: `shot:${i}` }))\n .filter((s): s is { url: string; pinKey: string } => Boolean(s.url))\n .map(({ url, pinKey }) => {\n const safeHref = safeExternalHref(url)\n // No clickable open-in-new-tab for non-http(s) screenshot URLs\n // (defense-in-depth — server returns presigned https URLs, but\n // we don't trust client-rendered content with the operator's\n // origin). The inline preview is `<img src>`, which is safe\n // even if the URL is weird because Preact escapes attrs and\n // the browser refuses to render non-image content.\n return safeHref ? (\n <a\n class=\"report-detail-screenshot\"\n href={safeHref}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n <img src={url} alt=\"\" loading=\"lazy\" onError={() => unpinUrl(pinKey)} />\n </a>\n ) : (\n <div class=\"report-detail-screenshot\">\n <img src={url} alt=\"\" loading=\"lazy\" onError={() => unpinUrl(pinKey)} />\n </div>\n )\n })}\n\n <h3 class=\"report-detail-section\">{strings['detail.thread']}</h3>\n {detail.comments.length === 0 ? (\n <p class=\"report-detail-empty\">{strings['detail.no_replies']}</p>\n ) : (\n <ul class=\"report-comments\">\n {detail.comments.map((c) => (\n <li>\n <CommentBubble\n comment={c}\n strings={strings}\n {...(onOpenSeq && { onOpenSeq })}\n stableUrl={(key, url) => pinUrl(key, url) ?? url}\n />\n </li>\n ))}\n </ul>\n )}\n\n {detail.status_history && detail.status_history.length > 0 && (\n <StatusHistorySection rows={detail.status_history} strings={strings} />\n )}\n\n {detail.technical_context && (\n <TechnicalContextDrawer ctx={detail.technical_context} strings={strings} />\n )}\n\n {showComposeBox ? (\n <div class=\"report-compose\">\n <textarea\n value={composeBody}\n placeholder={strings['detail.compose_placeholder']}\n onInput={(e) =>\n setComposeBody((e.target as HTMLTextAreaElement).value)\n }\n disabled={sending}\n />\n {composeShots.length > 0 && (\n <div class=\"compose-attachments\">\n {composeShots.map((s, i) => (\n <div class=\"compose-attachment\" key={s.preview}>\n <img src={s.preview} alt={strings['detail.attachment_alt']} />\n <button\n type=\"button\"\n class=\"compose-attachment-remove\"\n aria-label={strings['detail.attach_remove']}\n onClick={() => removeComposeShot(i)}\n disabled={sending}\n >\n ×\n </button>\n </div>\n ))}\n </div>\n )}\n {attachError && (\n <p class=\"compose-attach-error\" role=\"alert\">\n {attachError}\n </p>\n )}\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/png,image/jpeg,image/webp\"\n multiple\n class=\"compose-attach-input\"\n onChange={handleAttachChange}\n hidden\n />\n <div class=\"report-compose-actions\">\n <button\n type=\"button\"\n class=\"btn btn--ghost compose-attach-btn\"\n onClick={() => fileInputRef.current?.click()}\n disabled={sending || composeShots.length >= MAX_SCREENSHOTS}\n >\n {strings['detail.attach_add']}\n </button>\n {showCloseCta && (\n <button\n type=\"button\"\n class=\"btn\"\n onClick={handleClose}\n disabled={closing || reopening}\n >\n {closing\n ? strings['detail.close_busy']\n : strings['detail.close_cta']}\n </button>\n )}\n {showCloseCta && (\n <button\n type=\"button\"\n class=\"btn btn--ghost\"\n onClick={handleReopen}\n disabled={closing || reopening}\n >\n {reopening\n ? strings['detail.reopen_busy']\n : strings['detail.reopen_cta']}\n </button>\n )}\n <button\n type=\"button\"\n class=\"btn btn--primary\"\n onClick={handleSend}\n disabled={\n (!composeBody.trim() && composeShots.length === 0) || sending\n }\n >\n {sending\n ? strings['detail.compose_sending']\n : strings['detail.compose_send']}\n </button>\n </div>\n </div>\n ) : (\n // Teammate is reading a project-wide row. Replies are private\n // to the submitter, so we hide the compose box entirely (action\n // is genuinely unavailable) and surface a short notice so the\n // viewer understands why.\n <p class=\"report-detail-teammate-notice\" role=\"note\">\n {strings['detail.teammate_notice']}\n </p>\n )}\n\n {error && <div class=\"error\">{error}</div>}\n </div>\n </div>\n )\n}\n\nfunction appendComment(\n current: WidgetCommentRow[],\n next: WidgetCommentRow,\n): WidgetCommentRow[] {\n if (current.some((c) => c.id === next.id)) return current\n return [...current, next]\n}\n\ninterface ContextBlockProps {\n detail: WidgetReportDetail\n strings: Record<StringKey, string>\n}\n\n/**\n * The original-context block that sits above the description on the\n * detail page. Surfaces what the user originally reported (type +\n * severity at submit time, page they were on, when they sent it,\n * how the screenshot was captured) so they can recall the report\n * without having to re-derive it from the description.\n */\nfunction ContextBlock({ detail, strings }: ContextBlockProps) {\n const captureKey: StringKey = `detail.context.capture.${detail.capture_method}` as StringKey\n const captureLabel = strings[captureKey] ?? detail.capture_method\n return (\n <div class=\"report-detail-context\">\n <div class=\"report-detail-context-pills\">\n <span class=\"pill pill-type\">{strings[`type.${detail.feedback_type}` as StringKey]}</span>\n <span class={`pill pill-severity pill-severity--${detail.severity}`}>\n {strings[`severity.${detail.severity}` as StringKey]}\n </span>\n <span class=\"pill pill-capture\">{captureLabel}</span>\n </div>\n <div class=\"report-detail-context-line\">\n <span class=\"report-detail-context-label\">{strings['detail.context.submitted_at']}</span>\n <span>{formatSubmittedAt(detail.created_at)}</span>\n </div>\n {detail.page_url && (() => {\n const safeHref = safeExternalHref(detail.page_url)\n return (\n <div class=\"report-detail-context-line\" title={detail.page_url}>\n <span class=\"report-detail-context-label\">{strings['detail.context.page']}</span>\n {safeHref ? (\n <a\n class=\"report-detail-context-url\"\n href={safeHref}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {truncateUrl(detail.page_url, 64)}\n </a>\n ) : (\n <span class=\"report-detail-context-url\">{truncateUrl(detail.page_url, 64)}</span>\n )}\n </div>\n )\n })()}\n </div>\n )\n}\n\nfunction formatSubmittedAt(iso: string): string {\n try {\n return new Date(iso).toLocaleString(undefined, {\n dateStyle: 'medium',\n timeStyle: 'short',\n })\n } catch {\n return iso\n }\n}\n\ninterface TechnicalContextDrawerProps {\n ctx: CapturedContext\n strings: Record<StringKey, string>\n}\n\nconst TECH_CONSOLE_LIMIT = 20\nconst TECH_NETWORK_LIMIT = 15\n\n/**\n * The \"transparency drawer\" — collapsible panel showing the user the\n * exact diagnostic data their browser submitted with the report (console\n * tail, network tail, JS errors, device summary). Closed by default so\n * the regular conversation flow stays uncluttered; one click reveals\n * everything. Borrowed from thePnr's customer-facing `TechnicalContextBlock`.\n */\nfunction TechnicalContextDrawer({ ctx, strings }: TechnicalContextDrawerProps) {\n const errors = ctx.errors ?? []\n const consoleLogs = (ctx.consoleLogs ?? []).slice(-TECH_CONSOLE_LIMIT)\n const network = (ctx.networkRequests ?? []).slice(-TECH_NETWORK_LIMIT)\n const device = ctx.device\n const hasAny =\n !!device || errors.length > 0 || consoleLogs.length > 0 || network.length > 0\n if (!hasAny) return null\n const errorCount = errors.length\n const summary =\n errorCount > 0\n ? `${strings['detail.tech.title']} · ${errorCount} ${\n errorCount === 1 ? strings['detail.tech.errors_one'] : strings['detail.tech.errors_many']\n }`\n : strings['detail.tech.title']\n return (\n <details class=\"report-detail-tech\">\n <summary>{summary}</summary>\n <div class=\"tech-body\">\n {device && <DeviceSection device={device} strings={strings} />}\n {errors.length > 0 && <ErrorsSection errors={errors} strings={strings} />}\n {consoleLogs.length > 0 && <ConsoleSection logs={consoleLogs} strings={strings} />}\n {network.length > 0 && <NetworkSection rows={network} strings={strings} />}\n </div>\n </details>\n )\n}\n\nfunction DeviceSection({\n device,\n strings,\n}: {\n device: CapturedContext['device']\n strings: Record<StringKey, string>\n}) {\n const parts: Array<{ label: string; value: string }> = []\n if (device?.viewport) {\n const dpr = device.viewport.dpr ? ` @${device.viewport.dpr}x` : ''\n parts.push({\n label: strings['detail.tech.device.viewport'],\n value: `${device.viewport.w}×${device.viewport.h}${dpr}`,\n })\n }\n if (device?.platform) parts.push({ label: strings['detail.tech.device.platform'], value: device.platform })\n if (device?.language) parts.push({ label: strings['detail.tech.device.language'], value: device.language })\n if (device?.timezone) parts.push({ label: strings['detail.tech.device.timezone'], value: device.timezone })\n if (device?.connection) parts.push({ label: strings['detail.tech.device.connection'], value: device.connection })\n if (parts.length === 0) return null\n return (\n <div class=\"tech-section\">\n <h4>{strings['detail.tech.device']}</h4>\n <dl class=\"tech-device\">\n {parts.map((p) => (\n <>\n <dt>{p.label}</dt>\n <dd>{p.value}</dd>\n </>\n ))}\n </dl>\n </div>\n )\n}\n\nfunction ErrorsSection({\n errors,\n strings,\n}: {\n errors: ErrorEntry[]\n strings: Record<StringKey, string>\n}) {\n return (\n <div class=\"tech-section\">\n <h4>{strings['detail.tech.errors']}</h4>\n <ul class=\"tech-errors\">\n {errors.map((e) => (\n <li>\n <div class=\"tech-errors-msg\">{e.message}</div>\n {e.stack && <pre class=\"tech-errors-stack\">{truncateStack(e.stack)}</pre>}\n </li>\n ))}\n </ul>\n </div>\n )\n}\n\nfunction ConsoleSection({\n logs,\n strings,\n}: {\n logs: ConsoleEntry[]\n strings: Record<StringKey, string>\n}) {\n return (\n <div class=\"tech-section\">\n <h4>{strings['detail.tech.console']}</h4>\n <ul class=\"tech-console\">\n {logs.map((l) => (\n <li class={`tech-console-row tech-console-row--${l.level}`}>\n <span class=\"tech-console-level\">{l.level}</span>\n <span class=\"tech-console-msg\">{l.message}</span>\n </li>\n ))}\n </ul>\n </div>\n )\n}\n\nfunction NetworkSection({\n rows,\n strings,\n}: {\n rows: NetworkEntry[]\n strings: Record<StringKey, string>\n}) {\n return (\n <div class=\"tech-section\">\n <h4>{strings['detail.tech.network']}</h4>\n <ul class=\"tech-network\">\n {rows.map((n) => {\n const failed = n.status >= 400 || n.status === 0\n return (\n <li class={`tech-network-row${failed ? ' tech-network-row--fail' : ''}`}>\n <span class=\"tech-network-status\">{n.status || '—'}</span>\n <span class=\"tech-network-method\">{n.method}</span>\n <span class=\"tech-network-url\" title={n.url}>\n {truncateUrl(n.url, 56)}\n </span>\n <span class=\"tech-network-time\">{Math.round(n.durationMs)}ms</span>\n </li>\n )\n })}\n </ul>\n </div>\n )\n}\n\nfunction truncateStack(stack: string): string {\n // Clip to first 12 frames so the drawer doesn't blow out vertically on\n // a deep React stack — the user can always click through to a full view\n // if we add one later.\n const lines = stack.split('\\n')\n if (lines.length <= 12) return stack\n return lines.slice(0, 12).join('\\n') + '\\n …'\n}\n\ninterface StatusHistorySectionProps {\n rows: WidgetStatusChangeRow[]\n strings: Record<StringKey, string>\n}\n\n/**\n * The full audit trail of who changed the report's status, when, and via\n * which surface (operator UI, MCP agent, or system). Read-only — purely\n * a transparency / \"you're being heard by real humans\" gesture borrowed\n * from thePnr's customer-facing detail view.\n */\nfunction StatusHistorySection({ rows, strings }: StatusHistorySectionProps) {\n return (\n <div class=\"report-detail-history\">\n <h3 class=\"report-detail-section\">{strings['detail.history']}</h3>\n <ol class=\"status-history\">\n {rows.map((r) => {\n const from = strings[`status.${r.from_status}` as StringKey] ?? r.from_status\n const to = strings[`status.${r.to_status}` as StringKey] ?? r.to_status\n const sourceKey: StringKey =\n r.changed_by_source === 'mcp'\n ? 'detail.author.mcp'\n : r.changed_by_source === 'system'\n ? 'detail.author.system'\n : 'detail.author.staff'\n return (\n <li class=\"status-history-row\">\n <span class=\"status-history-time\">{formatSubmittedAt(r.created_at)}</span>\n <span class=\"status-history-transition\">\n <span class={`pill pill-status pill-status--${r.from_status}`}>{from}</span>\n <span class=\"status-history-arrow\" aria-hidden=\"true\">→</span>\n <span class={`pill pill-status pill-status--${r.to_status}`}>{to}</span>\n </span>\n <span class={`status-history-source status-history-source--${r.changed_by_source}`}>\n {strings[sourceKey]}\n </span>\n </li>\n )\n })}\n </ol>\n </div>\n )\n}\n","const ENCODED_ENTITIES = /[\"&<]/;\n\n/** @param {string} str */\nexport function encodeEntities(str) {\n\t// Skip all work for strings with no entities needing encoding:\n\tif (str.length === 0 || ENCODED_ENTITIES.test(str) === false) return str;\n\n\tlet last = 0,\n\t\ti = 0,\n\t\tout = '',\n\t\tch = '';\n\n\t// Seek forward in str until the next entity char:\n\tfor (; i < str.length; i++) {\n\t\tswitch (str.charCodeAt(i)) {\n\t\t\tcase 34:\n\t\t\t\tch = '"';\n\t\t\t\tbreak;\n\t\t\tcase 38:\n\t\t\t\tch = '&';\n\t\t\t\tbreak;\n\t\t\tcase 60:\n\t\t\t\tch = '<';\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tcontinue;\n\t\t}\n\t\t// Append skipped/buffered characters and the encoded entity:\n\t\tif (i !== last) out += str.slice(last, i);\n\t\tout += ch;\n\t\t// Start the next seek/buffer after the entity's offset:\n\t\tlast = i + 1;\n\t}\n\tif (i !== last) out += str.slice(last, i);\n\treturn out;\n}\n","/** Normal hydration that attaches to a DOM tree but does not diff it. */\nexport const MODE_HYDRATE = 1 << 5;\n/** Signifies this VNode suspended on the previous render */\nexport const MODE_SUSPENDED = 1 << 7;\n/** Indicates that this node needs to be inserted while patching children */\nexport const INSERT_VNODE = 1 << 2;\n/** Indicates a VNode has been matched with another VNode in the diff */\nexport const MATCHED = 1 << 1;\n\n/** Reset all mode flags */\nexport const RESET_MODE = ~(MODE_HYDRATE | MODE_SUSPENDED);\n\nexport const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';\nexport const XHTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';\nexport const MATH_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';\n\nexport const NULL = null;\nexport const UNDEFINED = undefined;\nexport const EMPTY_OBJ = /** @type {any} */ ({});\nexport const EMPTY_ARR = [];\nexport const IS_NON_DIMENSIONAL =\n\t/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;\n","import { options, Fragment } from 'preact';\nimport { encodeEntities } from './utils';\nimport { IS_NON_DIMENSIONAL } from '../../src/constants';\n\nlet vnodeId = 0;\n\nconst isArray = Array.isArray;\n\n/**\n * @fileoverview\n * This file exports various methods that implement Babel's \"automatic\" JSX runtime API:\n * - jsx(type, props, key)\n * - jsxs(type, props, key)\n * - jsxDEV(type, props, key, __source, __self)\n *\n * The implementation of createVNode here is optimized for performance.\n * Benchmarks: https://esbench.com/bench/5f6b54a0b4632100a7dcd2b3\n */\n\n/**\n * JSX.Element factory used by Babel's {runtime:\"automatic\"} JSX transform\n * @param {VNode['type']} type\n * @param {VNode['props']} props\n * @param {VNode['key']} [key]\n * @param {unknown} [isStaticChildren]\n * @param {unknown} [__source]\n * @param {unknown} [__self]\n */\nfunction createVNode(type, props, key, isStaticChildren, __source, __self) {\n\tif (!props) props = {};\n\t// We'll want to preserve `ref` in props to get rid of the need for\n\t// forwardRef components in the future, but that should happen via\n\t// a separate PR.\n\tlet normalizedProps = props,\n\t\tref,\n\t\ti;\n\n\tif ('ref' in normalizedProps) {\n\t\tnormalizedProps = {};\n\t\tfor (i in props) {\n\t\t\tif (i == 'ref') {\n\t\t\t\tref = props[i];\n\t\t\t} else {\n\t\t\t\tnormalizedProps[i] = props[i];\n\t\t\t}\n\t\t}\n\t}\n\n\t/** @type {VNode & { __source: any; __self: any }} */\n\tconst vnode = {\n\t\ttype,\n\t\tprops: normalizedProps,\n\t\tkey,\n\t\tref,\n\t\t_children: null,\n\t\t_parent: null,\n\t\t_depth: 0,\n\t\t_dom: null,\n\t\t_component: null,\n\t\tconstructor: undefined,\n\t\t_original: --vnodeId,\n\t\t_index: -1,\n\t\t_flags: 0,\n\t\t__source,\n\t\t__self\n\t};\n\n\t// If a Component VNode, check for and apply defaultProps.\n\t// Note: `type` is often a String, and can be `undefined` in development.\n\tif (typeof type === 'function' && (ref = type.defaultProps)) {\n\t\tfor (i in ref)\n\t\t\tif (normalizedProps[i] === undefined) {\n\t\t\t\tnormalizedProps[i] = ref[i];\n\t\t\t}\n\t}\n\n\tif (options.vnode) options.vnode(vnode);\n\treturn vnode;\n}\n\n/**\n * Create a template vnode. This function is not expected to be\n * used directly, but rather through a precompile JSX transform\n * @param {string[]} templates\n * @param {Array<string | null | VNode>} exprs\n * @returns {VNode}\n */\nfunction jsxTemplate(templates, ...exprs) {\n\tconst vnode = createVNode(Fragment, { tpl: templates, exprs });\n\t// Bypass render to string top level Fragment optimization\n\tvnode.key = vnode._vnode;\n\treturn vnode;\n}\n\nconst JS_TO_CSS = {};\nconst CSS_REGEX = /[A-Z]/g;\n\n/**\n * Unwrap potential signals.\n * @param {*} value\n * @returns {*}\n */\nfunction normalizeAttrValue(value) {\n\treturn value !== null &&\n\t\ttypeof value === 'object' &&\n\t\ttypeof value.valueOf === 'function'\n\t\t? value.valueOf()\n\t\t: value;\n}\n\n/**\n * Serialize an HTML attribute to a string. This function is not\n * expected to be used directly, but rather through a precompile\n * JSX transform\n * @param {string} name The attribute name\n * @param {*} value The attribute value\n * @returns {string}\n */\nfunction jsxAttr(name, value) {\n\tif (options.attr) {\n\t\tconst result = options.attr(name, value);\n\t\tif (typeof result === 'string') return result;\n\t}\n\n\tvalue = normalizeAttrValue(value);\n\n\tif (name === 'ref' || name === 'key') return '';\n\tif (name === 'style' && typeof value === 'object') {\n\t\tlet str = '';\n\t\tfor (let prop in value) {\n\t\t\tlet val = value[prop];\n\t\t\tif (val != null && val !== '') {\n\t\t\t\tconst name =\n\t\t\t\t\tprop[0] == '-'\n\t\t\t\t\t\t? prop\n\t\t\t\t\t\t: JS_TO_CSS[prop] ||\n\t\t\t\t\t\t\t(JS_TO_CSS[prop] = prop.replace(CSS_REGEX, '-$&').toLowerCase());\n\n\t\t\t\tlet suffix = ';';\n\t\t\t\tif (\n\t\t\t\t\ttypeof val === 'number' &&\n\t\t\t\t\t// Exclude custom-attributes\n\t\t\t\t\t!name.startsWith('--') &&\n\t\t\t\t\t!IS_NON_DIMENSIONAL.test(name)\n\t\t\t\t) {\n\t\t\t\t\tsuffix = 'px;';\n\t\t\t\t}\n\t\t\t\tstr = str + name + ':' + val + suffix;\n\t\t\t}\n\t\t}\n\t\treturn name + '=\"' + encodeEntities(str) + '\"';\n\t}\n\n\tif (\n\t\tvalue == null ||\n\t\tvalue === false ||\n\t\ttypeof value === 'function' ||\n\t\ttypeof value === 'object'\n\t) {\n\t\treturn '';\n\t} else if (value === true) return name;\n\n\treturn name + '=\"' + encodeEntities('' + value) + '\"';\n}\n\n/**\n * Escape a dynamic child passed to `jsxTemplate`. This function\n * is not expected to be used directly, but rather through a\n * precompile JSX transform\n * @param {*} value\n * @returns {string | null | VNode | Array<string | null | VNode>}\n */\nfunction jsxEscape(value) {\n\tif (\n\t\tvalue == null ||\n\t\ttypeof value === 'boolean' ||\n\t\ttypeof value === 'function'\n\t) {\n\t\treturn null;\n\t}\n\n\tif (typeof value === 'object') {\n\t\t// Check for VNode\n\t\tif (value.constructor === undefined) return value;\n\n\t\tif (isArray(value)) {\n\t\t\tfor (let i = 0; i < value.length; i++) {\n\t\t\t\tvalue[i] = jsxEscape(value[i]);\n\t\t\t}\n\t\t\treturn value;\n\t\t}\n\t}\n\n\treturn encodeEntities('' + value);\n}\n\nexport {\n\tcreateVNode as jsx,\n\tcreateVNode as jsxs,\n\tcreateVNode as jsxDEV,\n\tFragment,\n\t// precompiled JSX transform\n\tjsxTemplate,\n\tjsxAttr,\n\tjsxEscape\n};\n","import { h, type VNode } from 'preact'\n\nconst SEQ_RE = /#(\\d+)/g\n\n/**\n * Split text on \"#NN\" report references and render each as a clickable link\n * that opens that report (#85 — \"voir #81\" used to be dead text). Returns the\n * original text as a single item when there's nothing to linkify or no\n * handler is supplied (so callers can render the result directly).\n */\nexport function linkifySeq(\n text: string | null | undefined,\n onOpenSeq?: (seq: number) => void,\n): (string | VNode)[] {\n const value = text ?? ''\n if (!value || !onOpenSeq) return [value]\n const parts: (string | VNode)[] = []\n let last = 0\n SEQ_RE.lastIndex = 0\n let m: RegExpExecArray | null\n while ((m = SEQ_RE.exec(value)) !== null) {\n if (m.index > last) parts.push(value.slice(last, m.index))\n const seq = Number(m[1])\n parts.push(\n <button\n type=\"button\"\n class=\"seq-link\"\n onClick={() => onOpenSeq(seq)}\n aria-label={`Open report #${seq}`}\n >\n #{seq}\n </button>,\n )\n last = m.index + m[0].length\n }\n if (last < value.length) parts.push(value.slice(last))\n return parts.length ? parts : [value]\n}\n","/**\n * CommentBubble — single comment rendered inside the report detail.\n *\n * USER comments float right (the user's own follow-ups). MCP / STAFF /\n * SYSTEM float left and pick up an author label so you can tell at a\n * glance who wrote what. The \"Mhosaic Team\" label for MCP comments\n * matches PNR's pattern; hosts can hide it via a project flag (future).\n */\n\nimport { h } from 'preact'\n\nimport type { WidgetCommentRow } from '../types'\nimport type { StringKey } from './i18n'\nimport { linkifySeq } from './linkifySeq'\n\ninterface CommentBubbleProps {\n comment: WidgetCommentRow\n strings: Record<StringKey, string>\n /** Make \"#NN\" references in the comment body clickable (#85). */\n onOpenSeq?: (seq: number) => void\n /** Presigned attachment URLs change on every detail poll; the parent can\n * pin the first URL seen per attachment id so `src` stays stable and the\n * browser doesn't re-download every image every 30s. */\n stableUrl?: (key: string, url: string) => string\n}\n\nexport function CommentBubble({ comment, strings, onOpenSeq, stableUrl }: CommentBubbleProps) {\n // The submitter's own follow-ups float right; everything else (operator,\n // agent, system) floats left with an attribution label. Both submitter\n // and operator comments arrive as `author_source = USER` from the\n // backend, so we rely on the server-computed `is_mine` flag to tell\n // them apart instead of guessing from the source enum.\n const isMine = comment.is_mine\n const isAgent = !isMine && comment.author_source === 'mcp'\n const isSystem = !isMine && comment.author_source === 'system'\n const variant: 'mcp' | 'system' | 'staff' = isAgent\n ? 'mcp'\n : isSystem\n ? 'system'\n : 'staff'\n const labelKey: StringKey =\n variant === 'mcp'\n ? 'detail.author.mcp'\n : variant === 'system'\n ? 'detail.author.system'\n : 'detail.author.staff'\n const label = comment.author_label || strings[labelKey]\n // Only attachments the store could presign are renderable (#91).\n const images = (comment.attachments ?? []).filter((a) => a.url)\n return (\n <div class={`comment-bubble ${isMine ? 'is-mine' : 'is-other'}`}>\n {!isMine && label && (\n <div class={`comment-author comment-author--${variant}`}>{label}</div>\n )}\n {comment.body && (\n <div class=\"comment-body\">{linkifySeq(comment.body, onOpenSeq)}</div>\n )}\n {images.length > 0 && (\n <div class=\"comment-attachments\">\n {images.map((a) => {\n const src = stableUrl ? stableUrl(`att:${a.id}`, a.url!) : a.url!\n return (\n <a\n key={a.id}\n class=\"comment-attachment\"\n href={src}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n <img src={src} alt={strings['detail.attachment_alt']} loading=\"lazy\" />\n </a>\n )\n })}\n </div>\n )}\n <div class=\"comment-time\">{formatTime(comment.created_at)}</div>\n </div>\n )\n}\n\nfunction formatTime(iso: string): string {\n try {\n return new Date(iso).toLocaleString(undefined, {\n dateStyle: 'short',\n timeStyle: 'short',\n })\n } catch {\n return iso\n }\n}\n","/**\n * Helpers for the manual-screenshot-upload UX (v0.6.0).\n *\n * Kept tiny and dependency-free so they can be unit-tested without DOM/jsdom\n * setup. The interesting work — drag/drop, paste, file picker — lives in\n * Form.tsx.\n */\n\nexport const ALLOWED_IMAGE_TYPES = [\n 'image/png',\n 'image/jpeg',\n 'image/webp',\n] as const\n\nexport const MAX_SCREENSHOT_BYTES = 10 * 1024 * 1024 // 10 MB\n\n/** Per-report cap — mirrors the backend SCREENSHOT_MAX_COUNT default. */\nexport const MAX_SCREENSHOTS = 5\n\nexport type ScreenshotValidationError =\n | { kind: 'type' }\n | { kind: 'size'; maxMb: number }\n\nexport function validateScreenshotFile(file: File): ScreenshotValidationError | null {\n if (\n !ALLOWED_IMAGE_TYPES.includes(\n file.type as (typeof ALLOWED_IMAGE_TYPES)[number],\n )\n ) {\n return { kind: 'type' }\n }\n if (file.size > MAX_SCREENSHOT_BYTES) {\n return { kind: 'size', maxMb: MAX_SCREENSHOT_BYTES / (1024 * 1024) }\n }\n return null\n}\n\n/**\n * Truncate a URL while keeping the start and end visible. Used in the form's\n * \"Vous étiez ici\" footer so the user can verify the URL we'll send without\n * the long path overflowing the modal.\n *\n * \"https://app.example.com/very/long/deep/path?a=1&b=2\" (53 chars, max 30)\n * → \"https://app.examp…h?a=1&b=2\"\n */\nexport function truncateUrl(url: string, maxLength = 80): string {\n if (url.length <= maxLength) return url\n const keepStart = Math.floor((maxLength - 1) / 2)\n const keepEnd = maxLength - 1 - keepStart\n return `${url.slice(0, keepStart)}…${url.slice(url.length - keepEnd)}`\n}\n\n/**\n * Return `url` only if its scheme is in the safe-for-href allowlist —\n * `https:` always, `http:` for localhost only. Otherwise return `undefined`,\n * which renders as a non-clickable element (and React/Preact omit the href\n * attribute entirely). This stops a malicious widget user from planting\n * `javascript:`, `data:`, `vbscript:`, or `blob:` URLs in their report's\n * `page_url` / `screenshot_url` and getting an operator's click to execute\n * arbitrary code in the widget host's origin.\n */\nexport function safeExternalHref(url: string | null | undefined): string | undefined {\n if (!url) return undefined\n let parsed: URL\n try {\n parsed = new URL(url)\n } catch {\n return undefined\n }\n if (parsed.protocol === 'https:') return parsed.toString()\n if (\n parsed.protocol === 'http:' &&\n (parsed.hostname === 'localhost' ||\n parsed.hostname === '127.0.0.1' ||\n parsed.hostname === '[::1]')\n ) {\n return parsed.toString()\n }\n return undefined\n}\n\n","/**\n * ChangelogList — the \"This week\" tab.\n *\n * Renders the user's resolved reports grouped by ISO week of resolution\n * date, most recent week first. Trust-building surface borrowed from\n * thePnr's `FeedbackChangelogView` (\"see what's been fixed\"). Polls every\n * 30 s while open, same cadence as MineList.\n *\n * Scope is the submitter's own resolved reports only — multi-tenant\n * privacy default. A per-project \"show all-project changelog to\n * customers\" setting can expand the scope in a later release.\n */\n\nimport { h } from 'preact'\nimport { useEffect, useMemo, useRef, useState } from 'preact/hooks'\n\nimport type { ApiClient } from '../api/client'\nimport type { WidgetChangelogRow } from '../types'\nimport type { StringKey } from './i18n'\nimport { startPoll } from './poll'\nimport { ReportRow } from './ReportRow'\n\ninterface ChangelogListProps {\n api: ApiClient\n externalId: string\n strings: Record<StringKey, string>\n onSelect: (report: WidgetChangelogRow) => void\n}\n\nconst POLL_MS = 30_000\n\ninterface WeekGroup {\n /** ISO date (YYYY-MM-DD) of the Monday anchoring this week. */\n weekKey: string\n /** Localized \"Week of {Mon DD, YYYY}\" label, computed once per group. */\n label: string\n rows: WidgetChangelogRow[]\n}\n\n/**\n * Week-of-resolution key for a row. Anchored on Monday in UTC so the\n * grouping is consistent across timezones — the user-visible label is\n * localized at render time.\n */\nexport function isoWeekKey(iso: string): string {\n const d = new Date(iso)\n if (Number.isNaN(d.getTime())) return ''\n // JS Sunday=0; we want Monday=0 to anchor weeks the way thePnr does.\n const day = (d.getUTCDay() + 6) % 7\n const monday = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate() - day))\n return monday.toISOString().slice(0, 10)\n}\n\nexport function groupRowsByWeek(rows: WidgetChangelogRow[]): WeekGroup[] {\n const buckets = new Map<string, WidgetChangelogRow[]>()\n for (const row of rows) {\n const key = isoWeekKey(row.resolved_at)\n if (!key) continue\n const existing = buckets.get(key)\n if (existing) existing.push(row)\n else buckets.set(key, [row])\n }\n // Sort buckets by week descending (most recent week first); within each\n // bucket the server already returned -resolved_at order.\n return Array.from(buckets.entries())\n .sort(([a], [b]) => (a < b ? 1 : -1))\n .map(([weekKey, weekRows]) => ({\n weekKey,\n label: formatWeekLabel(weekKey),\n rows: weekRows,\n }))\n}\n\nfunction formatWeekLabel(weekKey: string): string {\n // Render just the date portion (\"May 5, 2026\"); the i18n template\n // wraps it as \"Week of {date}\".\n try {\n return new Date(weekKey).toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n })\n } catch {\n return weekKey\n }\n}\n\nexport function ChangelogList({ api, externalId, strings, onSelect }: ChangelogListProps) {\n const [rows, setRows] = useState<WidgetChangelogRow[] | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [refreshing, setRefreshing] = useState(false)\n const mountedRef = useRef(true)\n\n const fetchRows = async () => {\n setRefreshing(true)\n setError(null)\n try {\n const next = await api.listChangelog(externalId)\n if (!mountedRef.current) return true\n setRows(next)\n return true\n } catch (err) {\n // Friendly UI fallback; raw error to console for the developer.\n if (typeof console !== 'undefined')\n console.warn('[mhosaic] listChangelog:', err)\n if (!mountedRef.current) return false\n setError(strings['mine.error'])\n return false\n } finally {\n if (mountedRef.current) setRefreshing(false)\n }\n }\n\n useEffect(() => {\n mountedRef.current = true\n const poll = startPoll(fetchRows, POLL_MS)\n return () => {\n mountedRef.current = false\n poll.stop()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [externalId])\n\n const groups = useMemo(() => (rows ? groupRowsByWeek(rows) : []), [rows])\n const isLoading = rows === null && !error\n const isEmpty = rows !== null && rows.length === 0\n\n return (\n <div class=\"mine-list\">\n <div class=\"mine-list-header\">\n <h2>{strings['tab.changelog']}</h2>\n <button\n type=\"button\"\n class=\"btn\"\n onClick={() => {\n void fetchRows()\n }}\n disabled={refreshing}\n >\n {refreshing ? strings['mine.loading'] : strings['mine.refresh']}\n </button>\n </div>\n {isLoading && <div class=\"mine-loading\">{strings['mine.loading']}</div>}\n {error && <div class=\"error\">{error}</div>}\n {isEmpty && (\n <div class=\"mine-empty\">\n <strong>{strings['changelog.empty.title']}</strong>\n <p>{strings['changelog.empty.body']}</p>\n </div>\n )}\n {groups.length > 0 && (\n <div class=\"changelog-groups\">\n {groups.map((g) => (\n <section class=\"changelog-group\" key={g.weekKey}>\n <header class=\"changelog-group-header\">\n <span class=\"changelog-group-marker\" aria-hidden=\"true\">●</span>\n <span class=\"changelog-group-label\">\n {strings['changelog.week_of'].replace('{date}', g.label)}\n </span>\n <span class=\"changelog-group-rule\" aria-hidden=\"true\" />\n <span class=\"changelog-group-count\">\n {strings[\n g.rows.length === 1 ? 'changelog.resolved_one' : 'changelog.resolved_many'\n ].replace('{count}', String(g.rows.length))}\n </span>\n </header>\n <ul class=\"mine-rows\">\n {g.rows.map((row) => (\n <li>\n <ReportRow\n row={row}\n strings={strings}\n onClick={() => onSelect(row)}\n extraMeta={row.pr_number ? `PR #${row.pr_number}` : undefined}\n />\n </li>\n ))}\n </ul>\n </section>\n ))}\n </div>\n )}\n </div>\n )\n}\n","/**\n * ReportRow — single list item in MineList.\n *\n * Shows status / type / severity badges, a one-line description preview,\n * a relative timestamp, and a \"N replies\" hint pulled from the\n * comment_count annotation on the backend. Whole row is the click\n * target so keyboard nav (Tab + Enter) works.\n */\n\nimport { h } from 'preact'\n\nimport type { WidgetReportRow } from '../types'\nimport type { StringKey } from './i18n'\n\ninterface ReportRowProps {\n row: WidgetReportRow\n strings: Record<StringKey, string>\n onClick: () => void\n /** Extra trailing meta chip (e.g. the changelog's merged-PR ref). */\n extraMeta?: string | undefined\n}\n\nfunction statusClassName(status: string): string {\n return `pill pill-status pill-status--${status}`\n}\n\nfunction severityClassName(severity: string): string {\n return `pill pill-severity pill-severity--${severity}`\n}\n\nfunction typeClassName(): string {\n return 'pill pill-type'\n}\n\nfunction formatRelative(iso: string): string {\n const then = Date.parse(iso)\n if (!Number.isFinite(then)) return ''\n const seconds = Math.max(1, Math.round((Date.now() - then) / 1000))\n if (seconds < 60) return `${seconds}s`\n const minutes = Math.round(seconds / 60)\n if (minutes < 60) return `${minutes}m`\n const hours = Math.round(minutes / 60)\n if (hours < 48) return `${hours}h`\n const days = Math.round(hours / 24)\n return `${days}d`\n}\n\nfunction repliesLabel(count: number, strings: Record<StringKey, string>): string {\n if (count === 1) return strings['mine.replies_one']\n return strings['mine.replies_many'].replace('{count}', String(count))\n}\n\nexport function ReportRow({ row, strings, onClick, extraMeta }: ReportRowProps) {\n const preview =\n row.description.length > 120\n ? row.description.slice(0, 117) + '…'\n : row.description\n return (\n <button type=\"button\" class=\"mine-row\" onClick={onClick}>\n <div class=\"mine-row-pills\">\n <span class={statusClassName(row.status)}>{strings[`status.${row.status}` as StringKey] ?? row.status}</span>\n <span class={typeClassName()}>{strings[`type.${row.feedback_type}` as StringKey]}</span>\n <span class={severityClassName(row.severity)}>{strings[`severity.${row.severity}` as StringKey]}</span>\n </div>\n <div class=\"mine-row-preview\">{preview}</div>\n <div class=\"mine-row-meta\">\n <span>{formatRelative(row.updated_at || row.created_at)}</span>\n {row.comment_count > 0 && <span>· {repliesLabel(row.comment_count, strings)}</span>}\n {extraMeta && <span>· {extraMeta}</span>}\n </div>\n </button>\n )\n}\n","import { h } from 'preact'\n\ninterface FabProps {\n label: string\n onClick: () => void\n /** Open reports on the current page. 0/undefined → no badge. Display caps at 9+. */\n count?: number\n}\n\n/**\n * Bug glyph from lucide.dev (`Bug` icon), inlined as a single-color SVG.\n * Sized at 20×20 inside the 48×48 FAB so the icon reads as a confident\n * accent rather than dominating the circle (vs the prior 24/56 ratio,\n * which felt heavy). Picks up `currentColor` from `--mfb-fab-icon`\n * set on the .fab class.\n */\nfunction BugIcon() {\n return (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n <path d=\"m8 2 1.88 1.88\" />\n <path d=\"M14.12 3.88 16 2\" />\n <path d=\"M9 7.13v-1a3.003 3.003 0 1 1 6 0v1\" />\n <path d=\"M12 20c-3.3 0-6-2.7-6-6v-3a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v3c0 3.3-2.7 6-6 6\" />\n <path d=\"M12 20v-9\" />\n <path d=\"M6.53 9C4.6 8.8 3 7.1 3 5\" />\n <path d=\"M6 13H2\" />\n <path d=\"M3 21c0-2.1 1.7-3.9 3.8-4\" />\n <path d=\"M20.97 5c0 2.1-1.6 3.8-3.5 4\" />\n <path d=\"M22 13h-4\" />\n <path d=\"M17.2 17c2.1.1 3.8 1.9 3.8 4\" />\n </svg>\n )\n}\n\nexport function Fab({ label, onClick, count }: FabProps) {\n return (\n <button type=\"button\" class=\"fab\" aria-label={label} title={label} onClick={onClick}>\n <BugIcon />\n {count !== undefined && count > 0 && (\n <span class=\"fab-badge\" aria-hidden=\"true\">\n {count > 9 ? '9+' : String(count)}\n </span>\n )}\n </button>\n )\n}\n","import { h } from 'preact'\nimport { useEffect, useRef, useState } from 'preact/hooks'\n\nimport type { FeedbackSeverity, FeedbackType } from '../types'\nimport { Annotator } from './Annotator'\nimport type { StringKey } from './i18n'\nimport { MAX_SCREENSHOTS, truncateUrl, validateScreenshotFile } from './screenshot-utils'\n\nexport type FormStatus = 'idle' | 'submitting' | 'error' | 'success'\n\nexport interface FormValues {\n description: string\n feedback_type: FeedbackType\n severity: FeedbackSeverity\n /** Optional user-attached screenshots — via upload, paste, drag-drop, or\n * the annotator, in attach order (max MAX_SCREENSHOTS). v0.12 dropped\n * html2canvas-on-submit; v0.7.1 dropped the getDisplayMedia \"Capture this\n * page\" path (asked the operator for screen share permission on every\n * click, too high a friction for the value). */\n screenshots?: Blob[]\n /** Always \"manual\" when screenshots are attached — kept as a discriminated\n * field so the backend schema (which still accepts the legacy values) can\n * evolve independently. Undefined when no screenshot is attached. */\n capture_method?: 'manual'\n}\n\ninterface AttachedShot {\n blob: Blob\n preview: string\n}\n\ninterface FormProps {\n strings: Record<StringKey, string>\n onSubmit: (values: FormValues) => void\n onCancel: () => void\n status: FormStatus\n errorMessage?: string\n /** Sequence number of the just-created report, surfaced in the success\n * acknowledgement (\"report #42 sent\") so the submitter knows it\n * persisted (#82). Absent → the generic \"sent\" copy is shown. */\n submittedSeq?: number\n /** Reports whether the form holds unsaved input (typed text or attached\n * screenshots). The mount uses it to guard an accidental modal close (#89). */\n onDirtyChange?: (dirty: boolean) => void\n}\n\nconst TYPES: FeedbackType[] = ['bug', 'feature', 'question', 'praise', 'typo']\nconst SEVERITIES: FeedbackSeverity[] = ['blocker', 'high', 'medium', 'low']\n\nexport function Form({\n strings,\n onSubmit,\n onCancel,\n status,\n errorMessage,\n submittedSeq,\n onDirtyChange,\n}: FormProps) {\n const [description, setDescription] = useState('')\n const [feedbackType, setFeedbackType] = useState<FeedbackType>('bug')\n const [severity, setSeverity] = useState<FeedbackSeverity>('medium')\n const [localError, setLocalError] = useState('')\n const [shots, setShots] = useState<AttachedShot[]>([])\n const [isDragOver, setIsDragOver] = useState(false)\n const [annotatingIndex, setAnnotatingIndex] = useState<number | null>(null)\n const fileInputRef = useRef<HTMLInputElement | null>(null)\n const dropZoneRef = useRef<HTMLDivElement | null>(null)\n\n const submitting = status === 'submitting'\n const submitLabel = submitting ? strings['form.submitting'] : strings['form.submit']\n const pageUrl = typeof window !== 'undefined' ? window.location.href : ''\n\n // Report unsaved-input state up so the mount can guard an accidental close\n // (backdrop/Esc) with a discard confirmation (#89).\n const isDirty = description.trim() !== '' || shots.length > 0\n useEffect(() => {\n onDirtyChange?.(isDirty)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isDirty])\n\n // Revoke every object URL when the form unmounts; individual revokes\n // happen on remove/replace.\n const shotsRef = useRef(shots)\n shotsRef.current = shots\n useEffect(() => {\n return () => {\n shotsRef.current.forEach((s) => URL.revokeObjectURL(s.preview))\n }\n }, [])\n\n const acceptFiles = (files: Array<File | Blob>) => {\n setLocalError('')\n const remaining = MAX_SCREENSHOTS - shots.length\n const batch = files.slice(0, Math.max(0, remaining))\n for (const file of batch) {\n if (file instanceof File) {\n const err = validateScreenshotFile(file)\n if (err) {\n setLocalError(\n err.kind === 'type'\n ? strings['form.screenshot.error_type']\n : strings['form.screenshot.error_size'].replace('{max}', String(err.maxMb)),\n )\n return\n }\n }\n }\n if (batch.length) {\n const incoming = batch.map((blob) => ({ blob, preview: URL.createObjectURL(blob) }))\n setShots((prev) => [...prev, ...incoming])\n }\n if (files.length > batch.length) {\n setLocalError(\n strings['form.screenshot.error_count'].replace('{max}', String(MAX_SCREENSHOTS)),\n )\n }\n }\n\n const removeShot = (index: number) => {\n const shot = shots[index]\n if (shot) URL.revokeObjectURL(shot.preview)\n setShots((prev) => prev.filter((_, i) => i !== index))\n setLocalError('')\n if (fileInputRef.current) fileInputRef.current.value = ''\n }\n\n const handleFileInputChange = (e: Event) => {\n const input = e.target as HTMLInputElement\n const files = Array.from(input.files ?? [])\n if (files.length) acceptFiles(files)\n // Reset so picking the same file again re-triggers `change`.\n input.value = ''\n }\n\n const handleDragOver = (e: DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n setIsDragOver(true)\n }\n const handleDragLeave = (e: DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n if (e.currentTarget === e.target) setIsDragOver(false)\n }\n const handleDrop = (e: DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n setIsDragOver(false)\n const files = Array.from(e.dataTransfer?.files ?? [])\n if (files.length) acceptFiles(files)\n }\n\n // Clipboard paste, scoped to the dropzone — so we don't hijack paste from\n // textareas elsewhere in the page or in our own description field.\n useEffect(() => {\n const zone = dropZoneRef.current\n if (!zone) return\n const onPaste = (e: ClipboardEvent) => {\n const items = e.clipboardData?.items\n if (!items) return\n for (const item of Array.from(items)) {\n if (item.kind === 'file' && item.type.startsWith('image/')) {\n const file = item.getAsFile()\n if (file) {\n e.preventDefault()\n acceptFiles([file])\n return\n }\n }\n }\n }\n zone.addEventListener('paste', onPaste)\n return () => zone.removeEventListener('paste', onPaste)\n // The dropzone unmounts at the cap and remounts when a slot frees up,\n // so re-attach whenever the count changes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [shots.length])\n\n const handleAnnotated = (annotated: Blob) => {\n const index = annotatingIndex\n if (index === null || !shots[index]) {\n setAnnotatingIndex(null)\n return\n }\n URL.revokeObjectURL(shots[index].preview)\n const replacement = { blob: annotated, preview: URL.createObjectURL(annotated) }\n setShots((prev) => prev.map((s, i) => (i === index ? replacement : s)))\n setAnnotatingIndex(null)\n }\n\n const handleSubmit = (e: Event) => {\n e.preventDefault()\n if (!description.trim()) {\n setLocalError(strings['form.description.required'])\n return\n }\n setLocalError('')\n const values: FormValues = {\n description: description.trim(),\n feedback_type: feedbackType,\n severity,\n }\n if (shots.length) {\n values.screenshots = shots.map((s) => s.blob)\n values.capture_method = 'manual'\n }\n onSubmit(values)\n }\n\n return (\n <form onSubmit={handleSubmit}>\n <h2>{strings['form.title']}</h2>\n\n <div class=\"field\">\n <label for=\"mfb-desc\">{strings['form.description.label']}</label>\n <textarea\n id=\"mfb-desc\"\n value={description}\n placeholder={strings['form.description.placeholder']}\n onInput={(e) => setDescription((e.target as HTMLTextAreaElement).value)}\n />\n </div>\n\n <div class=\"row\">\n <div class=\"field\">\n <label for=\"mfb-type\">{strings['form.type.label']}</label>\n <select\n id=\"mfb-type\"\n value={feedbackType}\n onChange={(e) => setFeedbackType((e.target as HTMLSelectElement).value as FeedbackType)}\n >\n {TYPES.map((t) => <option value={t}>{strings[`type.${t}` as StringKey]}</option>)}\n </select>\n </div>\n <div class=\"field\">\n <label for=\"mfb-sev\">{strings['form.severity.label']}</label>\n <select\n id=\"mfb-sev\"\n value={severity}\n onChange={(e) => setSeverity((e.target as HTMLSelectElement).value as FeedbackSeverity)}\n >\n {SEVERITIES.map((s) => <option value={s}>{strings[`severity.${s}` as StringKey]}</option>)}\n </select>\n </div>\n </div>\n\n <div class=\"field\">\n <label>{strings['form.screenshot.label']}</label>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/png,image/jpeg,image/webp\"\n multiple\n class=\"mfb-sr-only\"\n onChange={handleFileInputChange}\n aria-hidden=\"true\"\n tabIndex={-1}\n />\n {shots.length > 0 && (\n <div class=\"screenshot-strip\">\n {shots.map((shot, index) => (\n <div class=\"screenshot-preview\" key={shot.preview}>\n <img src={shot.preview} alt=\"\" />\n <div class=\"screenshot-preview-actions\">\n <button\n type=\"button\"\n class=\"btn btn--primary screenshot-annotate\"\n onClick={() => setAnnotatingIndex(index)}\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\"/><path d=\"M18.5 2.5a2.12 2.12 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z\"/></svg>\n {strings['form.screenshot.annotate']}\n </button>\n <button type=\"button\" class=\"btn\" onClick={() => removeShot(index)}>\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6h14z\"/></svg>\n {strings['form.screenshot.remove']}\n </button>\n </div>\n </div>\n ))}\n </div>\n )}\n {shots.length < MAX_SCREENSHOTS && (\n <div\n ref={dropZoneRef}\n class={`screenshot-dropzone ${isDragOver ? 'is-dragover' : ''}`}\n tabIndex={0}\n role=\"button\"\n aria-label={strings['form.screenshot.label']}\n onClick={() => fileInputRef.current?.click()}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n fileInputRef.current?.click()\n }\n }}\n onDragOver={handleDragOver}\n onDragLeave={handleDragLeave}\n onDrop={handleDrop}\n >\n <svg class=\"screenshot-icon\" width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/>\n <polyline points=\"17 8 12 3 7 8\"/>\n <line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\"/>\n </svg>\n <div class=\"screenshot-cta\">\n <strong>{strings['form.screenshot.cta_click']}</strong>\n {', '}\n {strings['form.screenshot.cta_rest']}\n </div>\n <div class=\"screenshot-formats\">{strings['form.screenshot.formats']}</div>\n </div>\n )}\n </div>\n\n {pageUrl && (\n <div class=\"page-context\" title={pageUrl}>\n <span class=\"page-context-label\">{strings['form.context.label']}</span>\n <span class=\"page-context-url\">{truncateUrl(pageUrl, 90)}</span>\n </div>\n )}\n\n {/* REQ-A7: be transparent that technical context is captured. */}\n <div class=\"capture-notice\">{strings['form.capture.notice']}</div>\n\n {localError && <div class=\"error\">{localError}</div>}\n {status === 'error' && errorMessage && <div class=\"error\">{errorMessage}</div>}\n {status === 'success' && (\n <div class=\"success\">\n {submittedSeq !== undefined\n ? strings['form.success.seq'].replace('{seq}', String(submittedSeq))\n : strings['form.success']}\n </div>\n )}\n\n <div class=\"actions\">\n <button type=\"button\" class=\"btn\" onClick={onCancel} disabled={submitting}>{strings['form.cancel']}</button>\n <button type=\"submit\" class=\"btn btn--primary\" disabled={submitting}>{submitLabel}</button>\n </div>\n\n {annotatingIndex !== null && shots[annotatingIndex] && (\n <Annotator\n imageBlob={shots[annotatingIndex].blob}\n strings={strings}\n onCancel={() => setAnnotatingIndex(null)}\n onSave={handleAnnotated}\n />\n )}\n </form>\n )\n}\n","/**\n * Annotator — screenshot annotation modal.\n *\n * v0.6.0: rectangle, arrow, freehand, text. Custom canvas, no external lib.\n * v0.10.0: + highlight (semi-transparent fill), blur (pixelate sensitive\n * regions), full undo/redo stack, native color picker alongside swatches.\n *\n * Save rasterizes the original image + every shape onto a fresh canvas\n * and returns a PNG Blob. Blurs are always sampled from the source image\n * pixels (not the canvas), so strokes/text/highlights drawn near a blurred\n * region don't get re-blurred by accident.\n */\n\nimport { h } from 'preact'\nimport { useEffect, useLayoutEffect, useRef, useState } from 'preact/hooks'\n\nimport type { StringKey } from './i18n'\n\ntype Tool = 'rectangle' | 'arrow' | 'freehand' | 'text' | 'highlight' | 'blur'\n\ninterface BaseShape {\n color: string\n lineWidth: number\n}\n\ninterface RectShape extends BaseShape {\n kind: 'rectangle'\n x: number\n y: number\n w: number\n h: number\n}\n\ninterface ArrowShape extends BaseShape {\n kind: 'arrow'\n x1: number\n y1: number\n x2: number\n y2: number\n}\n\ninterface FreehandShape extends BaseShape {\n kind: 'freehand'\n points: Array<{ x: number; y: number }>\n}\n\ninterface TextShape extends BaseShape {\n kind: 'text'\n x: number\n y: number\n text: string\n fontSize: number\n}\n\ninterface HighlightShape extends BaseShape {\n kind: 'highlight'\n x: number\n y: number\n w: number\n h: number\n}\n\ninterface BlurShape extends BaseShape {\n kind: 'blur'\n x: number\n y: number\n w: number\n h: number\n /** Tile size in source-image pixels. Scales with canvas width. */\n tile: number\n}\n\ntype Shape =\n | RectShape\n | ArrowShape\n | FreehandShape\n | TextShape\n | HighlightShape\n | BlurShape\n\nconst COLORS: string[] = ['#ef4444', '#f59e0b', '#10b981', '#3b82f6', '#ffffff']\nconst HIGHLIGHT_ALPHA = 0.35\n\n// ---------------------------------------------------------------------------\n// Drawing helpers\n// ---------------------------------------------------------------------------\n\nfunction drawShape(\n ctx: CanvasRenderingContext2D,\n shape: Shape,\n sourceImage: HTMLImageElement | null,\n) {\n ctx.save()\n ctx.strokeStyle = shape.color\n ctx.fillStyle = shape.color\n ctx.lineWidth = shape.lineWidth\n ctx.lineCap = 'round'\n ctx.lineJoin = 'round'\n\n if (shape.kind === 'rectangle') {\n ctx.strokeRect(shape.x, shape.y, shape.w, shape.h)\n } else if (shape.kind === 'arrow') {\n drawArrow(ctx, shape.x1, shape.y1, shape.x2, shape.y2)\n } else if (shape.kind === 'freehand') {\n if (shape.points.length < 2) {\n ctx.restore()\n return\n }\n ctx.beginPath()\n ctx.moveTo(shape.points[0]!.x, shape.points[0]!.y)\n for (let i = 1; i < shape.points.length; i++) {\n ctx.lineTo(shape.points[i]!.x, shape.points[i]!.y)\n }\n ctx.stroke()\n } else if (shape.kind === 'text') {\n ctx.font = `bold ${shape.fontSize}px -apple-system, BlinkMacSystemFont, sans-serif`\n ctx.textBaseline = 'top'\n const metrics = ctx.measureText(shape.text)\n const padding = 4\n const w = metrics.width + padding * 2\n const hh = shape.fontSize + padding * 2\n ctx.fillStyle = 'rgba(0, 0, 0, 0.6)'\n ctx.fillRect(shape.x - padding, shape.y - padding, w, hh)\n ctx.fillStyle = shape.color\n ctx.fillText(shape.text, shape.x, shape.y)\n } else if (shape.kind === 'highlight') {\n // Semi-transparent filled rect. Multiply-ish blend so the underlying\n // pixels show through — typical marker-pen look.\n ctx.globalAlpha = HIGHLIGHT_ALPHA\n ctx.fillRect(\n normalizeStart(shape.x, shape.w),\n normalizeStart(shape.y, shape.h),\n Math.abs(shape.w),\n Math.abs(shape.h),\n )\n } else if (shape.kind === 'blur') {\n drawBlur(ctx, shape, sourceImage)\n }\n ctx.restore()\n}\n\nfunction normalizeStart(start: number, size: number): number {\n return size < 0 ? start + size : start\n}\n\nfunction drawArrow(\n ctx: CanvasRenderingContext2D,\n x1: number,\n y1: number,\n x2: number,\n y2: number,\n) {\n const headLen = 14\n const angle = Math.atan2(y2 - y1, x2 - x1)\n ctx.beginPath()\n ctx.moveTo(x1, y1)\n ctx.lineTo(x2, y2)\n ctx.stroke()\n ctx.beginPath()\n ctx.moveTo(x2, y2)\n ctx.lineTo(\n x2 - headLen * Math.cos(angle - Math.PI / 6),\n y2 - headLen * Math.sin(angle - Math.PI / 6),\n )\n ctx.lineTo(\n x2 - headLen * Math.cos(angle + Math.PI / 6),\n y2 - headLen * Math.sin(angle + Math.PI / 6),\n )\n ctx.closePath()\n ctx.fill()\n}\n\n/**\n * Pixelate a rectangular region by averaging tiles of the source image.\n * Always samples from the original `<img>` so blurs over text/strokes\n * still hide the underlying pixels, not the marks drawn on top.\n */\nfunction drawBlur(\n ctx: CanvasRenderingContext2D,\n shape: BlurShape,\n sourceImage: HTMLImageElement | null,\n) {\n if (!sourceImage) return\n const x = normalizeStart(shape.x, shape.w)\n const y = normalizeStart(shape.y, shape.h)\n const w = Math.abs(shape.w)\n const h = Math.abs(shape.h)\n if (w < 2 || h < 2) return\n const tile = Math.max(4, shape.tile)\n // Drop tiles aligned to the source image — pixelation is at native res\n // so the result is crisp regardless of CSS scale on the display canvas.\n for (let yy = y; yy < y + h; yy += tile) {\n for (let xx = x; xx < x + w; xx += tile) {\n const tw = Math.min(tile, x + w - xx)\n const th = Math.min(tile, y + h - yy)\n ctx.drawImage(\n sourceImage,\n xx,\n yy,\n tw,\n th, // source rect from the image\n xx,\n yy,\n tw,\n th, // dest rect on the canvas (same coords)\n )\n }\n }\n // Sampling a single tile-sized chunk and re-painting it at tile size\n // gives the mosaic. To get the visual look, downscale + upscale via\n // imageSmoothingDisabled.\n ctx.imageSmoothingEnabled = false\n for (let yy = y; yy < y + h; yy += tile) {\n for (let xx = x; xx < x + w; xx += tile) {\n const tw = Math.min(tile, x + w - xx)\n const th = Math.min(tile, y + h - yy)\n // Sample the top-left pixel of this tile from the source and stretch.\n ctx.drawImage(\n sourceImage,\n xx,\n yy,\n 1,\n 1, // single source pixel\n xx,\n yy,\n tw,\n th, // stretched dest tile\n )\n }\n }\n ctx.imageSmoothingEnabled = true\n}\n\n// ---------------------------------------------------------------------------\n// Inline icons\n// ---------------------------------------------------------------------------\n\nconst Icon = {\n rect: (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <rect x=\"2\" y=\"3\" width=\"12\" height=\"10\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" />\n </svg>\n ),\n arrow: (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M2 8h11M9 4l4 4-4 4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n ),\n pencil: (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M11.5 2.5l2 2L5 13H3v-2l8.5-8.5z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\" />\n </svg>\n ),\n text: (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M3 3h10M8 3v10M5 13h6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n ),\n highlight: (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M3 13l3-3L11 5l-2-2-5 5-3 3v2h2zM10 4l2 2\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n <path d=\"M2 14h12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n ),\n blur: (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <rect x=\"2\" y=\"2\" width=\"4\" height=\"4\" fill=\"currentColor\" opacity=\"0.85\" />\n <rect x=\"7\" y=\"2\" width=\"3\" height=\"3\" fill=\"currentColor\" opacity=\"0.55\" />\n <rect x=\"11\" y=\"2\" width=\"3\" height=\"4\" fill=\"currentColor\" opacity=\"0.4\" />\n <rect x=\"2\" y=\"7\" width=\"3\" height=\"3\" fill=\"currentColor\" opacity=\"0.6\" />\n <rect x=\"6\" y=\"7\" width=\"3\" height=\"3\" fill=\"currentColor\" opacity=\"0.85\" />\n <rect x=\"10\" y=\"7\" width=\"4\" height=\"3\" fill=\"currentColor\" opacity=\"0.5\" />\n <rect x=\"2\" y=\"11\" width=\"4\" height=\"3\" fill=\"currentColor\" opacity=\"0.4\" />\n <rect x=\"7\" y=\"11\" width=\"3\" height=\"3\" fill=\"currentColor\" opacity=\"0.65\" />\n <rect x=\"11\" y=\"11\" width=\"3\" height=\"3\" fill=\"currentColor\" opacity=\"0.85\" />\n </svg>\n ),\n undo: (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M4 7l3-3M4 7l3 3M4 7h6a3 3 0 0 1 0 6H7\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n ),\n redo: (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M12 7l-3-3M12 7l-3 3M12 7H6a3 3 0 0 0 0 6h2\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n ),\n trash: (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M3 4h10M6 4V2.5h4V4M5 4l.5 9h5L11 4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n ),\n close: (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M4 4l8 8M12 4l-8 8\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n ),\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nexport interface AnnotatorProps {\n imageBlob: Blob\n strings: Record<StringKey, string>\n onSave: (annotated: Blob) => void\n onCancel: () => void\n}\n\nexport function Annotator({ imageBlob, strings, onSave, onCancel }: AnnotatorProps) {\n const canvasRef = useRef<HTMLCanvasElement | null>(null)\n const containerRef = useRef<HTMLDivElement | null>(null)\n const imageRef = useRef<HTMLImageElement | null>(null)\n const [tool, setTool] = useState<Tool>('rectangle')\n const [color, setColor] = useState<string>(COLORS[0]!)\n const [shapes, setShapes] = useState<Shape[]>([])\n const [past, setPast] = useState<Shape[][]>([])\n const [future, setFuture] = useState<Shape[][]>([])\n const isDrawingRef = useRef(false)\n const [draftShape, setDraftShape] = useState<Shape | null>(null)\n const [imageLoaded, setImageLoaded] = useState(false)\n const [saving, setSaving] = useState(false)\n const scaleRef = useRef(1)\n\n function addShape(shape: Shape) {\n setShapes((s) => {\n // Use the functional setState so we read the most recent shape list\n // even when this fires from a stale-closure event handler. The past\n // stack captures `s` here (the truly current state).\n setPast((p) => [...p, s])\n return [...s, shape]\n })\n setFuture([])\n }\n\n function clearShapes() {\n setShapes((s) => {\n setPast((p) => [...p, s])\n return []\n })\n setFuture([])\n }\n\n function undo() {\n setPast((p) => {\n if (p.length === 0) return p\n const prev = p[p.length - 1]!\n setShapes((s) => {\n setFuture((f) => [s, ...f])\n return prev\n })\n return p.slice(0, -1)\n })\n }\n\n function redo() {\n setFuture((f) => {\n if (f.length === 0) return f\n const next = f[0]!\n setShapes((s) => {\n setPast((p) => [...p, s])\n return next\n })\n return f.slice(1)\n })\n }\n\n // Esc handler uses useLayoutEffect (not useEffect) so the listener is\n // attached SYNCHRONOUSLY after DOM commit, BEFORE paint. Tests (and\n // fast human users) that press Escape the moment the annotator becomes\n // visible would otherwise hit a brief window where the listener isn't\n // yet attached. The parent Modal's keydown handler bails when it sees\n // .annotator-backdrop in the shadow root, so we don't double-fire.\n useLayoutEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.stopPropagation()\n onCancel()\n }\n }\n window.addEventListener('keydown', onKey)\n return () => window.removeEventListener('keydown', onKey)\n }, [onCancel])\n\n // Cmd/Ctrl-Z = undo, Cmd/Ctrl-Shift-Z (or Cmd/Ctrl-Y) = redo. Re-binds\n // on each state mutation to capture fresh shapes/past/future closures.\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n const mod = e.metaKey || e.ctrlKey\n if (!mod) return\n const k = e.key.toLowerCase()\n if (k === 'z') {\n e.preventDefault()\n e.stopPropagation()\n if (e.shiftKey) redo()\n else undo()\n } else if (k === 'y') {\n e.preventDefault()\n e.stopPropagation()\n redo()\n }\n }\n window.addEventListener('keydown', onKey)\n return () => window.removeEventListener('keydown', onKey)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [past, future, shapes])\n\n useEffect(() => {\n const url = URL.createObjectURL(imageBlob)\n const img = new Image()\n img.onload = () => {\n imageRef.current = img\n setImageLoaded(true)\n }\n img.src = url\n return () => URL.revokeObjectURL(url)\n }, [imageBlob])\n\n // useLayoutEffect (not useEffect) so the canvas's width/height/style\n // are set SYNCHRONOUSLY after Preact's render commit and BEFORE the\n // browser's paint. Without this, the canvas first mounts at the HTML\n // default 300×150 and only resizes to the image's dimensions on the\n // next microtask — a window during which Playwright's\n // `canvas.boundingBox()` can read the stale layout. Tests then fire\n // mouse events at coordinates that no longer land inside the canvas,\n // mousedown is dispatched to the wrap/backdrop instead, no draftShape\n // is created, and `.annotator-count` stays at 0 → flake.\n useLayoutEffect(() => {\n if (\n !imageLoaded ||\n !canvasRef.current ||\n !imageRef.current ||\n !containerRef.current\n ) {\n return\n }\n const img = imageRef.current\n const container = containerRef.current\n const maxW = container.clientWidth - 16\n const maxH = window.innerHeight * 0.65\n const fitScale = Math.min(maxW / img.width, maxH / img.height, 1)\n scaleRef.current = fitScale\n\n const canvas = canvasRef.current\n canvas.width = img.width\n canvas.height = img.height\n canvas.style.width = `${img.width * fitScale}px`\n canvas.style.height = `${img.height * fitScale}px`\n redraw()\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [imageLoaded])\n\n useEffect(() => {\n redraw()\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [shapes, draftShape])\n\n function redraw() {\n const canvas = canvasRef.current\n const img = imageRef.current\n if (!canvas || !img) return\n const ctx = canvas.getContext('2d')\n if (!ctx) return\n ctx.clearRect(0, 0, canvas.width, canvas.height)\n ctx.drawImage(img, 0, 0)\n for (const s of shapes) drawShape(ctx, s, img)\n if (draftShape) drawShape(ctx, draftShape, img)\n }\n\n function getCanvasCoords(e: MouseEvent) {\n const canvas = canvasRef.current!\n const rect = canvas.getBoundingClientRect()\n return {\n x: (e.clientX - rect.left) / scaleRef.current,\n y: (e.clientY - rect.top) / scaleRef.current,\n }\n }\n\n const handleMouseDown = (e: MouseEvent) => {\n if (!imageLoaded) return\n const { x, y } = getCanvasCoords(e)\n const canvasW = canvasRef.current!.width\n const lineWidth = Math.max(3, Math.round(canvasW / 400))\n const blurTile = Math.max(8, Math.round(canvasW / 80))\n\n if (tool === 'text') {\n const text = window.prompt(strings['annotator.text_prompt'])\n if (text && text.trim()) {\n addShape({\n kind: 'text',\n x,\n y,\n text: text.trim(),\n color,\n fontSize: Math.max(16, Math.round(canvasW / 50)),\n lineWidth: 1,\n })\n }\n return\n }\n\n isDrawingRef.current = true\n if (tool === 'rectangle') {\n setDraftShape({ kind: 'rectangle', x, y, w: 0, h: 0, color, lineWidth })\n } else if (tool === 'arrow') {\n setDraftShape({ kind: 'arrow', x1: x, y1: y, x2: x, y2: y, color, lineWidth })\n } else if (tool === 'freehand') {\n setDraftShape({ kind: 'freehand', points: [{ x, y }], color, lineWidth })\n } else if (tool === 'highlight') {\n setDraftShape({\n kind: 'highlight',\n x,\n y,\n w: 0,\n h: 0,\n color: color === '#ffffff' ? '#fde047' : color,\n lineWidth: 1,\n })\n } else if (tool === 'blur') {\n setDraftShape({\n kind: 'blur',\n x,\n y,\n w: 0,\n h: 0,\n color: '#000000',\n lineWidth: 0,\n tile: blurTile,\n })\n }\n }\n\n const handleMouseMove = (e: MouseEvent) => {\n if (!isDrawingRef.current) return\n const { x, y } = getCanvasCoords(e)\n // Functional setState: read the most recent draftShape rather than the\n // closure-captured value. Playwright (and fast humans) can fire\n // mousedown→mousemove faster than Preact commits the re-render, leaving\n // the closure's `draftShape` at the previous frame (often null right\n // after mousedown). audit R6 e2e regression fix.\n setDraftShape((current) => {\n if (!current) return current\n if (\n current.kind === 'rectangle' ||\n current.kind === 'highlight' ||\n current.kind === 'blur'\n ) {\n return { ...current, w: x - current.x, h: y - current.y }\n }\n if (current.kind === 'arrow') {\n return { ...current, x2: x, y2: y }\n }\n if (current.kind === 'freehand') {\n return { ...current, points: [...current.points, { x, y }] }\n }\n return current\n })\n }\n\n const handleMouseUp = () => {\n // Same closure-staleness concern as handleMouseMove: read the\n // committed draft via the functional setter rather than rely on the\n // capture from this render.\n setDraftShape((current) => {\n if (isDrawingRef.current && current) {\n const isTiny =\n ((current.kind === 'rectangle' ||\n current.kind === 'highlight' ||\n current.kind === 'blur') &&\n Math.abs(current.w) < 4 &&\n Math.abs(current.h) < 4) ||\n (current.kind === 'arrow' &&\n Math.hypot(\n current.x2 - current.x1,\n current.y2 - current.y1,\n ) < 4) ||\n (current.kind === 'freehand' && current.points.length < 3)\n if (!isTiny) {\n addShape(current)\n }\n }\n return null\n })\n isDrawingRef.current = false\n }\n\n const handleSave = async () => {\n const canvas = canvasRef.current\n if (!canvas) return\n setSaving(true)\n try {\n const blob = await new Promise<Blob | null>((resolve) =>\n canvas.toBlob(resolve, 'image/png', 0.92),\n )\n if (blob) onSave(blob)\n } finally {\n setSaving(false)\n }\n }\n\n const tools: Array<{ id: Tool; icon: typeof Icon.rect; label: string }> = [\n { id: 'rectangle', icon: Icon.rect, label: strings['annotator.tool.rectangle'] },\n { id: 'arrow', icon: Icon.arrow, label: strings['annotator.tool.arrow'] },\n { id: 'freehand', icon: Icon.pencil, label: strings['annotator.tool.freehand'] },\n { id: 'text', icon: Icon.text, label: strings['annotator.tool.text'] },\n { id: 'highlight', icon: Icon.highlight, label: strings['annotator.tool.highlight'] },\n { id: 'blur', icon: Icon.blur, label: strings['annotator.tool.blur'] },\n ]\n\n return (\n <div\n class=\"annotator-backdrop\"\n role=\"presentation\"\n onClick={(e) => {\n if (e.target === e.currentTarget) onCancel()\n }}\n >\n <div class=\"annotator\" role=\"dialog\" aria-modal=\"true\" aria-label={strings['annotator.title']}>\n <div class=\"annotator-header\">\n <span>{strings['annotator.title']}</span>\n <button\n type=\"button\"\n class=\"modal-close\"\n aria-label={strings['form.close']}\n onClick={onCancel}\n >\n {Icon.close}\n </button>\n </div>\n\n <div class=\"annotator-toolbar\">\n <div class=\"annotator-tools\">\n {tools.map((t) => (\n <button\n key={t.id}\n type=\"button\"\n onClick={() => setTool(t.id)}\n title={t.label}\n aria-label={t.label}\n aria-pressed={tool === t.id}\n class={`annotator-tool ${tool === t.id ? 'is-active' : ''}`}\n >\n {t.icon}\n </button>\n ))}\n </div>\n\n <span class=\"annotator-sep\" />\n\n <div class=\"annotator-colors\">\n {COLORS.map((c) => (\n <button\n key={c}\n type=\"button\"\n onClick={() => setColor(c)}\n aria-label={c}\n aria-pressed={color === c}\n class={`annotator-color ${color === c ? 'is-active' : ''}`}\n style={{ backgroundColor: c }}\n />\n ))}\n <label class=\"annotator-color annotator-color-picker\" title={strings['annotator.color_picker']}>\n <input\n type=\"color\"\n value={color}\n onInput={(e) => setColor((e.target as HTMLInputElement).value)}\n aria-label={strings['annotator.color_picker']}\n />\n </label>\n </div>\n\n <span class=\"annotator-sep\" />\n\n <button\n type=\"button\"\n class=\"annotator-btn\"\n onClick={undo}\n disabled={past.length === 0}\n title={strings['annotator.undo']}\n >\n {Icon.undo}\n <span>{strings['annotator.undo']}</span>\n </button>\n <button\n type=\"button\"\n class=\"annotator-btn\"\n onClick={redo}\n disabled={future.length === 0}\n title={strings['annotator.redo']}\n >\n {Icon.redo}\n <span>{strings['annotator.redo']}</span>\n </button>\n <button\n type=\"button\"\n class=\"annotator-btn\"\n onClick={clearShapes}\n disabled={shapes.length === 0}\n >\n {Icon.trash}\n <span>{strings['annotator.clear']}</span>\n </button>\n\n <span class=\"annotator-spacer\" />\n\n <span class=\"annotator-count\">\n {shapes.length} {strings['annotator.count_suffix']}\n </span>\n </div>\n\n <div ref={containerRef} class=\"annotator-canvas-wrap\">\n {!imageLoaded ? (\n <span class=\"annotator-loading\">{strings['annotator.loading']}</span>\n ) : (\n <canvas\n ref={canvasRef}\n onMouseDown={handleMouseDown}\n onMouseMove={handleMouseMove}\n onMouseUp={handleMouseUp}\n onMouseLeave={handleMouseUp}\n class=\"annotator-canvas\"\n />\n )}\n </div>\n\n <div class=\"annotator-footer\">\n <button type=\"button\" class=\"btn\" onClick={onCancel}>\n {strings['form.cancel']}\n </button>\n <button\n type=\"button\"\n class=\"btn btn--primary\"\n onClick={handleSave}\n disabled={saving || !imageLoaded}\n >\n {saving ? strings['annotator.applying'] : strings['annotator.apply']}\n </button>\n </div>\n </div>\n </div>\n )\n}\n","/**\n * MineList — the user's own reports on this project.\n *\n * Polls every 30 s while mounted (Sentry-style staleTime + manual\n * refresh). Empty state when the submitter has no thread yet (404 from\n * the backend is mapped to []). Click a row to open the detail view.\n */\n\nimport { h } from 'preact'\nimport { useEffect, useRef, useState } from 'preact/hooks'\n\nimport type { ApiClient } from '../api/client'\nimport { rateLimitMessage } from './rateLimit'\nimport type { WidgetReportRow } from '../types'\nimport type { StringKey } from './i18n'\nimport { KpiStrip, type KpiFilter, rowsMatchingFilter } from './KpiStrip'\nimport { startPoll } from './poll'\nimport { ReportRow } from './ReportRow'\n\ninterface MineListProps {\n api: ApiClient\n externalId: string\n strings: Record<StringKey, string>\n onSelect: (report: WidgetReportRow) => void\n /** Resolve a report by its per-project #seq and open it. Resolves false\n * when no such report is visible (powers the jump-by-#seq box, #85).\n * When omitted the jump form is not rendered. */\n onOpenSeq?: (seq: number) => Promise<boolean>\n}\n\nconst POLL_MS = 30_000\n\nexport function MineList({ api, externalId, strings, onSelect, onOpenSeq }: MineListProps) {\n const [rows, setRows] = useState<WidgetReportRow[] | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [refreshing, setRefreshing] = useState(false)\n const [filter, setFilter] = useState<KpiFilter>('all')\n const [jumpValue, setJumpValue] = useState('')\n const [jumpError, setJumpError] = useState(false)\n const [jumping, setJumping] = useState(false)\n const mountedRef = useRef(true)\n\n const handleJump = async (e: Event) => {\n e.preventDefault()\n if (!onOpenSeq || jumping) return\n const seq = parseInt(jumpValue.replace(/^#/, '').trim(), 10)\n if (!Number.isInteger(seq) || seq <= 0) {\n setJumpError(true)\n return\n }\n setJumping(true)\n setJumpError(false)\n try {\n const found = await onOpenSeq(seq)\n if (!mountedRef.current) return\n if (found) setJumpValue('')\n else setJumpError(true)\n } finally {\n if (mountedRef.current) setJumping(false)\n }\n }\n\n const fetchRows = async () => {\n setRefreshing(true)\n setError(null)\n try {\n const next = await api.listMine(externalId)\n if (!mountedRef.current) return true\n setRows(next)\n return true\n } catch (err) {\n // Don't surface raw API error messages to the user — they used to\n // render as e.g. `listMine failed: 403 {\"detail\":\"Public API key…\"}`\n // straight into the panel. Friendly fallback in the UI, raw error\n // in the console for the developer.\n if (typeof console !== 'undefined') console.warn('[mhosaic] listMine:', err)\n if (!mountedRef.current) return false\n // A 429 gets an actionable \"retry in Xs\" message; anything else falls\n // back to the generic copy. Either way loading resolves (#80/#81).\n setError(rateLimitMessage(err, strings) ?? strings['mine.error'])\n return false\n } finally {\n if (mountedRef.current) setRefreshing(false)\n }\n }\n\n useEffect(() => {\n mountedRef.current = true\n const poll = startPoll(fetchRows, POLL_MS)\n return () => {\n mountedRef.current = false\n poll.stop()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [externalId])\n\n const isEmpty = rows !== null && rows.length === 0\n const isLoading = rows === null && !error\n const visibleRows = rows ? rowsMatchingFilter(rows, filter) : null\n const visibleEmpty = !!rows && rows.length > 0 && (visibleRows?.length ?? 0) === 0\n\n return (\n <div class=\"mine-list\">\n <div class=\"mine-list-header\">\n <h2>{strings['tab.mine']}</h2>\n <div class=\"mine-list-header-actions\">\n {onOpenSeq && (\n <form class=\"mine-jump\" onSubmit={handleJump}>\n <input\n type=\"text\"\n inputMode=\"numeric\"\n value={jumpValue}\n placeholder={strings['mine.jump.placeholder']}\n aria-label={strings['mine.jump.aria']}\n onInput={(e) => {\n setJumpValue((e.target as HTMLInputElement).value)\n setJumpError(false)\n }}\n disabled={jumping}\n />\n <button type=\"submit\" class=\"btn\" disabled={jumping || !jumpValue.trim()}>\n {strings['mine.jump.go']}\n </button>\n </form>\n )}\n <button\n type=\"button\"\n class=\"btn\"\n onClick={() => {\n void fetchRows()\n }}\n disabled={refreshing}\n >\n {refreshing ? strings['mine.loading'] : strings['mine.refresh']}\n </button>\n </div>\n </div>\n {jumpError && (\n <div class=\"mine-jump-error error\" role=\"alert\">\n {strings['mine.jump.not_found']}\n </div>\n )}\n {rows && rows.length > 0 && (\n <KpiStrip rows={rows} filter={filter} onFilter={setFilter} strings={strings} />\n )}\n {isLoading && <div class=\"mine-loading\">{strings['mine.loading']}</div>}\n {error && <div class=\"error\">{error}</div>}\n {isEmpty && (\n <div class=\"mine-empty\">\n <strong>{strings['mine.empty.title']}</strong>\n <p>{strings['mine.empty.body']}</p>\n </div>\n )}\n {visibleEmpty && (\n <div class=\"mine-empty\">\n <p>{strings['mine.filter.empty']}</p>\n </div>\n )}\n {visibleRows && visibleRows.length > 0 && (\n <ul class=\"mine-rows\">\n {visibleRows.map((row) => (\n <li>\n <ReportRow row={row} strings={strings} onClick={() => onSelect(row)} />\n </li>\n ))}\n </ul>\n )}\n </div>\n )\n}\n","/**\n * KpiStrip — four-pill summary at the top of the user's \"My reports\" tab.\n *\n * Computes counts client-side from the same `WidgetReportRow[]` that\n * MineList already fetches; no extra endpoint. Each pill is clickable\n * and toggles a status filter on the list below — matches thePnr's\n * customer-facing pattern (`FeedbackKPICards.tsx`).\n */\n\nimport { h } from 'preact'\n\nimport type { ReportStatus, WidgetReportRow } from '../types'\nimport type { StringKey } from './i18n'\n\nexport type KpiFilter = 'all' | 'new' | 'in_progress' | 'awaiting_validation' | 'resolved'\n\ninterface KpiStripProps {\n rows: WidgetReportRow[]\n filter: KpiFilter\n onFilter: (next: KpiFilter) => void\n strings: Record<StringKey, string>\n}\n\ninterface Counts {\n new: number\n in_progress: number\n awaiting_validation: number\n resolved: number\n total: number\n}\n\nexport function computeKpiCounts(rows: WidgetReportRow[]): Counts {\n const counts: Counts = {\n new: 0,\n in_progress: 0,\n awaiting_validation: 0,\n resolved: 0,\n total: 0,\n }\n for (const row of rows) {\n counts.total += 1\n switch (row.status) {\n case 'new':\n counts.new += 1\n break\n case 'in_progress':\n counts.in_progress += 1\n break\n case 'awaiting_validation':\n counts.awaiting_validation += 1\n break\n case 'closed':\n case 'rejected':\n case 'duplicate':\n case 'wontfix':\n counts.resolved += 1\n break\n }\n }\n return counts\n}\n\n/**\n * Resolution rate = (awaiting_validation + closed + rejected + duplicate +\n * wontfix) / total. Includes operator-resolved-as-no-action so the\n * percentage reflects \"your reports the team has acted on\" rather than\n * \"your reports that turned into shipped fixes\". Returns null if there\n * are no rows yet — avoids showing \"0%\" before the user has filed\n * anything, which reads as ominous.\n */\nexport function computeResolutionRate(counts: Counts): number | null {\n if (counts.total === 0) return null\n return Math.round(\n ((counts.awaiting_validation + counts.resolved) / counts.total) * 100,\n )\n}\n\nconst FILTER_FOR_STATUS: Record<ReportStatus, KpiFilter> = {\n new: 'new',\n in_progress: 'in_progress',\n awaiting_validation: 'awaiting_validation',\n closed: 'resolved',\n rejected: 'resolved',\n duplicate: 'resolved',\n wontfix: 'resolved',\n}\n\nexport function rowsMatchingFilter(\n rows: WidgetReportRow[],\n filter: KpiFilter,\n): WidgetReportRow[] {\n if (filter === 'all') return rows\n return rows.filter((r) => FILTER_FOR_STATUS[r.status] === filter)\n}\n\nexport function KpiStrip({ rows, filter, onFilter, strings }: KpiStripProps) {\n const counts = computeKpiCounts(rows)\n const resolutionRate = computeResolutionRate(counts)\n const cells: Array<{ key: KpiFilter; label: string; value: string }> = [\n { key: 'new', label: strings['kpi.new'], value: String(counts.new) },\n { key: 'in_progress', label: strings['kpi.in_progress'], value: String(counts.in_progress) },\n {\n key: 'awaiting_validation',\n label: strings['kpi.awaiting_validation'],\n value: String(counts.awaiting_validation),\n },\n {\n key: 'resolved',\n label: strings['kpi.resolution_rate'],\n value: resolutionRate === null ? '—' : `${resolutionRate}%`,\n },\n ]\n return (\n <div class=\"kpi-strip\" role=\"toolbar\">\n {cells.map((c) => {\n const active = filter === c.key\n const toggleTo: KpiFilter = active ? 'all' : c.key\n return (\n <button\n type=\"button\"\n class={`kpi-cell kpi-cell--${c.key}${active ? ' is-active' : ''}`}\n onClick={() => onFilter(toggleTo)}\n aria-pressed={active}\n >\n <span class=\"kpi-value\">{c.value}</span>\n <span class=\"kpi-label\">{c.label}</span>\n </button>\n )\n })}\n </div>\n )\n}\n","import { h, type ComponentChildren } from 'preact'\nimport { useEffect, useRef } from 'preact/hooks'\n\n/** Why the modal is being dismissed. `backdrop`/`escape` are \"accidental\"\n * gestures a caller may want to guard (e.g. unsaved input, #89); `button`\n * is the explicit ✕ and always closes. */\nexport type DismissReason = 'backdrop' | 'escape' | 'button'\n\ninterface ModalProps {\n onDismiss: (reason: DismissReason) => void\n children: ComponentChildren\n closeLabel?: string\n /** v0.12: when true, the modal grows toward near-fullscreen on desktop\n * and full-screen on mobile. Drives the \"transport\" feel for the Board\n * tab — opening the tab feels like clicking through to a real page. */\n expanded?: boolean\n}\n\nexport function Modal({ onDismiss, children, closeLabel = 'Close', expanded = false }: ModalProps) {\n const modalRef = useRef<HTMLDivElement>(null)\n const previouslyFocused = useRef<Element | null>(null)\n\n // Esc-to-close + focus management. Without this, users couldn't dismiss\n // with the keyboard (a real-world UX expectation). Listener installed on\n // window so it works regardless of which element inside the modal has focus.\n useEffect(() => {\n previouslyFocused.current = document.activeElement\n const onKey = (e: KeyboardEvent) => {\n if (e.key !== 'Escape') return\n // Ignore Esc when a topmost dialog (e.g. the Annotator) is mounted in\n // our shadow root — it owns the keystroke. Without this guard the\n // Esc fires both handlers on the same event and dismisses the form\n // behind the annotator.\n const root = modalRef.current?.getRootNode()\n if (\n root instanceof ShadowRoot &&\n root.querySelector('.annotator-backdrop')\n ) {\n return\n }\n e.stopPropagation()\n onDismiss('escape')\n }\n window.addEventListener('keydown', onKey)\n // Focus first focusable element inside the modal so the keyboard user\n // lands inside, not still on the FAB or whatever opened the modal.\n const first = modalRef.current?.querySelector<HTMLElement>(\n 'textarea, input, select, button',\n )\n first?.focus()\n return () => {\n window.removeEventListener('keydown', onKey)\n // Restore focus on dismiss so screen-readers don't lose place.\n const prev = previouslyFocused.current as HTMLElement | null\n if (prev && typeof prev.focus === 'function') prev.focus()\n }\n }, [onDismiss])\n\n return (\n <div\n class={`backdrop ${expanded ? 'is-expanded' : ''}`}\n role=\"presentation\"\n onClick={(e) => {\n if (e.target === e.currentTarget) onDismiss('backdrop')\n }}\n >\n <div\n ref={modalRef}\n class={`modal ${expanded ? 'is-expanded' : ''}`}\n role=\"dialog\"\n aria-modal=\"true\"\n >\n <button\n type=\"button\"\n class=\"modal-close\"\n aria-label={closeLabel}\n onClick={() => onDismiss('button')}\n >\n ×\n </button>\n {children}\n </div>\n </div>\n )\n}\n","/** One passive line on the Send tab: \"N open reports on this page — View\".\n * Dedup affordance (spec §3.3) — never blocks, never steals focus. */\nimport { h } from 'preact'\n\nimport type { StringKey } from './i18n'\n\nexport function PageActivityStrip({\n open,\n strings,\n onView,\n}: {\n open: number\n strings: Record<StringKey, string>\n onView: () => void\n}) {\n if (open === 0) return null\n const text =\n open === 1\n ? strings['page.strip.text.one']\n : strings['page.strip.text'].replace('{n}', String(open))\n return (\n <div class=\"page-strip\">\n <span>{text}</span>\n <button type=\"button\" class=\"page-strip-view\" onClick={onView}>\n {strings['page.strip.view']}\n </button>\n </div>\n )\n}\n","/** Desktop hover peek: up to 5 of this page's reports above the FAB\n * (spec §3.2). Non-modal; failures degrade to the footer actions. */\nimport { h } from 'preact'\nimport { useEffect, useState } from 'preact/hooks'\n\nimport type { ApiClient } from '../api/client'\nimport type { WidgetBoardRow } from '../types'\nimport { currentPagePath } from './currentPage'\nimport type { StringKey } from './i18n'\n\nexport function PagePeekPanel({\n api,\n externalId,\n strings,\n getCurrentPage,\n open,\n onViewAll,\n onPickRow,\n onSend,\n}: {\n api: ApiClient\n externalId: string\n strings: Record<StringKey, string>\n getCurrentPage?: () => string | null\n open: number\n onViewAll: () => void\n onPickRow?: (reportId: string) => void\n onSend: () => void\n}) {\n const [rows, setRows] = useState<WidgetBoardRow[] | null>(null)\n const [failed, setFailed] = useState(false)\n\n useEffect(() => {\n let cancelled = false\n const pagePath = currentPagePath(getCurrentPage !== undefined ? { getCurrentPage } : {})\n api\n .listBoard(externalId, {\n pagePath,\n status: ['new', 'in_progress', 'awaiting_validation'],\n })\n .then((page) => {\n if (!cancelled) setRows(page.results.slice(0, 5))\n })\n .catch(() => {\n if (!cancelled) setFailed(true)\n })\n return () => {\n cancelled = true\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [api, externalId])\n\n return (\n <div class=\"page-peek\" role=\"region\" aria-label={strings['page.peek.title']}>\n <div class=\"page-peek-title\">{strings['page.peek.title']}</div>\n {rows === null && !failed && (\n <div class=\"page-peek-skeleton\" aria-hidden=\"true\">\n <div /> <div />\n </div>\n )}\n {rows !== null &&\n rows.map((row) => (\n <button\n key={row.id}\n type=\"button\"\n class=\"page-peek-row\"\n onClick={() => onPickRow?.(row.id)}\n >\n <span class={`page-peek-dot status-${row.status}`} aria-hidden=\"true\" />\n <span class=\"page-peek-desc\">{row.description}</span>\n </button>\n ))}\n <div class=\"page-peek-footer\">\n <button type=\"button\" class=\"page-peek-viewall\" onClick={onViewAll}>\n {open === 1\n ? strings['page.peek.viewAll.one']\n : strings['page.peek.viewAll'].replace('{n}', String(open))}\n </button>\n <button type=\"button\" class=\"page-peek-send\" onClick={onSend}>\n {strings['fab.label']}\n </button>\n </div>\n </div>\n )\n}\n","/** \"What's open on this page\" — one signal powering the FAB badge, the\n * hover peek and the Send-tab strip (spec 2026-07-06-page-aware-widget).\n * Identity-gated server-side; every failure is silent (surfaces hide). */\nimport { useCallback, useEffect, useState } from 'preact/hooks'\n\nimport type { ApiClient } from '../api/client'\nimport type { WidgetBoardKpis } from '../types'\nimport { currentPagePath, onLocationChange } from './currentPage'\n\nconst TTL_MS = 60_000\n\nexport function computeOpenCount(kpis: WidgetBoardKpis): number {\n const s = kpis.by_status\n return (s.new ?? 0) + (s.in_progress ?? 0) + (s.awaiting_validation ?? 0)\n}\n\n// Module-level so remounts (modal open/close) reuse the fact. Stores the\n// full KPI payload (not just the derived open count) so every page-scoped\n// KPI consumer — the FAB badge, the smart-open check in openWidget — shares\n// ONE request per (user, path, TTL) instead of each firing its own.\nconst cache = new Map<string, { kpis: WidgetBoardKpis; fetchedAt: number }>()\nconst inflight = new Map<string, Promise<WidgetBoardKpis>>()\n\nexport function invalidatePageSignal(): void {\n cache.clear()\n}\n\nexport function _resetPageSignalCache(): void {\n cache.clear()\n inflight.clear()\n}\n\n/** Page-scoped KPIs through the module cache (60s TTL) + in-flight dedup.\n * The FAB-open path used to call `api.fetchBoardKpis` directly, firing a\n * second identical request right after the badge had fetched the same\n * endpoint+params. */\nexport function fetchPageKpis(\n api: ApiClient,\n externalId: string,\n path: string,\n): Promise<WidgetBoardKpis> {\n const key = `${externalId}|${path}`\n const hit = cache.get(key)\n if (hit && Date.now() - hit.fetchedAt < TTL_MS) return Promise.resolve(hit.kpis)\n const pending = inflight.get(key)\n if (pending) return pending\n const p = api\n .fetchBoardKpis(externalId, { pagePath: path })\n .then((kpis) => {\n cache.set(key, { kpis, fetchedAt: Date.now() })\n return kpis\n })\n .finally(() => inflight.delete(key))\n inflight.set(key, p)\n return p\n}\n\nfunction fetchOpen(api: ApiClient, externalId: string, path: string): Promise<number> {\n return fetchPageKpis(api, externalId, path).then(computeOpenCount)\n}\n\nexport function usePageOpenCount(opts: {\n api?: ApiClient\n externalId?: string\n getCurrentPage?: () => string | null\n enabled: boolean\n}): { open: number; refresh: () => void } {\n const { api, externalId, getCurrentPage, enabled } = opts\n const [open, setOpen] = useState(0)\n const [tick, setTick] = useState(0)\n\n const refresh = useCallback(() => {\n invalidatePageSignal()\n setTick((t) => t + 1)\n }, [])\n\n useEffect(() => {\n if (!enabled || !api || !externalId) {\n setOpen(0)\n return\n }\n let cancelled = false\n const load = () => {\n const path = currentPagePath(getCurrentPage !== undefined ? { getCurrentPage } : {})\n fetchOpen(api, externalId, path)\n .then((n) => {\n if (!cancelled) setOpen(n)\n })\n .catch(() => {\n // Silent by design — a failing count must never surface UI.\n if (!cancelled) setOpen(0)\n })\n }\n load()\n const unsub = onLocationChange(load)\n return () => {\n cancelled = true\n unsub()\n }\n }, [api, externalId, enabled, tick])\n\n return { open, refresh }\n}\n","export const WIDGET_STYLES = `\n:host {\n --mfb-accent: #3b82f6;\n --mfb-accent-contrast: #ffffff;\n\n /* FAB-specific palette — Mhosaic brand (steel blue family, per\n mhosaic-core's design system v4). Soft-black surface + light\n steel-blue glyph. Kept separate from --mfb-accent on purpose:\n the accent is used everywhere inside the panel for submit\n buttons / focus rings, and we don't want changing the FAB chrome\n to inadvertently restyle the whole widget. */\n --mfb-fab-bg: #1c2230; /* Mhosaic --black (oklch ≈ 0.22 0.025 258) */\n --mfb-fab-icon: #8aa9e5; /* Mhosaic --blue-300 (oklch ≈ 0.70 0.13 258) */\n --mfb-bg: #ffffff;\n --mfb-surface: #f9fafb;\n --mfb-surface-2: #f3f4f6;\n --mfb-text: #0a0a0a;\n --mfb-text-muted: #6b7280;\n --mfb-border: #e5e7eb;\n --mfb-border-strong: #d1d5db;\n --mfb-radius: 8px;\n --mfb-radius-lg: 14px;\n --mfb-font: system-ui, -apple-system, sans-serif;\n --mfb-z-index: 2147483640;\n\n /* Spacing scale — 4px base, used everywhere instead of magic numbers. */\n --mfb-space-1: 4px;\n --mfb-space-2: 8px;\n --mfb-space-3: 12px;\n --mfb-space-4: 16px;\n --mfb-space-5: 24px;\n --mfb-space-6: 32px;\n --mfb-space-7: 48px;\n\n /* Type scale — fixed sizes, no fluid scaling. Body 14px sits in the\n \"comfortable on every viewport\" range; headings step up gracefully. */\n --mfb-text-xs: 11px;\n --mfb-text-sm: 13px;\n --mfb-text-base: 14px;\n --mfb-text-md: 15px;\n --mfb-text-lg: 17px;\n --mfb-text-xl: 20px;\n\n /* Elevation — three tiers for layering. */\n --mfb-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.06), 0 1px 3px rgba(0, 0, 0, 0.08);\n --mfb-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.12);\n --mfb-shadow-lg: 0 24px 56px rgba(0, 0, 0, 0.18), 0 8px 20px rgba(0, 0, 0, 0.10);\n\n all: initial;\n font-family: var(--mfb-font);\n color: var(--mfb-text);\n position: fixed;\n z-index: var(--mfb-z-index);\n}\n\n@media (prefers-color-scheme: dark) {\n :host {\n --mfb-bg: #0f172a;\n --mfb-surface: #1e293b;\n --mfb-surface-2: #253349;\n --mfb-text: #f8fafc;\n --mfb-text-muted: #94a3b8;\n --mfb-border: #334155;\n --mfb-border-strong: #475569;\n --mfb-shadow-lg: 0 24px 56px rgba(0, 0, 0, 0.55), 0 8px 20px rgba(0, 0, 0, 0.40);\n }\n}\n\n/* FAB — 48px circle (down from Material's 56px) for a more discrete\n presence on client surfaces (matches PNR's 48px pattern). Mhosaic\n soft-black background with a light steel-blue lucide-bug glyph;\n custom SVG inlined (no emoji — emoji renders inconsistently across\n OSes and can't inherit color). Two-layer elevation, scale-on-press. */\n.fab-area {\n position: fixed;\n bottom: 24px;\n right: 24px;\n /* No z-index on purpose: the modal .backdrop/.modal are later siblings in\n the shadow root, so DOM order must keep them painting above the FAB —\n an explicit z-index here would trap the FAB on top of the open modal. */\n}\n.fab-area .fab {\n position: relative;\n right: auto;\n bottom: auto;\n}\n.fab {\n width: 48px;\n height: 48px;\n border-radius: 999px;\n background: var(--mfb-fab-bg);\n color: var(--mfb-fab-icon);\n border: none;\n cursor: pointer;\n /* Two-layer elevation: ambient (soft, large) + key (tighter, near). */\n box-shadow:\n 0 4px 12px rgba(0, 0, 0, 0.08),\n 0 2px 4px rgba(0, 0, 0, 0.12);\n display: grid;\n place-items: center;\n transition: transform 180ms cubic-bezier(0.4, 0, 0.2, 1),\n box-shadow 180ms cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.fab:hover {\n transform: translateY(-2px);\n box-shadow:\n 0 8px 24px rgba(0, 0, 0, 0.12),\n 0 3px 6px rgba(0, 0, 0, 0.16);\n}\n.fab:active {\n transform: translateY(0) scale(0.96);\n box-shadow:\n 0 3px 8px rgba(0, 0, 0, 0.10),\n 0 1px 2px rgba(0, 0, 0, 0.14);\n}\n.fab:focus-visible {\n outline: 2px solid var(--mfb-fab-icon);\n outline-offset: 3px;\n box-shadow:\n 0 0 0 4px var(--mfb-fab-bg),\n 0 4px 12px rgba(0, 0, 0, 0.08),\n 0 2px 4px rgba(0, 0, 0, 0.12);\n}\n@media (prefers-color-scheme: dark) {\n /* Slightly desaturate so the FAB doesn't glow against dark backgrounds. */\n .fab { box-shadow:\n 0 4px 12px rgba(0, 0, 0, 0.32),\n 0 2px 4px rgba(0, 0, 0, 0.40); }\n}\n\n/* Page-activity badge — absolute so it never shifts the FAB's layout. */\n.fab-badge {\n position: absolute;\n top: -2px;\n right: -2px;\n min-width: 16px;\n height: 16px;\n padding: 0 4px;\n box-sizing: border-box;\n border-radius: 999px;\n background: var(--mfb-accent);\n color: #fff;\n font-size: 10px;\n font-weight: 700;\n line-height: 16px;\n text-align: center;\n box-shadow: 0 0 0 2px var(--mfb-fab-bg);\n animation: mfb-badge-in 150ms ease-out;\n}\n@keyframes mfb-badge-in {\n from {\n opacity: 0;\n transform: scale(0.6);\n }\n to {\n opacity: 1;\n transform: scale(1);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .fab { transition: none; }\n .fab:hover, .fab:active { transform: none; }\n .fab-badge {\n animation: none;\n }\n}\n\n/* Backdrop — fade in with a slight blur for depth. The blur gives the\n modal that \"page that opens\" weight: the underlying page recedes\n visually so the widget feels foregrounded, not pasted on. */\n.backdrop {\n position: fixed;\n inset: 0;\n background: rgba(15, 23, 42, 0.55);\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n display: grid;\n place-items: center;\n animation: mfb-backdrop-in 220ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n@keyframes mfb-backdrop-in {\n from { opacity: 0; backdrop-filter: blur(0px); -webkit-backdrop-filter: blur(0px); }\n to { opacity: 1; backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }\n}\n\n.modal {\n background: var(--mfb-bg);\n border-radius: var(--mfb-radius-lg);\n box-shadow: var(--mfb-shadow-lg);\n /* 720px is the \"page that opens\" sweet spot — wide enough to feel\n like a workspace, narrow enough to not dominate the screen. Was\n 440px before; the bump trades a denser modal for a calmer canvas. */\n width: min(720px, calc(100vw - var(--mfb-space-7)));\n padding: var(--mfb-space-6);\n display: flex;\n flex-direction: column;\n gap: var(--mfb-space-5);\n position: relative;\n /* Cap height with breathing room. The form scrolls internally if\n the user attaches a tall screenshot. */\n max-height: min(820px, calc(100vh - var(--mfb-space-7)));\n overflow-y: auto;\n /* Entrance: opacity-only. Avoids any geometric shift during the\n animation so interaction tests that read boundingBox immediately\n after the modal becomes visible get stable coordinates. 220ms\n ease-out is snappy enough that human users barely register it. */\n animation: mfb-modal-in 220ms ease-out;\n /* Transition lives on the BASE rule with a SHORTER duration so the\n \"back\" trip (Board → other tab, where .is-expanded is removed)\n feels snappy. The grow direction overrides this with a slower\n curve in .modal.is-expanded below — the asymmetry is intentional:\n a slow grow reads as a transport, a slow shrink reads as\n hesitation. */\n transition:\n width 200ms cubic-bezier(0.4, 0, 0.2, 1),\n height 200ms cubic-bezier(0.4, 0, 0.2, 1),\n max-height 200ms cubic-bezier(0.4, 0, 0.2, 1),\n padding 200ms cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n@keyframes mfb-modal-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n/* Mobile: full-height sheet that slides up from the bottom. The\n slide-up gesture matches platform-native sheets users already know. */\n@media (max-width: 640px) {\n .backdrop { place-items: end center; }\n .modal {\n width: 100vw;\n height: calc(100vh - var(--mfb-space-7));\n max-height: none;\n border-radius: var(--mfb-radius-lg) var(--mfb-radius-lg) 0 0;\n padding: var(--mfb-space-5) var(--mfb-space-4) var(--mfb-space-4);\n animation: mfb-sheet-up 280ms cubic-bezier(0.16, 1, 0.3, 1);\n }\n}\n\n@keyframes mfb-sheet-up {\n from { transform: translateY(100%); }\n to { transform: translateY(0); }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .backdrop, .modal { animation: none; }\n}\n\n.modal h2 {\n margin: 0;\n font-size: var(--mfb-text-xl);\n font-weight: 600;\n padding-right: var(--mfb-space-6);\n letter-spacing: -0.015em;\n line-height: 1.2;\n}\n\n.modal form {\n display: flex;\n flex-direction: column;\n gap: var(--mfb-space-5);\n /* Match the entrance motion the Board/MineList/Changelog containers\n * use so switching tabs (especially Board → Send, where the modal\n * is mid-shrink) doesn't show a content snap behind the transition.\n * 40ms delay lets the modal's shrink finish first. */\n animation: mfb-board-in 220ms ease-out 40ms both;\n}\n@media (prefers-reduced-motion: reduce) {\n .modal form { animation: none; }\n}\n\n.modal-close {\n position: absolute;\n top: var(--mfb-space-3);\n right: var(--mfb-space-3);\n width: 36px;\n height: 36px;\n display: grid;\n place-items: center;\n background: transparent;\n border: none;\n border-radius: var(--mfb-radius);\n color: var(--mfb-text-muted);\n font: inherit;\n font-size: 22px;\n line-height: 1;\n cursor: pointer;\n transition: background 120ms ease, color 120ms ease;\n}\n.modal-close:hover { background: var(--mfb-surface); color: var(--mfb-text); }\n.modal-close:focus-visible { outline: 2px solid var(--mfb-accent); outline-offset: 2px; }\n\n/* Each field group: label + input stacked with breathing room.\n The 24px modal-level gap separates groups. */\n.field { display: flex; flex-direction: column; gap: var(--mfb-space-2); font-size: var(--mfb-text-sm); }\n\n.field label {\n color: var(--mfb-text-muted);\n font-weight: 500;\n font-size: var(--mfb-text-xs);\n letter-spacing: 0.03em;\n text-transform: uppercase;\n}\n\n.field input, .field select, .field textarea {\n font-family: inherit;\n font-size: var(--mfb-text-base);\n color: inherit;\n padding: 11px 14px;\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n background: var(--mfb-surface);\n transition: border-color 120ms ease, box-shadow 120ms ease, background 120ms ease;\n}\n\n.field input:hover, .field select:hover, .field textarea:hover { border-color: var(--mfb-border-strong); }\n.field input:focus, .field select:focus, .field textarea:focus {\n outline: none;\n border-color: var(--mfb-accent);\n background: var(--mfb-bg);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mfb-accent) 22%, transparent);\n}\n\n.field select {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n padding-right: 28px;\n background-image: url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path fill='none' stroke='%236b7280' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' d='M1 1l4 4 4-4'/></svg>\");\n background-repeat: no-repeat;\n background-position: right 10px center;\n}\n\n.field textarea { min-height: 120px; resize: vertical; line-height: 1.5; }\n\n.row { display: flex; gap: var(--mfb-space-3); }\n.row > * { flex: 1; }\n\n/* Footer: subtle separation via border, slightly more vertical space. */\n.actions {\n display: flex;\n gap: var(--mfb-space-2);\n justify-content: flex-end;\n padding-top: var(--mfb-space-4);\n margin-top: var(--mfb-space-1);\n border-top: 1px solid var(--mfb-border);\n}\n\n.btn {\n padding: 10px 18px;\n border-radius: var(--mfb-radius);\n border: 1px solid var(--mfb-border);\n background: var(--mfb-bg);\n color: var(--mfb-text);\n font: inherit;\n font-size: var(--mfb-text-sm);\n font-weight: 500;\n cursor: pointer;\n transition: background 120ms ease, border-color 120ms ease, transform 80ms ease;\n}\n.btn:hover { background: var(--mfb-surface); border-color: var(--mfb-border-strong); }\n.btn:active { transform: scale(0.98); }\n\n.btn--primary {\n background: var(--mfb-accent);\n color: var(--mfb-accent-contrast);\n border-color: var(--mfb-accent);\n}\n.btn--primary:hover {\n background: color-mix(in srgb, var(--mfb-accent) 88%, black);\n border-color: color-mix(in srgb, var(--mfb-accent) 88%, black);\n}\n\n/* Subdued button — borrows accent text but transparent background. Used\n * for the \"Capture this page\" alt-action that sits below the dropzone,\n * where a full --primary would compete with the form's main submit. */\n.btn--ghost {\n background: transparent;\n color: var(--mfb-accent);\n border-color: color-mix(in srgb, var(--mfb-accent) 40%, transparent);\n}\n.btn--ghost:hover {\n background: color-mix(in srgb, var(--mfb-accent) 8%, transparent);\n border-color: var(--mfb-accent);\n}\n\n.btn[disabled] { opacity: 0.6; cursor: not-allowed; }\n\n.error { color: #dc2626; font-size: 13px; }\n.success { color: #059669; font-size: 13px; }\n\n/* REQ-A7: quiet transparency line under the form. */\n.capture-notice { color: var(--mfb-muted, #6b7280); font-size: 11px; line-height: 1.4; margin-top: 6px; }\n\n/* #85: inline \"#NN\" report reference rendered as a link (a button for a11y). */\n.seq-link {\n display: inline;\n padding: 0;\n border: none;\n background: none;\n color: var(--mfb-accent);\n font: inherit;\n cursor: pointer;\n}\n.seq-link:hover { text-decoration: underline; }\n\n/* ---- #89: discard-unsaved-changes confirmation over the form -------- */\n.discard-confirm {\n position: absolute;\n inset: 0;\n z-index: 5;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 16px;\n background: rgba(15, 23, 42, 0.45);\n border-radius: inherit;\n}\n.discard-confirm-card {\n max-width: 260px;\n text-align: center;\n background: var(--mfb-bg);\n color: var(--mfb-text);\n border-radius: var(--mfb-radius-lg);\n padding: 18px 16px;\n box-shadow: 0 8px 30px rgba(0, 0, 0, 0.18);\n}\n.discard-confirm-title { font-weight: 600; font-size: 15px; margin: 0 0 4px; }\n.discard-confirm-body { font-size: 13px; margin: 0 0 14px; opacity: 0.8; }\n.discard-confirm-actions { display: flex; gap: 8px; justify-content: center; }\n\n/* ---- v0.6.0: manual screenshot upload + annotator -------------------- */\n\n.mfb-sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\n\n/* Dropzone — bigger, more inviting; the icon + heading + sub-line\n hierarchy makes it feel like a deliberate target, not an afterthought. */\n.screenshot-dropzone {\n border: 1.5px dashed var(--mfb-border-strong);\n border-radius: var(--mfb-radius-lg);\n padding: var(--mfb-space-6) var(--mfb-space-4);\n text-align: center;\n cursor: pointer;\n background: var(--mfb-surface);\n transition: border-color 160ms ease, background 160ms ease, transform 120ms ease;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--mfb-space-2);\n}\n.screenshot-dropzone:hover {\n border-color: var(--mfb-accent);\n background: color-mix(in srgb, var(--mfb-accent) 4%, var(--mfb-surface));\n}\n.screenshot-dropzone.is-dragover {\n border-color: var(--mfb-accent);\n border-style: solid;\n background: color-mix(in srgb, var(--mfb-accent) 10%, var(--mfb-surface));\n transform: scale(1.005);\n}\n.screenshot-dropzone:focus-visible {\n outline: 2px solid var(--mfb-accent);\n outline-offset: 2px;\n}\n.screenshot-icon {\n color: var(--mfb-text-muted);\n opacity: 0.7;\n margin-bottom: var(--mfb-space-1);\n transition: color 160ms ease, opacity 160ms ease;\n}\n.screenshot-dropzone:hover .screenshot-icon,\n.screenshot-dropzone.is-dragover .screenshot-icon {\n color: var(--mfb-accent);\n opacity: 1;\n}\n.screenshot-cta { font-size: var(--mfb-text-base); color: var(--mfb-text); font-weight: 500; }\n.screenshot-cta strong { color: var(--mfb-accent); font-weight: 600; }\n.screenshot-formats { font-size: var(--mfb-text-xs); color: var(--mfb-text-muted); }\n\n.screenshot-strip {\n display: flex;\n flex-direction: column;\n gap: var(--mfb-space-2);\n margin-bottom: var(--mfb-space-2);\n}\n.screenshot-preview {\n position: relative;\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n overflow: hidden;\n background: var(--mfb-surface);\n display: flex;\n flex-direction: column;\n}\n.screenshot-preview img {\n display: block;\n width: 100%;\n height: auto;\n max-height: 280px;\n object-fit: contain;\n background: #1a1a1a;\n}\n/* In the multi-shot strip each preview stays compact so several fit. */\n.screenshot-strip .screenshot-preview img {\n max-height: 120px;\n}\n.screenshot-preview-actions {\n display: flex;\n gap: var(--mfb-space-2);\n padding: var(--mfb-space-2);\n border-top: 1px solid var(--mfb-border);\n background: var(--mfb-bg);\n}\n.screenshot-preview-actions .btn {\n flex: 1;\n padding: 8px 12px;\n font-size: var(--mfb-text-sm);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n}\n.screenshot-preview-actions .btn--primary {\n background: var(--mfb-bg);\n color: var(--mfb-accent);\n border-color: var(--mfb-accent);\n}\n.screenshot-preview-actions .btn--primary:hover {\n background: color-mix(in srgb, var(--mfb-accent) 8%, var(--mfb-bg));\n border-color: var(--mfb-accent);\n color: var(--mfb-accent);\n}\n.screenshot-remove {\n /* Kept for legacy markup that uses the old corner-cross button. */\n position: absolute;\n top: 6px;\n right: 6px;\n width: 26px;\n height: 26px;\n display: grid;\n place-items: center;\n background: rgba(255, 255, 255, 0.9);\n border: 1px solid var(--mfb-border);\n border-radius: 999px;\n font-size: 18px;\n line-height: 1;\n cursor: pointer;\n color: #111827;\n}\n.screenshot-remove:hover { background: #fff; }\n.screenshot-annotate {\n /* Kept for legacy; new markup uses .screenshot-preview-actions. */\n border-radius: 0;\n border-width: 0;\n border-top: 1px solid var(--mfb-border);\n background: var(--mfb-bg);\n}\n.screenshot-annotate:hover { background: var(--mfb-surface); }\n\n.page-context {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 11px;\n color: var(--mfb-text-muted);\n}\n.page-context-label {\n text-transform: uppercase;\n font-weight: 600;\n letter-spacing: 0.04em;\n font-size: 10px;\n}\n.page-context-url {\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11px;\n color: var(--mfb-text-muted);\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Annotator modal — sits above the feedback modal (z-index +1). */\n\n.annotator-backdrop {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.78);\n display: grid;\n place-items: center;\n z-index: 1;\n padding: 12px;\n}\n.annotator {\n position: relative;\n background: var(--mfb-bg);\n color: var(--mfb-text);\n border-radius: calc(var(--mfb-radius) * 1.5);\n width: min(960px, 96vw);\n max-height: calc(100vh - 24px);\n display: flex;\n flex-direction: column;\n box-shadow: 0 24px 60px rgba(0, 0, 0, 0.4);\n}\n.annotator-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 14px;\n border-bottom: 1px solid var(--mfb-border);\n font-size: 13px;\n font-weight: 600;\n}\n.annotator-toolbar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n padding: 8px 14px;\n border-bottom: 1px solid var(--mfb-border);\n}\n.annotator-tools, .annotator-colors { display: flex; gap: 4px; }\n.annotator-sep {\n display: inline-block;\n width: 1px;\n height: 18px;\n background: var(--mfb-border);\n margin: 0 4px;\n}\n.annotator-spacer { flex: 1; }\n.annotator-tool {\n width: 30px;\n height: 30px;\n display: grid;\n place-items: center;\n background: var(--mfb-bg);\n color: var(--mfb-text);\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n cursor: pointer;\n}\n.annotator-tool:hover { background: var(--mfb-surface); }\n.annotator-tool.is-active {\n background: var(--mfb-text);\n color: var(--mfb-bg);\n border-color: var(--mfb-text);\n}\n.annotator-color {\n width: 24px;\n height: 24px;\n border-radius: 999px;\n border: 2px solid var(--mfb-border);\n cursor: pointer;\n padding: 0;\n transition: transform 120ms ease;\n position: relative;\n}\n.annotator-color.is-active {\n transform: scale(1.12);\n border-color: var(--mfb-text);\n}\n/* Color-picker swatch: rainbow gradient fill so users see this isn't\n a preset, plus a tiny native <input type=\"color\"> overlaid invisibly. */\n.annotator-color-picker {\n display: grid;\n place-items: center;\n background: conic-gradient(from 180deg, #ef4444, #f59e0b, #10b981, #3b82f6, #8b5cf6, #ec4899, #ef4444);\n overflow: hidden;\n}\n.annotator-color-picker input[type=\"color\"] {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n border: 0;\n padding: 0;\n margin: 0;\n opacity: 0;\n cursor: pointer;\n}\n.annotator-btn {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n height: 30px;\n padding: 0 10px;\n font: inherit;\n font-size: 12px;\n background: var(--mfb-bg);\n color: var(--mfb-text);\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n cursor: pointer;\n}\n.annotator-btn:hover { background: var(--mfb-surface); }\n.annotator-btn[disabled] { opacity: 0.5; cursor: not-allowed; }\n.annotator-count { font-size: 11px; color: var(--mfb-text-muted); }\n.annotator-canvas-wrap {\n flex: 1;\n overflow: auto;\n background: #1a1a1a;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 12px;\n min-height: 200px;\n}\n.annotator-canvas {\n cursor: crosshair;\n box-shadow: 0 8px 28px rgba(0, 0, 0, 0.45);\n background: #fff;\n}\n.annotator-loading {\n color: rgba(255, 255, 255, 0.7);\n font-size: 13px;\n}\n.annotator-footer {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 8px;\n padding: 10px 14px;\n border-top: 1px solid var(--mfb-border);\n}\n\n/* ---- v0.7: tabs + reader UI -------------------------------------- */\n\n.tab-strip {\n display: flex;\n gap: var(--mfb-space-1);\n border-bottom: 1px solid var(--mfb-border);\n margin: 0;\n padding: 0;\n}\n.tab-button {\n appearance: none;\n background: transparent;\n border: 0;\n border-bottom: 2px solid transparent;\n padding: var(--mfb-space-3) var(--mfb-space-4);\n margin-bottom: -1px;\n font: inherit;\n font-size: var(--mfb-text-sm);\n font-weight: 500;\n color: var(--mfb-text-muted);\n cursor: pointer;\n transition: color 120ms ease, border-color 120ms ease;\n}\n.tab-button:hover { color: var(--mfb-text); }\n.tab-button.is-active {\n color: var(--mfb-accent);\n border-bottom-color: var(--mfb-accent);\n}\n.tab-button[aria-selected=\"true\"] { font-weight: 600; }\n\n.mine-list {\n display: flex;\n flex-direction: column;\n gap: var(--mfb-space-3);\n /* Matches .board-view's entrance so switching tabs feels like a real\n * page transition rather than a flicker. */\n animation: mfb-board-in 280ms ease-out 40ms both;\n}\n@media (prefers-reduced-motion: reduce) {\n .mine-list { animation: none; }\n}\n.mine-list-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n}\n.mine-list-header h2 { margin: 0; font-size: var(--mfb-text-lg); font-weight: 600; letter-spacing: -0.01em; }\n/* #85 — header actions: jump-by-#seq box + refresh. */\n.mine-list-header-actions {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n.mine-jump {\n display: flex;\n align-items: center;\n gap: 4px;\n}\n.mine-jump input {\n font: inherit;\n font-size: 13px;\n width: 56px;\n padding: 5px 8px;\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n background: var(--mfb-surface);\n color: inherit;\n}\n.mine-jump-error {\n margin-top: 6px;\n font-size: 12px;\n}\n.mine-loading { color: var(--mfb-text-muted); font-size: 13px; }\n.mine-empty {\n text-align: center;\n padding: 24px 12px;\n color: var(--mfb-text-muted);\n font-size: 13px;\n}\n.mine-empty strong { display: block; color: var(--mfb-text); margin-bottom: 4px; }\n\n.mine-rows {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 6px;\n max-height: 380px;\n overflow-y: auto;\n}\n.mine-row {\n appearance: none;\n text-align: left;\n background: var(--mfb-surface);\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n padding: 10px 12px;\n font: inherit;\n color: inherit;\n cursor: pointer;\n display: flex;\n flex-direction: column;\n gap: 4px;\n width: 100%;\n transition:\n border-color 140ms ease,\n background 140ms ease,\n transform 100ms ease,\n box-shadow 140ms ease;\n}\n.mine-row:hover {\n border-color: var(--mfb-border-strong);\n background: var(--mfb-bg);\n transform: translateY(-1px);\n box-shadow: var(--mfb-shadow);\n}\n.mine-row:active { transform: scale(0.997); }\n.mine-row:focus-visible {\n outline: 2px solid var(--mfb-accent);\n outline-offset: 2px;\n}\n.mine-row-pills { display: flex; gap: 4px; flex-wrap: wrap; }\n.mine-row-preview {\n font-size: 13px;\n color: var(--mfb-text);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n.mine-row-meta {\n display: flex;\n gap: 6px;\n font-size: 11px;\n color: var(--mfb-text-muted);\n}\n\n.pill {\n display: inline-block;\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n padding: 2px 8px;\n border-radius: 999px;\n border: 1px solid transparent;\n}\n.pill-status { background: #eff6ff; color: #1e40af; border-color: #dbeafe; }\n.pill-status--in_progress { background: #fffbeb; color: #92400e; border-color: #fde68a; }\n.pill-status--awaiting_validation { background: #faf5ff; color: #6b21a8; border-color: #e9d5ff; }\n.pill-status--closed { background: #ecfdf5; color: #065f46; border-color: #a7f3d0; }\n.pill-status--rejected, .pill-status--wontfix, .pill-status--duplicate { background: #f3f4f6; color: #374151; border-color: #e5e7eb; }\n.pill-type { background: var(--mfb-surface); color: var(--mfb-text-muted); border-color: var(--mfb-border); }\n.pill-severity { background: var(--mfb-surface); color: var(--mfb-text-muted); border-color: var(--mfb-border); }\n.pill-severity--blocker { background: #fef2f2; color: #991b1b; border-color: #fecaca; }\n.pill-severity--high { background: #fff7ed; color: #9a3412; border-color: #fed7aa; }\n.pill-severity--medium { background: #fefce8; color: #854d0e; border-color: #fef08a; }\n.pill-severity--low { background: var(--mfb-surface); color: var(--mfb-text-muted); }\n\n.report-detail { display: flex; flex-direction: column; gap: 12px; }\n.report-detail-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n.report-detail-body { display: flex; flex-direction: column; gap: 10px; }\n.report-detail-description {\n font-size: 14px;\n white-space: pre-wrap;\n margin: 0;\n}\n.report-detail-screenshot {\n display: block;\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n overflow: hidden;\n background: #1a1a1a;\n}\n.report-detail-screenshot img {\n display: block;\n width: 100%;\n max-height: 200px;\n object-fit: contain;\n}\n.report-detail-section {\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mfb-text-muted);\n margin: 8px 0 4px;\n font-weight: 600;\n}\n\n.report-detail-context {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 10px 12px;\n background: var(--mfb-surface);\n border-radius: var(--mfb-radius);\n border: 1px solid var(--mfb-border);\n}\n.report-detail-context-pills { display: flex; gap: 4px; flex-wrap: wrap; }\n.report-detail-context-line {\n display: flex;\n align-items: baseline;\n gap: 8px;\n font-size: 12px;\n color: var(--mfb-text);\n}\n.report-detail-context-label {\n text-transform: uppercase;\n letter-spacing: 0.04em;\n font-size: 10px;\n font-weight: 600;\n color: var(--mfb-text-muted);\n min-width: 56px;\n}\n.report-detail-context-url {\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n color: var(--mfb-accent);\n text-decoration: none;\n font-size: 11px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.report-detail-context-url:hover { text-decoration: underline; }\n.pill-capture { background: var(--mfb-bg); color: var(--mfb-text-muted); border-color: var(--mfb-border); }\n.report-detail-empty {\n font-size: 12px;\n color: var(--mfb-text-muted);\n margin: 0;\n}\n\n.report-comments {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 6px;\n max-height: 300px;\n overflow-y: auto;\n}\n\n.comment-bubble {\n padding: 8px 10px;\n border-radius: 12px;\n max-width: 88%;\n font-size: 13px;\n line-height: 1.4;\n}\n.comment-bubble.is-mine {\n align-self: flex-end;\n background: var(--mfb-accent);\n color: var(--mfb-accent-contrast);\n}\n.comment-bubble.is-other {\n align-self: flex-start;\n background: var(--mfb-surface);\n border: 1px solid var(--mfb-border);\n color: var(--mfb-text);\n}\n.comment-author {\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n font-weight: 600;\n margin-bottom: 2px;\n}\n.comment-author--mcp { color: #6b21a8; }\n.comment-author--staff { color: #1e40af; }\n.comment-author--system { color: var(--mfb-text-muted); }\n.comment-body { white-space: pre-wrap; }\n/* #91 — image attachments rendered inside a comment bubble. */\n.comment-attachments {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n margin-top: 6px;\n}\n.comment-attachment {\n display: block;\n line-height: 0;\n border-radius: 8px;\n overflow: hidden;\n border: 1px solid var(--mfb-border);\n}\n.comment-attachment img {\n width: 96px;\n height: 96px;\n object-fit: cover;\n display: block;\n}\n.comment-time {\n font-size: 10px;\n margin-top: 4px;\n opacity: 0.7;\n}\n\n/* F4 — author edits the description in place. */\n.report-detail-description-row {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n}\n.report-detail-description-row .report-detail-description {\n flex: 1;\n margin: 0;\n}\n.report-detail-edit {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.report-detail-edit-input {\n font: inherit;\n font-size: 13px;\n padding: 8px 10px;\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n background: var(--mfb-surface);\n color: inherit;\n min-height: 72px;\n resize: vertical;\n}\n.report-detail-edit-actions {\n display: flex;\n justify-content: flex-end;\n gap: 6px;\n}\n.report-detail-edit.btn,\nbutton.report-detail-edit {\n flex: 0 0 auto;\n font-size: 12px;\n padding: 4px 8px;\n}\n/* #85 — shareable-permalink affordance under the description. */\n.report-detail-permalink {\n margin: -2px 0 6px;\n}\n.report-detail-copy-link {\n font-size: 12px;\n padding: 4px 8px;\n}\n.report-compose {\n display: flex;\n flex-direction: column;\n gap: 6px;\n margin-top: 4px;\n}\n.report-compose textarea {\n font: inherit;\n font-size: 13px;\n padding: 8px 10px;\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n background: var(--mfb-surface);\n color: inherit;\n min-height: 64px;\n resize: vertical;\n}\n.report-compose-actions {\n display: flex;\n justify-content: flex-end;\n gap: 6px;\n}\n/* #91 — pending image attachments in the reply compose box. */\n.compose-attachments {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n.compose-attachment {\n position: relative;\n width: 64px;\n height: 64px;\n border-radius: 8px;\n overflow: hidden;\n border: 1px solid var(--mfb-border);\n}\n.compose-attachment img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n}\n.compose-attachment-remove {\n position: absolute;\n top: 2px;\n right: 2px;\n width: 20px;\n height: 20px;\n display: grid;\n place-items: center;\n padding: 0;\n background: rgba(255, 255, 255, 0.92);\n border: 1px solid var(--mfb-border);\n border-radius: 999px;\n font-size: 15px;\n line-height: 1;\n cursor: pointer;\n color: #111827;\n}\n.compose-attachment-remove:hover { background: #fff; }\n.compose-attach-btn { margin-right: auto; }\n.compose-attach-error {\n margin: 0;\n font-size: 12px;\n color: var(--mfb-danger, #b91c1c);\n}\n\n/* v0.15.3 — teammate-viewing notice. Shown in place of the compose\n * box when a non-submitter opens a project-wide row from the Board\n * tab. Replies are private to the submitter; we surface this once\n * (calm tone, neutral surface) so the viewer understands why no\n * Reply box appears, instead of leaving them wondering. */\n.report-detail-teammate-notice {\n margin: 0;\n padding: var(--mfb-space-3) var(--mfb-space-4);\n background: var(--mfb-surface);\n border: 1px dashed var(--mfb-border);\n border-radius: var(--mfb-radius);\n font-size: var(--mfb-text-sm);\n color: var(--mfb-text-muted);\n font-style: italic;\n}\n\n/* v0.15.3 — friendly \"report not available\" empty state. Replaces\n * the raw API error message (e.g. getReport failed: 404 …) that\n * used to leak from setError(err.message) straight into the DOM. */\n.report-detail-empty-state {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--mfb-space-3);\n padding: var(--mfb-space-5);\n background: var(--mfb-surface);\n border-radius: var(--mfb-radius);\n border: 1px solid var(--mfb-border);\n}\n.report-detail-empty-state-title {\n margin: 0;\n font-size: var(--mfb-text-md);\n font-weight: 600;\n color: var(--mfb-text);\n}\n.report-detail-empty-state-body {\n margin: 0;\n font-size: var(--mfb-text-sm);\n color: var(--mfb-text-muted);\n}\n\n/* Status history — read-only audit trail visible to the submitter.\n Timeline of who flipped the status and when (operator vs. agent vs.\n system). Bordered, neutral surface so it doesn't compete visually\n with the conversation thread above. */\n.report-detail-history {\n display: flex;\n flex-direction: column;\n gap: 6px;\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n background: var(--mfb-surface);\n padding: 8px 10px;\n}\n.report-detail-history .report-detail-section {\n margin: 0;\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mfb-text-muted);\n}\n.status-history {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.status-history-row {\n display: grid;\n grid-template-columns: auto 1fr auto;\n gap: 8px;\n align-items: center;\n font-size: 11px;\n}\n.status-history-time {\n color: var(--mfb-text-muted);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n.status-history-transition {\n display: inline-flex;\n gap: 4px;\n align-items: center;\n flex-wrap: wrap;\n}\n.status-history-arrow {\n color: var(--mfb-text-muted);\n font-size: 11px;\n}\n.status-history-source {\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n font-weight: 600;\n white-space: nowrap;\n}\n.status-history-source--mcp { color: #6b21a8; }\n.status-history-source--user { color: #1e40af; }\n.status-history-source--system { color: var(--mfb-text-muted); }\n\n/* Transparency drawer — closed by default. The user can click open to\n verify what their browser sent: device, errors, console tail, network\n tail. Read-only; purely a trust-building gesture. */\n.report-detail-tech {\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n background: var(--mfb-surface);\n}\n.report-detail-tech > summary {\n cursor: pointer;\n padding: 8px 10px;\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mfb-text-muted);\n user-select: none;\n list-style: none;\n}\n.report-detail-tech > summary::-webkit-details-marker { display: none; }\n.report-detail-tech > summary::before {\n content: '▸';\n display: inline-block;\n margin-right: 6px;\n transition: transform 120ms;\n}\n.report-detail-tech[open] > summary::before { transform: rotate(90deg); }\n.tech-body {\n padding: 0 10px 10px 10px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n.tech-section h4 {\n margin: 0 0 4px 0;\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mfb-text-muted);\n font-weight: 600;\n}\n.tech-device {\n display: grid;\n grid-template-columns: max-content 1fr;\n column-gap: 8px;\n row-gap: 2px;\n margin: 0;\n font-size: 11px;\n font-variant-numeric: tabular-nums;\n}\n.tech-device dt { color: var(--mfb-text-muted); }\n.tech-device dd { margin: 0; }\n.tech-errors, .tech-console, .tech-network {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n font-size: 11px;\n font-family: ui-monospace, \"SF Mono\", Menlo, monospace;\n max-height: 200px;\n overflow-y: auto;\n}\n.tech-errors li { padding: 4px 0; border-top: 1px solid var(--mfb-border); }\n.tech-errors li:first-child { border-top: 0; }\n.tech-errors-msg { color: #991b1b; font-weight: 600; }\n.tech-errors-stack {\n margin: 4px 0 0 0;\n padding: 4px 6px;\n background: var(--mfb-bg);\n border-radius: 4px;\n font-size: 10px;\n white-space: pre-wrap;\n color: var(--mfb-text-muted);\n max-height: 120px;\n overflow-y: auto;\n}\n.tech-console-row {\n display: grid;\n grid-template-columns: 48px 1fr;\n gap: 6px;\n padding: 1px 0;\n}\n.tech-console-level {\n text-transform: uppercase;\n font-size: 9px;\n font-weight: 600;\n align-self: center;\n color: var(--mfb-text-muted);\n}\n.tech-console-row--warn .tech-console-level { color: #92400e; }\n.tech-console-row--error .tech-console-level { color: #991b1b; }\n.tech-console-row--info .tech-console-level { color: #1e40af; }\n.tech-console-msg { word-break: break-word; }\n.tech-network-row {\n display: grid;\n grid-template-columns: 48px 56px 1fr 56px;\n gap: 6px;\n padding: 1px 0;\n}\n.tech-network-row--fail .tech-network-status { color: #991b1b; font-weight: 700; }\n.tech-network-status { font-variant-numeric: tabular-nums; }\n.tech-network-method { color: var(--mfb-text-muted); }\n.tech-network-url { word-break: break-all; }\n.tech-network-time { text-align: right; color: var(--mfb-text-muted); font-variant-numeric: tabular-nums; }\n\n/* KPI strip — four pills above the My Reports list. Each is clickable\n to filter the rows below. Borrowed from thePnr's FeedbackKPICards. */\n.kpi-strip {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 6px;\n}\n.kpi-cell {\n appearance: none;\n background: var(--mfb-bg);\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n padding: 10px 10px 10px 14px;\n cursor: pointer;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 2px;\n font-family: inherit;\n color: inherit;\n position: relative;\n overflow: hidden;\n transition:\n background 140ms ease,\n border-color 140ms ease,\n transform 100ms ease,\n box-shadow 140ms ease;\n}\n.kpi-cell::before {\n /* Vertical tone accent — matches the Board KPI cards' visual rhythm so\n * the two surfaces feel like they belong to the same product. v0.12\n * polish pass. */\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n width: 3px;\n height: 100%;\n background: var(--mfb-border);\n transition: background 140ms ease;\n}\n.kpi-cell--new::before { background: #3b82f6; }\n.kpi-cell--in_progress::before { background: #f59e0b; }\n.kpi-cell--awaiting_validation::before { background: #a855f7; }\n.kpi-cell--resolved::before { background: #10b981; }\n.kpi-cell:hover {\n background: var(--mfb-surface);\n transform: translateY(-1px);\n box-shadow: var(--mfb-shadow);\n}\n.kpi-cell.is-active {\n border-color: var(--mfb-accent);\n background: rgba(59, 130, 246, 0.08);\n}\n.kpi-cell.is-active.kpi-cell--in_progress { border-color: #d97706; background: rgba(251, 191, 36, 0.10); }\n.kpi-cell.is-active.kpi-cell--awaiting_validation { border-color: #9333ea; background: rgba(168, 85, 247, 0.10); }\n.kpi-cell.is-active.kpi-cell--resolved { border-color: #059669; background: rgba(16, 185, 129, 0.10); }\n.kpi-value {\n font-size: 18px;\n font-weight: 700;\n font-variant-numeric: tabular-nums;\n line-height: 1;\n}\n.kpi-label {\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mfb-text-muted);\n}\n\n/* Changelog (\"This week\") — week-grouped resolved-report timeline.\n Borrowed from thePnr's FeedbackChangelogView; the header per group\n reads \"● Week of {date} ━━━ {n} resolved\". */\n.changelog-groups {\n display: flex;\n flex-direction: column;\n gap: 12px;\n animation: mfb-board-in 280ms ease-out 40ms both;\n}\n@media (prefers-reduced-motion: reduce) {\n .changelog-groups { animation: none; }\n}\n.changelog-group {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.changelog-group-header {\n display: grid;\n grid-template-columns: auto auto 1fr auto;\n gap: 6px;\n align-items: center;\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mfb-text-muted);\n}\n.changelog-group-marker {\n color: var(--mfb-accent);\n font-size: 14px;\n line-height: 1;\n}\n.changelog-group-label { font-weight: 600; }\n.changelog-group-rule {\n height: 1px;\n background: var(--mfb-border);\n align-self: center;\n}\n.changelog-group-count {\n font-variant-numeric: tabular-nums;\n font-weight: 600;\n}\n\n/* ---- v0.12: Board tab + expanded modal (\"transport\" feel) ------------ */\n\n/* When the Board tab is active, the modal grows toward near-fullscreen\n * on desktop and full-screen on mobile. The CSS transition gives the\n * sense of clicking through to a real page rather than switching tabs.\n * Width/height/max-* are animated together; padding too, since the\n * board's denser layout needs less of the modal's default padding. */\n.modal.is-expanded {\n /* Margin around the expanded board view. --mfb-space-7 (48px) matches\n * the breathing room of the default modal — 24px top + 24px bottom +\n * 24px on each side — so the panel reads as \"near-fullscreen with a\n * visible frame\" instead of \"the whole viewport, content cut off\".\n *\n * The base .modal rule uses the browser default content-box, so\n * without an override the 24px padding would be ADDED to the\n * calc(100vh - 48px) height — the rendered box would exactly equal\n * the viewport again, defeating the margin. Force border-box so\n * padding lives INSIDE the height we set. */\n box-sizing: border-box;\n width: min(1280px, calc(100vw - var(--mfb-space-7)));\n max-height: calc(100vh - var(--mfb-space-7));\n height: calc(100vh - var(--mfb-space-7));\n padding: var(--mfb-space-5);\n transition:\n width 320ms cubic-bezier(0.22, 1, 0.36, 1),\n height 320ms cubic-bezier(0.22, 1, 0.36, 1),\n max-height 320ms cubic-bezier(0.22, 1, 0.36, 1),\n padding 320ms cubic-bezier(0.22, 1, 0.36, 1);\n}\n@media (prefers-reduced-motion: reduce) {\n .modal.is-expanded { transition: none; }\n}\n.backdrop.is-expanded { /* hook for any backdrop-level overrides */ }\n\n@media (max-width: 640px) {\n /* On mobile the sheet still snaps to the bottom edge and stretches\n * almost the full viewport — 24px gap from the top is enough to\n * convey \"this is a panel, not a takeover\", but we don't pull margin\n * off the sides where users expect the sheet to fill the viewport. */\n .modal.is-expanded {\n width: 100vw;\n height: calc(100vh - var(--mfb-space-5));\n border-radius: var(--mfb-radius-lg) var(--mfb-radius-lg) 0 0;\n }\n}\n\n/* Decorative purple accent on the Board tab button so users discover\n * it's a different surface — subtle, not loud. */\n.tab-button--board.is-active {\n color: #6b21a8;\n box-shadow: inset 0 -2px 0 0 #a855f7;\n}\n.tab-button--board:not(.is-active):hover {\n color: #6b21a8;\n}\n\n/* ---- Board layout ---- */\n\n.board-view {\n display: flex;\n flex-direction: column;\n gap: var(--mfb-space-4);\n flex: 1 1 auto;\n min-height: 0; /* allow inner panes to scroll */\n animation: mfb-board-in 280ms ease-out 40ms both;\n}\n@keyframes mfb-board-in {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n@media (prefers-reduced-motion: reduce) {\n .board-view { animation: none; }\n}\n\n.board-header {\n display: flex;\n flex-direction: column;\n gap: var(--mfb-space-4);\n}\n.board-header-title {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n.board-header-emoji {\n font-size: 26px;\n line-height: 1;\n}\n.board-header-h {\n margin: 0;\n font-size: var(--mfb-text-2xl);\n font-weight: 600;\n letter-spacing: -0.01em;\n}\n.board-header-sub {\n margin: 2px 0 0 0;\n color: var(--mfb-text-muted);\n font-size: var(--mfb-text-sm);\n}\n\n/* KPI strip — 4 cards, equal width, subtle tone-tints by bucket. */\n.board-kpi-strip {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 12px;\n}\n.board-kpi-card {\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n padding: 14px 16px;\n background: var(--mfb-bg);\n display: flex;\n flex-direction: column;\n gap: 4px;\n position: relative;\n overflow: hidden;\n transition: transform 160ms ease, box-shadow 160ms ease;\n}\n.board-kpi-card::after {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n width: 3px;\n height: 100%;\n background: var(--mfb-border);\n transition: background 160ms ease;\n}\n.board-kpi-card:hover {\n transform: translateY(-1px);\n box-shadow: var(--mfb-shadow);\n}\n.board-kpi-card.tone-new::after { background: #3b82f6; }\n.board-kpi-card.tone-progress::after { background: #f59e0b; }\n.board-kpi-card.tone-validation::after { background: #a855f7; }\n.board-kpi-card.tone-rate::after { background: #10b981; }\n.board-kpi-value {\n font-size: 28px;\n font-weight: 700;\n line-height: 1;\n letter-spacing: -0.02em;\n color: var(--mfb-text);\n font-variant-numeric: tabular-nums;\n}\n.board-kpi-label {\n font-size: var(--mfb-text-xs);\n color: var(--mfb-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n font-weight: 500;\n}\n.board-kpi-strip.is-loading .board-kpi-value {\n opacity: 0.35;\n}\n@media (max-width: 720px) {\n .board-kpi-strip { grid-template-columns: repeat(2, 1fr); }\n}\n\n/* Filter row */\n.board-filters {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n.board-filter-select,\n.board-filter-search {\n padding: 6px 10px;\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n background: var(--mfb-bg);\n color: var(--mfb-text);\n font: inherit;\n font-size: var(--mfb-text-sm);\n height: 32px;\n transition: border-color 120ms ease, box-shadow 120ms ease;\n}\n.board-filter-select:focus-visible,\n.board-filter-search:focus-visible {\n outline: none;\n border-color: var(--mfb-accent);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mfb-accent) 18%, transparent);\n}\n.board-filter-search { min-width: 180px; flex: 1 1 220px; }\n.board-filter-toggle {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: var(--mfb-text-sm);\n color: var(--mfb-text);\n cursor: pointer;\n user-select: none;\n}\n.board-filter-toggle input { margin: 0; }\n.board-filter-clear {\n background: transparent;\n border: 0;\n color: var(--mfb-text-muted);\n font: inherit;\n font-size: var(--mfb-text-sm);\n display: inline-flex;\n align-items: center;\n gap: 4px;\n cursor: pointer;\n padding: 4px 6px;\n border-radius: var(--mfb-radius);\n transition: background 120ms ease, color 120ms ease;\n}\n.board-filter-clear:hover {\n color: var(--mfb-text);\n background: var(--mfb-surface);\n}\n\n/* Scope segmented control — [This page | All pages], front of the filter row. */\n.board-scope {\n display: inline-flex;\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n overflow: hidden;\n flex: none;\n}\n.board-scope-btn {\n border: none;\n background: none;\n padding: 6px 10px;\n font: inherit;\n font-size: var(--mfb-text-sm);\n color: var(--mfb-text-muted);\n cursor: pointer;\n}\n.board-scope-btn.is-active {\n background: color-mix(in srgb, var(--mfb-accent) 14%, transparent);\n color: var(--mfb-accent);\n font-weight: 600;\n}\n.board-scope-btn:focus-visible {\n outline: 2px solid var(--mfb-accent);\n outline-offset: -2px;\n}\n\n/* Send-tab page-activity strip — one line, muted, non-blocking. */\n.page-strip {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--mfb-space-3);\n margin: var(--mfb-space-3) 0 0;\n padding: var(--mfb-space-2) var(--mfb-space-3);\n border-radius: var(--mfb-radius);\n background: var(--mfb-surface-2);\n color: var(--mfb-text-muted);\n font-size: var(--mfb-text-sm);\n}\n.page-strip-view {\n border: none;\n background: none;\n padding: 0;\n color: var(--mfb-accent);\n font-size: var(--mfb-text-sm);\n font-weight: 600;\n cursor: pointer;\n}\n.page-strip-view:hover {\n text-decoration: underline;\n}\n\n/* Master/detail layout — two-column at >=900px, stacked below. */\n.board-body {\n display: grid;\n grid-template-columns: minmax(280px, 360px) 1fr;\n gap: var(--mfb-space-4);\n flex: 1 1 auto;\n min-height: 0;\n}\n@media (max-width: 900px) {\n .board-body { grid-template-columns: 1fr; }\n .board-detail-wrap:not(.has-selection) { display: none; }\n}\n\n.board-list-wrap {\n display: flex;\n flex-direction: column;\n gap: 6px;\n min-height: 0;\n overflow: hidden;\n}\n/* Stale-while-revalidate: a query change keeps the previous rows visible\n * (locally pre-filtered where the row data allows) and dims them while the\n * server confirms — the instant local response is the #352 \"never inert\"\n * guarantee, the dim is the \"still confirming\" affordance. */\n.board-view.is-stale .board-list,\n.board-view.is-stale .board-list-count,\n.board-view.is-stale .board-kpi-strip {\n opacity: 0.55;\n transition: opacity 0.15s ease;\n}\n.board-load-more {\n margin: 4px auto 8px;\n flex: 0 0 auto;\n}\n.board-list-count {\n font-size: var(--mfb-text-xs);\n color: var(--mfb-text-muted);\n padding: 0 4px;\n}\n.board-list {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 8px;\n overflow-y: auto;\n flex: 1 1 auto;\n scrollbar-width: thin;\n scrollbar-gutter: stable;\n}\n\n.board-row {\n display: flex;\n flex-direction: column;\n gap: 6px;\n width: 100%;\n text-align: left;\n padding: 12px 14px;\n border-radius: var(--mfb-radius);\n background: var(--mfb-bg);\n border: 1px solid var(--mfb-border);\n cursor: pointer;\n font: inherit;\n color: inherit;\n /* Stagger reveal on first paint. */\n animation: mfb-row-in 280ms ease-out both;\n transition: border-color 140ms ease, background 140ms ease, transform 80ms ease;\n}\n.board-row:hover {\n border-color: var(--mfb-border-strong);\n background: var(--mfb-surface);\n}\n.board-row:active { transform: scale(0.997); }\n.board-row.is-selected {\n border-color: var(--mfb-accent);\n background: color-mix(in srgb, var(--mfb-accent) 6%, var(--mfb-bg));\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--mfb-accent) 18%, transparent);\n}\n@keyframes mfb-row-in {\n from { opacity: 0; transform: translateY(4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n@media (prefers-reduced-motion: reduce) {\n .board-row { animation: none; }\n}\n/* Stagger first 6 rows so the list reveals top-down. */\n.board-row:nth-child(1) { animation-delay: 0ms; }\n.board-row:nth-child(2) { animation-delay: 40ms; }\n.board-row:nth-child(3) { animation-delay: 80ms; }\n.board-row:nth-child(4) { animation-delay: 120ms; }\n.board-row:nth-child(5) { animation-delay: 160ms; }\n.board-row:nth-child(6) { animation-delay: 200ms; }\n\n.board-row-badges {\n display: flex;\n gap: 6px;\n align-items: center;\n flex-wrap: wrap;\n}\n.board-row-description {\n font-size: var(--mfb-text-sm);\n color: var(--mfb-text);\n line-height: 1.4;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n.board-row-meta {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: var(--mfb-text-xs);\n color: var(--mfb-text-muted);\n flex-wrap: wrap;\n}\n.board-row-author { font-weight: 500; color: var(--mfb-text); }\n.board-row-comments {\n display: inline-flex;\n align-items: center;\n gap: 3px;\n font-variant-numeric: tabular-nums;\n}\n.board-row-time { margin-left: auto; font-variant-numeric: tabular-nums; }\n\n/* Status / severity badges shared with the detail panel. The tones\n * borrow PNR's badge palette — calm pastels that don't fight content. */\n.board-row-badges .badge {\n font-size: 11px;\n font-weight: 500;\n letter-spacing: 0.02em;\n padding: 2px 8px;\n border-radius: 999px;\n background: #f3f4f6;\n color: #374151;\n text-transform: lowercase;\n}\n.badge.status-new { background: #dbeafe; color: #1e40af; }\n.badge.status-in_progress { background: #fef3c7; color: #92400e; }\n.badge.status-awaiting_validation { background: #ede9fe; color: #6b21a8; }\n.badge.status-closed { background: #d1fae5; color: #065f46; }\n.badge.status-rejected { background: #fee2e2; color: #991b1b; }\n.badge.status-duplicate { background: #f3f4f6; color: #4b5563; }\n.badge.status-wontfix { background: #f3f4f6; color: #4b5563; }\n.badge.severity-blocker { background: #fee2e2; color: #991b1b; }\n.badge.severity-high { background: #fee2e2; color: #b91c1c; }\n.badge.severity-medium { background: #fef3c7; color: #92400e; }\n.badge.severity-low { background: #e0f2fe; color: #075985; }\n\n/* Detail pane (within Board). Inherits from .report-detail.variant-board */\n.board-detail-wrap {\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius);\n background: var(--mfb-bg);\n overflow-y: auto;\n min-height: 0;\n display: flex;\n flex-direction: column;\n}\n.board-detail-empty {\n flex: 1 1 auto;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n color: var(--mfb-text-muted);\n padding: var(--mfb-space-5);\n text-align: center;\n font-size: var(--mfb-text-sm);\n}\n.board-detail-empty svg { opacity: 0.6; }\n\n.report-detail.variant-board { padding: 16px 18px; }\n.report-detail-header--board {\n align-items: center;\n}\n.report-detail-back {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n height: 28px;\n padding: 0 10px;\n}\n\n/* Empty + loading + error states */\n.board-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n text-align: center;\n padding: var(--mfb-space-5);\n color: var(--mfb-text-muted);\n flex: 1 1 auto;\n}\n.board-empty h3 {\n margin: 0;\n font-size: var(--mfb-text-base);\n color: var(--mfb-text);\n}\n.board-empty p {\n margin: 0;\n font-size: var(--mfb-text-sm);\n}\n.board-error {\n padding: 12px 14px;\n border-radius: var(--mfb-radius);\n background: #fef2f2;\n color: #991b1b;\n font-size: var(--mfb-text-sm);\n}\n\n/* Skeleton rows pulse subtly so the user knows something's coming. */\n.board-list-skeleton .skeleton-row {\n cursor: default;\n pointer-events: none;\n}\n.skeleton-line {\n height: 12px;\n background: linear-gradient(\n 90deg,\n var(--mfb-surface) 0%,\n color-mix(in srgb, var(--mfb-surface) 60%, var(--mfb-border)) 50%,\n var(--mfb-surface) 100%\n );\n background-size: 200% 100%;\n border-radius: 4px;\n animation: mfb-skeleton 1400ms ease-in-out infinite;\n}\n.skeleton-badges { width: 60%; height: 14px; }\n.skeleton-text { width: 90%; }\n.skeleton-text.short { width: 60%; }\n@keyframes mfb-skeleton {\n from { background-position: 100% 0; }\n to { background-position: -100% 0; }\n}\n@media (prefers-reduced-motion: reduce) {\n .skeleton-line { animation: none; }\n}\n\n/* Hover peek panel — desktop-only ambient surface listing this page's\n open reports above the FAB (spec §3.2). Non-modal; never renders while\n the modal itself is open. */\n.page-peek {\n position: absolute;\n right: 0;\n bottom: 60px;\n width: 300px;\n padding: var(--mfb-space-3);\n border: 1px solid var(--mfb-border);\n border-radius: var(--mfb-radius-lg);\n background: var(--mfb-surface);\n color: var(--mfb-text);\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);\n font-size: var(--mfb-text-sm);\n}\n.page-peek-title {\n font-weight: 700;\n margin-bottom: var(--mfb-space-2);\n color: var(--mfb-text-muted);\n text-transform: uppercase;\n font-size: var(--mfb-text-xs);\n letter-spacing: 0.04em;\n}\n.page-peek-row {\n display: flex;\n align-items: center;\n gap: var(--mfb-space-2);\n width: 100%;\n padding: var(--mfb-space-2);\n border: none;\n border-radius: var(--mfb-radius);\n background: none;\n color: var(--mfb-text);\n text-align: left;\n cursor: pointer;\n}\n.page-peek-row:hover {\n background: var(--mfb-surface-2);\n}\n.page-peek-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n flex: none;\n}\n.page-peek-dot.status-new { background: #1e40af; }\n.page-peek-dot.status-in_progress { background: #92400e; }\n.page-peek-dot.status-awaiting_validation { background: #6b21a8; }\n.page-peek-dot.status-closed { background: #065f46; }\n.page-peek-dot.status-rejected { background: #991b1b; }\n.page-peek-desc {\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n.page-peek-footer {\n display: flex;\n justify-content: space-between;\n margin-top: var(--mfb-space-2);\n padding-top: var(--mfb-space-2);\n border-top: 1px solid var(--mfb-border);\n}\n.page-peek-viewall,\n.page-peek-send {\n border: none;\n background: none;\n padding: 0;\n cursor: pointer;\n color: var(--mfb-accent);\n font-weight: 600;\n font-size: var(--mfb-text-sm);\n}\n.page-peek-skeleton div {\n height: 14px;\n border-radius: 4px;\n background: var(--mfb-surface-2);\n margin: var(--mfb-space-2) 0;\n}\n`\n","import { createApiClient } from './api/client'\nimport { installCapture } from './capture'\nimport { resolveStrings } from './widget/i18n'\nimport { mountWidget } from './widget/mount'\nimport { currentPagePath } from './widget/currentPage'\nimport type { FeedbackApi, FeedbackConfig, ReportPayload, ReportTransformer, UserIdentity } from './types'\n\nexport interface InternalConfig extends FeedbackConfig {\n fetchImpl?: typeof fetch\n}\n\ninterface WindowWithGlobal extends Window {\n mhosaicFeedback?: FeedbackApi\n}\n\n/** Default offset for the secondary QA FAB: center the 24px QA dot above the\n * 48px feedback FAB (bottom:24 right:24). → { right: 36, bottom: 84 }. */\nfunction defaultQaPosition(): { right: number; bottom: number } {\n return { right: 24 + (48 - 24) / 2, bottom: 24 + 48 + 12 }\n}\n\nexport function createFeedback(config: InternalConfig): FeedbackApi & { _registerTransformer(fn: ReportTransformer): void } {\n const env = config.env ?? 'prod'\n const locale =\n config.locale ?? (typeof navigator !== 'undefined' ? navigator.language : undefined)\n const strings = resolveStrings(\n config.translations ?? {},\n locale !== undefined ? { locale } : {},\n )\n const capture = installCapture({\n ...(config.sanitizeUrl !== undefined && { sanitizeUrl: config.sanitizeUrl }),\n })\n let user: UserIdentity | undefined = config.user\n let metadata: Record<string, unknown> = config.metadata ?? {}\n\n const api = createApiClient({\n apiKey: config.apiKey,\n endpoint: config.endpoint,\n ...(config.fetchImpl !== undefined && { fetch: config.fetchImpl }),\n ...(config.beforeSend !== undefined && { beforeSend: config.beforeSend }),\n // v0.13: the API client reads this on every request so a late\n // `identify({userHash, exp, ...})` lights up signed headers\n // immediately on subsequent calls. Returns null when the host\n // hasn't supplied a signature (legacy unsigned path).\n getSignedIdentity: () => {\n if (!user || !user.userHash || !user.exp || !user.email) return null\n return {\n userHash: user.userHash,\n exp: user.exp,\n email: user.email,\n }\n },\n })\n const transformers: ReportTransformer[] = []\n\n const host = document.createElement('div')\n host.className = 'mhosaic-feedback'\n if (config.attachTo) {\n const attach = typeof config.attachTo === 'string' ? document.querySelector(config.attachTo) : config.attachTo\n attach?.appendChild(host)\n } else {\n document.body.appendChild(host)\n }\n\n async function buildAndSubmit(values: {\n description: string\n feedback_type?: string\n severity?: string\n synthetic?: boolean\n /** Screenshot(s) the user explicitly attached — via dropzone, paste,\n * file picker, or the annotator. `screenshots` (attach order) is what\n * the form emits; the single `screenshot` stays accepted for the\n * public fb.submit() API. v0.12 dropped html2canvas-on-submit;\n * v0.7.1 dropped getDisplayMedia. If the user didn't attach anything,\n * the report ships without a screenshot. */\n screenshot?: Blob\n screenshots?: Blob[]\n /** Capture-method label for the payload. Always \"manual\" now (v0.7.1);\n * the union type stays open against the backend schema, which still\n * recognizes legacy values on existing reports. */\n capture_method?: 'manual'\n }) {\n // Auto-error reports never carry a screenshot — the captured ring\n // buffer (errors, console, network) carries the signal, and the DOM\n // is typically in an inconsistent state when a JS error fires anyway.\n const attached = values.screenshots?.length\n ? values.screenshots\n : values.screenshot\n ? [values.screenshot]\n : undefined\n const manualScreenshots = values.synthetic ? undefined : attached\n const technical_context = capture.snapshot()\n // Surface identify()/setMetadata() values on the report. Without this\n // the host-supplied user identity and metadata sit in closure forever\n // and never reach the operator — a stack trace from a logged-in user\n // looks anonymous on the dashboard.\n //\n // SECURITY: identify() may carry `userHash` + `exp` (the host's HMAC\n // envelope used to attest the user's identity to the backend). That\n // envelope is a bearer-style auth token until it expires; leaking it\n // into technical_context surfaces it on the operator dashboard and to\n // anyone who can read the report. Strip it before embedding.\n if (user) {\n const { userHash: _hash, exp: _exp, ...safeUser } = user\n technical_context.user = safeUser\n }\n if (metadata && Object.keys(metadata).length > 0) {\n technical_context.metadata = { ...metadata }\n }\n const captureMethod: ReportPayload['capture_method'] = manualScreenshots?.length\n ? (values.capture_method ?? 'manual')\n : 'none'\n const payload: ReportPayload = {\n description: values.description,\n feedback_type: (values.feedback_type ?? 'bug') as ReportPayload['feedback_type'],\n severity: (values.severity ?? 'medium') as ReportPayload['severity'],\n env,\n page_url: window.location.href,\n user_agent: navigator.userAgent,\n capture_method: captureMethod,\n technical_context,\n }\n // Build-time stamp; tsup's `define` substitutes the package.json\n // version into __MFB_VERSION__. Conditionally assigned so\n // exactOptionalPropertyTypes stays happy under vitest (no tsup\n // pass) where the constant is undefined.\n if (typeof __MFB_VERSION__ !== 'undefined' && __MFB_VERSION__) {\n payload.widget_version = __MFB_VERSION__\n }\n if (manualScreenshots?.length) {\n payload.screenshots = manualScreenshots\n // Mirror the first into the legacy field for beforeSend hooks that\n // still read payload.screenshot.\n payload.screenshot = manualScreenshots[0]!\n }\n if (values.synthetic) payload.synthetic = true\n // current-page feedback: resolve path via host override or pathname.\n const pagePath = currentPagePath(config)\n if (pagePath) payload.page_path = pagePath\n // v0.7: lift the host-provided identity to the top level so the\n // backend can upsert a WidgetUser FK. technical_context.user still\n // carries it for backward-compat with the operator's tech-context\n // viewer (and so an audit-log entry knows who was identified at\n // submission time).\n if (user?.id !== undefined && user.id !== null && user.id !== '') {\n payload.user = {\n // The host can pass `id` as a string or number; the backend\n // stores it as an opaque string. Coerce here to a stable shape.\n id: String(user.id),\n ...(user.email !== undefined && { email: user.email }),\n ...(user.name !== undefined && { name: user.name }),\n }\n }\n let finalPayload: ReportPayload = payload\n for (const t of transformers) finalPayload = await t(finalPayload)\n try {\n const result = await api.submitReport(finalPayload)\n config.onSubmitSuccess?.(result)\n capture.clear()\n return result\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err))\n config.onError?.(error)\n throw error\n }\n }\n\n const handle = mountWidget({\n host,\n strings,\n showFAB: config.showFAB ?? true,\n // Return the created report so the modal can acknowledge it by #seq (#82).\n onSubmit: (values) => buildAndSubmit(values),\n api,\n // Keep this a callback (not a snapshot) so the mount picks up identity\n // changes that happen after createFeedback() — `notifyIdentityChanged()`\n // is the trigger for a re-render.\n getExternalId: () => (user?.id !== undefined && user.id !== null && user.id !== '' ? String(user.id) : undefined),\n // Phase 4: the manifest tells the loader whether this project gates the\n // widget per end-user; the loader forwards it as `requiresVisibilityCheck`.\n requiresVisibilityCheck: config.requiresVisibilityCheck ?? false,\n // Send the identified email alongside the id so an email-based allowlist\n // can match (the backend matches external_id OR email). Read from `user`\n // live so identity set after createFeedback() is reflected.\n checkVisibility: (externalId) => api.checkVisibility(externalId, user?.email),\n // Thread the host's getCurrentPage override so the Board chip resolves\n // the page identically to what the submit side records (§ currentPage).\n ...(config.getCurrentPage !== undefined && { getCurrentPage: config.getCurrentPage }),\n // Opt-in: FAB opens to the page-scoped Board (default off → Send-first).\n ...(config.openToCurrentPageFeedback !== undefined && {\n openToCurrentPageFeedback: config.openToCurrentPageFeedback,\n }),\n ...(config.showPageActivity !== undefined && {\n showPageActivity: config.showPageActivity,\n }),\n })\n\n // Opt-in QA Meter second FAB. Lazily imported so the QA bytes stay out of\n // the main bundle for consumers who don't enable it. The host serves its\n // own artifact via qaMeter.source.\n let qaHandle: { dispose(): void } | undefined\n let qaDisposed = false\n if (config.qaMeter?.source) {\n const qa = config.qaMeter\n const qaLocale: 'fr' | 'en' =\n qa.locale ?? (String(locale ?? '').toLowerCase().startsWith('fr') ? 'fr' : 'en')\n void import('./qa-meter').then(({ createQaMeter }) => {\n if (qaDisposed) return // shutdown() ran before the import resolved\n qaHandle = createQaMeter({\n source: qa.source,\n size: qa.size ?? 'sm',\n position: qa.position ?? defaultQaPosition(),\n locale: qaLocale,\n ...(qa.getCurrentPage && { getCurrentPage: qa.getCurrentPage }),\n })\n })\n }\n\n const instance: FeedbackApi & { _registerTransformer(fn: ReportTransformer): void } = {\n show() { handle.open() },\n hide() { handle.close() },\n open(opts) { handle.open(); void opts },\n async submit(partial) {\n return buildAndSubmit({\n description: partial.description,\n ...(partial.feedback_type !== undefined && { feedback_type: partial.feedback_type }),\n ...(partial.severity !== undefined && { severity: partial.severity }),\n ...(partial.synthetic !== undefined && { synthetic: partial.synthetic }),\n ...(partial.screenshot !== undefined && { screenshot: partial.screenshot }),\n ...(partial.screenshots !== undefined && { screenshots: partial.screenshots }),\n })\n },\n identify(u) {\n user = u\n // Tell the mount to re-evaluate FAB visibility / Mine tab gating.\n handle.notifyIdentityChanged()\n },\n setMetadata(kv) { metadata = { ...metadata, ...kv } },\n shutdown() {\n qaDisposed = true\n qaHandle?.dispose()\n handle.dispose()\n capture.dispose()\n host.remove()\n // Only release the global if it still points at *us* — otherwise a\n // newer instance has taken ownership and we mustn't blow it away.\n const w = window as unknown as WindowWithGlobal\n if (w.mhosaicFeedback === instance) {\n delete w.mhosaicFeedback\n }\n },\n _registerTransformer(fn: ReportTransformer) { transformers.push(fn) },\n }\n\n // Expose the instance globally for ad-hoc callers (DevTools, docs pages,\n // help-widget integrations). The most-recently-created instance wins.\n ;(window as unknown as WindowWithGlobal).mhosaicFeedback = instance\n\n return instance\n}\n","/**\n * Always-on debugger module. Hooks `window.error` and `unhandledrejection`,\n * then ships each fresh runtime error as a synthetic FeedbackReport via\n * the existing widget submit pipeline.\n *\n * Same opt-in pattern as `withReplay` / `withWebVitals` — host apps that\n * want curated-feedback-only simply don't import it.\n *\n * Design choices (see PR description):\n * - Per-fingerprint cooldown (default 5 min) absorbs render loops without\n * flooding the operator dashboard or burning the per-key write throttle.\n * - Server-side fingerprinting groups across sessions; the client-side\n * fingerprint exists only to dedup within a single page lifetime, so\n * it doesn't need to match the server's hash.\n * - Recursion guard: an error thrown *inside* the submit pipeline must\n * not re-enter the handler.\n * - Description is the error message truncated at 200 chars — the full\n * stack lives in technical_context.errors[].\n * - No screenshot is taken (buildAndSubmit honors `synthetic: true`):\n * html2canvas is slow + the DOM is unreliable mid-error.\n */\n\nimport type { FeedbackApi } from '../types'\n\nexport interface ErrorTrackingOptions {\n /** Opt-out kill switch without removing the import. Default `true`. */\n enabled?: boolean\n /**\n * Drop repeats of the same fingerprint within this window.\n * Default 300_000 ms (5 minutes). Set to 0 to disable dedup\n * (every fired error becomes a report — useful only for tests).\n */\n perFingerprintCooldownMs?: number\n /**\n * Probabilistic head sampling: 1.0 captures everything, 0.5 drops half.\n * Default 1.0. Sampled-out errors don't count toward the cooldown — if\n * you sample at 0.1, you'll still see ~10% of the volume per fingerprint.\n */\n sampleRate?: number\n /** Max characters in the auto-generated description. Default 200. */\n maxDescriptionLen?: number\n /**\n * Global ceiling on synthetic submissions per minute, regardless of\n * fingerprint. Defends against a hostile / buggy page that throws\n * errors with attacker-varying `message` strings (counter, nonce) —\n * each unique message creates a fresh fingerprint, bypassing the\n * per-fingerprint cooldown, and would otherwise burn the project's\n * backend write quota.\n * Default 30. Set to 0 to disable the global cap (not recommended).\n */\n globalRatePerMinute?: number\n /**\n * Max number of distinct fingerprints emitted per page lifetime. Once\n * exceeded, NEW fingerprints are dropped (the cooldown still applies\n * to already-seen ones, so legitimate repeating errors keep cycling).\n * Defense against a page that synthesizes an unbounded number of\n * unique error shapes.\n * Default 200. Set to 0 to disable.\n */\n maxDistinctFingerprints?: number\n}\n\ninterface InternalApi extends FeedbackApi {\n _registerTransformer?: (fn: unknown) => void\n /** Set once withErrorTracking has armed this instance (idempotency guard). */\n _errorTrackingArmed?: boolean\n}\n\ninterface NormalizedError {\n name: string\n message: string\n stack?: string\n}\n\nconst DEFAULTS = {\n enabled: true,\n perFingerprintCooldownMs: 5 * 60 * 1000,\n sampleRate: 1,\n maxDescriptionLen: 200,\n globalRatePerMinute: 30,\n maxDistinctFingerprints: 200,\n} as const satisfies Required<ErrorTrackingOptions>\n\n/**\n * Pull a normalized {name, message, stack} from whatever the runtime\n * handed us. Browsers throw genuine Error subclasses for uncaught\n * exceptions but `unhandledrejection.reason` is sometimes a string,\n * a plain object, or undefined.\n */\nfunction normalize(thrown: unknown): NormalizedError {\n if (thrown instanceof Error) {\n return {\n name: thrown.name || 'Error',\n message: String(thrown.message ?? ''),\n ...(thrown.stack && { stack: String(thrown.stack) }),\n }\n }\n if (typeof thrown === 'string') return { name: 'Error', message: thrown }\n if (thrown && typeof thrown === 'object') {\n const o = thrown as Record<string, unknown>\n return {\n name: (typeof o.name === 'string' && o.name) || 'Error',\n message:\n (typeof o.message === 'string' && o.message) ||\n (() => {\n try { return JSON.stringify(thrown) } catch { return String(thrown) }\n })(),\n ...(typeof o.stack === 'string' && { stack: o.stack }),\n }\n }\n return { name: 'Error', message: String(thrown) }\n}\n\n/**\n * Stable per-session signature. Server still computes its own fingerprint\n * across sessions — this only needs to dedup within one page lifetime.\n * Stack frames are the strongest discriminator; pathname keeps two distinct\n * routes from collapsing onto the same key.\n *\n * SECURITY: we include only a 30-char message prefix, not the full message.\n * A hostile or buggy page that throws errors with counter-suffixed messages\n * (e.g. `Error 1`, `Error 2`, …) would otherwise produce a fresh fingerprint\n * per throw and bypass the per-fingerprint cooldown. The stack frames are\n * the same across those counter variants, so collapsing the message into a\n * prefix preserves real-error distinction while killing the counter bypass.\n */\nfunction clientFingerprint(err: NormalizedError, pathname: string): string {\n const firstFrames = (err.stack ?? '').split('\\n').slice(0, 4).join('\\n')\n const messagePrefix = err.message.slice(0, 30)\n return `${err.name}:${messagePrefix}|${pathname}|${firstFrames}`\n}\n\nfunction truncate(s: string, max: number): string {\n if (s.length <= max) return s\n return s.slice(0, Math.max(0, max - 1)) + '…'\n}\n\nexport function withErrorTracking(\n fb: FeedbackApi,\n options: ErrorTrackingOptions = {},\n): FeedbackApi {\n const opts = { ...DEFAULTS, ...options }\n if (!opts.enabled) return fb\n if (typeof window === 'undefined') return fb\n\n const internal = fb as InternalApi\n\n // Idempotency guard. The same instance can now be armed from two places:\n // the host-side <FeedbackProvider> (opt-out default) AND the runtime\n // widget bundle (which self-arms on the loader path). A client who both\n // upgrades the provider and receives the self-arming bundle would\n // otherwise double-register the window listeners and file every error\n // twice. First arm wins; later calls are no-ops.\n if (internal._errorTrackingArmed) return fb\n internal._errorTrackingArmed = true\n\n // Recursion guard, scoped per-fingerprint: while we're posting a synthetic\n // report for fingerprint X, a re-entry for the SAME X (e.g. an error\n // raised by the submit pipeline itself) is dropped. Distinct errors that\n // fire concurrently still both go through.\n const inFlight = new Set<string>()\n\n // fingerprint -> last-sent epoch ms. Periodically pruned in shouldSend().\n const lastSent = new Map<string, number>()\n // Sliding-window of recent send timestamps for the global rate cap.\n // Trimmed in shouldSend(); never grows beyond globalRatePerMinute entries.\n const recentSendTimes: number[] = []\n\n function shouldSend(fp: string, now: number): boolean {\n // (1) Global rate ceiling — runs first because it doesn't depend on\n // fingerprint state. Defends against the \"counter-suffixed error\n // messages\" attack that would otherwise produce one unique\n // fingerprint per throw.\n if (opts.globalRatePerMinute > 0) {\n const windowStart = now - 60_000\n // Drop expired entries from the front of the sliding window.\n while (recentSendTimes.length > 0 && recentSendTimes[0]! < windowStart) {\n recentSendTimes.shift()\n }\n if (recentSendTimes.length >= opts.globalRatePerMinute) {\n return false\n }\n }\n // (2) Per-page cap on the number of distinct fingerprints. A hostile\n // page that synthesizes an unbounded number of unique error\n // shapes runs out of new slots after this many — already-seen\n // fingerprints keep cycling under the per-fp cooldown.\n if (\n opts.maxDistinctFingerprints > 0 &&\n !lastSent.has(fp) &&\n lastSent.size >= opts.maxDistinctFingerprints\n ) {\n return false\n }\n if (opts.perFingerprintCooldownMs <= 0) {\n recentSendTimes.push(now)\n return true\n }\n const prev = lastSent.get(fp)\n if (prev !== undefined && now - prev < opts.perFingerprintCooldownMs) {\n return false\n }\n // Cheap stale-entry sweep so the Map doesn't grow unboundedly on a\n // long-lived session with many distinct errors. First pass: drop\n // entries past `cooldown * 2`. If we're still above a hard cap of\n // 256 — i.e. an SPA that's been live for hours and keeps emitting\n // *fresh* fingerprints faster than they go stale — drop the oldest\n // entries until we're back to a comfortable ceiling. Map iteration\n // order is insertion order so the first N keys are the oldest.\n const HARD_CAP = 256\n const TARGET_AFTER_TRIM = 192\n if (lastSent.size > HARD_CAP) {\n const cutoff = now - opts.perFingerprintCooldownMs * 2\n for (const [k, v] of lastSent) {\n if (v < cutoff) lastSent.delete(k)\n }\n if (lastSent.size > HARD_CAP) {\n const toDrop = lastSent.size - TARGET_AFTER_TRIM\n let dropped = 0\n for (const k of lastSent.keys()) {\n if (dropped >= toDrop) break\n lastSent.delete(k)\n dropped++\n }\n }\n }\n recentSendTimes.push(now)\n return true\n }\n\n async function report(err: NormalizedError) {\n if (opts.sampleRate < 1 && Math.random() >= opts.sampleRate) return\n\n const fp = clientFingerprint(err, window.location.pathname)\n if (inFlight.has(fp)) return\n const now = Date.now()\n if (!shouldSend(fp, now)) return\n lastSent.set(fp, now)\n\n const description = truncate(\n err.message ? `${err.name}: ${err.message}` : err.name,\n opts.maxDescriptionLen,\n )\n\n inFlight.add(fp)\n try {\n await internal.submit({\n description,\n feedback_type: 'bug',\n severity: 'high',\n synthetic: true,\n } as Parameters<FeedbackApi['submit']>[0] & { synthetic: boolean })\n } catch {\n // Swallow submit errors — propagating them would just trigger another\n // unhandledrejection and feed the loop. The host's `onError` callback\n // (configured on createFeedback) still fires for visibility.\n } finally {\n inFlight.delete(fp)\n }\n }\n\n function onError(event: ErrorEvent) {\n const candidate: unknown = event.error ?? {\n name: 'Error',\n message: event.message,\n stack: `at ${event.filename}:${event.lineno}:${event.colno}`,\n }\n void report(normalize(candidate))\n }\n\n function onUnhandledRejection(event: PromiseRejectionEvent) {\n void report(normalize(event.reason))\n }\n\n window.addEventListener('error', onError)\n window.addEventListener('unhandledrejection', onUnhandledRejection)\n\n // Wrap shutdown so listeners come down with the widget. Without this, a\n // host that re-mounts the widget under React StrictMode would double the\n // listeners and emit each error twice.\n const originalShutdown = internal.shutdown.bind(internal)\n internal.shutdown = () => {\n window.removeEventListener('error', onError)\n window.removeEventListener('unhandledrejection', onUnhandledRejection)\n lastSent.clear()\n originalShutdown()\n }\n\n return fb\n}\n"],"mappings":"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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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AAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EzBuDtB,SAASC,GACdC,EACAC,EACAC,EACS,CAGT,MAFI,GAACF,EAAK,SAC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","names":["assign","obj","props","i","removeNode","node","parentNode","removeChild","createElement","type","children","key","ref","normalizedProps","arguments","length","slice","call","defaultProps","createVNode","original","vnode","__k","__","__b","__e","__c","constructor","__v","vnodeId","__i","__u","options","Fragment","BaseComponent","context","this","getDomSibling","childIndex","sibling","renderComponent","component","__P","__d","oldVNode","oldDom","commitQueue","refQueue","newVNode","diff","__n","namespaceURI","commitRoot","updateParentDomPointers","base","some","child","enqueueRender","c","rerenderQueue","push","process","__r","prevDebounce","debounceRendering","defer","l","sort","depthSort","shift","diffChildren","parentDom","renderResult","newParentVNode","oldParentVNode","globalContext","namespace","excessDomChildren","isHydrating","childVNode","newDom","firstChildDom","result","shouldPlace","oldChildren","EMPTY_ARR","newChildrenLength","constructNewChildrenArray","EMPTY_OBJ","applyRef","insert","nextSibling","skewedIndex","matchingIndex","oldChildrenLength","remainingOldChildren","skew","Array","String","isArray","findMatchingIndex","unmount","parentVNode","insertBefore","nodeType","x","y","matched","setStyle","style","value","setProperty","IS_NON_DIMENSIONAL","test","dom","name","oldValue","useCapture","lowerCaseName","o","cssText","replace","CAPTURE_REGEX","toLowerCase","EVENT_ATTACHED","eventClock","addEventListener","eventProxyCapture","eventProxy","removeEventListener","e","removeAttribute","setAttribute","createEventProxy","eventHandler","EVENT_DISPATCHED","event","tmp","isNew","oldProps","oldState","snapshot","clearProcessingException","newProps","isClassComponent","provider","componentContext","renderHook","count","newType","outer","prototype","render","contextType","__E","doRender","sub","state","__h","_sb","__s","getDerivedStateFromProps","componentWillMount","componentDidMount","componentWillReceiveProps","shouldComponentUpdate","apply","componentWillUpdate","componentDidUpdate","getChildContext","getSnapshotBeforeUpdate","cloneNode","then","MODE_HYDRATE","indexOf","markAsForce","diffElementNodes","diffed","root","cb","map","newHtml","oldHtml","newChildren","inputValue","checked","localName","document","createTextNode","createElementNS","is","__m","data","childNodes","attributes","__html","innerHTML","content","undefined","hasRefUnmount","current","skipRemove","r","componentWillUnmount","replaceNode","documentElement","firstChild","isValidElement","_id","init_preact","__esmMin","error","errorInfo","ctor","handled","getDerivedStateFromError","setState","componentDidCatch","update","callback","s","forceUpdate","Promise","bind","resolve","setTimeout","a","b","Math","random","toString","getHookState","index","type","options","__h","currentComponent","currentHook","hooks","__H","__","length","push","useState","initialState","useReducer","invokeOrReturn","reducer","init","hookState","currentIndex","_reducer","__c","action","currentValue","__N","nextValue","setState","__f","updateHookState","p","s","c","stateHooks","filter","x","every","prevScu","call","this","shouldUpdate","props","some","hookItem","shouldComponentUpdate","prevCWU","componentWillUpdate","__e","tmp","useEffect","callback","args","state","__s","argsChanged","_pendingArgs","useLayoutEffect","useRef","initialValue","useMemo","current","factory","useCallback","flushAfterPaintEffects","component","afterPaintEffects","shift","__P","invokeCleanup","invokeEffect","e","__v","afterNextFrame","raf","done","clearTimeout","timeout","HAS_RAF","cancelAnimationFrame","setTimeout","requestAnimationFrame","hook","comp","cleanup","oldArgs","newArgs","arg","f","previousComponent","prevRaf","oldBeforeDiff","oldBeforeRender","oldAfterDiff","oldCommit","oldBeforeUnmount","oldRoot","_options","__b","__r","diffed","unmount","vnode","parentDom","__k","__m","commitQueue","cb","hasErrored","resolveStrings","locale","QA_STRINGS_EN","QA_STRINGS_FR","init_i18n","__esmMin","normalizePattern","pattern","n","matchesPattern","pathSegs","patternSegs","required","last","seg","i","pat","resolvePage","pathname","patterns","segments","best","bestScore","patSegs","score","s","init_resolver","__esmMin","p","QA_METER_STYLES","init_styles","__esmMin","botTriColor","lane","humanTriColor","aggregateTri","colors","c","formatQaDate","iso","date","shortVersion","version","QA_COLOR_MODIFIER","QA_COLOR_LABEL_KEY","QA_PRIORITY_MODIFIER","QA_CASE_STATUS_LABEL_KEY","QA_GROUNDING_LABEL_KEY","init_ui","__esmMin","Pastille","color","state","strings","onOpenModal","onHover","base","detail","QA_COLOR_LABEL_KEY","label","k","QA_COLOR_MODIFIER","init_Pastille","__esmMin","init_preact","init_ui","LaneIcons","dev","bot","human","strings","k","TRI_MODIFIER","init_LaneIcons","__esmMin","init_preact","laneTriColor","scenario","humanTriColor","botTriColor","groupScenarios","scenarios","_a","_b","_c","map","key","bucket","a","b","interpolate","template","params","_","k","ScenarioRow","strings","lanes","provenance","QA_PRIORITY_MODIFIER","QA_CASE_STATUS_LABEL_KEY","QA_GROUNDING_LABEL_KEY","LaneIcons","TestPanel","page","state","meta","expanded","setExpanded","d","toggleGroup","prev","next","freshness","formatQaDate","shortVersion","body","hasGroups","s","groups","group","isOpen","agg","aggregateTri","init_TestPanel","__esmMin","init_preact","init_hooks","init_LaneIcons","init_ui","buildTree","nodes","_a","childrenByParent","node","bucket","a","b","out","visit","parentId","depth","_b","children","dedupeScenarios","artifact","_c","byKey","page","scenario","key","entry","ref","rank","p","_d","ga","gb","interpolate","template","params","_","k","formatPct","pct","SitemapModal","currentPattern","strings","onClose","activeTab","setActiveTab","d","search","setSearch","y","onKeydown","event","here","normalizePattern","tree","T","cases","titleByPattern","map","patternTitle","pattern","query","filteredCases","c","freshness","formatQaDate","shortVersion","e","row","isHere","QA_COLOR_MODIFIER","lanes","humanTriColor","botTriColor","traversed","LaneIcons","init_SitemapModal","__esmMin","init_preact","init_hooks","init_LaneIcons","init_resolver","init_ui","patchHistory","historyRefCount","historyPatched","originalPushState","originalReplaceState","fire","LOCATION_CHANGE_EVENT","args","ret","unpatchHistory","isValidArtifact","value","QA_ARTIFACT_FORMAT","warnBadFormatOnce","warnedBadFormat","patternsFromArtifact","artifact","set","page","node","resolveCurrent","pattern","UNKNOWN","p","createQaMeter","options","_a","_b","_c","_d","source","strings","resolveStrings","inlineArtifact","NOOP_HANDLE","loadStarted","loadPromise","load","r","forceRender","host","shadow","style","QA_METER_STYLES","mountPoint","getCurrentPattern","resolvePage","Container","tick","setTick","d","bump","q","n","resolved","meta","onHover","onOpenModal","uiOpen","closeModal","y","onNav","k","cancelHoverClose","scheduleHoverClose","uiHover","TestPanel","Pastille","SitemapModal","hoverCloseTimer","HOVER_CLOSE_MS","disposed","mountTree","R","init_mount","__esmMin","init_preact","init_hooks","init_i18n","init_resolver","init_styles","init_Pastille","init_TestPanel","init_SitemapModal","qa_meter_exports","__export","createQaMeter","options","init_qa_meter","__esmMin","init_mount","widget_exports","__export","createFeedback","SCALAR_FIELDS","assertSafeEndpoint","endpoint","parsed","e","RateLimitError","retryAfterSeconds","__publicField","parseRetryAfter","response","raw","n","ensureReadable","label","text","readJsonArray","data","readJsonObject","createApiClient","options","_a","_b","fetcher","submitReport","input","_c","_d","payload","form","field","blob","i","headers","signed","widgetHeaders","externalId","listMine","listChangelog","getReport","reportId","opts","getReportBySeq","seq","addComment","body","clientNonce","attachments","init","editDescription","description","closeAsResolved","reopenUnresolved","boardQueryString","filters","params","s","t","qs","listBoard","page","fetchBoardKpis","checkVisibility","email","SENSITIVE_NAME","SENSITIVE_VALUE_PATTERNS","looksLikeCredential","value","re","sanitizeUrl","url","isAbsolute","isRootRelative","base","u","clean","name","e","SENSITIVE_TOKEN_PATTERNS","scrubCredentials","text","out","re","collectDevice","_a","nav","connection","deviceMemory","rawReferrer","referrer","sanitizeUrl","sanitizedHere","pathname","parsed","e","scrubCredentials","safeStringify","arg","_k","v","e","installConsolePatch","buffer","levels","originals","level","original","args","rawMessage","message","scrubCredentials","entry","stack","fn","installFetchPatch","buffer","sanitize","original","input","init","start","url","method","response","err","rawErr","scrubCredentials","installXhrPatch","Original","originalOpen","originalSend","body","ctx","e","installErrorHandlers","buffer","onError","e","rawStack","stack","scrubCredentials","onRejection","reason","rawMessage","createPerformanceCollector","slowResourceMs","longTasks","slowResources","observer","list","entry","e","sanitizeUrl","nav","navigation","RingBuffer","max","__publicField","item","installCapture","options","_a","maxConsole","maxNetwork","maxErrors","sanitize","sanitizeUrl","consoleBuf","RingBuffer","networkBuf","errorBuf","uninstallConsole","installConsolePatch","uninstallFetch","installFetchPatch","uninstallXhr","installXhrPatch","uninstallErrors","installErrorHandlers","perf","createPerformanceCollector","collectDevice","DEFAULT_STRINGS","FRENCH_STRINGS","LOCALE_PACKS","packFor","locale","_a","tag","resolveStrings","overrides","options","localePack","init_preact","init_hooks","currentPagePath","opts","_a","override","LOCATION_CHANGE","patched","patchHistory","m","orig","args","r","onLocationChange","cb","init_hooks","cache","boardCacheKey","externalId","fetchKey","readBoardCache","key","hit","writeBoardCache","page","kpis","oldest","PREFIX","loadBoardView","externalId","raw","parsed","e","saveBoardView","view","startPoll","tick","intervalMs","stopped","parked","failures","timer","doc","delayMs","run","ok","e","onVisibilityChange","rateLimitMessage","err","strings","RateLimitError","init_hooks","vnodeId","createVNode","type","props","key","isStaticChildren","__source","__self","ref","i","normalizedProps","vnode","__k","__","__b","__e","__c","constructor","__v","vnodeId","__i","__u","defaultProps","options","SEQ_RE","linkifySeq","text","onOpenSeq","value","parts","last","m","seq","u","CommentBubble","comment","strings","onOpenSeq","stableUrl","_a","isMine","isAgent","isSystem","variant","labelKey","label","images","a","u","linkifySeq","src","formatTime","iso","e","ALLOWED_IMAGE_TYPES","validateScreenshotFile","file","ALLOWED_IMAGE_TYPES","truncateUrl","url","maxLength","keepStart","keepEnd","safeExternalHref","parsed","e","POLL_MS","ReportDetailView","api","externalId","reportId","strings","onBack","canModerate","variant","onOpenSeq","buildPermalink","seed","_a","_b","_c","_d","_e","_f","detail","setDetail","d","error","setError","copied","setCopied","editing","setEditing","editBody","setEditBody","savingEdit","setSavingEdit","editError","setEditError","composeBody","setComposeBody","sending","setSending","closing","setClosing","reopening","setReopening","composeShots","setComposeShots","attachError","setAttachError","fileInputRef","A","mountedRef","urlPins","bumpPinTick","PIN_MAX_AGE_MS","pinUrl","key","url","pin","unpinUrl","t","shotsRef","y","s","acceptComposeFiles","files","remaining","batch","file","err","validateScreenshotFile","incoming","blob","prev","handleAttachChange","e","input","removeComposeShot","index","shot","_","i","fetchDetail","next","rl","rateLimitMessage","poll","startPoll","handleSend","nonce","attachments","created","appendComment","handleCopyLink","startEdit","cancelEdit","handleSaveEdit","updated","handleClose","handleReopen","seedPill","u","linkifySeq","isMine","canAct","showCloseCta","showComposeBox","ContextBlock","pinKey","safeHref","safeExternalHref","c","CommentBubble","StatusHistorySection","TechnicalContextDrawer","current","captureKey","captureLabel","formatSubmittedAt","truncateUrl","iso","TECH_CONSOLE_LIMIT","TECH_NETWORK_LIMIT","ctx","errors","consoleLogs","network","device","errorCount","summary","DeviceSection","ErrorsSection","ConsoleSection","NetworkSection","parts","dpr","p","S","truncateStack","logs","l","rows","n","failed","stack","lines","from","to","sourceKey","POLL_MS","SEARCH_DEBOUNCE_MS","SORT_OPTIONS","STATUSES","TYPES","SEVERITIES","BoardView","api","externalId","strings","getCurrentPage","initialSelectedId","_a","_b","filters","setFilters","d","loadBoardView","thisPage","setThisPage","y","saveBoardView","filtersHash","page","setPage","kpis","setKpis","loading","setLoading","stale","setStale","error","setError","selectedId","setSelectedId","detailKey","setDetailKey","reloadTick","setReloadTick","extra","setExtra","loadingMore","setLoadingMore","qDraft","setQDraft","debouncedQ","useDebouncedValue","f","_drop","rest","activeFilterCount","T","_c","_d","_e","_f","onLocationChange","t","fetchFilters","searching","currentPagePath","cancelled","controller","cacheKey","boardCacheKey","cached","readBoardCache","prev","applyLocalFilters","poll","startPoll","list","k","writeBoardCache","e","rateLimitMessage","visibleRows","seen","r","hasMore","loadMore","pageNum","res","selectedRow","onPickRow","row","u","BoardHeader","BoardFilters","v","BoardListSkeleton","BoardEmpty","S","BoardList","ReportDetailView","seq","DetailEmptyIllustration","scopeLabel","BoardKpiStrip","cells","onChange","searchDraft","onSearchDraftChange","activeCount","onToggleThisPage","_g","setStatus","value","setType","setSeverity","setOrdering","toggleMine","clear","o","s","CloseIcon","rows","onPick","total","BoardRowCard","selected","author","CommentIcon","formatRelative","onAllPages","EmptyIllustration","_","i","ms","debounced","setDebounced","SEVERITY_RANK","out","sorted","a","b","iso","then","delta","m","h","init_hooks","statusClassName","status","severityClassName","severity","typeClassName","formatRelative","iso","then","seconds","minutes","hours","repliesLabel","count","strings","ReportRow","row","onClick","extraMeta","_a","preview","u","POLL_MS","isoWeekKey","iso","d","day","groupRowsByWeek","rows","buckets","row","key","existing","a","b","weekKey","weekRows","formatWeekLabel","e","ChangelogList","api","externalId","strings","onSelect","setRows","error","setError","refreshing","setRefreshing","mountedRef","A","fetchRows","next","err","y","poll","startPoll","groups","T","isLoading","isEmpty","u","g","ReportRow","BugIcon","u","Fab","label","onClick","count","init_hooks","init_hooks","COLORS","HIGHLIGHT_ALPHA","drawShape","ctx","shape","sourceImage","drawArrow","i","metrics","padding","w","hh","normalizeStart","drawBlur","start","size","x1","y1","x2","y2","angle","x","y","h","tile","yy","xx","tw","th","Icon","u","Annotator","imageBlob","strings","onSave","onCancel","canvasRef","A","containerRef","imageRef","tool","setTool","d","color","setColor","shapes","setShapes","past","setPast","future","setFuture","isDrawingRef","draftShape","setDraftShape","imageLoaded","setImageLoaded","saving","setSaving","scaleRef","addShape","s","p","clearShapes","undo","prev","f","redo","next","_","onKey","e","k","url","img","maxW","maxH","fitScale","canvas","redraw","getCanvasCoords","rect","handleMouseDown","canvasW","lineWidth","blurTile","text","handleMouseMove","current","handleMouseUp","handleSave","blob","resolve","tools","t","c","TYPES","SEVERITIES","Form","strings","onSubmit","onCancel","status","errorMessage","submittedSeq","onDirtyChange","description","setDescription","d","feedbackType","setFeedbackType","severity","setSeverity","localError","setLocalError","shots","setShots","isDragOver","setIsDragOver","annotatingIndex","setAnnotatingIndex","fileInputRef","A","dropZoneRef","submitting","submitLabel","pageUrl","isDirty","y","shotsRef","s","acceptFiles","files","remaining","batch","file","err","validateScreenshotFile","incoming","blob","prev","removeShot","index","shot","_","i","handleFileInputChange","e","_a","input","handleDragOver","handleDragLeave","handleDrop","_b","zone","onPaste","items","item","handleAnnotated","annotated","replacement","u","values","t","truncateUrl","Annotator","init_hooks","computeKpiCounts","rows","counts","row","computeResolutionRate","FILTER_FOR_STATUS","rowsMatchingFilter","filter","KpiStrip","onFilter","strings","resolutionRate","cells","u","c","active","toggleTo","POLL_MS","MineList","api","externalId","strings","onSelect","onOpenSeq","_a","rows","setRows","d","error","setError","refreshing","setRefreshing","filter","setFilter","jumpValue","setJumpValue","jumpError","setJumpError","jumping","setJumping","mountedRef","A","handleJump","e","seq","found","fetchRows","next","err","rateLimitMessage","y","poll","startPoll","isEmpty","isLoading","visibleRows","rowsMatchingFilter","visibleEmpty","u","KpiStrip","row","ReportRow","init_hooks","Modal","onDismiss","children","closeLabel","expanded","modalRef","A","previouslyFocused","y","_a","onKey","e","root","first","prev","u","PageActivityStrip","open","strings","onView","text","u","init_hooks","PagePeekPanel","api","externalId","strings","getCurrentPage","open","onViewAll","onPickRow","onSend","rows","setRows","d","failed","setFailed","y","cancelled","pagePath","currentPagePath","page","u","row","init_hooks","TTL_MS","computeOpenCount","kpis","_a","_b","_c","s","cache","inflight","invalidatePageSignal","fetchPageKpis","api","externalId","path","key","hit","cache","TTL_MS","pending","inflight","p","kpis","fetchOpen","computeOpenCount","usePageOpenCount","opts","getCurrentPage","enabled","open","setOpen","d","tick","setTick","refresh","q","invalidatePageSignal","t","y","cancelled","load","currentPagePath","n","unsub","onLocationChange","WIDGET_STYLES","computeFabVisible","opts","externalId","visibilityAllowed","mountWidget","options","shadow","style","WIDGET_STYLES","mountPoint","currentState","postSubmitTimer","formDirty","rerender","state","R","k","Root","clearSelected","s","_drop","_drop2","rest","openWidget","fetchPageKpis","currentPagePath","kpis","_a","handleSubmit","q","values","result","pageSignal","err","openSeq","seq","r","buildPermalink","id","param","u","e","pageActivityEnabled","usePageOpenCount","fabLabel","setVisibilityAllowed","d","y","cancelled","show","fabVisible","showMineTab","peekOpen","setPeekOpen","peekTimers","A","S","_b","PagePeekPanel","reportId","Fab","Modal","reason","PageActivityStrip","Form","MineList","row","ReportDetailView","ChangelogList","BoardView","openReportRef","ref","api","value","defaultQaPosition","createFeedback","config","_a","_b","_c","_d","_e","_f","_g","_h","env","locale","strings","resolveStrings","capture","installCapture","user","metadata","api","createApiClient","transformers","host","attach","buildAndSubmit","values","attached","manualScreenshots","technical_context","_hash","_exp","safeUser","captureMethod","payload","pagePath","currentPagePath","finalPayload","t","result","err","error","handle","mountWidget","externalId","qaHandle","qaDisposed","qa","qaLocale","createQaMeter","instance","opts","partial","u","kv","fn","DEFAULTS","normalize","thrown","_a","o","e","clientFingerprint","err","pathname","firstFrames","messagePrefix","truncate","s","max","withErrorTracking","fb","options","opts","internal","inFlight","lastSent","recentSendTimes","shouldSend","fp","now","windowStart","prev","HARD_CAP","TARGET_AFTER_TRIM","cutoff","k","v","toDrop","dropped","report","description","onError","event","candidate","onUnhandledRejection","originalShutdown","createFeedback","config","errorTracking","base","fb","withErrorTracking"]}
|