Constant INTERACTION_CLIENT
Source const INTERACTION_CLIENT: &str = "window.pharosErrorMessage = (body, fallback = \'Request failed.\') => {\n try {\n const value = JSON.parse(body);\n const report = value?.error?.details?.schema === \'pharos.validation.report.v1\'\n ? value.error.details : value;\n let validationMessage = \'\';\n if (report?.schema === \'pharos.validation.report.v1\') {\n const entries = [\n ...(Array.isArray(report.field_errors) ? report.field_errors.slice(0, 8) : []),\n ...(Array.isArray(report.form_errors) ? report.form_errors.slice(0, 8) : [])\n ].slice(0, 8);\n const messages = entries.map((entry) => {\n if (!entry || typeof entry !== \'object\') return \'\';\n const code = typeof entry.code === \'string\' ? entry.code : \'\';\n const field = typeof entry.field_id === \'string\' ? entry.field_id : \'\';\n const message = typeof entry.message === \'string\' ? entry.message : \'\';\n return [code, field && message ? `${field}: ${message}` : field || message]\n .filter(Boolean).join(\', \');\n }).filter(Boolean);\n validationMessage = messages.join(\'; \');\n }\n if (value && typeof value.error === \'object\' && value.error) {\n const fields = [\'code\', \'message\', \'correlation_id\']\n .map((key) => key === \'message\' ? validationMessage || value.error[key] : value.error[key])\n .filter((item) => typeof item === \'string\' && item.trim());\n if (fields.length) return `error: ${fields.join(\', \')}`;\n }\n if (validationMessage) return `error: ${validationMessage}`;\n } catch (_) {}\n return body || fallback;\n};\n\nconst busy = (form, state) => {\n form.toggleAttribute(\"aria-busy\", state);\n form.querySelectorAll(\"button, input[type=submit]\").forEach((control) => {\n control.disabled = state;\n });\n};\n\nconst submittedValues = (form, submitter) => {\n const values = new FormData(form);\n if (submitter?.name) values.set(submitter.name, submitter.value);\n return values;\n};\n\n// Keep the native swap target and one source boundary, including updated fragment keys.\nconst editableBoundaryFor = (node) => {\n if (!(node instanceof HTMLElement)) return null;\n const parent = node.parentElement;\n if (!parent?.matches(\'[data-editor-key][data-editor-native]:not([data-editor-chrome])\') || parent.childElementCount !== 1 || parent.firstElementChild !== node) return null;\n if (parent.getAttribute(\'data-editor-native\') !== node.tagName.toLowerCase()) return null;\n if ([...parent.childNodes].some(child => child.nodeType === Node.TEXT_NODE && child.textContent.trim())) return null;\n return parent;\n};\n\nwindow.pharosSyncEditableBoundary = (current, replacement) => {\n const before = editableBoundaryFor(current);\n const after = editableBoundaryFor(replacement);\n if (!before || !after || current.tagName !== replacement.tagName || current.id !== replacement.id || before.getAttribute(\'data-editor-app\') !== after.getAttribute(\'data-editor-app\')) return false;\n for (const attribute of [...before.attributes]) before.removeAttribute(attribute.name);\n for (const attribute of after.attributes) before.setAttribute(attribute.name, attribute.value);\n return true;\n};\n\ndocument.addEventListener(\'htmx:beforeSwap\', (event) => {\n const detail = event.detail;\n const current = detail?.target;\n if (!detail?.shouldSwap || !editableBoundaryFor(current) || typeof detail.serverResponse !== \'string\' || detail.serverResponse.length > 8 * 1024 * 1024) return;\n const source = detail.requestConfig?.elt;\n const swap = source?.closest?.(\'[hx-swap]\')?.getAttribute(\'hx-swap\') || window.htmx?.config?.defaultSwapStyle;\n if (swap?.trim().split(/\\s+/)[0] !== \'outerHTML\') return;\n const parsed = document.createElement(\'template\');\n parsed.innerHTML = detail.serverResponse;\n let candidates;\n if (current.id) candidates = [parsed.content.getElementById(current.id)].filter(Boolean);\n else {\n const boundary = editableBoundaryFor(current);\n candidates = [...parsed.content.querySelectorAll(\'[data-editor-key][data-editor-native]\')]\n .filter(node => node.getAttribute(\'data-editor-key\') === boundary.getAttribute(\'data-editor-key\'))\n .map(node => node.firstElementChild)\n .filter(node => node?.tagName === current.tagName && node.getAttribute(\'data-task-id\') === current.getAttribute(\'data-task-id\') && node.getAttribute(\'data-editor-instance\') === current.getAttribute(\'data-editor-instance\'));\n }\n if (candidates.length !== 1 || !window.pharosSyncEditableBoundary(current, candidates[0])) return;\n const replacement = candidates[0];\n replacement.parentElement.replaceWith(replacement);\n detail.serverResponse = parsed.innerHTML;\n}, true);\n\nconst initializeRegion = (region) => {\n if (!(region instanceof HTMLElement)) return;\n window.htmx?.process(region);\n region.dispatchEvent(new CustomEvent(\"pharos:afterSwap\", {\n bubbles: true,\n detail: {target: region}\n }));\n};\n\nconst replaceTarget = (html, targetId) => {\n const parsed = document.createElement(\"template\");\n parsed.innerHTML = html;\n parsed.content.querySelectorAll(\"[data-pharos-oob][id]\").forEach((replacement) => {\n const current = document.getElementById(replacement.id);\n if (current) {\n window.pharosSyncEditableBoundary(current, replacement);\n current.replaceWith(document.importNode(replacement, true));\n initializeRegion(document.getElementById(replacement.id));\n }\n });\n const replacement = parsed.content.getElementById(targetId);\n const current = document.getElementById(targetId);\n if (!replacement || !current) throw new Error(\"The response did not contain the declared region.\");\n window.pharosSyncEditableBoundary(current, replacement);\n current.replaceWith(document.importNode(replacement, true));\n initializeRegion(document.getElementById(targetId));\n document.getElementById(targetId)?.focus({preventScroll: true});\n const announcer = document.querySelector(\"[data-pharos-announcer]\");\n if (announcer) announcer.textContent = \"Content updated.\";\n};\n\nconst navigate = async (url, targetId, push) => {\n document.body.setAttribute(\"aria-busy\", \"true\");\n try {\n const response = await fetch(url, {\n credentials: \"same-origin\",\n headers: {\"HX-Request\": \"true\", \"HX-Target\": targetId}\n });\n const html = await response.text();\n if (!response.ok) throw new Error(window.pharosErrorMessage(html, `Navigation failed (${response.status})`));\n replaceTarget(html, targetId);\n if (push) history.pushState({pharosTarget: targetId}, \"\", url);\n } finally {\n document.body.removeAttribute(\"aria-busy\");\n }\n};\n\ndocument.addEventListener(\"click\", (event) => {\n const closeButton = event.target.closest(\"button[data-pharos-close-target]\");\n if (closeButton) {\n const target = document.getElementById(closeButton.dataset.pharosCloseTarget);\n if (target) target.setAttribute(\"hidden\", \"\");\n return;\n }\n const link = event.target.closest(\"a[data-pharos-nav]\");\n if (!link || event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;\n const url = new URL(link.href, location.href);\n if (url.origin !== location.origin) return;\n event.preventDefault();\n navigate(url, link.dataset.pharosNav, true).catch(() => { location.assign(url); });\n});\n\ndocument.addEventListener(\"submit\", (event) => {\n const form = event.target.closest(\"form[data-pharos-get]\");\n if (!form || event.defaultPrevented) return;\n event.preventDefault();\n const url = new URL(form.action, location.href);\n const values = submittedValues(form, event.submitter);\n for (const [name, value] of values) {\n if (typeof value === \"string\" && value !== \"\") url.searchParams.append(name, value);\n }\n navigate(url, form.dataset.pharosGet, true).catch(() => { location.assign(url); });\n});\n\nwindow.addEventListener(\"popstate\", (event) => {\n const targetId = event.state?.pharosTarget || \"app-main\";\n navigate(location.href, targetId, false).catch(() => { location.reload(); });\n});\n\ndocument.addEventListener(\"submit\", async (event) => {\n const form = event.target.closest(\"form[data-pharos-action]\");\n if (!form || event.defaultPrevented) return;\n event.preventDefault();\n const targetId = form.dataset.pharosTarget;\n const target = document.getElementById(targetId);\n if (!target) return form.submit();\n const values = submittedValues(form, event.submitter);\n busy(form, true);\n try {\n const multipart = form.enctype === \"multipart/form-data\";\n const response = await fetch(form.dataset.pharosAction, {\n method: form.method.toUpperCase(),\n body: multipart ? values : new URLSearchParams(values),\n credentials: \"same-origin\",\n headers: multipart ? {\"HX-Request\": \"true\", \"HX-Target\": targetId} : {\"Content-Type\": \"application/x-www-form-urlencoded\", \"HX-Request\": \"true\", \"HX-Target\": targetId}\n });\n const hxRedirect = response.headers.get(\"HX-Redirect\");\n if (hxRedirect) {\n location.assign(hxRedirect);\n return;\n }\n const html = await response.text();\n if (!response.ok) throw new Error(window.pharosErrorMessage(html, `Request failed (${response.status})`));\n replaceTarget(html, targetId);\n } catch (error) {\n let status = form.querySelector(\"[data-pharos-error]\");\n if (!status) {\n status = document.createElement(\"p\");\n status.dataset.pharosError = \"\";\n status.setAttribute(\"role\", \"alert\");\n form.append(status);\n }\n status.textContent = error instanceof Error ? error.message : \"The request could not be completed.\";\n } finally {\n busy(form, false);\n }\n});\n";