pub const SEARCH_JS: &str = "/* docgen search: Cmd/Ctrl-K modal over /search-index.json. No deps, no npm. */\n(function () {\n \"use strict\";\n var index = null;\n var loading = null;\n // Deployed sub-path prefix (e.g. \"/docs\"); \"\" when served at the origin root.\n var BASE = (typeof window !== \"undefined\" && window.DOCGEN_BASE) || \"\";\n\n function loadIndex() {\n if (index) return Promise.resolve(index);\n if (loading) return loading;\n loading = fetch(BASE + \"/search-index.json\")\n .then(function (r) { return r.json(); })\n .then(function (data) { index = data; return index; });\n return loading;\n }\n\n // Substring score; lower is better. -1 means no match.\n function score(entry, q) {\n var hay = (entry.title + \" \" + entry.text).toLowerCase();\n var i = hay.indexOf(q);\n if (i === -1) return -1;\n // Prefer title hits and earlier positions.\n var titleHit = entry.title.toLowerCase().indexOf(q) !== -1 ? 0 : 1000;\n return titleHit + i;\n }\n\n function search(q) {\n q = q.trim().toLowerCase();\n if (!q || !index) return [];\n return index\n .map(function (e) { return { e: e, s: score(e, q) }; })\n .filter(function (x) { return x.s >= 0; })\n .sort(function (a, b) { return a.s - b.s; })\n .slice(0, 20)\n .map(function (x) { return x.e; });\n }\n\n var modal, input, list, selected = 0, results = [];\n // Element focused before the modal opened, restored on close (WCAG 2.4.3).\n var opener = null;\n // App-shell siblings hidden from AT / removed from tab order while the modal\n // is open (focus trap + no obscured-content navigation; WCAG 2.1.2/2.4.3).\n var inertEls = [];\n\n function setShellInert(on) {\n inertEls = [];\n var kids = document.body.children;\n for (var i = 0; i < kids.length; i++) {\n var el = kids[i];\n if (el === modal || el.tagName === \"SCRIPT\") continue;\n if (on) {\n el.setAttribute(\"aria-hidden\", \"true\");\n el.setAttribute(\"inert\", \"\");\n inertEls.push(el);\n }\n }\n if (!on) {\n var all = document.body.children;\n for (var j = 0; j < all.length; j++) {\n all[j].removeAttribute(\"aria-hidden\");\n all[j].removeAttribute(\"inert\");\n }\n }\n }\n\n function focusables() {\n return Array.prototype.slice.call(\n modal.querySelectorAll(\'input, a[href], button:not([disabled])\')\n ).filter(function (el) { return el.offsetParent !== null || el === input; });\n }\n\n function trapTab(ev) {\n if (ev.key !== \"Tab\") return;\n var f = focusables();\n if (!f.length) { ev.preventDefault(); return; }\n var first = f[0], last = f[f.length - 1];\n var active = document.activeElement;\n if (ev.shiftKey && active === first) { ev.preventDefault(); last.focus(); }\n else if (!ev.shiftKey && active === last) { ev.preventDefault(); first.focus(); }\n }\n\n function buildModal() {\n modal = document.createElement(\"div\");\n modal.className = \"docgen-search-modal\";\n modal.setAttribute(\"hidden\", \"\");\n modal.innerHTML =\n \'<div class=\"docgen-search-backdrop\" data-close></div>\' +\n \'<div class=\"docgen-search-box\" role=\"dialog\" aria-modal=\"true\" aria-label=\"Search\">\' +\n \'<input class=\"docgen-search-input\" type=\"text\" placeholder=\"Search docs...\" aria-label=\"Search docs\" />\' +\n \'<ul class=\"docgen-search-results\"></ul></div>\';\n document.body.appendChild(modal);\n input = modal.querySelector(\".docgen-search-input\");\n list = modal.querySelector(\".docgen-search-results\");\n\n input.addEventListener(\"input\", function () { render(search(input.value)); });\n input.addEventListener(\"keydown\", onKey);\n // Escape + Tab-trap work no matter where focus sits inside the dialog.\n modal.addEventListener(\"keydown\", function (ev) {\n if (ev.key === \"Escape\") { ev.preventDefault(); close(); return; }\n trapTab(ev);\n });\n modal.addEventListener(\"click\", function (ev) {\n if (ev.target.hasAttribute(\"data-close\")) close();\n });\n }\n\n function render(rs) {\n results = rs; selected = 0;\n list.innerHTML = \"\";\n rs.forEach(function (e, i) {\n var li = document.createElement(\"li\");\n li.className = \"docgen-search-result\" + (i === 0 ? \" is-selected\" : \"\");\n // Build the row with DOM APIs so a slug containing `\"`, `<` or `>`\n // (legal in file names) cannot break out of the attribute or inject markup.\n var a = document.createElement(\"a\");\n a.setAttribute(\"href\", BASE + \"/\" + e.slug);\n var span = document.createElement(\"span\");\n span.className = \"title\";\n span.textContent = e.title;\n a.appendChild(span);\n li.appendChild(a);\n li.addEventListener(\"mouseenter\", function () { select(i); });\n list.appendChild(li);\n });\n }\n\n function select(i) {\n if (!results.length) return;\n selected = (i + results.length) % results.length;\n var items = list.querySelectorAll(\".docgen-search-result\");\n items.forEach(function (el, idx) { el.classList.toggle(\"is-selected\", idx === selected); });\n }\n\n function go() {\n if (results[selected]) window.location.href = BASE + \"/\" + results[selected].slug;\n }\n\n function onKey(ev) {\n if (ev.key === \"ArrowDown\") { ev.preventDefault(); select(selected + 1); }\n else if (ev.key === \"ArrowUp\") { ev.preventDefault(); select(selected - 1); }\n else if (ev.key === \"Enter\") { ev.preventDefault(); go(); }\n else if (ev.key === \"Escape\") { close(); }\n }\n\n function open() {\n if (!modal) buildModal();\n if (!modal.hasAttribute(\"hidden\")) return;\n opener = document.activeElement;\n loadIndex().then(function () { render(search(input.value)); });\n modal.removeAttribute(\"hidden\");\n setShellInert(true);\n input.value = \"\"; list.innerHTML = \"\";\n input.focus();\n }\n function close() {\n if (!modal || modal.hasAttribute(\"hidden\")) return;\n modal.setAttribute(\"hidden\", \"\");\n setShellInert(false);\n if (opener && typeof opener.focus === \"function\") opener.focus();\n opener = null;\n }\n\n document.addEventListener(\"keydown\", function (ev) {\n if ((ev.metaKey || ev.ctrlKey) && (ev.key === \"k\" || ev.key === \"K\")) {\n ev.preventDefault(); open();\n }\n });\n document.addEventListener(\"click\", function (ev) {\n var t = ev.target.closest(\"[data-docgen-search]\");\n if (t) { ev.preventDefault(); open(); }\n });\n})();\n";👎Deprecated:
use docgen-assets::core_assets() / emit()
Expand description
The vendored search client script, emitted to dist/search.js.
Deprecated: assets now flow through the docgen-assets crate. Kept for one
phase so dependents migrate without breakage. The bytes are byte-identical to
docgen-assets’ embedded copy.