diff --git a/.DS_Store b/.DS_Store index a1546ce..ef4b97e 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.env.example b/.env.example deleted file mode 100644 index 7a550fe..0000000 --- a/.env.example +++ /dev/null @@ -1,9 +0,0 @@ -# GEMINI_API_KEY: Required for Gemini AI API calls. -# AI Studio automatically injects this at runtime from user secrets. -# Users configure this via the Secrets panel in the AI Studio UI. -GEMINI_API_KEY="MY_GEMINI_API_KEY" - -# APP_URL: The URL where this applet is hosted. -# AI Studio automatically injects this at runtime with the Cloud Run service URL. -# Used for self-referential links, OAuth callbacks, and API endpoints. -APP_URL="MY_APP_URL" diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index dfe0770..0000000 --- a/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -# Auto detect text files and perform LF normalization -* text=auto diff --git a/.gitignore b/.gitignore deleted file mode 100644 index d8792b9..0000000 --- a/.gitignore +++ /dev/null @@ -1,14 +0,0 @@ -node_modules/ -build/ -dist/ -!dist/ -dist/* -!dist/reef-agent-ready.js -!dist/reef-agent-ready.min.js -!dist/reef-agent-ready.js.map -!dist/reef-agent-ready.min.js.map -coverage/ -.DS_Store -*.log -.env* -!.env.example diff --git a/@extension/index.html b/@extension/index.html deleted file mode 100644 index ad0c5d8..0000000 --- a/@extension/index.html +++ /dev/null @@ -1,556 +0,0 @@ - - - - - - - - - Reef Search Extension - Browser Companion - - - - -
-
- -
Alpha
-
Reef Search
-
- -
- -
-
-
-
-
Browser extension — v2.4.0
-

Search tabs, page content, and commands.

-

- Zero-dependency extension for open tab titles, deep page text, - and quick browser commands. Built with long nights and a lot of coffee. -

-
- -
- - - -
-
-
-

Install the extension

-

Load the unpacked package into Chrome and pin it to the toolbar.

-
    -
  1. - 01 - Open Chrome and go to chrome://extensions -
  2. -
  3. - 02 - Enable Developer mode in the top-right corner -
  4. -
  5. - 03 - Click Load unpacked and select the extension folder -
  6. -
  7. - 04 - Pin the extension using the 📌 icon on the toolbar -
  8. -
-
-
-
-
1
- chrome://extensions — Extensions page -
-
-
2
- Developer mode — Toggle in top-right -
-
-
3
- Load unpacked — Select the folder -
-
-
4
- Pin — Attach to toolbar -
-
-
-
-
- -
-
-
01
-

Tab search

-

Match titles and URLs across every open tab, instantly.

-
-
-
02
-

Deep content

-

Index headings, snippets, and form fields on the active page.

-
-
-
03
-

Quick commands

-

Duplicate, mute, or copy the URL of any tab from the keyboard.

-
-
- - -
- - - diff --git a/@extension/popup.css b/@extension/popup.css deleted file mode 100644 index 0ac897f..0000000 --- a/@extension/popup.css +++ /dev/null @@ -1,163 +0,0 @@ -/* Reef Search Extension - Raw Vanilla CSS (No Tailwind / No Dependencies) */ -:root { - --bg-main: #09090b; - --bg-card: #121215; - --bg-hover: #1f1f23; - --bg-active: #27272a; - --bg-input: #18181b; - - --border-color: rgba(255, 255, 255, 0.12); - --border-active: rgba(255, 255, 255, 0.3); - - --text-main: #ffffff; - --text-muted: #a1a1aa; - --text-dim: #71717a; - - --accent-white: #ffffff; - --accent-black: #000000; - - --font-sans: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; - --font-mono: 'JetBrains Mono', monospace, Consolas, 'Courier New'; -} - -*, *::before, *::after { - box-sizing: border-box; - margin: 0; - padding: 0; -} - -body { - width: 440px; - min-height: 320px; - max-height: 480px; - background-color: var(--bg-main); - color: var(--text-main); - font-family: var(--font-sans); - font-size: 13px; - line-height: 1.5; - display: flex; - flex-direction: column; - overflow: hidden; - -webkit-font-smoothing: antialiased; -} - -/* HEADER */ -.header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 12px 16px; - background-color: var(--bg-card); - border-bottom: 1px solid var(--border-color); -} - -.brand { - display: flex; - align-items: center; - gap: 10px; -} - -.brand-icon { - width: 28px; - height: 28px; - background-color: var(--accent-white); - color: var(--accent-black); - border-radius: 8px; - display: flex; - align-items: center; - justify-content: center; - font-weight: 800; - font-size: 14px; -} - -.brand-title { - font-weight: 700; - font-size: 14px; - color: var(--text-main); - letter-spacing: -0.01em; -} - -.version-tag { - font-family: var(--font-mono); - font-size: 10px; - color: var(--text-muted); - background: var(--bg-hover); - border: 1px solid var(--border-color); - padding: 2px 8px; - border-radius: 999px; -} - -/* DOWNLOAD SECTION */ -.download-container { - flex: 1; - display: flex; - align-items: center; - justify-content: center; - padding: 24px 16px; -} - -.download-content { - display: flex; - flex-direction: column; - align-items: center; - gap: 12px; - text-align: center; -} - -.download-icon { - font-size: 48px; - margin-bottom: 8px; -} - -.download-title { - font-size: 16px; - font-weight: 700; - color: var(--text-main); - margin: 0; -} - -.download-description { - font-size: 12px; - color: var(--text-muted); - max-width: 320px; - line-height: 1.5; - margin: 0; -} - -.download-btn { - display: inline-flex; - align-items: center; - gap: 8px; - padding: 10px 24px; - background-color: var(--accent-white); - color: var(--accent-black); - font-family: var(--font-sans); - font-size: 13px; - font-weight: 700; - text-decoration: none; - border-radius: 8px; - border: none; - cursor: pointer; - transition: all 0.15s ease; - margin-top: 8px; -} - -.download-btn:hover { - background-color: var(--text-muted); - color: var(--bg-main); -} - -/* SCROLLBAR */ -::-webkit-scrollbar { - width: 5px; -} -::-webkit-scrollbar-track { - background: var(--bg-main); -} -::-webkit-scrollbar-thumb { - background: var(--border-color); - border-radius: 3px; -} -::-webkit-scrollbar-thumb:hover { - background: var(--text-dim); -} diff --git a/@extension/popup.html b/@extension/popup.html deleted file mode 100644 index 69c00df..0000000 --- a/@extension/popup.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - Reef Search Extension - - - - - -
-
-
🔍
-
Reef Search
-
-
v2.4.0
-
- - -
-
-
📥
-

Reef Search Extension

-

- Search open tabs, page content, and commands — now available for download. -

- - Download Extension - -
-
- - - - diff --git a/@extension/popup.js b/@extension/popup.js deleted file mode 100644 index 903cc9d..0000000 --- a/@extension/popup.js +++ /dev/null @@ -1,21 +0,0 @@ -/* Reef Search - Download Page (No Preview) */ - -(function () { - 'use strict'; - - const downloadBtn = document.getElementById('downloadBtn'); - - function init() { - if (downloadBtn) { - downloadBtn.addEventListener('click', function () { - window.open('https://reef.js.org/extension', '_blank'); - }); - } - } - - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', init); - } else { - init(); - } -})(); \ No newline at end of file diff --git a/ReefSearch.zip b/ReefSearch.zip index 70296b3..f3771d7 100644 Binary files a/ReefSearch.zip and b/ReefSearch.zip differ diff --git a/dist/reef.min.js b/dist/reef.min.js index 71da26a..efff0e1 100644 --- a/dist/reef.min.js +++ b/dist/reef.min.js @@ -1,4 +1,4 @@ -"use strict";var ReefSearch=(()=>{var ot=Object.defineProperty;var Ne=(i,e,t)=>()=>{if(t)throw t[0];try{return i&&(e=i(i=0)),e}catch(n){throw t=[n],n}};var We=(i,e)=>{for(var t in e)ot(i,t,{get:e[t],enumerable:!0})};var nt={};We(nt,{addSectionsToIndex:()=>Ye,addToIndex:()=>F,createSearchIndex:()=>H,deserializeIndex:()=>fe,facets:()=>pe,findClosestWord:()=>ue,getAllSections:()=>ce,getPopularQueries:()=>be,getSnippet:()=>Ze,getTotalResultCount:()=>de,levenshteinDistance:()=>W,parseExtendedQuery:()=>Fe,removeFromIndex:()=>Pe,searchSections:()=>N,searchWithPagination:()=>le,serializeIndex:()=>he,suggest:()=>ge,trackQuery:()=>me,updateRecord:()=>tt});function He(){return{children:new Map,records:[]}}function U(i,e,t){let n=i;for(let r=0;ra.id===t.id)||n.records.push(t)}}function Ke(i,e,t){let n=i,r=[];for(let s=0;sc.id!==t),o.push(a))}for(let s of o)s.records.length===0&&s.children.size}function ct(i,e){let t=i;for(let n=0;n{a.length>=2&&r.add(a)}),n.toLowerCase().split(/\s+/).forEach(a=>{a.length>=2&&r.add(a)});let o=new Set;for(let[a,s]of i.queryCache.entries()){if(s.resultIds.includes(e)){o.add(a);continue}let c=a.toLowerCase().split(/\s+/).filter(l=>l.length>=2);for(let l of c)if(r.has(l)){o.add(a);break}}if(o.size>0)for(let a of o)i.queryCache.delete(a);else i.queryCache.clear()}function lt(i,e){if(i.size===0||e.size===0)return 0;let t=0;for(let r of i)e.has(r)&&t++;let n=i.size+e.size-t;return t/n}function _e(i){let e=i.toLowerCase().split(/\s+/);return new Set(e.filter(t=>t.length>2))}function dt(i,e=.5){if(i.length<=1)return i;let t=[],n=[],r=[...i].sort((o,a)=>a[1]-o[1]);t.push(r[0]),n.push(_e(r[0][0].headingText+" "+r[0][0].bodyText));for(let o=1;ot[h][1]){t.splice(h,0,[a,u]),n.splice(h,0,c),f=!0;break}f||(t.push([a,u]),n.push(c))}return t}function H(){return{headingIndex:new Map,headingIds:new Map,bodyIndex:new Map,allSections:[],queryCache:new Map,popularQueries:[],docFrequency:new Map,fieldDocFreq:{headingText:new Map,bodyText:new Map,label:new Map,breadcrumb:new Map},totalDocs:0,queryPopularity:new Map,version:1,headingTrie:He()}}function F(i,e,t){i.totalDocs+=e.length;for(let n of e){let r=n.headingText.toLowerCase();if(r.length>=2){let c=i.headingIds.get(r)??[];c.push(n),i.headingIds.set(r,c);let l=r.normalize("NFD").replace(/[\u0300-\u036f]/g,"");if(l!==r){let u=i.headingIds.get(l)??[];u.push(n),i.headingIds.set(l,u)}}for(let c=2;c<=r.length;c++){let l=r.slice(0,c),u=i.headingIndex.get(l)??[];u.push(n),i.headingIndex.set(l,u)}let o=r.normalize("NFD").replace(/[\u0300-\u036f]/g,"");for(let c=2;c<=o.length;c++){let l=o.slice(0,c);if(l!==r.slice(0,c)){let u=i.headingIndex.get(l)??[];u.push(n),i.headingIndex.set(l,u)}}U(i.headingTrie,r,n),o!==r&&U(i.headingTrie,o,n);let s=n.bodyText.toLowerCase().split(/\s+/);for(let c of s)if(c.length>=3){let l=i.bodyIndex.get(c)??[];l.push(n),i.bodyIndex.set(c,l);let u=c.normalize("NFD").replace(/[\u0300-\u036f]/g,"");if(u!==c){let f=i.bodyIndex.get(u)??[];f.push(n),i.bodyIndex.set(u,f)}}for(let c of s)if(c.length>=2){let l=i.docFrequency.get(c)??0;i.docFrequency.set(c,l+1);let u=i.fieldDocFreq.bodyText.get(c)??0;i.fieldDocFreq.bodyText.set(c,u+1)}let d=r.split(/\s+/);for(let c of d)if(c.length>=2){let l=i.fieldDocFreq.headingText.get(c)??0;i.fieldDocFreq.headingText.set(c,l+1)}if(n.label){let l=n.label.toLowerCase().split(/\s+/);for(let u of l)if(u.length>=2){let f=i.fieldDocFreq.label.get(u)??0;i.fieldDocFreq.label.set(u,f+1)}}if(n.breadcrumb){let l=n.breadcrumb.toLowerCase().split(/\s+/);for(let u of l)if(u.length>=2){let f=i.fieldDocFreq.breadcrumb.get(u)??0;i.fieldDocFreq.breadcrumb.set(u,f+1)}}if(n.label){let c=n.label.toLowerCase();if(c.length>=2){let l=i.bodyIndex.get(c)??[];l.push(n),i.bodyIndex.set(c,l)}}if(n.type==="structured"&&n.structuredData){if(n.structuredData.question){let c=n.structuredData.question.toLowerCase().split(/\s+/);for(let l of c)if(l.length>=3){let u=i.bodyIndex.get(l)??[];u.push(n),i.bodyIndex.set(l,u)}}if(n.structuredData.answer){let c=n.structuredData.answer.toLowerCase().split(/\s+/);for(let l of c)if(l.length>=3){let u=i.bodyIndex.get(l)??[];u.push(n),i.bodyIndex.set(l,u)}}}}i.allSections.push(...e)}function ce(i){return i.allSections}function le(i,e,t={}){let n=Math.max(1,t.pageSize??t.limit??20),r=t.cursor?Math.max(0,Number.parseInt(t.cursor,10)||0):0,o=N(i,e,{...t,limit:Number.MAX_SAFE_INTEGER}),a=o.slice(r,r+n).map(d=>"score"in d?d:{record:d,score:0}),s=r+a.length;return{results:a,total:o.length,hasMore:s{if(!a||Math.abs(a.length-n.length)>t)return;let s=W(a,n),d=r;s<=t&&(!d||s0;){let s=!1,d=n.exec(t);if(d&&d.index===0){e.push({type:"exact",value:d[1]}),t=t.slice(d[0].length),s=!0;continue}let c=r.exec(t);if(c&&c.index===0){e.push({type:"exclude",value:c[1]}),t=t.slice(c[0].length),s=!0;continue}let l=o.exec(t);if(l&&l.index===0){e.push({type:"prefix",value:l[1]}),t=t.slice(l[0].length),s=!0;continue}let u=a.exec(t);if(u&&u.index===0){e.push({type:"suffix",value:u[1]}),t=t.slice(u[0].length),s=!0;continue}if(t.match(/^\|/)){e.push({type:"or",value:"|"}),t=t.slice(1),s=!0;continue}let h=t.match(/^\s+/);if(h&&!s){t=t.slice(h[0].length);continue}let p=t.match(/^(\S+)/);if(p){let b=p[1].match(/^([\w-]+):(.+)$/);e.push({type:"term",value:b?`${b[1]}:${b[2]}`:p[1]}),t=t.slice(p[0].length),s=!0}}return e}function ft(i){let e={type:"and",children:[]},t=null;for(let n of i)n.type==="or"?t||(t={type:"or",children:[]},e.children.push(t)):(t&&(e.children.push(t),t=null),e.children.push({type:n.type,value:n.value}));return t&&e.children.push(t),e}function Je(i,e,t){let n=new Set,r=i.length;for(let[o,a]of e.headingIds)if(Math.abs(o.length-r)<=t)for(let s of a)n.add(s);for(let[o,a]of e.bodyIndex)if(Math.abs(o.length-r)<=t)for(let s of a)n.add(s);return n}function qe(i,e){switch(i.type){case"term":let t=i.value.match(/^([\w-]+):(.+)$/),n=(t?t[2]:i.value).toLowerCase(),r=t?.[1].toLowerCase();return(r==="title"||r==="heading"?[e.headingText]:r==="body"?[e.bodyText]:r==="label"?[e.label??""]:r==="breadcrumb"?[e.breadcrumb]:r==="type"?[e.type]:[e.headingText,e.bodyText,e.label??"",e.breadcrumb]).some(h=>h.toLowerCase().includes(n));case"exact":let a=i.value.toLowerCase(),s=V(a),d=V(e.headingText),c=V(e.bodyText);return ae(d,s)||ae(c,s);case"exclude":let l=i.value.toLowerCase();return!e.headingText.toLowerCase().includes(l)&&!e.bodyText.toLowerCase().includes(l);case"prefix":let u=i.value.toLowerCase();return e.headingText.toLowerCase().startsWith(u)||e.bodyText.toLowerCase().startsWith(u);case"suffix":let f=i.value.toLowerCase();return e.headingText.toLowerCase().endsWith(f)||e.bodyText.toLowerCase().endsWith(f);case"and":return i.children.every(h=>qe(h,e));case"or":return i.children.some(h=>qe(h,e))}}function De(i,e,t,n,r,o=1.5,a=.75){let s=Math.log((t-e+.5)/(e+.5)+1),d=1-a+a*(n/r);return s*(i*(o+1)/(i+o*d))}function gt(i,e,t,n,r=1.5,o=.75){let a=et(t),s=t.totalDocs,d=i.headingText.length*(n.headingText||1)+i.bodyText.length*(n.bodyText||1)+(i.label?i.label.length*(n.label||1):0)+(i.breadcrumb?i.breadcrumb.length*(n.breadcrumb||1):0),c=0;for(let l of e){let u=l.toLowerCase(),f=0,h=0;if(n.headingText){let p=i.headingText.toLowerCase(),b=se(p,u),v=t.fieldDocFreq.headingText.get(u)||1;f+=b*(n.headingText||1),h=Math.max(h,v)}if(n.bodyText){let p=i.bodyText.toLowerCase(),b=se(p,u),v=t.fieldDocFreq.bodyText.get(u)||1;f+=b*(n.bodyText||1),h=Math.max(h,v)}if(i.label&&n.label){let p=i.label.toLowerCase(),b=se(p,u),v=t.fieldDocFreq.label.get(u)||1;f+=b*(n.label||1),h=Math.max(h,v)}if(i.breadcrumb&&n.breadcrumb){let p=i.breadcrumb.toLowerCase(),b=se(p,u),v=t.fieldDocFreq.breadcrumb.get(u)||1;f+=b*(n.breadcrumb||1),h=Math.max(h,v)}if(f>0&&h>0){let p=Math.log((s-h+.5)/(h+.5)+1),b=1-o+o*(d/a),v=p*(f*(r+1)/(f+r*b));c+=v}}return c}function se(i,e){if(!i||!e)return 0;let t=i.split(/\s+/),n=0;for(let r of t)r===e&&n++;return n}function V(i){return i?i.toLowerCase().split(/\s+/).filter(e=>e.length>0):[]}function ae(i,e){if(e.length===0)return!0;if(e.length>i.length)return!1;for(let t=0;t<=i.length-e.length;t++){let n=!0;for(let r=0;rt*2?"...":"");let r=[...e].sort((C,A)=>C.start-A.start),o=r[0];for(let C of r)C.end-C.start>o.end-o.start&&(o=C);let a=o.start,s=o.end,d=Math.max(0,a-t),c=Math.min(n.length,s+t);d===0&&(c=Math.min(n.length,s+t*2)),c===n.length&&(d=Math.max(0,a-t*2));let l=n.slice(d,c),u=d>0?"...":"",f=c"+v+""+T+f}function pt(i,e){if(e.length<=1)return 1;let t=new Map;for(let o=0;o0?1+(n-r)/r:1}function et(i){return i.allSections.length===0?50:i.allSections.reduce((t,n)=>t+n.bodyText.length+n.headingText.length,0)/i.allSections.length}function mt(i,e){let t=e.queryCache.get(i);if(t){let n=new Set;for(let r of t.resultIds){let o=e.allSections.find(a=>a.id===r);o&&n.add(o)}return n}return null}function Ge(i,e,t){let n={resultIds:e.map(r=>r.id),timestamp:Date.now()};if(t.queryCache.set(i,n),t.queryCache.size>100){let r=t.queryCache.keys().next().value;r!==void 0&&t.queryCache.delete(r)}}function N(i,e,t){let n=8,r;typeof t=="number"?n=t:t&&(r=t,n=t.limit??8);let o=i.trim();if(!o)return e.allSections.slice(0,n);let a=o.normalize("NFD").replace(/[\u0300-\u036f]/g,""),s=new Map,d=new Map,c={...ut,...r?.weights??{}},l=et(e),u=(m,y,g,x,w)=>{s.set(m,(s.get(m)??0)+y);let S=d.get(m)??[];S.push({key:g,start:x,end:w}),d.set(m,S)},f=r?.scoringAlgorithm==="bm25",h=r?.scoringAlgorithm==="bm25f",p=r?.bm25fOptions||{k1:1.5,b:.75},b=mt(a.toLowerCase(),e),v=b?[...b]:e.allSections,T=e.headingIds.get(a.toLowerCase())??e.headingIds.get(o.toLowerCase());if(T)for(let m of T){let y=c.headingText??1,g=z(m.headingText.length),x;if(f){let S=e.docFrequency.get(a.toLowerCase())??1;x=De(1,S,e.totalDocs,m.headingText.length+m.bodyText.length,l)*100}else x=100*y*(1+g);u(m,x,"headingText",0,m.headingText.length)}let C=e.headingIndex.get(a.toLowerCase())??e.headingIndex.get(o.toLowerCase());if(C)for(let m of C){if(s.has(m))continue;let y=c.headingText??1,g=z(m.headingText.length),x;if(f){let k=e.docFrequency.get(a.toLowerCase())??1;x=De(1,k,e.totalDocs,m.headingText.length+m.bodyText.length,l)*50}else x=50*y*(1+g);let w=Ae(m.headingText,o);if(w.length>0){let S=w[0];u(m,x,"headingText",S.start,S.end)}else u(m,x,"headingText",0,Math.min(o.length,m.headingText.length))}let A=a.toLowerCase().split(/\s+/);for(let m of A){let y=e.bodyIndex.get(m);if(y)for(let g of y){if(s.has(g))continue;let x=c.bodyText??1,w=z(g.bodyText.length),S;if(f){let I=e.docFrequency.get(m)??1;S=De(1,I,e.totalDocs,g.headingText.length+g.bodyText.length,l)*20}else S=20*x*(1+w);let k=Ae(g.bodyText,o);if(k.length>0){let M=k[0];u(g,S,"bodyText",M.start,M.end)}else u(g,S,"bodyText",0,Math.min(m.length,g.bodyText.length))}}if(h&&s.size===0){let m=a.toLowerCase().split(/\s+/);for(let y of e.allSections){let g=gt(y,m,e,c,p.k1,p.b);g>0&&u(y,g*100,"bm25f",0,Math.min(a.length,y.headingText.length+y.bodyText.length))}}if(s.size>0){let m=a.toLowerCase().split(/\s+/);if(m.length>1)for(let[y,g]of s.entries()){let x=V(y.headingText),w=V(y.bodyText),S=m.every(M=>x.includes(M)),k=m.every(M=>w.includes(M));if(S||k){let M=[...x,...w],I=pt(M,m);s.set(y,g*I)}(ae(x,m)||ae(w,m))&&s.set(y,g*1.5)}}if(r?.trackPopularity){let m=r.popularityBoost||1.2;for(let[y,g]of s.entries()){let x=`${a.toLowerCase()}||${y.id}`,w=e.queryPopularity.get(x);if(w){let S=1+(w.count-1)*.1;s.set(y,g*S*m)}}}if(r?.fuzzy){let m=r.fuzzyDistance??2;if(s.size===0){let y=Je(o.toLowerCase(),e,1);for(let g of y){if(s.has(g))continue;let x=g.headingText.toLowerCase();if(Ae(x,o).length===0){let I=W(x,o);if(I<=1){let D=(1-I/Math.max(o.length,x.length))*60,E=c.headingText??1,rt=z(g.headingText.length);u(g,D*E*(1+rt),"headingText",0,Math.min(o.length,x.length))}}if(s.has(g))continue;let S=g.bodyText.toLowerCase(),k=1/0,M=0;for(let I=0;I<=S.length-o.length;I++){let D=S.slice(I,I+o.length),E=W(o,D);Er.filter(m)));let P=q.sort((m,y)=>y[1]-m[1]);if(r?.sortFn&&(P=P.sort((m,y)=>{let g=d.get(m[0])??[],x=d.get(y[0])??[];return r.sortFn({record:m[0],score:m[1],matches:g},{record:y[0],score:y[1],matches:x})})),r?.diversify){let m=r.mmrLambda??.5;P=dt(P,m)}let $=P.slice(0,n).map(([m])=>m);if(r?.typeWeights){let m=r.typeWeights;for(let g of $){let x=m[g.type]??1,w=s.get(g);w!==void 0&&x!==1&&s.set(g,w*x)}P=[...s.entries()].sort((g,x)=>x[1]-g[1]);let y=P.slice(0,n).map(([g])=>g);return Ge(o.toLowerCase(),y,e),r?.includeScore||r?.includeMatches?y.map(g=>{let x=d.get(g)??[],w=s.get(g)??0,k=Math.max(0,Math.min(1,1-w/(f?100:200)));return{record:g,score:k,matches:r.includeMatches?x:void 0}}):y}return Ge(o.toLowerCase(),$,e),r?.includeScore||r?.includeMatches?$.map(m=>{let y=d.get(m)??[],g=s.get(m)??0,w=Math.max(0,Math.min(1,1-g/(f?100:200)));return{record:m,score:w,matches:r.includeMatches?y:void 0}}):$}function Pe(i,e){let t=i.allSections.findIndex(d=>d.id===e);if(t===-1)return;let n=i.allSections[t];i.allSections.splice(t,1),i.totalDocs=Math.max(0,i.totalDocs-1),Xe(i,n.id,n.headingText,n.bodyText);let r=n.headingText.toLowerCase(),o=i.headingIds.get(r);if(o){let d=o.findIndex(c=>c.id===e);d!==-1&&o.splice(d,1),o.length===0&&i.headingIds.delete(r)}for(let d=2;d<=r.length;d++){let c=r.slice(0,d),l=i.headingIndex.get(c);if(l){let u=l.findIndex(f=>f.id===e);u!==-1&&l.splice(u,1),l.length===0&&i.headingIndex.delete(c)}}Ke(i.headingTrie,r,e);let a=n.headingText.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g,"");a!==r&&Ke(i.headingTrie,a,e);let s=n.bodyText.toLowerCase().split(/\s+/);for(let d of s){if(d.length<3)continue;let c=i.bodyIndex.get(d);if(c){let l=c.findIndex(u=>u.id===e);l!==-1&&c.splice(l,1),c.length===0&&i.bodyIndex.delete(d)}}}function tt(i,e){Pe(i,e.id),F(i,[e]),Xe(i,e.id,e.headingText,e.bodyText)}function he(i){let e={allSections:i.allSections,headingIndex:Array.from(i.headingIndex.entries()),headingIds:Array.from(i.headingIds.entries()),bodyIndex:Array.from(i.bodyIndex.entries()),docFrequency:Array.from(i.docFrequency.entries()),fieldDocFreq:{headingText:Array.from(i.fieldDocFreq.headingText.entries()),bodyText:Array.from(i.fieldDocFreq.bodyText.entries()),label:Array.from(i.fieldDocFreq.label.entries()),breadcrumb:Array.from(i.fieldDocFreq.breadcrumb.entries())},totalDocs:i.totalDocs,popularQueries:i.popularQueries,queryPopularity:Array.from(i.queryPopularity.entries()),version:i.version};return JSON.stringify(e)}function fe(i){let e=JSON.parse(i),t={headingText:new Map,bodyText:new Map,label:new Map,breadcrumb:new Map},n=new Map;if(e.queryPopularity&&Array.isArray(e.queryPopularity))for(let[o,a]of e.queryPopularity)typeof a=="object"&&a!==null&&n.set(o,a);if(e.fieldDocFreq)t.headingText=new Map(e.fieldDocFreq.headingText??[]),t.bodyText=new Map(e.fieldDocFreq.bodyText??[]),t.label=new Map(e.fieldDocFreq.label??[]),t.breadcrumb=new Map(e.fieldDocFreq.breadcrumb??[]);else if(e.docFrequency)for(let[o,a]of e.docFrequency)t.bodyText.set(o,a),t.headingText.set(o,Math.floor(a*.3)),t.label.set(o,Math.floor(a*.1)),t.breadcrumb.set(o,Math.floor(a*.1));let r=He();if(e.headingIndex&&Array.isArray(e.headingIndex)){let o=new Map(e.headingIndex),a=new Set;for(let[s,d]of o.entries())if(s.length>=2&&Array.isArray(d))for(let c of d){if(a.has(c.id))continue;a.add(c.id);let l=c.headingText.toLowerCase();U(r,l,c);let u=l.normalize("NFD").replace(/[\u0300-\u036f]/g,"");u!==l&&U(r,u,c)}}else{let o=e.allSections??[];for(let a of o){let s=a.headingText.toLowerCase();U(r,s,a);let d=s.normalize("NFD").replace(/[\u0300-\u036f]/g,"");d!==s&&U(r,d,a)}}return{allSections:e.allSections??[],headingIndex:new Map(e.headingIndex??[]),headingIds:new Map(e.headingIds??[]),bodyIndex:new Map(e.bodyIndex??[]),queryCache:new Map,popularQueries:e.popularQueries??[],docFrequency:new Map(e.docFrequency??[]),fieldDocFreq:t,totalDocs:e.totalDocs??0,queryPopularity:n,version:e.version??1,headingTrie:r}}function ge(i,e,t=10){let n=i.trim().toLowerCase();if(!n||n.length<2)return[];let r=[],o=new Set,a=ct(e.headingTrie,n);for(let s of a){if(r.length>=t)break;let d=s.headingText;o.has(d)||(o.add(d),r.push(d))}if(r.length=t)break;let u=l.headingText;o.has(u)||(o.add(u),r.push(u))}}if(r.length=t)break;s.includes(n)&&!o.has(d[0].headingText)&&(o.add(d[0].headingText),r.push(d[0].headingText))}return r.slice(0,t)}function pe(i){let e={section:0,action:0,field:0,link:0,file:0,media:0,structured:0};for(let t of i.allSections)e[t.type]=(e[t.type]??0)+1;return e}function me(i,e,t){let n=e.trim();if(n&&(i.popularQueries.length>=100&&i.popularQueries.shift(),i.popularQueries.push(n),t)){let r=`${n}||${t}`,o=i.queryPopularity.get(r);o?o.count++:i.queryPopularity.set(r,{query:n,recordId:t,count:1})}}function be(i,e=5){let t=new Map;for(let n of i.popularQueries)t.set(n,(t.get(n)??0)+1);return[...t.entries()].sort((n,r)=>r[1]-n[1]).slice(0,e).map(([n])=>n)}var Ye,ut,K=Ne(()=>{"use strict";Ye=F;ut={headingText:2,bodyText:1,label:1.5,breadcrumb:.5}});var we={};We(we,{clearCache:()=>St,loadIndex:()=>vt,loadSiteGraph:()=>Tt,openDB:()=>B,saveIndex:()=>wt,saveSiteGraph:()=>It});async function B(){return new Promise((i,e)=>{let t=indexedDB.open(bt,1);t.onsuccess=()=>i(t.result),t.onerror=()=>e(t.error),t.onupgradeneeded=()=>{let n=t.result;n.objectStoreNames.contains(L)||n.createObjectStore(L,{keyPath:ze})}})}async function yt(i){if(typeof window>"u"||!window.CompressionStream)return{compressed:!1,data:i};try{let t=new Blob([i]).stream().pipeThrough(new CompressionStream("gzip")),n=[],r=t.getReader();for(;;){let{done:d,value:c}=await r.read();if(d)break;n.push(new Uint8Array(c))}let o=n.reduce((d,c)=>d+c.length,0),a=new Uint8Array(o),s=0;for(let d of n)a.set(d,s),s+=d.length;return{compressed:!0,data:a.buffer}}catch{return{compressed:!1,data:i}}}async function xt(i){if(typeof i=="string")return i;if(typeof window>"u"||!window.DecompressionStream)throw new Error("Compressed data but no decompression available");try{let t=new Blob([i]).stream().pipeThrough(new DecompressionStream("gzip")),n=[],r=t.getReader(),o=new TextDecoder;for(;;){let{done:a,value:s}=await r.read();if(a)break;n.push(o.decode(s,{stream:!0}))}return n.join("")+o.decode()}catch{throw new Error("Failed to decompress data")}}async function wt(i,e){let r=(await B()).transaction(L,"readwrite").objectStore(L),o=he(i),{compressed:a,data:s}=await yt(o),d={[ze]:e.versionHash,index:s,metadata:e,compressed:a};await new Promise((c,l)=>{let u=r.put(d);u.onsuccess=()=>c(void 0),u.onerror=()=>l(u.error)})}async function vt(i){let n=(await B()).transaction(L,"readonly").objectStore(L),r=await new Promise((c,l)=>{let u=n.getAll();u.onsuccess=()=>c(u.result),u.onerror=()=>l(u.error)});if(!r.length)return null;let o=r[0],a=o.index;if(o.compressed)try{a=await xt(o.index)}catch(c){if(console.warn("[reef] decompression failed, trying uncompressed fallback:",c),typeof o.index=="string")a=o.index;else return null}else if(typeof o.index=="string")a=o.index;else return null;let s=fe(a),d=o.metadata;return i&&d.buildTime&&Date.now()-d.buildTime>i?null:{index:s,metadata:d}}async function St(){let t=(await B()).transaction(L,"readwrite").objectStore(L);await new Promise((n,r)=>{let o=t.clear();o.onsuccess=()=>n(void 0),o.onerror=()=>r(o.error)})}async function It(i,e="site-graph"){(await B()).transaction(L,"readwrite").objectStore(L).put({[ze]:`${e}:${i.startUrl}`,graph:i,metadata:{versionHash:e,buildTime:Date.now(),pageMetadata:{}}})}async function Tt(i,e="site-graph"){let n=(await B()).transaction(L,"readonly");return new Promise(r=>{let o=n.objectStore(L).get(`${e}:${i}`);o.onsuccess=()=>r(o.result?.graph??null),o.onerror=()=>r(null)})}var bt,L,ze,ve=Ne(()=>{"use strict";K();bt="reef-index",L="indices",ze="version"});function Me(i){let e="",t=!1;for(let n=0;n"&&t===!0?t=!1:t===!1&&(e+=r)}return e.replace(/\s+/g," ").trim()}function Be(i){let e=[],t=i;for(;t&&t.nodeType===Node.ELEMENT_NODE;){let n=t.tagName.toLowerCase();if(t.id){n+=`#${t.id}`,e.unshift(n);break}else if(t.className){let a=t.className.trim().split(/\s+/);a.length&&(n+=`.${a.join(".")}`)}let r=1,o=t.previousElementSibling;for(;o;)o.tagName===t.tagName&&r++,o=o.previousElementSibling;r>1&&(n+=`:nth-child(${r})`),e.unshift(n),t=t.parentElement}return e.length>0?e.join(" > "):i.tagName.toLowerCase()}function O(i){let e=[],t=c=>{c&&!e.includes(c)&&e.push(c)},n=c=>typeof CSS<"u"&&CSS.escape?CSS.escape(c):c.replace(/(["\\])/g,"\\$1");for(let c of["data-testid","data-test","data-agent-id","id"]){let l=i.getAttribute(c);l&&t(c==="id"?`#${n(l)}`:`[${c}="${n(l)}"]`)}let r=i.getAttribute("aria-label");r&&t(`[aria-label="${n(r)}"]`);let o=i.getAttribute("role"),a=ee(i);o&&a&&t(`[role="${n(o)}"][aria-label="${n(a)}"]`),o&&t(`[role="${n(o)}"]`),t(Be(i));let s=i.previousElementSibling,d=1;for(;s;)s.tagName===i.tagName&&d++,s=s.previousElementSibling;return t(`xpath=//${i.tagName.toLowerCase()}[${d}]`),e}function it(i,e,t=[]){let n=i instanceof Document?Array.from(i.documentElement?[i.documentElement]:[]):[i],r=(a,s)=>{e(a,s),a.shadowRoot&&o(a.shadowRoot,s);for(let d of Array.from(a.children))r(d,s);if(a.tagName.toLowerCase()==="iframe")try{let d=a.contentDocument;d&&o(d,[...s,Array.from(a.parentElement?.children||[]).indexOf(a)])}catch{}},o=(a,s)=>{if(a instanceof Document)a.documentElement&&r(a.documentElement,s);else if(a instanceof ShadowRoot)for(let d of Array.from(a.children))r(d,s);else r(a,s)};for(let a of n)r(a,t)}function st(i){let e=i.tagName.toLowerCase();return["a","button","input","textarea","select","summary"].includes(e)||["button","link","tab","menuitem","checkbox","radio","combobox","option"].includes(i.getAttribute("role")||"")||i.hasAttribute("contenteditable")||i.hasAttribute("tabindex")&&Number(i.getAttribute("tabindex"))>=0||i.hasAttribute("data-reef-action")}function Y(i=document){let e=[];return it(i,(t,n)=>{if(!st(t))return;let r=ee(t);if(!r)return;let o=O(t);e.push({id:`${typeof location<"u"?location.href:""}#accessibility-${e.length}`,url:typeof location<"u"?location.href:"",headingText:r,headingId:`accessibility-${e.length}`,breadcrumb:"",bodyText:r,type:t.matches("input,textarea,select,[contenteditable]")?"field":"action",selector:o[0],selectors:o,iframePath:n,label:r,destructive:Ee(r)})}),e}function je(i,e){let t=i.match(/\bid=["']([^"']+)['"]/i);return t?.[1]?t[1]:e.toLowerCase().replace(/[^a-z0-9]+/g,"-")||Math.random().toString(36).slice(2)}function Qe(i){return/\bid=["'][^"']+["']/i.test(i)}function Ve(i,e){let t=i.slice(e,e+500),n=t.match(/]*id="([^"]+)"/i);if(n?.[1])return n[1];let r=t.match(/]*id="([^"]+)"/i);return r?.[1]?r[1]:null}var ke=new Map;function Z(i,e){if(ke.has(e))return ke.get(e);let t=i.replace(//gi," ").replace(//gi," ").replace(//gi," ").replace(//g," "),n=[],r=/<(h[1-6])[^>]*>([\s\S]*?)<\/h[1-6]>/gi,o;for(r.lastIndex=0;(o=r.exec(t))!==null;){let[,d,c]=o,l=Me(c),u=parseInt(d[1],10);n.push({level:u,index:o.index,text:l,id:je(o[0],l),hasRealId:Qe(o[0])})}let a=n.length,s=new Array(a);for(let d=0;d0&&(b+=" \u203A "),b+=n[C].text;let v=c.hasRealId?null:Ve(t,c.index+c.text.length),T=c.hasRealId?"#"+c.id:v?"#"+v:void 0;s[d]={id:`${e}#${c.id}`,url:`${e}#${c.id}`,headingText:c.text,headingId:c.id,breadcrumb:b,bodyText:p,type:"section",selector:T}}return ke.set(e,s),s}function ee(i){let e=i.getAttribute("aria-label");if(e?.trim())return e.trim();let t=i.getAttribute("aria-labelledby");if(t){let o=document.getElementById(t);if(o?.textContent?.trim())return o.textContent.trim()}let n=i.textContent?.trim();if(n)return n;let r=i.getAttribute("title");return r?.trim()?r.trim():null}function Ee(i){let e=["delete","remove","cancel subscription","unsubscribe","pay","checkout","submit order","confirm"],t=i.toLowerCase();return e.some(n=>t.includes(n))}function te(i,e,t){let n=[],r=new DOMParser().parseFromString(i,"text/html"),o=["button",'[role="button"]','input[type="button"]','input[type="submit"]',"summary","[data-reef-action]"],a=Array.from(r.querySelectorAll(o.join(",")));for(let s of a){if(t&&s.matches(t))continue;let d=ee(s);if(!d)continue;let c=O(s);n.push({id:`${e}#action-${n.length}`,url:e,headingText:d,headingId:`action-${n.length}`,breadcrumb:"",bodyText:d,type:"action",selector:c[0],selectors:c,destructive:Ee(d),label:d})}return n}function ne(i,e){let t=[],n=new DOMParser().parseFromString(i,"text/html"),r=Array.from(n.querySelectorAll("form"));for(let o of r){let a="",s=o.parentElement;for(;s&&s!==n.body;){if(s.matches('h1, h2, h3, h4, h5, h6, article, section, [role="main"], main')){let c=s.textContent?.trim()||"";c&&(a=c);break}s=s.parentElement}let d=Array.from(o.querySelectorAll("input, textarea, select"));for(let c of d){if(c.matches('input[type="hidden"], input[type="button"], input[type="submit"], input[type="reset"]'))continue;let l="",u=c.id;if(u){let p=n.querySelector(`label[for="${u}"]`);p&&(l=p.textContent?.trim()||"")}if(!l){let p=c.closest("label");if(p){l=p.textContent?.trim()||"";let b=c;l&&b.value&&l.includes(b.value)&&(l=l.replace(b.value,"").trim())}}if(!l){let p=c;l=("placeholder"in p?p.placeholder:"")||c.getAttribute("aria-label")||""}if(!l)continue;let f=O(c),h=c;t.push({id:`${e}#field-${t.length}`,url:e,headingText:l,headingId:`field-${t.length}`,breadcrumb:a,bodyText:l,type:"field",selector:f[0],selectors:f,label:l,value:h.value})}}return t}function Re(i){i.querySelectorAll("details").forEach(e=>{e.hasAttribute("open")||e.setAttribute("data-reef-was-closed","true"),e.setAttribute("open","")}),i.querySelectorAll('[aria-hidden="false"]').forEach(e=>{e.removeAttribute("aria-hidden")})}function j(i,e){let t=[],n=new DOMParser().parseFromString(i,"text/html"),r=Array.from(n.querySelectorAll("a[href]"));for(let o of r){if(o.hasAttribute("rel")&&o.getAttribute("rel")?.toLowerCase().includes("nofollow"))continue;let a=o.getAttribute("href");if(!a||a==="#"||a.startsWith("javascript:"))continue;let s=o.textContent?.trim()||"";if(!s)continue;let d=Le(a,e),c=!d.startsWith(window.location.origin),l=O(o);t.push({id:`${e}#link-${t.length}`,url:d,headingText:s,headingId:`link-${t.length}`,breadcrumb:"",bodyText:s,type:c?"link":"section",selector:l[0],selectors:l})}return t}function re(i,e,t){let n=[],r=new DOMParser().parseFromString(i,"text/html"),o=t?.split(",").map(s=>s.trim().toLowerCase())??["pdf","doc","docx","xls","xlsx","ppt","pptx","zip","csv"],a=Array.from(r.querySelectorAll("a[href]"));for(let s of a){let d=s.getAttribute("href");if(!d||!o.some(h=>d.toLowerCase().endsWith(`.${h}`)||d.toLowerCase().endsWith(`.${h}?`)||d.toLowerCase().endsWith(`.${h}#`)))continue;let l=s.textContent?.trim()||d.split("/").pop()||"";if(!l)continue;let u=Le(d,e),f=O(s);n.push({id:`${e}#file-${n.length}`,url:u,headingText:l,headingId:`file-${n.length}`,breadcrumb:"",bodyText:l,type:"file",selector:f[0],selectors:f})}return n}function oe(i,e){let t=[],n=new DOMParser().parseFromString(i,"text/html"),r=Array.from(n.querySelectorAll("img"));for(let a of r){let s=a.alt.trim();if(!s)continue;let d="",c=a.closest("figure");if(c){let f=c.querySelector("figcaption");f&&(d=f.textContent?.trim()||"")}let l=d?`${s} ${d}`:s;if(!l.trim())continue;let u=O(a);t.push({id:`${e}#media-image-${t.length}`,url:e,headingText:s,headingId:`media-image-${t.length}`,breadcrumb:"",bodyText:l,type:"media",selector:u[0],selectors:u})}let o=Array.from(n.querySelectorAll("video, audio"));for(let a of o){let s=a.getAttribute("title")||a.getAttribute("aria-label")||"";if(!s)continue;let d="",c=Array.from(a.querySelectorAll('track[kind="captions"], track[kind="subtitles"]'));for(let f of c){let h=f.getAttribute("src");h&&(d+=`[Transcript available: ${h}] `)}let l=d?`${s} ${d}`:s;if(!l.trim())continue;let u=O(a);t.push({id:`${e}#media-${t.length}`,url:e,headingText:s,headingId:`media-${t.length}`,breadcrumb:"",bodyText:l,type:"media",selector:u[0],selectors:u,transcript:d.trim()})}return t}function ie(i,e){let t=[],n=new DOMParser().parseFromString(i,"text/html"),r=Array.from(n.querySelectorAll('script[type="application/ld+json"]'));for(let o of r)try{let a=JSON.parse(o.textContent||"{}");if(Array.isArray(a)?a.some(s=>s["@type"]==="FAQPage"):a["@type"]==="FAQPage"){let s=Array.isArray(a)?a.flatMap(d=>d.mainEntity||[]):a.mainEntity||[];for(let[d,c]of s.entries()){if(!c||!c.name)continue;let l=c.acceptedAnswer?.text||c.suggestedAnswer?.text||"";if(!l)continue;let u=`${c.name} ${l}`;t.push({id:`${e}#structured-faq-${d}`,url:e,headingText:c.name,headingId:`structured-faq-${d}`,breadcrumb:"",bodyText:u,type:"structured",structuredData:{question:c.name,answer:l}})}}else if(a["@type"]){let s=a["@type"],d=a.name||a.headline||"",c=a.description||"";if(!d&&!c)continue;let l=`${d} ${c}`.trim();if(!l)continue;t.push({id:`${e}#structured-${s.toLowerCase()}-${t.length}`,url:e,headingText:d||"Structured Data",headingId:`structured-${s.toLowerCase()}-${t.length}`,breadcrumb:"",bodyText:l,type:"structured",structuredData:a})}}catch{continue}return t}var at=new Set(["utm_source","utm_medium","utm_campaign","utm_term","utm_content","gclid","fbclid","mc_cid","mc_eid","mc_id","referrer","ref","source","campaign","click_id"]);function Ue(i){try{let e=new URL(i),t=new URLSearchParams(e.search);for(let n of at)t.delete(n);for(let[n,r]of t.entries())r||t.delete(n);return e.search=t.toString(),e.toString()}catch{return i}}function Q(i,e){if(!i)return i;let t=i.replace(/\/$/,"");if(t=Ue(t),e){let n=e.match(/]*rel=["']canonical["'][^>]*href=["']([^"']+)["']/i);if(n?.[1]){let r=Ue(n[1]);if(r!==t)return r.replace(/\/$/,"")}}return t}function Le(i,e){if(!i)return e;try{return new URL(i,e).toString()}catch{return i}}K();function R(i){let e={"&":"&","<":"<",">":">",'"':""","'":"'"},t="";for(let n=0;n${a}${s}`}function $e(i,e){if(!e.trim())return i.slice(0,90)+"\u2026";let t=i.toLowerCase(),n=e.toLowerCase(),r=t.indexOf(n);if(r===-1)return i.slice(0,90)+"\u2026";let o=Math.max(0,r-30),a=Math.min(i.length,r+e.length+40);return(o>0?"\u2026":"")+i.slice(o,a)+(a';case"action":return'';case"field":return'';case"link":return'';case"file":return'';case"media":return'';case"structured":return'';default:return''}}function xe(i){switch(i){case"section":return"Section";case"action":return"Action";case"field":return"Field";case"link":return"Link";case"file":return"File";case"media":return"Media";case"structured":return"Answer";default:return"Section"}}var _=class{constructor(){this.host=null;this.root=null;this.input=null;this.resultsList=null;this.focusableElements=[];this.resultMouseEnterHandler=null;this.resultClickHandler=null;this.isOpen=!1;this.modeChangeCallback=null;this.activeCategory="all";this.categoryChangeCallback=null;this.settingsChangeCallback=null;this.rebuildIndexCallback=null;this.toggleInspectorCallback=null;this.onOpenCallback=null;this.isSettingsOpen=!1}getHost(){return this.host}getRoot(){return this.root}getInput(){return this.input}getResultsList(){return this.resultsList}getIsOpen(){return this.isOpen}getFocusableElements(){return this.focusableElements}getActiveCategory(){return this.activeCategory}getOnOpenCallback(){return this.onOpenCallback}setIsOpen(e){this.isOpen=e}clearFocusableElements(){this.focusableElements=[]}setCategoryCallback(e){this.categoryChangeCallback=e}setSettingsCallback(e){this.settingsChangeCallback=e}setRebuildIndexCallback(e){this.rebuildIndexCallback=e}setToggleInspectorCallback(e){this.toggleInspectorCallback=e}setOnOpenCallback(e){this.onOpenCallback=e}renderUI(e,t,n){this.modeChangeCallback=n;let r=document.createElement("div");if(r.className="reef-host is-hidden",document.body)document.body.appendChild(r);else{let T=()=>{document.body&&document.body.appendChild(r)};document.addEventListener("DOMContentLoaded",T)}this.host=r,this.root=r.attachShadow({mode:"open"});let o=this.root;o.innerHTML=` +"use strict";var ReefSearch=(()=>{var ot=Object.defineProperty;var $e=(s,e,t)=>()=>{if(t)throw t[0];try{return s&&(e=s(s=0)),e}catch(n){throw t=[n],n}};var Ne=(s,e)=>{for(var t in e)ot(s,t,{get:e[t],enumerable:!0})};var et={};Ne(et,{addSectionsToIndex:()=>Ge,addToIndex:()=>P,createSearchIndex:()=>F,deserializeIndex:()=>he,facets:()=>ge,findClosestWord:()=>de,getAllSections:()=>ae,getPopularQueries:()=>me,getSnippet:()=>Xe,getTotalResultCount:()=>le,levenshteinDistance:()=>z,parseExtendedQuery:()=>qe,removeFromIndex:()=>He,searchSections:()=>N,searchWithPagination:()=>ce,serializeIndex:()=>ue,suggest:()=>fe,trackQuery:()=>pe,updateRecord:()=>Ze});function De(){return{children:new Map,records:[]}}function W(s,e,t){let n=s;for(let r=0;ra.id===t.id)||n.records.push(t)}}function Qe(s,e,t){let n=s,r=[];for(let i=0;il.id!==t),o.push(a))}for(let i of o)i.records.length===0&&i.children.size}function ct(s,e){let t=s;for(let n=0;n{a.length>=2&&r.add(a)}),n.toLowerCase().split(/\s+/).forEach(a=>{a.length>=2&&r.add(a)});let o=new Set;for(let[a,i]of s.queryCache.entries()){if(i.resultIds.includes(e)){o.add(a);continue}let l=a.toLowerCase().split(/\s+/).filter(c=>c.length>=2);for(let c of l)if(r.has(c)){o.add(a);break}}if(o.size>0)for(let a of o)s.queryCache.delete(a);else s.queryCache.clear()}function lt(s,e){if(s.size===0||e.size===0)return 0;let t=0;for(let r of s)e.has(r)&&t++;let n=s.size+e.size-t;return t/n}function Ve(s){let e=s.toLowerCase().split(/\s+/);return new Set(e.filter(t=>t.length>2))}function dt(s,e=.5){if(s.length<=1)return s;let t=[],n=[],r=[...s].sort((o,a)=>a[1]-o[1]);t.push(r[0]),n.push(Ve(r[0][0].headingText+" "+r[0][0].bodyText));for(let o=1;ot[h][1]){t.splice(h,0,[a,u]),n.splice(h,0,l),f=!0;break}f||(t.push([a,u]),n.push(l))}return t}function F(){return{headingIndex:new Map,headingIds:new Map,bodyIndex:new Map,allSections:[],queryCache:new Map,popularQueries:[],docFrequency:new Map,fieldDocFreq:{headingText:new Map,bodyText:new Map,label:new Map,breadcrumb:new Map},totalDocs:0,queryPopularity:new Map,version:1,headingTrie:De()}}function P(s,e,t){s.totalDocs+=e.length;for(let n of e){let r=n.headingText.toLowerCase();if(r.length>=2){let l=s.headingIds.get(r)??[];l.push(n),s.headingIds.set(r,l);let c=r.normalize("NFD").replace(/[\u0300-\u036f]/g,"");if(c!==r){let u=s.headingIds.get(c)??[];u.push(n),s.headingIds.set(c,u)}}for(let l=2;l<=r.length;l++){let c=r.slice(0,l),u=s.headingIndex.get(c)??[];u.push(n),s.headingIndex.set(c,u)}let o=r.normalize("NFD").replace(/[\u0300-\u036f]/g,"");for(let l=2;l<=o.length;l++){let c=o.slice(0,l);if(c!==r.slice(0,l)){let u=s.headingIndex.get(c)??[];u.push(n),s.headingIndex.set(c,u)}}W(s.headingTrie,r,n),o!==r&&W(s.headingTrie,o,n);let i=n.bodyText.toLowerCase().split(/\s+/);for(let l of i)if(l.length>=3){let c=s.bodyIndex.get(l)??[];c.push(n),s.bodyIndex.set(l,c);let u=l.normalize("NFD").replace(/[\u0300-\u036f]/g,"");if(u!==l){let f=s.bodyIndex.get(u)??[];f.push(n),s.bodyIndex.set(u,f)}}for(let l of i)if(l.length>=2){let c=s.docFrequency.get(l)??0;s.docFrequency.set(l,c+1);let u=s.fieldDocFreq.bodyText.get(l)??0;s.fieldDocFreq.bodyText.set(l,u+1)}let d=r.split(/\s+/);for(let l of d)if(l.length>=2){let c=s.fieldDocFreq.headingText.get(l)??0;s.fieldDocFreq.headingText.set(l,c+1)}if(n.label){let c=n.label.toLowerCase().split(/\s+/);for(let u of c)if(u.length>=2){let f=s.fieldDocFreq.label.get(u)??0;s.fieldDocFreq.label.set(u,f+1)}}if(n.breadcrumb){let c=n.breadcrumb.toLowerCase().split(/\s+/);for(let u of c)if(u.length>=2){let f=s.fieldDocFreq.breadcrumb.get(u)??0;s.fieldDocFreq.breadcrumb.set(u,f+1)}}if(n.label){let l=n.label.toLowerCase();if(l.length>=2){let c=s.bodyIndex.get(l)??[];c.push(n),s.bodyIndex.set(l,c)}}if(n.type==="structured"&&n.structuredData){if(n.structuredData.question){let l=n.structuredData.question.toLowerCase().split(/\s+/);for(let c of l)if(c.length>=3){let u=s.bodyIndex.get(c)??[];u.push(n),s.bodyIndex.set(c,u)}}if(n.structuredData.answer){let l=n.structuredData.answer.toLowerCase().split(/\s+/);for(let c of l)if(c.length>=3){let u=s.bodyIndex.get(c)??[];u.push(n),s.bodyIndex.set(c,u)}}}}s.allSections.push(...e)}function ae(s){return s.allSections}function ce(s,e,t={}){let n=Math.max(1,t.pageSize??t.limit??20),r=t.cursor?Math.max(0,Number.parseInt(t.cursor,10)||0):0,o=N(s,e,{...t,limit:Number.MAX_SAFE_INTEGER}),a=o.slice(r,r+n).map(d=>"score"in d?d:{record:d,score:0}),i=r+a.length;return{results:a,total:o.length,hasMore:i{if(!a||Math.abs(a.length-n.length)>t)return;let i=z(a,n),d=r;i<=t&&(!d||i0;){let i=!1,d=n.exec(t);if(d&&d.index===0){e.push({type:"exact",value:d[1]}),t=t.slice(d[0].length),i=!0;continue}let l=r.exec(t);if(l&&l.index===0){e.push({type:"exclude",value:l[1]}),t=t.slice(l[0].length),i=!0;continue}let c=o.exec(t);if(c&&c.index===0){e.push({type:"prefix",value:c[1]}),t=t.slice(c[0].length),i=!0;continue}let u=a.exec(t);if(u&&u.index===0){e.push({type:"suffix",value:u[1]}),t=t.slice(u[0].length),i=!0;continue}if(t.match(/^\|/)){e.push({type:"or",value:"|"}),t=t.slice(1),i=!0;continue}let h=t.match(/^\s+/);if(h&&!i){t=t.slice(h[0].length);continue}let m=t.match(/^(\S+)/);if(m){let y=m[1].match(/^([\w-]+):(.+)$/);e.push({type:"term",value:y?`${y[1]}:${y[2]}`:m[1]}),t=t.slice(m[0].length),i=!0}}return e}function ft(s){let e={type:"and",children:[]},t=null;for(let n of s)n.type==="or"?t||(t={type:"or",children:[]},e.children.push(t)):(t&&(e.children.push(t),t=null),e.children.push({type:n.type,value:n.value}));return t&&e.children.push(t),e}function Ke(s,e,t){let n=new Set,r=s.length;for(let[o,a]of e.headingIds)if(Math.abs(o.length-r)<=t)for(let i of a)n.add(i);for(let[o,a]of e.bodyIndex)if(Math.abs(o.length-r)<=t)for(let i of a)n.add(i);return n}function Ae(s,e){switch(s.type){case"term":let t=s.value.match(/^([\w-]+):(.+)$/),n=(t?t[2]:s.value).toLowerCase(),r=t?.[1].toLowerCase();return(r==="title"||r==="heading"?[e.headingText]:r==="body"?[e.bodyText]:r==="label"?[e.label??""]:r==="breadcrumb"?[e.breadcrumb]:r==="type"?[e.type]:[e.headingText,e.bodyText,e.label??"",e.breadcrumb]).some(h=>h.toLowerCase().includes(n));case"exact":let a=s.value.toLowerCase(),i=Q(a),d=Q(e.headingText),l=Q(e.bodyText);return se(d,i)||se(l,i);case"exclude":let c=s.value.toLowerCase();return!e.headingText.toLowerCase().includes(c)&&!e.bodyText.toLowerCase().includes(c);case"prefix":let u=s.value.toLowerCase();return e.headingText.toLowerCase().startsWith(u)||e.bodyText.toLowerCase().startsWith(u);case"suffix":let f=s.value.toLowerCase();return e.headingText.toLowerCase().endsWith(f)||e.bodyText.toLowerCase().endsWith(f);case"and":return s.children.every(h=>Ae(h,e));case"or":return s.children.some(h=>Ae(h,e))}}function Le(s,e,t,n,r,o=1.5,a=.75){let i=Math.log((t-e+.5)/(e+.5)+1),d=1-a+a*(n/r);return i*(s*(o+1)/(s+o*d))}function gt(s,e,t,n,r=1.5,o=.75){let a=Ye(t),i=t.totalDocs,d=s.headingText.length*(n.headingText||1)+s.bodyText.length*(n.bodyText||1)+(s.label?s.label.length*(n.label||1):0)+(s.breadcrumb?s.breadcrumb.length*(n.breadcrumb||1):0),l=0;for(let c of e){let u=c.toLowerCase(),f=0,h=0;if(n.headingText){let m=s.headingText.toLowerCase(),y=ie(m,u),C=t.fieldDocFreq.headingText.get(u)||1;f+=y*(n.headingText||1),h=Math.max(h,C)}if(n.bodyText){let m=s.bodyText.toLowerCase(),y=ie(m,u),C=t.fieldDocFreq.bodyText.get(u)||1;f+=y*(n.bodyText||1),h=Math.max(h,C)}if(s.label&&n.label){let m=s.label.toLowerCase(),y=ie(m,u),C=t.fieldDocFreq.label.get(u)||1;f+=y*(n.label||1),h=Math.max(h,C)}if(s.breadcrumb&&n.breadcrumb){let m=s.breadcrumb.toLowerCase(),y=ie(m,u),C=t.fieldDocFreq.breadcrumb.get(u)||1;f+=y*(n.breadcrumb||1),h=Math.max(h,C)}if(f>0&&h>0){let m=Math.log((i-h+.5)/(h+.5)+1),y=1-o+o*(d/a),C=m*(f*(r+1)/(f+r*y));l+=C}}return l}function ie(s,e){if(!s||!e)return 0;let t=s.split(/\s+/),n=0;for(let r of t)r===e&&n++;return n}function Q(s){return s?s.toLowerCase().split(/\s+/).filter(e=>e.length>0):[]}function se(s,e){if(e.length===0)return!0;if(e.length>s.length)return!1;for(let t=0;t<=s.length-e.length;t++){let n=!0;for(let r=0;rt*2?"...":"");let r=[...e].sort((w,k)=>w.start-k.start),o=r[0];for(let w of r)w.end-w.start>o.end-o.start&&(o=w);let a=o.start,i=o.end,d=Math.max(0,a-t),l=Math.min(n.length,i+t);d===0&&(l=Math.min(n.length,i+t*2)),l===n.length&&(d=Math.max(0,a-t*2));let c=n.slice(d,l),u=d>0?"...":"",f=l"+C+""+I+f}function pt(s,e){if(e.length<=1)return 1;let t=new Map;for(let o=0;o0?1+(n-r)/r:1}function Ye(s){return s.allSections.length===0?50:s.allSections.reduce((t,n)=>t+n.bodyText.length+n.headingText.length,0)/s.allSections.length}function mt(s,e){let t=e.queryCache.get(s);if(t){let n=new Set;for(let r of t.resultIds){let o=e.allSections.find(a=>a.id===r);o&&n.add(o)}return n}return null}function _e(s,e,t){let n={resultIds:e.map(r=>r.id),timestamp:Date.now()};if(t.queryCache.set(s,n),t.queryCache.size>100){let r=t.queryCache.keys().next().value;r!==void 0&&t.queryCache.delete(r)}}function N(s,e,t){let n=8,r;typeof t=="number"?n=t:t&&(r=t,n=t.limit??8);let o=s.trim();if(!o)return e.allSections.slice(0,n);let a=o.normalize("NFD").replace(/[\u0300-\u036f]/g,""),i=new Map,d=new Map,l={...ut,...r?.weights??{}},c=Ye(e),u=(g,b,p,x,v)=>{i.set(g,(i.get(g)??0)+b);let S=d.get(g)??[];S.push({key:p,start:x,end:v}),d.set(g,S)},f=r?.scoringAlgorithm==="bm25",h=r?.scoringAlgorithm==="bm25f",m=r?.bm25fOptions||{k1:1.5,b:.75},y=mt(a.toLowerCase(),e),C=y?[...y]:e.allSections,I=e.headingIds.get(a.toLowerCase())??e.headingIds.get(o.toLowerCase());if(I)for(let g of I){let b=l.headingText??1,p=$(g.headingText.length),x;if(f){let S=e.docFrequency.get(a.toLowerCase())??1;x=Le(1,S,e.totalDocs,g.headingText.length+g.bodyText.length,c)*100}else x=100*b*(1+p);u(g,x,"headingText",0,g.headingText.length)}let w=e.headingIndex.get(a.toLowerCase())??e.headingIndex.get(o.toLowerCase());if(w)for(let g of w){if(i.has(g))continue;let b=l.headingText??1,p=$(g.headingText.length),x;if(f){let M=e.docFrequency.get(a.toLowerCase())??1;x=Le(1,M,e.totalDocs,g.headingText.length+g.bodyText.length,c)*50}else x=50*b*(1+p);let v=Re(g.headingText,o);if(v.length>0){let S=v[0];u(g,x,"headingText",S.start,S.end)}else u(g,x,"headingText",0,Math.min(o.length,g.headingText.length))}let k=a.toLowerCase().split(/\s+/);for(let g of k){let b=e.bodyIndex.get(g);if(b)for(let p of b){if(i.has(p))continue;let x=l.bodyText??1,v=$(p.bodyText.length),S;if(f){let T=e.docFrequency.get(g)??1;S=Le(1,T,e.totalDocs,p.headingText.length+p.bodyText.length,c)*20}else S=20*x*(1+v);let M=Re(p.bodyText,o);if(M.length>0){let R=M[0];u(p,S,"bodyText",R.start,R.end)}else u(p,S,"bodyText",0,Math.min(g.length,p.bodyText.length))}}if(h&&i.size===0){let g=a.toLowerCase().split(/\s+/);for(let b of e.allSections){let p=gt(b,g,e,l,m.k1,m.b);p>0&&u(b,p*100,"bm25f",0,Math.min(a.length,b.headingText.length+b.bodyText.length))}}if(i.size>0){let g=a.toLowerCase().split(/\s+/);if(g.length>1)for(let[b,p]of i.entries()){let x=Q(b.headingText),v=Q(b.bodyText),S=g.every(R=>x.includes(R)),M=g.every(R=>v.includes(R));if(S||M){let R=[...x,...v],T=pt(R,g);i.set(b,p*T)}(se(x,g)||se(v,g))&&i.set(b,p*1.5)}}if(r?.trackPopularity){let g=r.popularityBoost||1.2;for(let[b,p]of i.entries()){let x=`${a.toLowerCase()}||${b.id}`,v=e.queryPopularity.get(x);if(v){let S=1+(v.count-1)*.1;i.set(b,p*S*g)}}}if(r?.fuzzy){let g=r.fuzzyDistance??2;if(i.size===0){let b=Ke(o.toLowerCase(),e,1);for(let p of b){if(i.has(p))continue;let x=p.headingText.toLowerCase();if(Re(x,o).length===0){let T=z(x,o);if(T<=1){let H=(1-T/Math.max(o.length,x.length))*60,A=l.headingText??1,rt=$(p.headingText.length);u(p,H*A*(1+rt),"headingText",0,Math.min(o.length,x.length))}}if(i.has(p))continue;let S=p.bodyText.toLowerCase(),M=1/0,R=0;for(let T=0;T<=S.length-o.length;T++){let H=S.slice(T,T+o.length),A=z(o,H);Ar.filter(g)));let L=E.sort((g,b)=>b[1]-g[1]);if(r?.sortFn&&(L=L.sort((g,b)=>{let p=d.get(g[0])??[],x=d.get(b[0])??[];return r.sortFn({record:g[0],score:g[1],matches:p},{record:b[0],score:b[1],matches:x})})),r?.diversify){let g=r.mmrLambda??.5;L=dt(L,g)}let D=L.slice(0,n).map(([g])=>g);if(r?.typeWeights){let g=r.typeWeights;for(let p of D){let x=g[p.type]??1,v=i.get(p);v!==void 0&&x!==1&&i.set(p,v*x)}L=[...i.entries()].sort((p,x)=>x[1]-p[1]);let b=L.slice(0,n).map(([p])=>p);return _e(o.toLowerCase(),b,e),r?.includeScore||r?.includeMatches?b.map(p=>{let x=d.get(p)??[],v=i.get(p)??0,M=Math.max(0,Math.min(1,1-v/(f?100:200)));return{record:p,score:M,matches:r.includeMatches?x:void 0}}):b}return _e(o.toLowerCase(),D,e),r?.includeScore||r?.includeMatches?D.map(g=>{let b=d.get(g)??[],p=i.get(g)??0,v=Math.max(0,Math.min(1,1-p/(f?100:200)));return{record:g,score:v,matches:r.includeMatches?b:void 0}}):D}function He(s,e){let t=s.allSections.findIndex(d=>d.id===e);if(t===-1)return;let n=s.allSections[t];s.allSections.splice(t,1),s.totalDocs=Math.max(0,s.totalDocs-1),Je(s,n.id,n.headingText,n.bodyText);let r=n.headingText.toLowerCase(),o=s.headingIds.get(r);if(o){let d=o.findIndex(l=>l.id===e);d!==-1&&o.splice(d,1),o.length===0&&s.headingIds.delete(r)}for(let d=2;d<=r.length;d++){let l=r.slice(0,d),c=s.headingIndex.get(l);if(c){let u=c.findIndex(f=>f.id===e);u!==-1&&c.splice(u,1),c.length===0&&s.headingIndex.delete(l)}}Qe(s.headingTrie,r,e);let a=n.headingText.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g,"");a!==r&&Qe(s.headingTrie,a,e);let i=n.bodyText.toLowerCase().split(/\s+/);for(let d of i){if(d.length<3)continue;let l=s.bodyIndex.get(d);if(l){let c=l.findIndex(u=>u.id===e);c!==-1&&l.splice(c,1),l.length===0&&s.bodyIndex.delete(d)}}}function Ze(s,e){He(s,e.id),P(s,[e]),Je(s,e.id,e.headingText,e.bodyText)}function ue(s){let e={allSections:s.allSections,headingIndex:Array.from(s.headingIndex.entries()),headingIds:Array.from(s.headingIds.entries()),bodyIndex:Array.from(s.bodyIndex.entries()),docFrequency:Array.from(s.docFrequency.entries()),fieldDocFreq:{headingText:Array.from(s.fieldDocFreq.headingText.entries()),bodyText:Array.from(s.fieldDocFreq.bodyText.entries()),label:Array.from(s.fieldDocFreq.label.entries()),breadcrumb:Array.from(s.fieldDocFreq.breadcrumb.entries())},totalDocs:s.totalDocs,popularQueries:s.popularQueries,queryPopularity:Array.from(s.queryPopularity.entries()),version:s.version};return JSON.stringify(e)}function he(s){let e=JSON.parse(s),t={headingText:new Map,bodyText:new Map,label:new Map,breadcrumb:new Map},n=new Map;if(e.queryPopularity&&Array.isArray(e.queryPopularity))for(let[o,a]of e.queryPopularity)typeof a=="object"&&a!==null&&n.set(o,a);if(e.fieldDocFreq)t.headingText=new Map(e.fieldDocFreq.headingText??[]),t.bodyText=new Map(e.fieldDocFreq.bodyText??[]),t.label=new Map(e.fieldDocFreq.label??[]),t.breadcrumb=new Map(e.fieldDocFreq.breadcrumb??[]);else if(e.docFrequency)for(let[o,a]of e.docFrequency)t.bodyText.set(o,a),t.headingText.set(o,Math.floor(a*.3)),t.label.set(o,Math.floor(a*.1)),t.breadcrumb.set(o,Math.floor(a*.1));let r=De();if(e.headingIndex&&Array.isArray(e.headingIndex)){let o=new Map(e.headingIndex),a=new Set;for(let[i,d]of o.entries())if(i.length>=2&&Array.isArray(d))for(let l of d){if(a.has(l.id))continue;a.add(l.id);let c=l.headingText.toLowerCase();W(r,c,l);let u=c.normalize("NFD").replace(/[\u0300-\u036f]/g,"");u!==c&&W(r,u,l)}}else{let o=e.allSections??[];for(let a of o){let i=a.headingText.toLowerCase();W(r,i,a);let d=i.normalize("NFD").replace(/[\u0300-\u036f]/g,"");d!==i&&W(r,d,a)}}return{allSections:e.allSections??[],headingIndex:new Map(e.headingIndex??[]),headingIds:new Map(e.headingIds??[]),bodyIndex:new Map(e.bodyIndex??[]),queryCache:new Map,popularQueries:e.popularQueries??[],docFrequency:new Map(e.docFrequency??[]),fieldDocFreq:t,totalDocs:e.totalDocs??0,queryPopularity:n,version:e.version??1,headingTrie:r}}function fe(s,e,t=10){let n=s.trim().toLowerCase();if(!n||n.length<2)return[];let r=[],o=new Set,a=ct(e.headingTrie,n);for(let i of a){if(r.length>=t)break;let d=i.headingText;o.has(d)||(o.add(d),r.push(d))}if(r.length=t)break;let u=c.headingText;o.has(u)||(o.add(u),r.push(u))}}if(r.length=t)break;i.includes(n)&&!o.has(d[0].headingText)&&(o.add(d[0].headingText),r.push(d[0].headingText))}return r.slice(0,t)}function ge(s){let e={section:0,action:0,field:0,link:0,file:0,media:0,structured:0};for(let t of s.allSections)e[t.type]=(e[t.type]??0)+1;return e}function pe(s,e,t){let n=e.trim();if(n&&(s.popularQueries.length>=100&&s.popularQueries.shift(),s.popularQueries.push(n),t)){let r=`${n}||${t}`,o=s.queryPopularity.get(r);o?o.count++:s.queryPopularity.set(r,{query:n,recordId:t,count:1})}}function me(s,e=5){let t=new Map;for(let n of s.popularQueries)t.set(n,(t.get(n)??0)+1);return[...t.entries()].sort((n,r)=>r[1]-n[1]).slice(0,e).map(([n])=>n)}var Ge,ut,V=$e(()=>{"use strict";Ge=P;ut={headingText:2,bodyText:1,label:1.5,breadcrumb:.5}});var ye={};Ne(ye,{clearCache:()=>St,loadIndex:()=>vt,loadSiteGraph:()=>Tt,openDB:()=>U,saveIndex:()=>wt,saveSiteGraph:()=>Ct});async function U(){return new Promise((s,e)=>{let t=indexedDB.open(bt,1);t.onsuccess=()=>s(t.result),t.onerror=()=>e(t.error),t.onupgradeneeded=()=>{let n=t.result;n.objectStoreNames.contains(q)||n.createObjectStore(q,{keyPath:Oe})}})}async function yt(s){if(typeof window>"u"||!window.CompressionStream)return{compressed:!1,data:s};try{let t=new Blob([s]).stream().pipeThrough(new CompressionStream("gzip")),n=[],r=t.getReader();for(;;){let{done:d,value:l}=await r.read();if(d)break;n.push(new Uint8Array(l))}let o=n.reduce((d,l)=>d+l.length,0),a=new Uint8Array(o),i=0;for(let d of n)a.set(d,i),i+=d.length;return{compressed:!0,data:a.buffer}}catch{return{compressed:!1,data:s}}}async function xt(s){if(typeof s=="string")return s;if(typeof window>"u"||!window.DecompressionStream)throw new Error("Compressed data but no decompression available");try{let t=new Blob([s]).stream().pipeThrough(new DecompressionStream("gzip")),n=[],r=t.getReader(),o=new TextDecoder;for(;;){let{done:a,value:i}=await r.read();if(a)break;n.push(o.decode(i,{stream:!0}))}return n.join("")+o.decode()}catch{throw new Error("Failed to decompress data")}}async function wt(s,e){let r=(await U()).transaction(q,"readwrite").objectStore(q),o=ue(s),{compressed:a,data:i}=await yt(o),d={[Oe]:e.versionHash,index:i,metadata:e,compressed:a};await new Promise((l,c)=>{let u=r.put(d);u.onsuccess=()=>l(void 0),u.onerror=()=>c(u.error)})}async function vt(s){let n=(await U()).transaction(q,"readonly").objectStore(q),r=await new Promise((l,c)=>{let u=n.getAll();u.onsuccess=()=>l(u.result),u.onerror=()=>c(u.error)});if(!r.length)return null;let o=r[0],a=o.index;if(o.compressed)try{a=await xt(o.index)}catch(l){if(console.warn("[reef] decompression failed, trying uncompressed fallback:",l),typeof o.index=="string")a=o.index;else return null}else if(typeof o.index=="string")a=o.index;else return null;let i=he(a),d=o.metadata;return s&&d.buildTime&&Date.now()-d.buildTime>s?null:{index:i,metadata:d}}async function St(){let t=(await U()).transaction(q,"readwrite").objectStore(q);await new Promise((n,r)=>{let o=t.clear();o.onsuccess=()=>n(void 0),o.onerror=()=>r(o.error)})}async function Ct(s,e="site-graph"){(await U()).transaction(q,"readwrite").objectStore(q).put({[Oe]:`${e}:${s.startUrl}`,graph:s,metadata:{versionHash:e,buildTime:Date.now(),pageMetadata:{}}})}async function Tt(s,e="site-graph"){let n=(await U()).transaction(q,"readonly");return new Promise(r=>{let o=n.objectStore(q).get(`${e}:${s}`);o.onsuccess=()=>r(o.result?.graph??null),o.onerror=()=>r(null)})}var bt,q,Oe,xe=$e(()=>{"use strict";V();bt="reef-index",q="indices",Oe="version"});function Ie(s){let e="",t=!1;for(let n=0;n"&&t===!0?t=!1:t===!1&&(e+=r)}return e.replace(/\s+/g," ").trim()}function We(s){let e=[],t=s;for(;t&&t.nodeType===Node.ELEMENT_NODE;){let n=t.tagName.toLowerCase();if(t.id){n+=`#${t.id}`,e.unshift(n);break}else if(t.className){let a=t.className.trim().split(/\s+/);a.length&&(n+=`.${a.join(".")}`)}let r=1,o=t.previousElementSibling;for(;o;)o.tagName===t.tagName&&r++,o=o.previousElementSibling;r>1&&(n+=`:nth-child(${r})`),e.unshift(n),t=t.parentElement}return e.length>0?e.join(" > "):s.tagName.toLowerCase()}function O(s){let e=[],t=l=>{l&&!e.includes(l)&&e.push(l)},n=l=>typeof CSS<"u"&&CSS.escape?CSS.escape(l):l.replace(/(["\\])/g,"\\$1");for(let l of["data-testid","data-test","data-agent-id","id"]){let c=s.getAttribute(l);c&&t(l==="id"?`#${n(c)}`:`[${l}="${n(c)}"]`)}let r=s.getAttribute("aria-label");r&&t(`[aria-label="${n(r)}"]`);let o=s.getAttribute("role"),a=Z(s);o&&a&&t(`[role="${n(o)}"][aria-label="${n(a)}"]`),o&&t(`[role="${n(o)}"]`),t(We(s));let i=s.previousElementSibling,d=1;for(;i;)i.tagName===s.tagName&&d++,i=i.previousElementSibling;return t(`xpath=//${s.tagName.toLowerCase()}[${d}]`),e}function it(s,e,t=[]){let n=s instanceof Document?Array.from(s.documentElement?[s.documentElement]:[]):[s],r=(a,i)=>{e(a,i),a.shadowRoot&&o(a.shadowRoot,i);for(let d of Array.from(a.children))r(d,i);if(a.tagName.toLowerCase()==="iframe")try{let d=a.contentDocument;d&&o(d,[...i,Array.from(a.parentElement?.children||[]).indexOf(a)])}catch{}},o=(a,i)=>{if(a instanceof Document)a.documentElement&&r(a.documentElement,i);else if(a instanceof ShadowRoot)for(let d of Array.from(a.children))r(d,i);else r(a,i)};for(let a of n)r(a,t)}function st(s){let e=s.tagName.toLowerCase();return["a","button","input","textarea","select","summary"].includes(e)||["button","link","tab","menuitem","checkbox","radio","combobox","option"].includes(s.getAttribute("role")||"")||s.hasAttribute("contenteditable")||s.hasAttribute("tabindex")&&Number(s.getAttribute("tabindex"))>=0||s.hasAttribute("data-reef-action")}function X(s=document){let e=[];return it(s,(t,n)=>{if(!st(t))return;let r=Z(t);if(!r)return;let o=O(t);e.push({id:`${typeof location<"u"?location.href:""}#accessibility-${e.length}`,url:typeof location<"u"?location.href:"",headingText:r,headingId:`accessibility-${e.length}`,breadcrumb:"",bodyText:r,type:t.matches("input,textarea,select,[contenteditable]")?"field":"action",selector:o[0],selectors:o,iframePath:n,label:r,destructive:ke(r)})}),e}function Ue(s,e){let t=s.match(/\bid=["']([^"']+)['"]/i);return t?.[1]?t[1]:e.toLowerCase().replace(/[^a-z0-9]+/g,"-")||Math.random().toString(36).slice(2)}function Be(s){return/\bid=["'][^"']+["']/i.test(s)}function je(s,e){let t=s.slice(e,e+500),n=t.match(/]*id="([^"]+)"/i);if(n?.[1])return n[1];let r=t.match(/]*id="([^"]+)"/i);return r?.[1]?r[1]:null}var Te=new Map;function Y(s,e){if(Te.has(e))return Te.get(e);let t=s.replace(//gi," ").replace(//gi," ").replace(//gi," ").replace(//g," "),n=[],r=/<(h[1-6])[^>]*>([\s\S]*?)<\/h[1-6]>/gi,o;for(r.lastIndex=0;(o=r.exec(t))!==null;){let[,d,l]=o,c=Ie(l),u=parseInt(d[1],10);n.push({level:u,index:o.index,text:c,id:Ue(o[0],c),hasRealId:Be(o[0])})}let a=n.length,i=new Array(a);for(let d=0;d0&&(y+=" \u203A "),y+=n[w].text;let C=l.hasRealId?null:je(t,l.index+l.text.length),I=l.hasRealId?"#"+l.id:C?"#"+C:void 0;i[d]={id:`${e}#${l.id}`,url:`${e}#${l.id}`,headingText:l.text,headingId:l.id,breadcrumb:y,bodyText:m,type:"section",selector:I}}return Te.set(e,i),i}function Z(s){let e=s.getAttribute("aria-label");if(e?.trim())return e.trim();let t=s.getAttribute("aria-labelledby");if(t){let o=document.getElementById(t);if(o?.textContent?.trim())return o.textContent.trim()}let n=s.textContent?.trim();if(n)return n;let r=s.getAttribute("title");return r?.trim()?r.trim():null}function ke(s){let e=["delete","remove","cancel subscription","unsubscribe","pay","checkout","submit order","confirm"],t=s.toLowerCase();return e.some(n=>t.includes(n))}function ee(s,e,t){let n=[],r=new DOMParser().parseFromString(s,"text/html"),o=["button",'[role="button"]','input[type="button"]','input[type="submit"]',"summary","[data-reef-action]"],a=Array.from(r.querySelectorAll(o.join(",")));for(let i of a){if(t&&i.matches(t))continue;let d=Z(i);if(!d)continue;let l=O(i);n.push({id:`${e}#action-${n.length}`,url:e,headingText:d,headingId:`action-${n.length}`,breadcrumb:"",bodyText:d,type:"action",selector:l[0],selectors:l,destructive:ke(d),label:d})}return n}function te(s,e){let t=[],n=new DOMParser().parseFromString(s,"text/html"),r=Array.from(n.querySelectorAll("form"));for(let o of r){let a="",i=o.parentElement;for(;i&&i!==n.body;){if(i.matches('h1, h2, h3, h4, h5, h6, article, section, [role="main"], main')){let l=i.textContent?.trim()||"";l&&(a=l);break}i=i.parentElement}let d=Array.from(o.querySelectorAll("input, textarea, select"));for(let l of d){if(l.matches('input[type="hidden"], input[type="button"], input[type="submit"], input[type="reset"]'))continue;let c="",u=l.id;if(u){let m=n.querySelector(`label[for="${u}"]`);m&&(c=m.textContent?.trim()||"")}if(!c){let m=l.closest("label");if(m){c=m.textContent?.trim()||"";let y=l;c&&y.value&&c.includes(y.value)&&(c=c.replace(y.value,"").trim())}}if(!c){let m=l;c=("placeholder"in m?m.placeholder:"")||l.getAttribute("aria-label")||""}if(!c)continue;let f=O(l),h=l;t.push({id:`${e}#field-${t.length}`,url:e,headingText:c,headingId:`field-${t.length}`,breadcrumb:a,bodyText:c,type:"field",selector:f[0],selectors:f,label:c,value:h.value})}}return t}function Ee(s){s.querySelectorAll("details").forEach(e=>{e.hasAttribute("open")||e.setAttribute("data-reef-was-closed","true"),e.setAttribute("open","")}),s.querySelectorAll('[aria-hidden="false"]').forEach(e=>{e.removeAttribute("aria-hidden")})}function B(s,e){let t=[],n=new DOMParser().parseFromString(s,"text/html"),r=Array.from(n.querySelectorAll("a[href]"));for(let o of r){if(o.hasAttribute("rel")&&o.getAttribute("rel")?.toLowerCase().includes("nofollow"))continue;let a=o.getAttribute("href");if(!a||a==="#"||a.startsWith("javascript:"))continue;let i=o.textContent?.trim()||"";if(!i)continue;let d=Me(a,e),l=!d.startsWith(window.location.origin),c=O(o);t.push({id:`${e}#link-${t.length}`,url:d,headingText:i,headingId:`link-${t.length}`,breadcrumb:"",bodyText:i,type:l?"link":"section",selector:c[0],selectors:c})}return t}function ne(s,e,t){let n=[],r=new DOMParser().parseFromString(s,"text/html"),o=t?.split(",").map(i=>i.trim().toLowerCase())??["pdf","doc","docx","xls","xlsx","ppt","pptx","zip","csv"],a=Array.from(r.querySelectorAll("a[href]"));for(let i of a){let d=i.getAttribute("href");if(!d||!o.some(h=>d.toLowerCase().endsWith(`.${h}`)||d.toLowerCase().endsWith(`.${h}?`)||d.toLowerCase().endsWith(`.${h}#`)))continue;let c=i.textContent?.trim()||d.split("/").pop()||"";if(!c)continue;let u=Me(d,e),f=O(i);n.push({id:`${e}#file-${n.length}`,url:u,headingText:c,headingId:`file-${n.length}`,breadcrumb:"",bodyText:c,type:"file",selector:f[0],selectors:f})}return n}function re(s,e){let t=[],n=new DOMParser().parseFromString(s,"text/html"),r=Array.from(n.querySelectorAll("img"));for(let a of r){let i=a.alt.trim();if(!i)continue;let d="",l=a.closest("figure");if(l){let f=l.querySelector("figcaption");f&&(d=f.textContent?.trim()||"")}let c=d?`${i} ${d}`:i;if(!c.trim())continue;let u=O(a);t.push({id:`${e}#media-image-${t.length}`,url:e,headingText:i,headingId:`media-image-${t.length}`,breadcrumb:"",bodyText:c,type:"media",selector:u[0],selectors:u})}let o=Array.from(n.querySelectorAll("video, audio"));for(let a of o){let i=a.getAttribute("title")||a.getAttribute("aria-label")||"";if(!i)continue;let d="",l=Array.from(a.querySelectorAll('track[kind="captions"], track[kind="subtitles"]'));for(let f of l){let h=f.getAttribute("src");h&&(d+=`[Transcript available: ${h}] `)}let c=d?`${i} ${d}`:i;if(!c.trim())continue;let u=O(a);t.push({id:`${e}#media-${t.length}`,url:e,headingText:i,headingId:`media-${t.length}`,breadcrumb:"",bodyText:c,type:"media",selector:u[0],selectors:u,transcript:d.trim()})}return t}function oe(s,e){let t=[],n=new DOMParser().parseFromString(s,"text/html"),r=Array.from(n.querySelectorAll('script[type="application/ld+json"]'));for(let o of r)try{let a=JSON.parse(o.textContent||"{}");if(Array.isArray(a)?a.some(i=>i["@type"]==="FAQPage"):a["@type"]==="FAQPage"){let i=Array.isArray(a)?a.flatMap(d=>d.mainEntity||[]):a.mainEntity||[];for(let[d,l]of i.entries()){if(!l||!l.name)continue;let c=l.acceptedAnswer?.text||l.suggestedAnswer?.text||"";if(!c)continue;let u=`${l.name} ${c}`;t.push({id:`${e}#structured-faq-${d}`,url:e,headingText:l.name,headingId:`structured-faq-${d}`,breadcrumb:"",bodyText:u,type:"structured",structuredData:{question:l.name,answer:c}})}}else if(a["@type"]){let i=a["@type"],d=a.name||a.headline||"",l=a.description||"";if(!d&&!l)continue;let c=`${d} ${l}`.trim();if(!c)continue;t.push({id:`${e}#structured-${i.toLowerCase()}-${t.length}`,url:e,headingText:d||"Structured Data",headingId:`structured-${i.toLowerCase()}-${t.length}`,breadcrumb:"",bodyText:c,type:"structured",structuredData:a})}}catch{continue}return t}var at=new Set(["utm_source","utm_medium","utm_campaign","utm_term","utm_content","gclid","fbclid","mc_cid","mc_eid","mc_id","referrer","ref","source","campaign","click_id"]);function ze(s){try{let e=new URL(s),t=new URLSearchParams(e.search);for(let n of at)t.delete(n);for(let[n,r]of t.entries())r||t.delete(n);return e.search=t.toString(),e.toString()}catch{return s}}function j(s,e){if(!s)return s;let t=s.replace(/\/$/,"");if(t=ze(t),e){let n=e.match(/]*rel=["']canonical["'][^>]*href=["']([^"']+)["']/i);if(n?.[1]){let r=ze(n[1]);if(r!==t)return r.replace(/\/$/,"")}}return t}function Me(s,e){if(!s)return e;try{return new URL(s,e).toString()}catch{return s}}V();function Fe(s,e){if(!e.trim())return s.slice(0,90)+"\u2026";let t=s.toLowerCase(),n=e.toLowerCase(),r=t.indexOf(n);if(r===-1)return s.slice(0,90)+"\u2026";let o=Math.max(0,r-30),a=Math.min(s.length,r+e.length+40);return(o>0?"\u2026":"")+s.slice(o,a)+(a';case"action":return'';case"field":return'';case"link":return'';case"file":return'';case"media":return'';case"structured":return'';default:return''}}function be(s){switch(s){case"section":return"Section";case"action":return"Action";case"field":return"Field";case"link":return"Link";case"file":return"File";case"media":return"Media";case"structured":return"Answer";default:return"Section"}}var K=class{constructor(){this.host=null;this.root=null;this.input=null;this.resultsList=null;this.focusableElements=[];this.resultMouseEnterHandler=null;this.resultClickHandler=null;this.isOpen=!1;this.modeChangeCallback=null;this.activeCategory="all";this.categoryChangeCallback=null;this.settingsChangeCallback=null;this.rebuildIndexCallback=null;this.toggleInspectorCallback=null;this.onOpenCallback=null;this.isSettingsOpen=!1}getHost(){return this.host}getRoot(){return this.root}getInput(){return this.input}getResultsList(){return this.resultsList}getIsOpen(){return this.isOpen}getFocusableElements(){return this.focusableElements}getActiveCategory(){return this.activeCategory}getOnOpenCallback(){return this.onOpenCallback}setIsOpen(e){this.isOpen=e}clearFocusableElements(){this.focusableElements=[]}setCategoryCallback(e){this.categoryChangeCallback=e}setSettingsCallback(e){this.settingsChangeCallback=e}setRebuildIndexCallback(e){this.rebuildIndexCallback=e}setToggleInspectorCallback(e){this.toggleInspectorCallback=e}setOnOpenCallback(e){this.onOpenCallback=e}renderUI(e,t,n){this.modeChangeCallback=n;let r=document.createElement("div");if(r.className="reef-host is-hidden",document.body)document.body.appendChild(r);else{let k=()=>{document.body&&document.body.appendChild(r)};document.addEventListener("DOMContentLoaded",k)}this.host=r,this.root=r.attachShadow({mode:"open"});let o=this.root,i=new DOMParser().parseFromString(` \n
\n
\n \n \n ESC\n \n
\n\n \n
\n
\n \n \n \n \n \n
\n
\n
\u2191\u2193 navigate \u21B5 open
\n
\n\n \n
\n
\n

User Preferences

\n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n
\n\n
\n

Developer & Agent Settings

\n
\n \n
\n
\n \n \n
\n
\n
\n
\n\n
\n

Diagnostics & Actions

\n
\n
\n Pages Indexed:\n 0\n
\n
\n Interactive Elements:\n 0\n
\n
\n Files / Downloads:\n 0\n
\n
\n \n \n
\n
\n
\n
\n
\n `, 'text/html');\n\n while (parsedDoc.head.firstChild) {\n shadow.appendChild(parsedDoc.head.firstChild);\n }\n while (parsedDoc.body.firstChild) {\n shadow.appendChild(parsedDoc.body.firstChild);\n }\n\n this.input = shadow.querySelector('input') as HTMLInputElement | null;\n if (this.input) {\n this.input.placeholder = placeholder;\n }\n this.resultsList = shadow.querySelector('.results') as HTMLElement | null;\n\n // Toggle Settings View\n const settingsToggle = shadow.querySelector('.settings-toggle-btn') as HTMLElement | null;\n const mainView = shadow.querySelector('.main-content-view') as HTMLElement | null;\n const settingsView = shadow.querySelector('.settings-view') as HTMLElement | null;\n\n settingsToggle?.addEventListener('click', () => {\n this.isSettingsOpen = !this.isSettingsOpen;\n if (this.isSettingsOpen) {\n mainView?.classList.add('is-hidden');\n settingsView?.classList.remove('is-hidden');\n settingsToggle.style.opacity = '1';\n settingsToggle.style.stroke = 'var(--primary-color)';\n } else {\n settingsView?.classList.add('is-hidden');\n mainView?.classList.remove('is-hidden');\n settingsToggle.style.opacity = '';\n settingsToggle.style.stroke = '';\n this.input?.focus();\n }\n });\n\n // Category Tabs Event Listeners\n const tabs = shadow.querySelectorAll('.tab-chip');\n tabs.forEach((tab) => {\n tab.addEventListener('click', () => {\n tabs.forEach(t => t.classList.remove('active'));\n tab.classList.add('active');\n this.activeCategory = tab.getAttribute('data-cat') || 'all';\n this.categoryChangeCallback?.(this.activeCategory);\n });\n });\n\n // Controls Event Handlers\n const themeControl = shadow.querySelector('#themeControl') as HTMLSelectElement | null;\n themeControl?.addEventListener('change', () => {\n this.settingsChangeCallback?.('theme', themeControl.value);\n });\n\n const modeControl = shadow.querySelector('#modeControl') as HTMLSelectElement | null;\n modeControl?.addEventListener('change', () => {\n this.settingsChangeCallback?.('mode', modeControl.value);\n });\n\n const hotkeyControl = shadow.querySelector('#hotkeyControl') as HTMLSelectElement | null;\n hotkeyControl?.addEventListener('change', () => {\n this.settingsChangeCallback?.('hotkey', hotkeyControl.value);\n });\n\n const actionsModeControl = shadow.querySelector('#actionsModeControl') as HTMLSelectElement | null;\n actionsModeControl?.addEventListener('change', () => {\n this.settingsChangeCallback?.('actionsMode', actionsModeControl.value);\n });\n\n const agentInspectorToggle = shadow.querySelector('#agentInspectorToggle') as HTMLInputElement | null;\n agentInspectorToggle?.addEventListener('change', () => {\n this.toggleInspectorCallback?.(!!agentInspectorToggle.checked);\n });\n\n const btnRebuildIndex = shadow.querySelector('#btnRebuildIndex') as HTMLButtonElement | null;\n btnRebuildIndex?.addEventListener('click', () => {\n btnRebuildIndex.disabled = true;\n const originalText = btnRebuildIndex.textContent;\n btnRebuildIndex.textContent = 'Rebuilding...';\n this.rebuildIndexCallback?.();\n setTimeout(() => {\n btnRebuildIndex.disabled = false;\n btnRebuildIndex.textContent = originalText;\n }, 1000);\n });\n\n const btnCopyConfig = shadow.querySelector('#btnCopyConfig') as HTMLButtonElement | null;\n btnCopyConfig?.addEventListener('click', () => {\n const config = {\n theme: themeControl?.value || 'auto',\n mode: modeControl?.value || 'regular',\n hotkey: hotkeyControl?.value || 'ctrlk,cmdk',\n actionsMode: actionsModeControl?.value || 'execute',\n };\n const json = JSON.stringify(config, null, 2);\n navigator.clipboard.writeText(json).then(() => {\n const originalText = btnCopyConfig.textContent;\n btnCopyConfig.textContent = 'Copied!';\n setTimeout(() => {\n btnCopyConfig.textContent = originalText;\n }, 1500);\n });\n });\n }\n\n setupFocusTrap(): void {\n const focusInHandler = (event: FocusEvent) => {\n if (!this.isOpen || !this.host) return;\n\n if (this.focusableElements.length === 0) {\n this.focusableElements = this.getAllFocusableElements();\n }\n\n if (this.focusableElements.length === 0) return;\n\n const first = this.focusableElements[0];\n const last = this.focusableElements[this.focusableElements.length - 1];\n const target = event.target as HTMLElement;\n\n if (event.type === 'focusin' && target) {\n if (!this.isElementInModal(target)) {\n const activeElement = document.activeElement;\n if (activeElement === first) {\n last?.focus();\n } else {\n first?.focus();\n }\n }\n }\n };\n\n document.addEventListener('focusin', focusInHandler);\n }\n\n private getAllFocusableElements(): HTMLElement[] {\n if (!this.host) return [];\n const focusableSelectors = [\n 'button', 'input', 'select', 'textarea', 'a[href]',\n '[tabindex]:not([tabindex=\"-1\"])', '[role=\"button\"]'\n ];\n return Array.from(this.host.shadowRoot?.querySelectorAll(focusableSelectors.join(',')) ?? []) as HTMLElement[];\n }\n\n private isElementInModal(element: HTMLElement): boolean {\n if (!this.host) return false;\n return this.host.contains(element) || (this.host.shadowRoot?.contains(element) ?? false);\n }\n\n applyAriaHidden(): void {\n document.body.setAttribute('aria-hidden', 'true');\n const mainContent = document.querySelector('main, [role=\"main\"], body > *:not(.reef-host)');\n if (mainContent) {\n mainContent.setAttribute('aria-hidden', 'true');\n }\n }\n\n restoreBodyAriaHidden(): void {\n document.body.removeAttribute('aria-hidden');\n const mainContent = document.querySelector('main, [role=\"main\"], body > *:not(.reef-host)');\n if (mainContent) {\n mainContent.removeAttribute('aria-hidden');\n }\n }\n\n scrollSelectedIntoView(selectedIndex: number): void {\n if (!this.resultsList) return;\n const selected = this.resultsList.querySelector(\n `.result[data-index=\"${selectedIndex}\"]`\n ) as HTMLElement | null;\n if (!selected) return;\n\n const allResults = this.resultsList.querySelectorAll('.result');\n if (!allResults.length) return;\n\n // For the first item, immediately scroll to top to avoid getting stuck\n if (selectedIndex === 0) {\n this.resultsList.scrollTop = 0;\n return;\n }\n\n const isLast = selectedIndex === allResults.length - 1;\n const block = isLast ? 'center' : 'nearest';\n selected.scrollIntoView({ block, inline: 'nearest' });\n }\n\n renderResults(\n query: string,\n results: IndexRecord[],\n selectedIndex: number,\n onResultMouseEnter: (index: number) => void,\n onResultClick: (event: MouseEvent, index: number) => void\n ): void {\n const countEl = this.root?.querySelector('#count');\n\n if (!this.resultsList) return;\n this.clearFocusableElements();\n\n if (!results.length) {\n const div = document.createElement('div');\n div.className = 'empty';\n div.textContent = `No sections match \"${query}\"`;\n this.resultsList.replaceChildren(div);\n if (countEl) countEl.textContent = '0 results';\n return;\n }\n\n if (countEl) {\n const counts: Record = {};\n for (const result of results) {\n const type = getResultTypeLabel(result.type);\n counts[type] = (counts[type] || 0) + 1;\n }\n const countParts = Object.entries(counts)\n .map(([type, count]) => `${count} ${type.toLowerCase()}${count !== 1 ? 's' : ''}`)\n .join(', ');\n countEl.textContent = countParts;\n }\n\n if (this.resultsList) {\n const fragment = document.createDocumentFragment();\n\n results.forEach((result, index) => {\n const isSelected = index === selectedIndex;\n const snippet = getSnippet(result.bodyText, query);\n const typeLabel = getResultTypeLabel(result.type);\n\n const isAction = result.type === 'action';\n const isSamePage = result.url === window.location.href.split('#')[0];\n const canExecuteHere = isAction && isSamePage && !result.destructive;\n\n const btn = document.createElement('button');\n btn.className = `result ${isSelected ? 'is-selected' : ''}`;\n btn.type = 'button';\n btn.setAttribute('data-index', String(index));\n\n const typeDiv = document.createElement('div');\n typeDiv.className = 'result-type';\n\n const iconSpan = document.createElement('span');\n iconSpan.className = 'result-type-icon';\n iconSpan.appendChild(getResultTypeIconNode(result.type));\n\n const labelSpan = document.createElement('span');\n labelSpan.className = 'result-type-label';\n labelSpan.textContent = typeLabel;\n\n typeDiv.appendChild(iconSpan);\n typeDiv.appendChild(labelSpan);\n btn.appendChild(typeDiv);\n\n if (result.breadcrumb) {\n const breadcrumbDiv = document.createElement('div');\n breadcrumbDiv.className = 'breadcrumb';\n breadcrumbDiv.textContent = result.breadcrumb;\n btn.appendChild(breadcrumbDiv);\n }\n\n const headingDiv = document.createElement('div');\n headingDiv.className = 'heading';\n appendHighlightedText(headingDiv, result.headingText, query);\n btn.appendChild(headingDiv);\n\n if (result.type === 'structured' && result.structuredData) {\n if (result.structuredData.answer || (result.structuredData.question && result.structuredData.answer)) {\n const answerDiv = document.createElement('div');\n answerDiv.className = 'answer-preview';\n if (result.structuredData.question && result.structuredData.answer) {\n const qStrong = document.createElement('strong');\n qStrong.textContent = result.structuredData.question;\n answerDiv.appendChild(qStrong);\n answerDiv.appendChild(document.createTextNode(': '));\n }\n const ansText = result.structuredData.answer;\n const truncatedAns = ansText.substring(0, 100) + (ansText.length > 100 ? '\u2026' : '');\n answerDiv.appendChild(document.createTextNode(truncatedAns));\n btn.appendChild(answerDiv);\n }\n }\n\n const snippetDiv = document.createElement('div');\n snippetDiv.className = 'match';\n appendHighlightedText(snippetDiv, snippet, query);\n btn.appendChild(snippetDiv);\n\n if (isAction) {\n const actionSpan = document.createElement('span');\n actionSpan.className = `result-action-hint ${canExecuteHere ? 'run-here' : 'go-there'}`;\n actionSpan.textContent = canExecuteHere ? '\u21B5 to run here' : '\u21B5 to go there';\n btn.appendChild(actionSpan);\n }\n\n fragment.appendChild(btn);\n });\n\n this.resultsList.replaceChildren(fragment);\n\n // Use event delegation instead of per-button listeners to avoid DOM detachment bugs\n const handleMouseEnter = (event: Event) => {\n const button = (event.target as HTMLElement).closest('button[data-index]');\n if (!button) return;\n const idx = Number(button.getAttribute('data-index')) ?? 0;\n // Update selected class directly without triggering full re-render\n this.resultsList?.querySelectorAll('.result').forEach((el, i) => {\n if (i === idx) {\n el.classList.add('is-selected');\n } else {\n el.classList.remove('is-selected');\n }\n });\n onResultMouseEnter(idx);\n };\n\n const handleClick = (event: Event) => {\n const button = (event.target as HTMLElement).closest('button[data-index]');\n if (!button) return;\n const idx = Number(button.getAttribute('data-index')) ?? 0;\n onResultClick(event as MouseEvent, idx);\n };\n\n // Rendering replaces the result buttons, so remove the delegated listeners\n // from the list itself before installing the new closures.\n if (this.resultMouseEnterHandler) {\n this.resultsList.removeEventListener('mouseenter', this.resultMouseEnterHandler, true);\n }\n if (this.resultClickHandler) {\n this.resultsList.removeEventListener('click', this.resultClickHandler);\n }\n\n // Attach delegated listeners\n this.resultMouseEnterHandler = handleMouseEnter as EventListener;\n this.resultClickHandler = handleClick as EventListener;\n this.resultsList.addEventListener('mouseenter', this.resultMouseEnterHandler, true);\n this.resultsList.addEventListener('click', this.resultClickHandler);\n }\n }\n\n showToast(message: string): void {\n const toast = document.createElement('div');\n toast.textContent = message;\n toast.style.position = 'fixed';\n toast.style.bottom = '20px';\n toast.style.right = '20px';\n toast.style.background = '#333';\n toast.style.color = '#fff';\n toast.style.padding = '10px 20px';\n toast.style.borderRadius = '4px';\n toast.style.zIndex = '9999';\n toast.style.opacity = '0';\n toast.style.transition = 'opacity 0.3s';\n\n document.body.appendChild(toast);\n void toast.offsetWidth;\n toast.style.opacity = '1';\n\n setTimeout(() => {\n toast.style.opacity = '0';\n setTimeout(() => {\n document.body.removeChild(toast);\n }, 300);\n }, 2000);\n }\n\n updateDiagnostics(data: { pages: number; interactive: number; files: number }): void {\n if (!this.root) return;\n const diagPages = this.root.querySelector('#diagPagesCount');\n const diagInteractive = this.root.querySelector('#diagInteractiveCount');\n const diagFiles = this.root.querySelector('#diagFilesCount');\n\n if (diagPages) diagPages.textContent = String(data.pages);\n if (diagInteractive) diagInteractive.textContent = String(data.interactive);\n if (diagFiles) diagFiles.textContent = String(data.files);\n }\n\n updateCategoryBadges(counts: Record): void {\n if (!this.root) return;\n for (const [cat, count] of Object.entries(counts)) {\n const badge = this.root.querySelector(`#badge-${cat}`);\n if (badge) {\n badge.textContent = String(count);\n }\n }\n }\n\n updateSettingsValues(config: { theme: string; mode: string; hotkey: string; actionsMode: string; inspectorActive: boolean }): void {\n if (!this.root) return;\n const themeControl = this.root.querySelector('#themeControl') as HTMLSelectElement | null;\n const modeControl = this.root.querySelector('#modeControl') as HTMLSelectElement | null;\n const hotkeyControl = this.root.querySelector('#hotkeyControl') as HTMLSelectElement | null;\n const actionsModeControl = this.root.querySelector('#actionsModeControl') as HTMLSelectElement | null;\n const agentInspectorToggle = this.root.querySelector('#agentInspectorToggle') as HTMLInputElement | null;\n\n if (themeControl) themeControl.value = config.theme || 'auto';\n if (modeControl) modeControl.value = config.mode || 'regular';\n if (hotkeyControl) hotkeyControl.value = config.hotkey || 'ctrlk,cmdk';\n if (actionsModeControl) actionsModeControl.value = config.actionsMode || 'execute';\n if (agentInspectorToggle) agentInspectorToggle.checked = config.inspectorActive;\n }\n}", "/**\n * @file Visual inspector overlay for highlighting page actions and fields.\n * Helpful for developers debugging indexing and AI agents locating interactive elements.\n */\n\nimport type { IndexRecord } from '../types.js';\n\nexport class VisualInspector {\n private overlayContainer: HTMLDivElement | null = null;\n private records: IndexRecord[] = [];\n private active = false;\n private resizeObserver: ResizeObserver | null = null;\n\n constructor() {\n this.handleScroll = this.handleScroll.bind(this);\n }\n\n public setRecords(records: IndexRecord[]): void {\n this.records = records;\n if (this.active) {\n this.refresh();\n }\n }\n\n public activate(): void {\n if (this.active) return;\n this.active = true;\n this.createContainer();\n this.refresh();\n\n window.addEventListener('scroll', this.handleScroll, { passive: true });\n window.addEventListener('resize', this.handleScroll, { passive: true });\n\n if (typeof ResizeObserver !== 'undefined') {\n this.resizeObserver = new ResizeObserver(() => this.refresh());\n this.resizeObserver.observe(document.body);\n }\n }\n\n public deactivate(): void {\n if (!this.active) return;\n this.active = false;\n this.removeContainer();\n\n window.removeEventListener('scroll', this.handleScroll);\n window.removeEventListener('resize', this.handleScroll);\n\n if (this.resizeObserver) {\n this.resizeObserver.disconnect();\n this.resizeObserver = null;\n }\n }\n\n public isActive(): boolean {\n return this.active;\n }\n\n private createContainer(): void {\n this.removeContainer();\n const container = document.createElement('div');\n container.id = 'reef-inspector-container';\n container.style.position = 'absolute';\n container.style.top = '0';\n container.style.left = '0';\n container.style.width = '100%';\n container.style.height = '100%';\n container.style.pointerEvents = 'none';\n container.style.zIndex = '2147483640'; // Just under the search modal but above everything else\n document.body.appendChild(container);\n this.overlayContainer = container;\n }\n\n private removeContainer(): void {\n if (this.overlayContainer) {\n this.overlayContainer.remove();\n this.overlayContainer = null;\n }\n }\n\n private handleScroll(): void {\n if (this.active) {\n this.refresh();\n }\n }\n\n public refresh(): void {\n if (!this.active || !this.overlayContainer) return;\n\n // Clear existing overlay boxes\n this.overlayContainer.replaceChildren();\n\n const currentUrl = window.location.href.split('#')[0];\n\n // Find all actions and fields relevant to the current page\n const pageRecords = this.records.filter(\n r => (r.type === 'action' || r.type === 'field') &&\n r.selector &&\n (!r.url || r.url.split('#')[0] === currentUrl)\n );\n\n const docScrollTop = window.scrollY || document.documentElement.scrollTop;\n const docScrollLeft = window.scrollX || document.documentElement.scrollLeft;\n\n for (const record of pageRecords) {\n if (!record.selector) continue;\n const element = document.querySelector(record.selector);\n if (!element) continue;\n\n const rect = element.getBoundingClientRect();\n\n // Check if element is visible/has dimensions\n if (rect.width === 0 || rect.height === 0) continue;\n\n // Skip elements that are outside the viewport by too much, but render them if we scroll into them\n const top = rect.top + docScrollTop;\n const left = rect.left + docScrollLeft;\n\n const overlay = document.createElement('div');\n overlay.style.position = 'absolute';\n overlay.style.top = `${top}px`;\n overlay.style.left = `${left}px`;\n overlay.style.width = `${rect.width}px`;\n overlay.style.height = `${rect.height}px`;\n overlay.style.pointerEvents = 'auto'; // allow hover interactions\n overlay.style.boxSizing = 'border-box';\n overlay.style.borderRadius = '6px';\n overlay.style.transition = 'all 0.15s ease';\n\n const isAction = record.type === 'action';\n const color = isAction ? '#ff007f' : '#00e5ff'; // Pink for actions, Neon Blue for fields\n overlay.style.border = `2px dashed ${color}`;\n overlay.style.backgroundColor = isAction ? 'rgba(255, 0, 127, 0.04)' : 'rgba(0, 229, 255, 0.04)';\n\n // Floating Badge\n const badge = document.createElement('div');\n badge.style.position = 'absolute';\n badge.style.top = '-20px';\n badge.style.left = '0';\n badge.style.backgroundColor = color;\n badge.style.color = '#000000';\n badge.style.fontFamily = 'ui-monospace, JetBrains Mono, monospace';\n badge.style.fontSize = '9px';\n badge.style.fontWeight = 'bold';\n badge.style.padding = '1px 5px';\n badge.style.borderRadius = '3px';\n badge.style.whiteSpace = 'nowrap';\n badge.style.boxShadow = '0 2px 4px rgba(0,0,0,0.2)';\n badge.style.zIndex = '2';\n badge.textContent = `${record.type.toUpperCase()}: ${record.id}`;\n\n overlay.appendChild(badge);\n\n // Tooltip/label details on hover\n overlay.addEventListener('mouseenter', () => {\n overlay.style.border = `2px solid ${color}`;\n overlay.style.backgroundColor = isAction ? 'rgba(255, 0, 127, 0.12)' : 'rgba(0, 229, 255, 0.12)';\n overlay.style.boxShadow = `0 0 10px ${color}`;\n badge.style.transform = 'scale(1.05)';\n });\n\n overlay.addEventListener('mouseleave', () => {\n overlay.style.border = `2px dashed ${color}`;\n overlay.style.backgroundColor = isAction ? 'rgba(255, 0, 127, 0.04)' : 'rgba(0, 229, 255, 0.04)';\n overlay.style.boxShadow = 'none';\n badge.style.transform = 'none';\n });\n\n this.overlayContainer.appendChild(overlay);\n }\n }\n}\n", "/**\n * @file Indexing and crawling functionality for the search index.\n * Handles sitemap fetching, page crawling, and worker-based indexing.\n */\n\nimport {\n extractSections,\n extractActions,\n extractFields,\n extractLinks,\n extractFiles,\n extractMedia,\n extractStructuredData,\n extractHiddenContent,\n normalizeUrl,\n} from '../extraction.js';\nimport {\n createSearchIndex,\n addToIndex,\n type SearchIndex,\n type IndexRecord,\n type TokenFilter,\n} from '../search-index.js';\nimport type { ReefConfig } from '../types.js';\n\nexport class Indexer {\n private index: SearchIndex = createSearchIndex();\n private config: ReefConfig;\n private robotsCache: Map; timestamp: number }> = new Map();\n private lastCrawlTime: number = 0;\n\n constructor(config: ReefConfig) {\n this.config = config;\n }\n\n getIndex(): SearchIndex { return this.index; }\n setIndex(index: SearchIndex): void { this.index = index; }\n\n private extractAllContent(html: string, url: string): IndexRecord[] {\n const doc = new DOMParser().parseFromString(html, 'text/html');\n\n if (this.config.indexHidden) {\n extractHiddenContent(doc);\n }\n\n let rootElement: Element | Document = doc;\n if (this.config.scope) {\n const scopeElement = doc.querySelector(this.config.scope);\n if (scopeElement) {\n rootElement = scopeElement;\n }\n }\n\n const htmlToProcess = new XMLSerializer().serializeToString(rootElement as Element);\n\n const sections = extractSections(htmlToProcess, url);\n const actions = this.config.indexActions ? extractActions(htmlToProcess, url, this.config.excludeAction) : [];\n const fields = this.config.indexActions ? extractFields(htmlToProcess, url) : [];\n const links = extractLinks(htmlToProcess, url);\n const files = extractFiles(htmlToProcess, url, this.config.fileExtensions);\n const media = this.config.indexMedia ? extractMedia(htmlToProcess, url) : [];\n const structured = this.config.indexStructuredData ? extractStructuredData(htmlToProcess, url) : [];\n\n const combined: IndexRecord[] = [];\n combined.push(...sections, ...actions, ...fields, ...links, ...files, ...media, ...structured);\n return combined;\n }\n\n async boot(\n onReady: () => void,\n showToast?: (msg: string) => void\n ): Promise {\n // Try prebuilt index first if configured\n if (this.config.prebuiltIndexUrl) {\n try {\n const response = await fetch(this.config.prebuiltIndexUrl);\n if (response.ok) {\n const json = await response.text();\n this.index = (await import('../search-index.js')).deserializeIndex(json);\n console.info(`[reef] loaded prebuilt index with ${this.index.allSections.length} sections`);\n onReady();\n return;\n }\n } catch (error) {\n console.warn('[reef] prebuilt index fetch failed, falling back to crawling', error);\n }\n }\n\n // Try IndexedDB cache first\n try {\n const { loadIndex, saveIndex } = await import('../cache.js');\n const ttl = this.config.ttl;\n const cached = await loadIndex(ttl);\n if (cached?.index && cached.index.allSections.length > 0) {\n this.index = cached.index;\n console.info(`[reef] loaded cached index with ${this.index.allSections.length} sections`);\n onReady();\n return;\n }\n } catch (error) {\n console.warn('[reef] cache load failed, continuing with fresh index', error);\n }\n\n // Try to load existing page metadata from cache first for incremental crawling\n let cachedMetadata: { metadata: any } | null = null;\n try {\n const { loadIndex } = await import('../cache.js');\n cachedMetadata = await loadIndex();\n } catch (e) {\n console.warn('[reef] failed to load cached metadata for incremental crawling', e);\n }\n\n const pageHashes = cachedMetadata?.metadata?.pageMetadata ?? {};\n\n // Use the new recursive sitemap processing\n const urls = await this.fetchSitemapUrls();\n const maxPages = this.config.maxPages ?? 500;\n\n if (urls.length === 0) {\n // Fallback to same-origin crawling if no sitemaps found\n this.crawlSameOrigin(onReady);\n return;\n }\n\n if (this.config.useWorkerIndexing) {\n // TODO: Implement incremental crawling for worker-based indexing\n await this.fetchPagesWithWorker(urls.slice(0, maxPages), urls[0], onReady);\n } else {\n const fetchedSections = await this.fetchPagesParallel(urls.slice(0, maxPages), urls[0], pageHashes);\n if (fetchedSections.length) {\n addToIndex(this.index, fetchedSections, this.config.tokenizePipeline);\n console.info(`[reef] indexed ${fetchedSections.length} sections`);\n }\n // Save to cache with updated page metadata\n try {\n const { saveIndex } = await import('../cache.js');\n const versionHash = this.computeVersionHash(urls);\n const newPageMetadata: Record = {};\n \n // Update metadata for crawled pages\n for (const url of urls.slice(0, maxPages)) {\n newPageMetadata[url] = pageHashes[url] ?? { timestamp: Date.now() };\n }\n \n const metadata = {\n versionHash,\n buildTime: Date.now(),\n pageMetadata: newPageMetadata\n };\n await saveIndex(this.index, metadata);\n } catch (e) {\n console.warn('[reef] cache save failed', e);\n }\n onReady();\n return;\n }\n\n this.crawlSameOrigin(onReady);\n }\n\n async fetchSitemapUrls(): Promise {\n const candidates = this.getSitemapCandidates();\n const allUrls: string[] = [];\n const seenUrls = new Set();\n\n for (const candidate of candidates) {\n try {\n await this.processSitemap(candidate, allUrls, seenUrls);\n } catch (e) {\n console.warn('[reef] failed to process sitemap:', candidate, e);\n continue;\n }\n }\n\n return allUrls;\n }\n\n private async processSitemap(url: string, allUrls: string[], seenUrls: Set): Promise {\n if (seenUrls.has(url)) return;\n seenUrls.add(url);\n\n try {\n const response = await fetch(url);\n if (!response.ok) return;\n const xml = await response.text();\n\n // Check if this is a sitemap index\n const isSitemapIndex = xml.includes('');\n \n if (isSitemapIndex) {\n // Parse child sitemaps from sitemap index\n const sitemapRegex = /\\s*(.*?)<\\/loc>\\s*<\\/sitemap>/g;\n let match: RegExpExecArray | null;\n \n while ((match = sitemapRegex.exec(xml)) !== null) {\n const childSitemapUrl = match[1].trim();\n const resolvedUrl = this.resolveUrl(childSitemapUrl, url);\n await this.processSitemap(resolvedUrl, allUrls, seenUrls);\n }\n } else {\n // Parse URLs from regular sitemap\n const locRegex = /(.*?)<\\/loc>/g;\n let match: RegExpExecArray | null;\n while ((match = locRegex.exec(xml)) !== null) {\n const pageUrl = match[1].trim();\n const resolvedUrl = this.resolveUrl(pageUrl, url);\n if (!seenUrls.has(resolvedUrl)) {\n allUrls.push(resolvedUrl);\n seenUrls.add(resolvedUrl);\n }\n }\n }\n } catch (e) {\n console.warn('[reef] failed to process sitemap:', url, e);\n }\n }\n\n private computeVersionHash(urls: string[]): string {\n let hash = 0;\n for (const url of urls) {\n for (let i = 0; i < url.length; i++) {\n const char = url.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash;\n }\n }\n return Math.abs(hash).toString(36);\n }\n\n // Simple hash function for content hashing\n private hashContent(content: string): string {\n let hash = 0;\n for (let i = 0; i < Math.min(content.length, 10000); i++) { // Only hash first 10k chars for performance\n const char = content.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash;\n }\n return Math.abs(hash).toString(36);\n }\n\n getSitemapCandidates(): string[] {\n const base = window.location.href;\n const configured = this.config.sitemap ?? 'sitemap.xml';\n const candidates = [this.resolveUrl(configured, base)];\n\n if (!configured.startsWith('/')) {\n candidates.push(this.resolveUrl('./sitemap.xml', base));\n } else if (configured === '/sitemap.xml') {\n candidates.push(this.resolveUrl('sitemap.xml', base));\n }\n\n return [...new Set(candidates)];\n }\n\n private resolveUrl(value: string, base: string): string {\n if (!value) return base;\n try {\n return new URL(value, base).toString();\n } catch {\n return value;\n }\n }\n\n // Fetch and parse robots.txt for a given origin\n private async fetchRobotsTxt(origin: string): Promise> {\n const cacheKey = origin;\n const cacheEntry = this.robotsCache.get(cacheKey);\n \n // Return cached entry if it's still fresh (cache for 1 hour)\n if (cacheEntry && Date.now() - cacheEntry.timestamp < 3600000) {\n return cacheEntry.disallowed;\n }\n\n try {\n const robotsUrl = new URL('/robots.txt', origin).toString();\n const response = await fetch(robotsUrl);\n \n if (!response.ok) {\n // If we can't fetch robots.txt, assume crawling is allowed\n this.robotsCache.set(cacheKey, { disallowed: new Set(), timestamp: Date.now() });\n return new Set();\n }\n\n const robotsText = await response.text();\n const disallowed = this.parseRobotsTxt(robotsText);\n \n this.robotsCache.set(cacheKey, { disallowed, timestamp: Date.now() });\n return disallowed;\n } catch (e) {\n console.warn('[reef] failed to fetch robots.txt:', e);\n // On error, assume crawling is allowed\n this.robotsCache.set(cacheKey, { disallowed: new Set(), timestamp: Date.now() });\n return new Set();\n }\n }\n\n // Parse robots.txt content and extract disallowed paths\n private parseRobotsTxt(robotsText: string): Set {\n const disallowed = new Set();\n const lines = robotsText.split('\\n');\n \n let currentUserAgent = '';\n \n for (const line of lines) {\n const trimmedLine = line.trim();\n if (!trimmedLine || trimmedLine.startsWith('#')) continue;\n \n const userAgentMatch = trimmedLine.match(/^User-agent:\\s*(.+)$/i);\n if (userAgentMatch) {\n currentUserAgent = userAgentMatch[1].trim();\n continue;\n }\n\n const disallowMatch = trimmedLine.match(/^Disallow:\\s*(.+)$/i);\n if (disallowMatch && currentUserAgent === '*') {\n const path = disallowMatch[1].trim();\n if (path) {\n disallowed.add(path);\n }\n }\n }\n \n return disallowed;\n }\n\n // Check if a URL is allowed by robots.txt\n private isUrlAllowed(url: string, origin: string): boolean {\n // If we don't have cached robots.txt for this origin, assume it's allowed\n const cacheEntry = this.robotsCache.get(origin);\n if (!cacheEntry) return true;\n \n const disallowed = cacheEntry.disallowed;\n if (disallowed.size === 0) return true;\n \n // Check if the URL path starts with any disallowed path\n try {\n const urlObj = new URL(url);\n const path = urlObj.pathname;\n \n for (const disallowedPath of disallowed) {\n if (path.startsWith(disallowedPath)) {\n return false;\n }\n }\n } catch {\n return true; // If URL parsing fails, assume allowed\n }\n \n return true;\n }\n\n // Apply crawl delay between batches\n private async applyCrawlDelay(): Promise {\n const delay = this.config.crawlDelay ?? 0;\n if (delay <= 0) return;\n \n const now = Date.now();\n const timeSinceLastCrawl = now - this.lastCrawlTime;\n \n if (timeSinceLastCrawl < delay) {\n await new Promise(resolve => setTimeout(resolve, delay - timeSinceLastCrawl));\n }\n \n this.lastCrawlTime = Date.now();\n }\n\n private crawlSameOrigin(onReady: () => void): void {\n const origin = window.location.origin;\n const visited = new Set();\n const queue: string[] = [normalizeUrl(window.location.href)];\n const maxPages = this.config.maxPages ?? 500;\n const concurrency = this.config.maxPages ? Math.min(this.config.maxPages, 6) : 6;\n\n const processQueue = async () => {\n // Fetch robots.txt first\n await this.fetchRobotsTxt(origin);\n\n while (queue.length && visited.size < maxPages) {\n // Apply crawl delay between batches\n await this.applyCrawlDelay();\n \n const batch = queue.splice(0, concurrency);\n \n for (const url of batch) {\n const normalizedUrlStr = normalizeUrl(url);\n if (visited.has(normalizedUrlStr) || !normalizedUrlStr.startsWith(origin)) continue;\n \n // Check robots.txt\n if (!this.isUrlAllowed(normalizedUrlStr, origin)) {\n console.info(`[reef] skipping disallowed URL: ${normalizedUrlStr}`);\n continue;\n }\n \n visited.add(normalizedUrlStr);\n\n try {\n const response = await fetch(normalizedUrlStr);\n if (!response.ok) continue;\n const html = await response.text();\n const content = this.extractAllContent(html, normalizedUrlStr);\n addToIndex(this.index, content, this.config.tokenizePipeline);\n\n const links = extractLinks(html, normalizedUrlStr)\n .filter(l => l.url.startsWith(origin))\n .map(l => normalizeUrl(l.url))\n .filter((u, i, arr) => arr.indexOf(u) === i);\n queue.push(...links);\n } catch (e) {\n continue;\n }\n }\n }\n console.info(`[reef] indexed ${visited.size} pages via same-origin crawl`);\n onReady();\n };\n\n processQueue();\n }\n\n private async fetchPagesParallel(urls: string[], sitemapUrl: string, pageHashes: Record = {}): Promise {\n const concurrency = 6;\n const sections: IndexRecord[] = [];\n const results: (IndexRecord[] | null)[] = new Array(urls.length);\n\n let idx = 0;\n const fetchBatch = async () => {\n while (idx < urls.length) {\n const i = idx++;\n const pageUrl = this.resolveUrl(urls[i], sitemapUrl);\n try {\n // Check if we have cached info for this URL\n const cachedPageInfo = pageHashes[pageUrl];\n \n // Skip if content hasn't changed (incremental crawling)\n if (cachedPageInfo) {\n const headResponse = await fetch(pageUrl, { method: 'HEAD' });\n if (headResponse.ok) {\n const currentEtag = headResponse.headers.get('ETag');\n const currentLastModified = headResponse.headers.get('Last-Modified');\n \n // Check if content is unchanged\n if ((cachedPageInfo.etag && currentEtag === cachedPageInfo.etag) ||\n (cachedPageInfo.lastModified && currentLastModified === cachedPageInfo.lastModified)) {\n console.info(`[reef] skipping unchanged page: ${pageUrl}`);\n results[i] = null; // Skip this page\n return;\n }\n }\n }\n \n // Fetch the full page content\n const pageResponse = await fetch(pageUrl);\n if (pageResponse.ok) {\n const html = await pageResponse.text();\n \n // Check content hash if we have it\n if (cachedPageInfo?.contentHash) {\n const currentContentHash = this.hashContent(html);\n if (currentContentHash === cachedPageInfo.contentHash) {\n console.info(`[reef] skipping unchanged page (content hash match): ${pageUrl}`);\n results[i] = null; // Skip this page\n return;\n }\n }\n \n const pageSections = this.extractAllContent(html, pageUrl);\n results[i] = pageSections;\n \n // Update page metadata with new hash/etag info\n pageHashes[pageUrl] = {\n etag: pageResponse.headers.get('ETag'),\n lastModified: pageResponse.headers.get('Last-Modified'),\n contentHash: this.hashContent(html),\n timestamp: Date.now()\n };\n }\n } catch {\n results[i] = null;\n }\n }\n };\n\n await Promise.all([...Array(concurrency)].map(() => fetchBatch()));\n\n for (let i = 0; i < results.length; i++) {\n if (results[i]) {\n sections.push(...(results[i] ?? []));\n }\n }\n\n return sections;\n }\n\n private async fetchPagesWithWorker(urls: string[], sitemapUrl: string, onReady: () => void): Promise {\n const workerUrl = new URL('../worker.js', import.meta.url).href;\n const workerCount = Math.min(navigator.hardwareConcurrency || 4, 4);\n \n return new Promise((resolve, reject) => {\n const workers: Worker[] = [];\n const results: Map = new Map();\n let completedWorkers = 0;\n \n // Shard the URLs across workers\n const shards = this.shardArray(urls, workerCount);\n \n for (let i = 0; i < workerCount; i++) {\n const worker = new Worker(workerUrl);\n workers.push(worker);\n \n const workerId = i;\n const shard = shards[i] || [];\n \n const messageHandler = (e: MessageEvent) => {\n const { result, error, json, workerIndex } = e.data as { \n result: string; \n error: string; \n json: string; \n workerIndex: number \n };\n \n if (workerIndex !== workerId) return; // Not for this worker\n \n if (error) {\n console.error(`[reef] worker ${workerId} error:`, error);\n results.set(workerId, { result: 'error', error });\n } else if (json) {\n results.set(workerId, { result: 'ok', error: '' });\n // Store the serialized index from this worker\n // We'll merge them after all workers complete\n }\n \n completedWorkers++;\n \n if (completedWorkers === workerCount) {\n // All workers completed, merge results\n this.mergeWorkerResults(workers, results, onReady, resolve, reject);\n }\n };\n \n const errorHandler = (e: ErrorEvent) => {\n console.error(`[reef] worker ${workerId} error:`, e.error);\n results.set(workerId, { result: 'error', error: e.error?.toString() || 'Unknown error' });\n completedWorkers++;\n \n if (completedWorkers === workerCount) {\n this.mergeWorkerResults(workers, results, onReady, resolve, reject);\n }\n };\n \n worker.addEventListener('message', messageHandler);\n worker.addEventListener('error', errorHandler);\n \n const config = {\n scope: this.config.scope,\n indexActions: this.config.indexActions,\n indexMedia: this.config.indexMedia,\n indexStructuredData: this.config.indexStructuredData,\n indexHidden: this.config.indexHidden,\n excludeAction: this.config.excludeAction,\n fileExtensions: this.config.fileExtensions,\n };\n \n const id = Date.now() + i; // Unique ID for each worker\n worker.postMessage({\n id,\n workerIndex: i,\n action: 'indexPages',\n payload: {\n pages: shard.map(url => [this.resolveUrl(url, sitemapUrl), '']), // URL and empty HTML initially\n config,\n shardIndex: i,\n totalShards: workerCount\n },\n });\n }\n });\n }\n \n // Helper to shard an array into N parts\n private shardArray(array: T[], count: number): T[][] {\n const shards: T[][] = [];\n const shardSize = Math.ceil(array.length / count);\n \n for (let i = 0; i < count; i++) {\n const start = i * shardSize;\n const end = start + shardSize;\n shards.push(array.slice(start, end));\n }\n \n return shards;\n }\n \n // Merge results from all workers\n private async mergeWorkerResults(\n workers: Worker[], \n results: Map, \n onReady: () => void, \n resolve: (value: IndexRecord[] | PromiseLike) => void, \n reject: (reason?: any) => void\n ): Promise {\n // Clean up workers\n for (const worker of workers) {\n worker.terminate();\n }\n \n // Check for errors\n let hasErrors = false;\n for (const [workerId, result] of results) {\n if (result.error) {\n hasErrors = true;\n break;\n }\n }\n \n if (hasErrors) {\n // If any worker failed, fall back to single-threaded approach\n console.warn('[reef] worker pool had errors, falling back to single-threaded');\n try {\n const fetchedSections = await this.fetchPagesParallel(\n Array.from(results.keys()).map(i => this.resolveUrl('', '')), // Empty for fallback\n '',\n {}\n );\n onReady();\n resolve(fetchedSections);\n } catch (e) {\n reject(e);\n }\n return;\n }\n \n // All workers completed successfully\n // For now, fall back to single-threaded as the worker communication \n // would need significant changes to return partial indices\n console.warn('[reef] worker pool: TODO implement proper merging of worker results');\n onReady();\n resolve([]);\n }\n}", "/**\n * @file Action execution and navigation logic.\n * Handles navigating to sections, executing actions on elements, and deferred actions.\n */\n\nimport type { IndexRecord } from '../types.js';\nimport { escapeHtml } from '../ui/ui-helpers.js';\nimport { findClosestWord } from '../search-index.js';\n\nexport class ActionExecutor {\n private deferredScrollHandler: () => void = () => {};\n private deferredActionHandler: () => void = () => {};\n\n navigateToSection(result: IndexRecord, closeCallback: () => void): void {\n const currentUrl = this.urlWithoutHash(window.location.href);\n const targetUrl = this.urlWithoutHash(result.url);\n const isSamePage = currentUrl === targetUrl;\n\n if (isSamePage) {\n const heading = this.findHeadingElementByText(result.headingText);\n if (heading) {\n this.highlightAndScrollTo(heading, closeCallback);\n return;\n }\n const element = this.findElement(result.selector);\n if (element) {\n this.highlightAndScrollTo(element, closeCallback);\n return;\n }\n // Same page but element not found \u2014 use native hash navigation when possible.\n closeCallback();\n const fragment = this.urlFragment(result.url);\n if (fragment) {\n window.location.hash = this.decodeFragment(fragment);\n }\n return;\n }\n\n // Cross-page navigation\n this.setupDeferredScroll(result);\n closeCallback();\n window.location.href = result.url;\n }\n\n private findHeadingElementByText(headingText: string): HTMLElement | null {\n const normalized = headingText.trim().toLowerCase();\n if (!normalized) return null;\n const headings = document.querySelectorAll('h1, h2, h3, h4, h5, h6');\n for (const heading of Array.from(headings)) {\n if (heading.textContent?.trim().toLowerCase() === normalized) {\n return heading as HTMLElement;\n }\n }\n return null;\n }\n\n private findElement(selector?: string): HTMLElement | null {\n if (!selector) return null;\n try {\n return document.querySelector(selector) as HTMLElement | null;\n } catch {\n return null;\n }\n }\n\n private urlWithoutHash(url: string): string {\n try {\n const parsed = new URL(url, window.location.href);\n parsed.hash = '';\n return parsed.href;\n } catch {\n return url.split('#')[0];\n }\n }\n\n private urlFragment(url: string): string {\n try {\n return new URL(url, window.location.href).hash.slice(1);\n } catch {\n return url.split('#')[1] ?? '';\n }\n }\n\n private decodeFragment(fragment: string): string {\n try {\n return decodeURIComponent(fragment);\n } catch {\n return fragment;\n }\n }\n\n private highlightAndScrollTo(element: HTMLElement, closeCallback: () => void): void {\n closeCallback();\n this.revealTabContaining(element);\n\n // Tab switches can update layout after the click handler returns. Waiting\n // for two frames makes the target visible before measuring/scrolling it.\n requestAnimationFrame(() => requestAnimationFrame(() => {\n element.scrollIntoView({ behavior: 'smooth', block: 'center' });\n element.style.boxShadow = '0 0 0 3px rgba(67, 217, 200, 0.5)';\n element.style.borderRadius = '4px';\n setTimeout(() => {\n element.style.boxShadow = '';\n }, 2000);\n }));\n }\n\n private revealTabContaining(element: HTMLElement): void {\n const pane = element.closest('.tab-pane') as HTMLElement | null;\n if (!pane || pane.getAttribute('aria-hidden') === 'false' || pane.style.display !== 'none') return;\n\n const tabId = pane.id.replace(/^tab-/, '');\n const tabButton = Array.from(document.querySelectorAll('[data-tab]'))\n .find(button => button.getAttribute('data-tab') === tabId) as HTMLElement | undefined;\n\n // The host page owns tab state, so use its normal click handler when one\n // exists. The fallback keeps navigation working on pages without one.\n if (tabButton) {\n tabButton.click();\n } else {\n pane.style.display = 'flex';\n pane.classList.add('active');\n }\n }\n\n private setupDeferredScroll(result: IndexRecord): void {\n const target = {\n headingText: result.headingText,\n selector: result.selector,\n fragment: this.urlFragment(result.url),\n };\n try {\n sessionStorage.setItem('reef-deferred-scroll', JSON.stringify(target));\n } catch (error) {\n console.error('[reef] Failed to store deferred scroll target:', error);\n }\n }\n\n handleDeferredScroll(): void {\n const raw = sessionStorage.getItem('reef-deferred-scroll');\n if (!raw) return;\n sessionStorage.removeItem('reef-deferred-scroll');\n\n let target: { headingText?: string; selector?: string; fragment?: string } | null = null;\n try {\n target = JSON.parse(raw);\n } catch (error) {\n console.error('[reef] Failed to parse deferred scroll target:', error);\n return;\n }\n if (!target || (!target.headingText && !target.selector && !target.fragment)) return;\n const deferredTarget = target;\n\n const findTarget = (): HTMLElement | null => {\n if (deferredTarget.fragment) {\n const element = document.getElementById(this.decodeFragment(deferredTarget.fragment));\n if (element) return element as HTMLElement;\n }\n const selectorElement = this.findElement(deferredTarget.selector);\n if (selectorElement) return selectorElement;\n return deferredTarget.headingText\n ? this.findHeadingElementByText(deferredTarget.headingText)\n : null;\n };\n\n const attempt = (): boolean => {\n const element = findTarget();\n if (!element) return false;\n this.revealTabContaining(element);\n requestAnimationFrame(() => {\n element.scrollIntoView({ behavior: 'smooth', block: 'center' });\n element.style.boxShadow = '0 0 0 3px rgba(67, 217, 200, 0.5)';\n element.style.borderRadius = '4px';\n setTimeout(() => {\n element.style.boxShadow = '';\n }, 2000);\n });\n return true;\n };\n\n const start = () => {\n if (attempt()) return;\n const observer = new MutationObserver(() => {\n if (attempt()) observer.disconnect();\n });\n observer.observe(document.body, { childList: true, subtree: true });\n setTimeout(() => observer.disconnect(), 5000);\n };\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', start, { once: true });\n } else {\n start();\n }\n }\n\n executeAction(result: IndexRecord, actionsMode?: string, showToast?: (msg: string) => void): void {\n switch (result.type) {\n case 'action':\n this.executeActionResult(result, actionsMode, showToast);\n break;\n case 'field':\n this.focusField(result);\n break;\n case 'link':\n case 'file':\n case 'media':\n case 'structured':\n window.location.href = result.url;\n break;\n case 'section':\n this.navigateToSection(result, () => {});\n break;\n }\n }\n\n private executeActionResult(result: IndexRecord, actionsMode?: string, showToast?: (msg: string) => void): void {\n if (result.destructive && actionsMode !== 'execute') {\n this.highlightAndNavigate(result);\n return;\n }\n\n const currentUrl = window.location.href.split('#')[0];\n const targetUrl = result.url.split('#')[0];\n\n if (currentUrl === targetUrl) {\n this.executeActionOnCurrentPage(result, showToast);\n } else {\n this.setupDeferredAction(result);\n }\n }\n\n private executeActionOnCurrentPage(result: IndexRecord, showToast?: (msg: string) => void): void {\n if (!result.selector) {\n if (result.type === 'field') {\n this.focusField(result);\n }\n return;\n }\n\n try {\n const element = document.querySelector(result.selector);\n if (!element) {\n showToast?.('Could not find that element on the page. It may have changed.');\n return;\n }\n\n if (result.type === 'action') {\n const clickEvent = new MouseEvent('click', {\n bubbles: true,\n cancelable: true,\n view: window,\n });\n element.dispatchEvent(clickEvent);\n } else if (result.type === 'field') {\n (element as HTMLElement).focus();\n }\n } catch (error) {\n console.error('Failed to execute action:', error);\n showToast?.('Could not interact with that element. It may have changed or be unavailable.');\n }\n }\n\n private focusField(result: IndexRecord): void {\n if (!result.selector) return;\n try {\n const element = document.querySelector(result.selector);\n if (element) {\n (element as HTMLElement).focus();\n if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) {\n element.select();\n }\n }\n } catch (error) {\n console.error('Failed to focus field:', error);\n }\n }\n\n private setupDeferredAction(result: IndexRecord): void {\n const deferredAction = {\n selector: result.selector,\n type: result.type,\n label: result.label,\n destructive: result.destructive,\n };\n sessionStorage.setItem('reef-deferred-action', JSON.stringify(deferredAction));\n window.location.href = result.url;\n }\n\n handleDeferredActions(): void {\n const deferredActionStr = sessionStorage.getItem('reef-deferred-action');\n if (!deferredActionStr) return;\n\n try {\n const deferredAction = JSON.parse(deferredActionStr);\n sessionStorage.removeItem('reef-deferred-action');\n\n if (deferredAction.selector) {\n const element = document.querySelector(deferredAction.selector);\n if (element) {\n if (deferredAction.type === 'action' && !deferredAction.destructive) {\n const clickEvent = new MouseEvent('click', {\n bubbles: true,\n cancelable: true,\n view: window,\n });\n element.dispatchEvent(clickEvent);\n } else if (deferredAction.type === 'field') {\n (element as HTMLElement).focus();\n }\n }\n }\n } catch (error) {\n console.error('Failed to handle deferred action:', error);\n sessionStorage.removeItem('reef-deferred-action');\n }\n }\n\n private highlightAndNavigate(result: IndexRecord): void {\n window.location.href = result.url;\n if (result.selector) {\n const selector = result.selector;\n const observer = new MutationObserver(() => {\n const element = document.querySelector(selector) as HTMLElement | null;\n if (element) {\n element.scrollIntoView({ behavior: 'smooth', block: 'center' });\n element.style.boxShadow = '0 0 0 3px rgba(108, 140, 255, 0.5)';\n element.style.borderRadius = '4px';\n observer.disconnect();\n }\n });\n observer.observe(document.body, { childList: true, subtree: true });\n setTimeout(() => observer.disconnect(), 5000);\n }\n }\n}\n", "/**\n * @file Configuration reading and application from script attributes.\n * Parses data attributes and builds configuration objects for the search.\n */\n\nimport type { IndexRecord, ReefConfig, TokenFilter } from '../types.js';\n\nexport class ConfigReader {\n private static pendingConfig: Partial | null = null;\n\n static setConfig(config: Partial): void {\n this.pendingConfig = config;\n }\n\n static readConfig(): ReefConfig {\n if (this.pendingConfig) {\n const config = this.pendingConfig;\n this.pendingConfig = null;\n return this.mergeWithDefaults(config);\n }\n\n if (typeof document === 'undefined') {\n return this.getDefaultConfig();\n }\n\n const script = document.currentScript as HTMLScriptElement | null;\n const dataset = script?.dataset;\n\n if (!dataset) {\n return this.getDefaultConfig();\n }\n\n const datasetRecord: Record = {};\n for (const key in dataset) {\n const value = dataset[key];\n if (value !== undefined) {\n datasetRecord[key] = value;\n }\n }\n\n return this.parseDataset(datasetRecord);\n }\n\n private static getDefaultConfig(): ReefConfig {\n return {\n sitemap: '/sitemap.xml',\n maxPages: 500,\n scope: undefined,\n indexActions: true,\n indexMedia: true,\n indexStructuredData: true,\n indexHidden: true,\n fileExtensions: undefined,\n excludeAction: undefined,\n actionsMode: 'execute',\n primaryColor: undefined,\n secondaryColor: undefined,\n backgroundColor: undefined,\n textColor: undefined,\n borderColor: undefined,\n radius: 24,\n theme: undefined,\n fontFamily: undefined,\n mode: 'opaque',\n hotkey: undefined,\n placeholder: undefined,\n headless: false,\n onReady: undefined,\n tokenizePipeline: undefined,\n synonyms: undefined,\n prebuiltIndexUrl: undefined,\n useWorkerIndexing: false,\n ttl: undefined,\n };\n }\n\n private static parseDataset(dataset: Record): ReefConfig {\n let tokenizePipeline: TokenFilter[] | undefined = undefined;\n if (dataset.stemming === 'true' || dataset.stopwords === 'true' || dataset.diacritics === 'true') {\n const pipeline: TokenFilter[] = [];\n\n if (dataset.stopwords === 'true') {\n const stopWords = new Set([\n 'a', 'an', 'the', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by',\n 'from', 'up', 'about', 'into', 'through', 'during', 'before', 'after', 'above', 'below',\n ]);\n pipeline.push((token: string) => stopWords.has(token) ? null : token);\n }\n\n if (dataset.diacritics === 'true') {\n pipeline.push((token: string) => token.normalize('NFD').replace(/[\\u0300-\\u036f]/g, ''));\n }\n\n if (dataset.stemming === 'true') {\n pipeline.push((token: string) => {\n if (token.length <= 2) return token;\n if (/ing$/.test(token)) return token.slice(0, -3);\n if (/ed$/.test(token)) return token.slice(0, -2);\n if (/es$/.test(token) && token.length > 3) return token.slice(0, -2);\n if (/s$/.test(token) && token.length > 3) return token.slice(0, -1);\n return token;\n });\n }\n\n tokenizePipeline = pipeline.length > 0 ? pipeline : undefined;\n }\n\n let synonyms: Record | undefined = undefined;\n if (dataset.synonyms) {\n try {\n synonyms = JSON.parse(dataset.synonyms);\n } catch {\n console.warn('[reef] Invalid synonyms JSON in data-synonyms attribute');\n }\n }\n\n return {\n sitemap: dataset.sitemap ?? '/sitemap.xml',\n maxPages: Number(dataset.maxPages ?? 500),\n scope: dataset.scope,\n indexActions: dataset.indexActions !== 'false',\n indexMedia: dataset.indexMedia !== 'false',\n indexStructuredData: dataset.indexStructuredData !== 'false',\n indexHidden: dataset.indexHidden !== 'false',\n fileExtensions: dataset.fileExtensions,\n excludeAction: dataset.excludeAction,\n actionsMode: dataset.actionsMode as 'execute' | 'navigate-only' | undefined || 'execute',\n primaryColor: dataset.primaryColor,\n secondaryColor: dataset.secondaryColor,\n backgroundColor: dataset.backgroundColor,\n textColor: dataset.textColor,\n borderColor: dataset.borderColor,\n radius: dataset.radius ? Number(dataset.radius) : 24,\n theme: dataset.theme as 'light' | 'dark' | 'auto' | undefined,\n fontFamily: dataset.fontFamily,\n mode: dataset.mode ? dataset.mode as 'regular' | 'opaque' | 'high-contrast' : 'opaque',\n hotkey: dataset.hotkey,\n placeholder: dataset.placeholder,\n headless: dataset.headless === 'true',\n onReady: undefined,\n tokenizePipeline,\n synonyms,\n prebuiltIndexUrl: dataset.prebuiltIndexUrl,\n useWorkerIndexing: dataset.useWorkerIndexing === 'true',\n ttl: dataset.ttl ? Number(dataset.ttl) : undefined,\n };\n }\n\n private static mergeWithDefaults(overrides: Partial): ReefConfig {\n return {\n sitemap: overrides.sitemap ?? '/sitemap.xml',\n maxPages: overrides.maxPages ?? 500,\n scope: overrides.scope,\n indexActions: overrides.indexActions ?? true,\n indexMedia: overrides.indexMedia ?? true,\n indexStructuredData: overrides.indexStructuredData ?? true,\n indexHidden: overrides.indexHidden ?? true,\n fileExtensions: overrides.fileExtensions,\n excludeAction: overrides.excludeAction,\n actionsMode: overrides.actionsMode ?? 'execute',\n primaryColor: overrides.primaryColor,\n secondaryColor: overrides.secondaryColor,\n backgroundColor: overrides.backgroundColor,\n textColor: overrides.textColor,\n borderColor: overrides.borderColor,\n radius: overrides.radius ?? 24,\n theme: overrides.theme,\n fontFamily: overrides.fontFamily,\n mode: overrides.mode ?? 'opaque',\n hotkey: overrides.hotkey,\n placeholder: overrides.placeholder,\n headless: overrides.headless ?? false,\n onReady: overrides.onReady,\n tokenizePipeline: overrides.tokenizePipeline,\n synonyms: overrides.synonyms,\n prebuiltIndexUrl: overrides.prebuiltIndexUrl,\n useWorkerIndexing: overrides.useWorkerIndexing ?? false,\n ttl: overrides.ttl,\n };\n }\n}\n\nexport class ConfigApplier {\n static applyConfigToUI(host: HTMLDivElement, config: ReefConfig & { mode?: string }): void {\n const cfg = config;\n host.style.setProperty('--primary-color', cfg.primaryColor ?? '#66d9c8');\n host.style.setProperty('--secondary-color', cfg.secondaryColor ?? '#ff8562');\n host.style.setProperty('--background-color', cfg.backgroundColor ?? 'rgba(20,30,28,0.88)');\n host.style.setProperty('--text-color', cfg.textColor ?? '#edebe6');\n host.style.setProperty('--border-color', cfg.borderColor ?? 'rgba(255,255,255,0.1)');\n host.style.setProperty('--radius', cfg.radius?.toString() ?? '24');\n host.style.setProperty('--font-family', cfg.fontFamily ?? 'Inter, system-ui, sans-serif');\n\n host.classList.remove('mode-regular', 'mode-opaque', 'mode-high-contrast');\n switch ((cfg.mode ?? 'opaque') as string) {\n case 'opaque':\n host.classList.add('mode-opaque');\n break;\n case 'high-contrast':\n host.classList.add('mode-high-contrast');\n break;\n default:\n host.classList.add('mode-regular');\n }\n }\n}", "import type { IndexRecord, WorkflowStep, AgentSession, WorkflowOptions, ActionResult, AgentOptions, ObservationOptions, StableWaitOptions, PaginationOptions } from './types.js';\nimport type { SearchIndex } from './search-index.js';\nimport { extractAccessibilityTree } from './extraction.js';\n\ntype InspectorInterface = {\n activate: () => void;\n deactivate: () => void;\n isActive: () => boolean;\n setRecords: (records: IndexRecord[]) => void;\n};\n\nexport { type WorkflowStep, type WorkflowOptions, type AgentSession, type ActionResult };\n\nexport class Agent {\n private index: SearchIndex;\n private inspector: InspectorInterface;\n private actionsMode: 'execute' | 'navigate-only';\n private options: AgentOptions;\n private actionCount = 0;\n private lastRoute = typeof window !== 'undefined' ? window.location.href : '';\n private lastAction: ActionResult = { success: false, reason: 'no-action' };\n\n constructor(index: SearchIndex, inspector: InspectorInterface, actionsMode: 'execute' | 'navigate-only' | AgentOptions = 'execute') {\n this.index = index;\n this.inspector = inspector;\n this.options = typeof actionsMode === 'string' ? { actionsMode } : actionsMode;\n this.actionsMode = this.options.actionsMode ?? 'execute';\n this.installRouteObserver();\n }\n\n async click(selector: string | IndexRecord): Promise {\n if (typeof selector !== 'string' && selector.destructive && (this.actionsMode === 'navigate-only' || this.options.destructive === false)) throw new Error('destructive-action-blocked');\n this.guardAction();\n const resolved = await this.resolveSelector(selector);\n if (!resolved.success) {\n if (typeof selector !== 'string' && this.actionsMode === 'navigate-only') return this;\n throw new Error(resolved.reason || 'Click failed');\n }\n\n const element = resolved.element;\n this.ensureVisible(element ?? null);\n const before = this.fingerprint();\n if (element && typeof MouseEvent !== 'undefined') {\n const clickEvent = new MouseEvent('click', {\n bubbles: true,\n cancelable: true,\n view: typeof window !== 'undefined' ? window : undefined,\n });\n element.dispatchEvent(clickEvent);\n }\n await this.waitForStable({ quietMs: 50, timeout: 1000 });\n if (!this.changedSince(before) && typeof selector !== 'string' && selector.selectors?.length) {\n const retry = await this.resolveSelector({ ...selector, selectors: selector.selectors.slice(1) });\n if (retry.success && retry.element && retry.element !== element) retry.element.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));\n }\n this.lastAction = { success: true, changed: this.changedSince(before), url: typeof window !== 'undefined' ? window.location.href : undefined, element };\n return this;\n }\n\n async type(selector: string | IndexRecord, value: string): Promise {\n this.guardAction();\n const resolved = await this.resolveSelector(selector);\n if (!resolved.success) {\n throw new Error(resolved.reason || 'Type failed');\n }\n\n const element = resolved.element as HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | null;\n this.ensureVisible(element);\n if (element && ('value' in element)) {\n const descriptor = Object.getOwnPropertyDescriptor(element, 'value');\n if (descriptor && descriptor.set) {\n descriptor.set.call(element, value);\n } else {\n element.value = value;\n }\n element.dispatchEvent(new Event('input', { bubbles: true }));\n element.dispatchEvent(new Event('change', { bubbles: true }));\n }\n this.lastAction = { success: true, changed: true, url: typeof window !== 'undefined' ? window.location.href : undefined, element: element ?? undefined };\n return this;\n }\n\n async submit(selector?: string | IndexRecord): Promise {\n let element: Element | null = null;\n\n if (selector) {\n const resolved = await this.resolveSelector(selector);\n if (resolved.success && resolved.element) {\n element = resolved.element;\n }\n } else if (typeof document !== 'undefined') {\n element = document.querySelector('form') || document.querySelector('button[type=\"submit\"]') ||\n document.querySelector('input[type=\"submit\"]');\n }\n\n if (element) {\n if (element instanceof HTMLFormElement) {\n element.dispatchEvent(new Event('submit', { bubbles: true }));\n } else {\n const clickEvent = new MouseEvent('click', {\n bubbles: true,\n cancelable: true,\n view: typeof window !== 'undefined' ? window : undefined,\n });\n element.dispatchEvent(clickEvent);\n }\n this.lastAction = { success: true, changed: true, url: typeof window !== 'undefined' ? window.location.href : undefined, element };\n }\n return this;\n }\n\n getLastActionResult(): ActionResult { return { ...this.lastAction }; }\n\n async navigate(url: string): Promise {\n if ((url.startsWith('http://') || url.startsWith('https://') || url.startsWith('/')) && typeof window !== 'undefined') {\n const target = new URL(url, window.location.href).toString();\n const sameOrigin = new URL(target).origin === window.location.origin;\n if (sameOrigin && target.split('#')[0] === window.location.href.split('#')[0] && window.history.pushState) {\n window.history.pushState({}, '', target);\n window.dispatchEvent(new PopStateEvent('popstate'));\n await this.waitForStable();\n } else window.location.href = target;\n }\n return this;\n }\n\n async back(): Promise {\n if (typeof window !== 'undefined') {\n window.history.back();\n }\n return this;\n }\n\n async forward(): Promise {\n if (typeof window !== 'undefined') {\n window.history.forward();\n }\n return this;\n }\n\n async wait(timeout: number = 1000): Promise {\n await new Promise(resolve => setTimeout(resolve, timeout));\n return this;\n }\n\n async extract(selector: string | IndexRecord): Promise {\n const resolved = await this.resolveSelector(selector);\n if (!resolved.success) {\n throw new Error(resolved.reason || 'Extract failed');\n }\n\n const element = resolved.element;\n if (!element) {\n throw new Error('Element not found');\n }\n\n if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {\n return element.value;\n }\n\n return element.textContent?.trim() || '';\n }\n\n async observe(options?: ObservationOptions): Promise {\n if (typeof document === 'undefined') return [];\n const records = extractAccessibilityTree(options?.root ?? document).filter(record => {\n if (options?.includeHidden) return true;\n const element = this.resolveRecordElement(record);\n return !!element && (!options?.inViewport && options?.inViewport !== false ? this.isVisible(element) : this.isVisible(element));\n });\n this.inspector.setRecords(records);\n return records;\n }\n\n async waitForStable(options?: StableWaitOptions): Promise {\n if (typeof document === 'undefined' || typeof MutationObserver === 'undefined') return this;\n const quietMs = options?.quietMs ?? 250;\n const timeout = options?.timeout ?? 5000;\n await new Promise(resolve => {\n let quietTimer: ReturnType;\n const observer = new MutationObserver(() => { clearTimeout(quietTimer); quietTimer = setTimeout(done, quietMs); });\n const done = () => { clearTimeout(quietTimer); observer.disconnect(); resolve(); };\n observer.observe(document, { subtree: true, childList: true, attributes: true, characterData: true });\n quietTimer = setTimeout(done, quietMs);\n setTimeout(done, timeout);\n });\n return this;\n }\n\n isVisible(element: Element | null): boolean {\n if (!element) return false;\n const style = typeof window !== 'undefined' ? window.getComputedStyle(element) : null;\n if (style && (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0')) return false;\n const rect = element.getBoundingClientRect();\n return rect.width > 0 && rect.height > 0 && (typeof window === 'undefined' || (rect.bottom >= 0 && rect.right >= 0 && rect.top <= window.innerHeight && rect.left <= window.innerWidth));\n }\n\n async exhaustPagination(options?: PaginationOptions): Promise {\n const maxPages = options?.maxPages ?? 10;\n const nextText = (options?.nextText ?? ['next', 'older', 'more']).map(v => v.toLowerCase());\n const merged = new Map();\n for (let page = 0; page < maxPages && this.actionCount < (options?.maxActionsPerRun ?? this.options.maxActionsPerRun ?? Infinity); page++) {\n const records = await this.observe({ inViewport: false, includeHidden: true });\n records.forEach(record => merged.set(`${record.label}:${record.type}`, record));\n const next = records.find(record => record.type === 'action' && !record.destructive && nextText.some(text => (record.label || '').toLowerCase().includes(text)));\n if (next) await this.click(next);\n else if (options?.scroll !== false && typeof window !== 'undefined') { window.scrollTo(0, document.body.scrollHeight); await this.waitForStable(); }\n else break;\n if (!next && options?.scroll === false) break;\n }\n return [...merged.values()];\n }\n\n private async resolveSelector(selector: string | IndexRecord): Promise {\n let element: Element | null = null;\n\n if (typeof selector === 'string') {\n element = this.queryComposed(selector);\n } else if (selector) {\n const record = this.index.allSections.find(r => r.id === selector.id);\n const candidates = record?.selectors?.length ? record.selectors : (record?.selector ? [record.selector] : []);\n for (const candidate of candidates) { element = this.queryComposed(candidate, record?.iframePath); if (element) break; }\n if (!element) element = this.resolveRecordElement(selector);\n if (!element && selector.headingText) { const match = await this.findActionable(selector.headingText); if (match && match.id !== selector.id) return this.resolveSelector(match); }\n }\n\n if (!element) {\n return { success: false, reason: 'element-not-found' };\n }\n\n return { success: true, element };\n }\n\n private queryComposed(selector: string, iframePath?: number[]): Element | null {\n if (typeof document === 'undefined') return null;\n const find = (root: Document | ShadowRoot): Element | null => {\n if (selector.startsWith('xpath=')) { try { return document.evaluate(selector.slice(6), root, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue as Element | null; } catch { return null; } }\n const direct = root.querySelector(selector); if (direct) return direct;\n for (const host of Array.from(root.querySelectorAll('*'))) if (host.shadowRoot) { const found = find(host.shadowRoot); if (found) return found; }\n return null;\n };\n if (!iframePath?.length) return find(document);\n let current: Document | null = document;\n for (const position of iframePath) { const frame = current?.querySelectorAll('iframe')[position] as HTMLIFrameElement | undefined; current = frame?.contentDocument ?? null; }\n return current ? find(current) : null;\n }\n\n private resolveRecordElement(record: IndexRecord): Element | null { const candidates = record.selectors?.length ? record.selectors : (record.selector ? [record.selector] : []); for (const selector of candidates) { const found = this.queryComposed(selector, record.iframePath); if (found) return found; } return null; }\n private ensureVisible(element: Element | null): void { if (element && !this.isVisible(element)) element.scrollIntoView({ block: 'center', inline: 'nearest' }); }\n private fingerprint(): string { return `${typeof window !== 'undefined' ? window.location.href : ''}|${typeof document !== 'undefined' ? document.body?.textContent?.length : 0}`; }\n private changedSince(before: string): boolean { return this.fingerprint() !== before; }\n private guardAction(): void { this.actionCount++; if (this.actionCount > (this.options.maxActionsPerRun ?? Infinity)) throw new Error('max-actions-per-run-exceeded'); if (this.options.rateLimitMs) return; }\n private installRouteObserver(): void {\n if (typeof window === 'undefined') return;\n const onRoute = () => { this.lastRoute = window.location.href; void this.observe({ inViewport: false }); };\n window.addEventListener('popstate', onRoute); window.addEventListener('hashchange', onRoute);\n for (const method of ['pushState', 'replaceState'] as const) {\n const historyMethod: (...args: any[]) => any = (window.history[method] as any).bind(window.history);\n (window.history as any)[method] = (...args: any[]) => { const result = historyMethod(...args); onRoute(); return result; };\n }\n }\n\n async findActionable(text: string): Promise {\n const results = this.index.allSections.filter(r =>\n r.type === 'action' || r.type === 'field'\n );\n\n const normalized = text.toLowerCase().trim();\n for (const record of results) {\n if (record.headingText.toLowerCase().includes(normalized) ||\n record.label?.toLowerCase().includes(normalized)) {\n return record;\n }\n }\n return null;\n }\n\n async executeWorkflow(steps: WorkflowStep[], options?: WorkflowOptions): Promise {\n const maxRetries = options?.maxRetries ?? 0;\n const retryDelay = options?.retryDelay ?? 500;\n const stopOnError = options?.stopOnError ?? true;\n\n for (let i = 0; i < steps.length; i++) {\n const step = steps[i];\n options?.onStepStart?.(step, i);\n\n let attempt = 0;\n let success = false;\n\n while (attempt <= maxRetries && !success) {\n try {\nswitch (step.action) {\n case 'click':\n if (step.selector) {\n await this.click(step.selector);\n } else if (step.recordId) {\n const record = this.index.allSections.find(r => r.id === step.recordId);\n if (record) await this.click(record);\n }\n success = true;\n break;\n case 'type':\n if (step.selector) {\n await this.type(step.selector, step.value || '');\n } else if (step.recordId) {\n const record = this.index.allSections.find(r => r.id === step.recordId);\n if (record) await this.type(record, step.value || '');\n }\n success = true;\n break;\n case 'navigate':\n if (step.url) {\n await this.navigate(step.url);\n await this.waitForNavigation();\n }\n success = true;\n break;\n case 'extract':\n success = true;\n break;\n case 'submit':\n await this.submit(step.selector);\n success = true;\n break;\n case 'back':\n await this.back();\n success = true;\n break;\n case 'forward':\n await this.forward();\n success = true;\n break;\n case 'wait':\n if (step.timeout) {\n await this.wait(step.timeout);\n }\n success = true;\n break;\n }\n options?.onStepComplete?.(step, i, success);\n } catch (error) {\n attempt++;\n if (attempt > maxRetries) {\n options?.onStepError?.(step, i, error as Error);\n if (stopOnError) {\n throw error;\n }\n } else {\n await new Promise(resolve => setTimeout(resolve, retryDelay));\n }\n }\n }\n }\n }\n\n private async waitForNavigation(): Promise {\n await new Promise(resolve => {\n if (typeof document !== 'undefined') {\n const checkReady = () => {\n if (document.readyState === 'complete') {\n resolve();\n } else {\n setTimeout(checkReady, 100);\n }\n };\n checkReady();\n } else {\n resolve();\n }\n });\n }\n\n getSession(): AgentSession {\n return {\n id: this.generateSessionId(),\n url: typeof window !== 'undefined' ? window.location.href : 'about:blank',\n timestamp: Date.now(),\n cookies: this.getCookies(),\n localStorage: this.getLocalStorageSnapshot(),\n };\n }\n\n private generateSessionId(): string {\n return Math.random().toString(36).substring(2, 15) +\n Math.random().toString(36).substring(2, 15);\n }\n\n private getCookies(): Record {\n const cookies: Record = {};\n if (typeof document !== 'undefined') {\n document.cookie.split(';').forEach(cookie => {\n const [name, value] = cookie.trim().split('=');\n if (name) cookies[name] = value || '';\n });\n }\n return cookies;\n }\n\n private getLocalStorageSnapshot(): Record {\n const storage: Record = {};\n try {\n if (typeof localStorage !== 'undefined') {\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key) {\n storage[key] = localStorage.getItem(key) || '';\n }\n }\n }\n } catch (e) {\n // localStorage may not be available\n }\n return storage;\n }\n}\n", "import { searchSections, addToIndex, createSearchIndex, getAllSections, findClosestWord, suggest, facets, trackQuery, getPopularQueries, searchWithPagination, getTotalResultCount, type IndexRecord, type ScoredRecord, type SearchOptions } from './search.js';\nimport { UIRenderer, VisualInspector } from './ui/index.js';\nimport { Indexer } from './indexing/index.js';\nimport { ActionExecutor } from './actions/index.js';\nimport { ConfigReader, ConfigApplier } from './config/config-reader.js';\nimport { Agent } from './agent.js';\nimport type { ReefConfig, WorkflowStep, WorkflowOptions } from './types.js';\n\nclass ReefSearch {\n private config: ReefConfig;\n private index = createSearchIndex();\n private ui: UIRenderer;\n private indexer: Indexer;\n private executor: ActionExecutor;\n private inspector = new VisualInspector();\n private selectCallback: ((result: IndexRecord) => void) | null = null;\n private hotkeyHandler: ((event: KeyboardEvent) => void) | null = null;\n private currentQuery = '';\n private searchDebounce = 0;\n private selectedIndex = 0;\n\n constructor() {\n this.config = ConfigReader.readConfig();\n this.indexer = new Indexer(this.config);\n this.ui = new UIRenderer();\n this.executor = new ActionExecutor();\n \n // Set up category tab callback\n this.ui.setCategoryCallback(() => {\n this.selectedIndex = 0;\n this.renderResults();\n });\n\n // Set up settings callbacks\n this.ui.setSettingsCallback((key, val) => {\n if (key === 'theme') {\n this.setTheme(val as 'light' | 'dark' | 'auto');\n } else if (key === 'mode') {\n this.setMode(val as 'regular' | 'opaque' | 'high-contrast');\n } else if (key === 'hotkey') {\n this.setHotkey(val as string);\n } else if (key === 'actionsMode') {\n this.config.actionsMode = val as 'execute' | 'navigate-only';\n }\n });\n\n this.ui.setRebuildIndexCallback(() => {\n void this.rebuildIndex().then(() => {\n this.updateDiagnosticsAndBadges();\n });\n });\n\n this.ui.setToggleInspectorCallback((active) => {\n this.toggleInspector(active);\n });\n\n if (!this.config.headless) {\n this.renderUI();\n this.registerHotkey();\n }\n this.executor.handleDeferredScroll();\n void this.boot();\n }\n\n private renderUI() {\n const placeholder = this.config.placeholder || 'Search this site';\n const currentMode = this.config.mode ?? 'opaque';\n this.ui.renderUI(placeholder, currentMode as 'regular' | 'opaque' | 'high-contrast', (mode) => {\n this.setMode(mode as 'regular' | 'opaque' | 'high-contrast');\n });\n this.setupEventListeners();\n }\n\n private setupEventListeners() {\n const input = this.ui.getInput();\n const host = this.ui.getHost()!;\n const root = this.ui.getRoot();\n\n input?.addEventListener('input', () => {\n this.currentQuery = input?.value ?? '';\n this.selectedIndex = 0;\n if (this.searchDebounce) cancelAnimationFrame(this.searchDebounce);\n this.searchDebounce = requestAnimationFrame(() => this.renderResults());\n });\n\n // Keyboard navigation at document level - works regardless of which element has focus\n document.addEventListener('keydown', (event) => {\n if (!this.ui.getIsOpen()) return;\n // Events crossing a shadow root are retargeted to the host. Use the\n // composed path to identify keystrokes that originated in the input.\n const originatedInInput = input ? event.composedPath().includes(input) : false;\n if (originatedInInput && !['ArrowDown', 'ArrowUp', 'Enter', 'Escape', 'Tab'].includes(event.key)) {\n return;\n }\n\n const results = this.getVisibleResults();\n \n if (event.key === 'ArrowDown') {\n event.preventDefault();\n if (results.length) {\n this.selectedIndex = (this.selectedIndex + 1) % results.length;\n this.renderResults();\n this.ui.scrollSelectedIntoView(this.selectedIndex);\n }\n } else if (event.key === 'ArrowUp') {\n event.preventDefault();\n if (results.length) {\n this.selectedIndex = (this.selectedIndex - 1 + results.length) % results.length;\n this.renderResults();\n this.ui.scrollSelectedIntoView(this.selectedIndex);\n }\n } else if (event.key === 'Enter') {\n event.preventDefault();\n const match = results[this.selectedIndex];\n if (match) {\n this.runSelectCallback(match);\n this.executeAction(match);\n this.close();\n }\n } else if (event.key === 'Escape') {\n event.preventDefault();\n this.close();\n } else if (event.key === 'Tab') {\n // Only handle Tab if input has focus - otherwise let normal tab navigation work\n if (originatedInInput) {\n event.preventDefault();\n const categories = ['all', 'pages', 'actions', 'files', 'links'];\n const currentCat = this.ui.getActiveCategory();\n const idx = categories.indexOf(currentCat);\n const nextIdx = event.shiftKey\n ? (idx - 1 + categories.length) % categories.length\n : (idx + 1) % categories.length;\n const nextCat = categories[nextIdx];\n \n // Update active tab chip\n const tabEl = root?.querySelector(`.tab-chip[data-cat=\"${nextCat}\"]`) as HTMLElement | null;\n if (tabEl) {\n tabEl.click();\n }\n }\n }\n });\n\n // Also handle Escape key globally\n document.addEventListener('keydown', (event) => {\n if (this.ui.getIsOpen() && event.key === 'Escape') {\n event.preventDefault();\n this.close();\n }\n });\n\n host?.addEventListener('click', (event) => {\n const path = event.composedPath();\n const clickedInsidePanel = path.some(\n (el) => el instanceof Element && el.classList.contains('panel')\n );\n if (!clickedInsidePanel) {\n this.close();\n }\n });\n\n // Focus input when modal opens\n this.ui.setOnOpenCallback(() => {\n input?.focus();\n });\n\n this.ui.setupFocusTrap();\n this.executor.handleDeferredActions();\n }\n\n private async boot() {\n await this.indexer.boot(() => this.callOnReady());\n this.index = this.indexer.getIndex();\n this.inspector.setRecords(this.index.allSections);\n this.updateDiagnosticsAndBadges();\n }\n\n private callOnReady(): void {\n if (this.config.onReady) {\n try {\n this.config.onReady({ index: this.getIndex() });\n } catch (error) {\n console.error('[reef] onReady callback error:', error);\n }\n }\n }\n\n private updateDiagnosticsAndBadges() {\n const all = this.index.allSections;\n const pages = all.filter(r => r.type === 'section' || r.type === 'structured').length;\n const interactive = all.filter(r => r.type === 'action' || r.type === 'field').length;\n const files = all.filter(r => r.type === 'file').length;\n const links = all.filter(r => r.type === 'link' || r.type === 'media').length;\n\n this.ui.updateDiagnostics({\n pages: all.filter(r => r.type === 'section').length,\n interactive,\n files\n });\n\n this.ui.updateCategoryBadges({\n all: all.length,\n pages,\n actions: interactive,\n files,\n links\n });\n }\n\n private getVisibleResults(): IndexRecord[] {\n const rawResults = searchSections(this.currentQuery, this.index, 100) as IndexRecord[];\n const category = this.ui.getActiveCategory();\n\n let filtered = rawResults;\n if (category === 'pages') {\n filtered = rawResults.filter(r => r.type === 'section' || r.type === 'structured');\n } else if (category === 'actions') {\n filtered = rawResults.filter(r => r.type === 'action' || r.type === 'field');\n } else if (category === 'files') {\n filtered = rawResults.filter(r => r.type === 'file');\n } else if (category === 'links') {\n filtered = rawResults.filter(r => r.type === 'link' || r.type === 'media');\n }\n\n return filtered.slice(0, 8);\n }\n\n private renderResults() {\n const query = this.currentQuery;\n const results = this.getVisibleResults();\n \n // Ensure selectedIndex is within valid bounds\n if (this.selectedIndex >= results.length) {\n this.selectedIndex = Math.max(0, results.length - 1);\n }\n\n if (!this.ui.getResultsList()) return;\n\n if (!results.length) {\n const suggestion = findClosestWord(query, this.index);\n const list = this.ui.getResultsList();\n if (!list) return;\n\n const makeEmpty = (text: string) => {\n const div = document.createElement('div');\n div.className = 'empty';\n div.textContent = text;\n return div;\n };\n\n if (suggestion) {\n const div = makeEmpty(`No sections match \"${query}\". Did you mean `);\n const strong = document.createElement('strong');\n strong.textContent = suggestion;\n div.appendChild(strong);\n div.appendChild(document.createTextNode('?'));\n list.replaceChildren(div);\n } else {\n list.replaceChildren(makeEmpty(`No sections match \"${query}\"`));\n }\n const countEl = this.ui.getRoot()?.querySelector('#count');\n if (countEl) countEl.textContent = '0 results';\n return;\n }\n\n this.ui.renderResults(query, results, this.selectedIndex,\n (index) => {\n this.selectedIndex = index;\n },\n(event, index) => {\n event.preventDefault();\n event.stopPropagation();\n const match = results[index] ?? results[0];\n if (match) {\n this.runSelectCallback(match);\n this.executeAction(match);\n this.close();\n }\n }\n );\n }\n\n private escapeHtml(s: string): string {\n const map: Record = { '&': '&', '<': '<', '>': '>', '\"': '"', \"'\": ''' };\n let result = '';\n for (let i = 0; i < s.length; i++) {\n result += map[s[i]] ?? s[i];\n }\n return result;\n }\n\n private executeAction(result: IndexRecord): void {\n this.executor.executeAction(result, this.config.actionsMode, (msg) => this.ui.showToast(msg));\n }\n\n private runSelectCallback(result: IndexRecord): void {\n if (this.selectCallback) {\n try {\n this.selectCallback(result);\n } catch (error) {\n console.error('[reef] select callback error:', error);\n }\n }\n }\n\n public open() {\n if (this.config.headless) {\n console.warn('[reef] Cannot open modal in headless mode. Use getIndex() instead.');\n return;\n }\n if (!this.ui.getRoot()) {\n this.renderUI();\n }\n const placeholder = this.config.placeholder || 'Search this site';\n if (this.ui.getInput()) {\n this.ui.getInput()!.placeholder = placeholder;\n }\n this.ui.setIsOpen(true);\n this.selectedIndex = 0;\n this.ui.getHost()?.classList.remove('is-hidden');\n this.ui.getHost()?.classList.add('open');\n this.applyConfigToUI();\n this.ui.updateSettingsValues({\n theme: this.config.theme || 'auto',\n mode: this.config.mode || 'regular',\n hotkey: this.config.hotkey || 'ctrlk,cmdk',\n actionsMode: this.config.actionsMode || 'execute',\n inspectorActive: this.inspector.isActive()\n });\n this.updateDiagnosticsAndBadges();\n this.ui.getInput()?.focus();\n this.ui.applyAriaHidden();\n this.renderResults();\n this.ui.getOnOpenCallback()?.();\n }\n\n private closeInternal(): void {\n this.ui.setIsOpen(false);\n this.ui.getHost()?.classList.remove('open');\n this.ui.getHost()?.classList.add('is-hidden');\n }\n\n public close() {\n this.ui.restoreBodyAriaHidden();\n this.closeInternal();\n }\n\n private applyConfigToUI(): void {\n const host = this.ui.getHost();\n if (!host) return;\n ConfigApplier.applyConfigToUI(host, { ...this.config, mode: this.config.mode });\n }\n\n private registerHotkey() {\n this.unregisterHotkey();\n this.hotkeyHandler = (event) => {\n const hotkey = this.config.hotkey || 'ctrlk,cmdk';\n const handlers: Record = {\n 'ctrlk': event.ctrlKey && event.key === 'k' && !event.shiftKey && !event.altKey && !event.metaKey,\n 'cmdk': event.metaKey && event.key === 'k' && !event.shiftKey && !event.altKey && !event.ctrlKey,\n 'ctrlshiftk': event.ctrlKey && event.shiftKey && event.key.toLowerCase() === 'k',\n 'altk': event.altKey && event.key === 'k',\n 'f': event.ctrlKey && event.key === 'f',\n };\n\n const matched = hotkey.split(',').some(k => handlers[k.trim()]);\n\n if (matched) {\n event.preventDefault();\n this.open();\n }\n };\n document.addEventListener('keydown', this.hotkeyHandler);\n }\n\n private unregisterHotkey() {\n if (this.hotkeyHandler) {\n document.removeEventListener('keydown', this.hotkeyHandler);\n }\n }\n\n public setHeadless(headless: boolean): void {\n this.config.headless = headless;\n if (headless) {\n this.unregisterHotkey();\n if (this.ui.getHost()) {\n this.ui.getHost()!.remove();\n }\n }\n }\n\n public setColorScheme(scheme: { primary: string, secondary: string, background: string, text: string, border: string, radius: number }): void {\n this.config.primaryColor = scheme.primary;\n this.config.secondaryColor = scheme.secondary;\n this.config.backgroundColor = scheme.background;\n this.config.textColor = scheme.text;\n this.config.borderColor = scheme.border;\n this.config.radius = scheme.radius;\n if (this.ui.getIsOpen()) {\n this.applyConfigToUI();\n }\n }\n\n public setTheme(theme: 'light' | 'dark' | 'auto'): void {\n this.config.theme = theme;\n if (this.ui.getIsOpen()) {\n this.applyConfigToUI();\n }\n }\n\n public setFontFamily(fontFamily: string): void {\n this.config.fontFamily = fontFamily;\n if (this.ui.getIsOpen()) {\n this.applyConfigToUI();\n }\n }\n\n public setMode(mode: 'regular' | 'opaque' | 'high-contrast'): void {\n this.config.mode = mode;\n if (this.ui.getIsOpen()) {\n this.applyConfigToUI();\n }\n }\n\n public setHotkey(hotkey: string): void {\n this.config.hotkey = hotkey;\n this.registerHotkey();\n }\n\n public setPlaceholder(placeholder: string): void {\n this.config.placeholder = placeholder;\n if (this.ui.getInput()) {\n this.ui.getInput()!.placeholder = placeholder;\n }\n }\n\n public onselect(callback: (result: IndexRecord) => void): void {\n this.selectCallback = callback;\n }\n\n public offselect(): void {\n this.selectCallback = null;\n }\n\n public reindex(): void {\n this.index = createSearchIndex();\n this.indexer.setIndex(this.index);\n void this.boot();\n }\n\n public rebuildIndex(): Promise {\n return new Promise((resolve) => {\n this.index = createSearchIndex();\n this.indexer.setIndex(this.index);\n const originalOnReady = this.config.onReady;\n this.config.onReady = ({ index }) => {\n originalOnReady?.({ index });\n resolve();\n };\n void this.boot();\n });\n }\n\n public getIndex(): IndexRecord[] {\n return getAllSections(this.index);\n }\n\n public addCustomRecords(records: IndexRecord[]): void {\n addToIndex(this.index, records);\n }\n\n public clearCustomRecords(): void {\n this.index = createSearchIndex();\n this.indexer.setIndex(this.index);\n void this.boot();\n }\n\n public openWithQuery(query: string): void {\n if (this.config.headless) {\n console.warn('[reef] openWithQuery not available in headless mode. Use search() instead.');\n return;\n }\n if (!this.ui.getRoot()) {\n this.renderUI();\n }\n this.currentQuery = query;\n this.ui.setIsOpen(true);\n this.selectedIndex = 0;\n this.ui.getHost()?.classList.remove('is-hidden');\n this.ui.getHost()?.classList.add('open');\n if (this.ui.getInput()) {\n this.ui.getInput()!.value = query;\n }\n this.ui.getInput()?.focus();\n this.renderResults();\n }\n\n public getHotkey(): string {\n return this.config.hotkey || 'ctrlk,cmdk';\n }\n\n public isOpenState(): boolean {\n return this.ui.getIsOpen();\n }\n\n public getConfig(): ReefConfig {\n return { ...this.config };\n }\n\n public search(query: string, limit: number = 8): IndexRecord[] {\n return searchSections(query, this.index, limit) as IndexRecord[];\n }\n\n public searchSections(query: string, options?: SearchOptions | number): ScoredRecord[] {\n return searchSections(query, this.index, options ?? 8) as ScoredRecord[];\n }\n\n public searchWithPagination(query: string, options: SearchOptions & { cursor?: string; pageSize?: number } = {}) {\n return searchWithPagination(query, this.index, options);\n }\n\n public getTotalResultCount(query: string, options: SearchOptions = {}): number {\n return getTotalResultCount(query, this.index, options);\n }\n\n public suggest(query: string, limit: number = 10): string[] {\n return suggest(query, this.index, limit);\n }\n\n public facets(): Record {\n return facets(this.index);\n }\n\n public trackQuery(query: string): void {\n trackQuery(this.index, query);\n }\n\n public getPopularQueries(n: number = 10): string[] {\n return getPopularQueries(this.index, n);\n }\n\n public setOnReady(callback: (data: { index: IndexRecord[] }) => void): void {\n this.config.onReady = callback;\n if (this.index.allSections.length > 0) {\n try {\n callback({ index: this.getIndex() });\n } catch (error) {\n console.error('[reef] onReady callback error:', error);\n }\n }\n }\n\n public getSitemapUrls(): Promise {\n return this.indexer.fetchSitemapUrls();\n }\n\n public act(recordId: string): Promise<{ success: boolean; reason?: string }> {\n return new Promise((resolve) => {\n const record = this.index.allSections.find(r => r.id === recordId);\n if (!record) {\n resolve({ success: false, reason: 'not-found' });\n return;\n }\n\n if (record.type === 'action' && record.destructive && this.config.actionsMode !== 'execute') {\n resolve({ success: false, reason: 'blocked-destructive' });\n return;\n }\n\n try {\n this.executor.executeAction(record, this.config.actionsMode, (msg) => this.ui.showToast(msg));\n resolve({ success: true });\n } catch (error) {\n console.error('[reef] act() error:', error);\n resolve({ success: false, reason: 'error' });\n }\n });\n }\n\n public fillField(recordId: string, value: string): Promise<{ success: boolean; reason?: string }> {\n return new Promise((resolve) => {\n const record = this.index.allSections.find(r => r.id === recordId);\n if (!record) {\n resolve({ success: false, reason: 'not-found' });\n return;\n }\n\n if (record.type !== 'field') {\n resolve({ success: false, reason: 'not-a-field' });\n return;\n }\n\n if (!record.selector) {\n resolve({ success: false, reason: 'no-selector' });\n return;\n }\n\n try {\n const element = document.querySelector(record.selector);\n if (!element) {\n resolve({ success: false, reason: 'element-not-found' });\n return;\n }\n\n const inputElement = element as HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement;\n\n const descriptor = Object.getOwnPropertyDescriptor(inputElement, 'value');\n if (descriptor && descriptor.set) {\n descriptor.set.call(inputElement, value);\n } else {\n inputElement.value = value;\n }\n\n const event = new Event('input', { bubbles: true });\n inputElement.dispatchEvent(event);\n const changeEvent = new Event('change', { bubbles: true });\n inputElement.dispatchEvent(changeEvent);\n\n resolve({ success: true });\n } catch (error) {\n console.error('[reef] fillField() error:', error);\n resolve({ success: false, reason: 'error' });\n }\n });\n }\n\n public getInteractiveRecords(): IndexRecord[] {\n return this.index.allSections.filter(r => r.type === 'action' || r.type === 'field');\n }\n\n public getAgentTools(): Array<{ name: string; description: string; type: string; selector?: string; id: string }> {\n return this.getInteractiveRecords().map(r => ({\n name: r.headingText || r.label || r.id,\n description: r.bodyText || '',\n type: r.type,\n selector: r.selector,\n id: r.id\n }));\n }\n\n public agent(): Agent {\n return new Agent(this.index, this.inspector, this.config.actionsMode || 'execute');\n }\n\n public async executeWorkflow(definition: WorkflowStep[] | { steps: WorkflowStep[]; options?: WorkflowOptions }, options?: WorkflowOptions): Promise {\n const agent = this.agent();\n const steps = Array.isArray(definition) ? definition : definition.steps;\n await agent.executeWorkflow(steps, options ?? (Array.isArray(definition) ? undefined : definition.options));\n }\n\n public toggleInspector(force?: boolean): void {\n const shouldActive = typeof force === 'boolean' ? force : !this.inspector.isActive();\n if (shouldActive) {\n this.inspector.activate();\n } else {\n this.inspector.deactivate();\n }\n }\n}\n\n\nexport { ReefSearch };\nexport default ReefSearch;\n", "/**\n * @file Browser entry point.\n * Instantiates ReefSearch and exposes it globally as `window.Reef`.\n */\n\nimport { ReefSearch } from './reef';\n\nconst reef = new ReefSearch();\n(window as Window & { Reef?: ReefSearch }).Reef = reef;"], + "mappings": "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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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", + "names": ["search_index_exports", "__export", "addSectionsToIndex", "addToIndex", "createSearchIndex", "deserializeIndex", "facets", "findClosestWord", "getAllSections", "getPopularQueries", "getSnippet", "getTotalResultCount", "levenshteinDistance", "parseExtendedQuery", "removeFromIndex", "searchSections", "searchWithPagination", "serializeIndex", "suggest", "trackQuery", "updateRecord", "createTrieNode", "trieInsert", "root", "text", "record", "current", "i", "char", "r", "trieDelete", "recordId", "path", "nodesToClean", "tempCurrent", "node", "triePrefixQuery", "prefix", "invalidateAffectedQueries", "index", "headingText", "bodyText", "recordTokens", "token", "queriesToInvalidate", "query", "entry", "queryTokens", "t", "queryToken", "jaccardSimilarity", "tokens1", "tokens2", "intersection", "union", "extractTokensForMMR", "tokens", "applyMMR", "scoredEntries", "lambda", "results", "selectedTokens", "sortedByScore", "a", "b", "originalScore", "recordText", "maxSimilarity", "selectedTokenSet", "similarity", "mmrScore", "inserted", "j", "records", "tokenizePipeline", "headingLower", "existing", "normalizedHeading", "normalizedExisting", "bodyWords", "word", "normalizedWord", "currentFreq", "currentBodyFreq", "headingWords", "currentHeadingFreq", "labelWords", "currentLabelFreq", "breadcrumbWords", "currentBreadcrumbFreq", "labelLower", "questionWords", "answerWords", "options", "pageSize", "offset", "ranked", "nextOffset", "m", "dp", "prev", "temp", "maxDistance", "term", "closest", "checkWord", "distance", "key", "getLengthNorm", "textLength", "avgLength", "norm", "findMatchPositions", "positions", "lowerText", "lowerQuery", "idx", "tokenizeExtendedQuery", "buildQueryTree", "rest", "exactRegex", "excludeRegex", "prefixRegex", "suffixRegex", "matched", "exactMatch", "excludeMatch", "prefixMatch", "suffixMatch", "spaceMatch", "wordMatch", "fieldMatch", "result", "currentOr", "getFuzzyCandidates", "candidates", "queryLen", "matchExtendedNode", "searchTerm", "field", "value", "exactTerm", "phraseTokens", "tokenizeForPhraseMatching", "headingTokens", "bodyTokens", "hasPhraseMatch", "excludeTerm", "prefixTerm", "suffixTerm", "child", "bm25Score", "termFreq", "docFreq", "totalDocs", "docLength", "avgDocLength", "k1", "idf", "bm25fScore", "queryTerms", "weights", "getAvgDocLength", "totalScore", "termLower", "weightedTermFreq", "combinedDocFreq", "headingTermFreq", "countTermFrequency", "headingDocFreq", "bodyTermFreq", "bodyDocFreq", "labelText", "labelTermFreq", "labelDocFreq", "breadcrumbText", "breadcrumbTermFreq", "breadcrumbDocFreq", "bm25f", "words", "count", "textTokens", "match", "matches", "contextChars", "sortedMatches", "bestMatch", "matchStart", "matchEnd", "start", "end", "snippetText", "suffix", "snippetMatchStart", "snippetMatchEnd", "beforeMatch", "matchText", "afterMatch", "calculateProximityBonus", "termPositions", "totalProximity", "pairCount", "term1", "term2", "positions1", "positions2", "minDistance", "pos1", "pos2", "bonus", "sum", "getCachedShortlist", "cached", "id", "cacheShortlist", "firstKey", "limitOrOptions", "limit", "q", "normalizedQ", "scores", "DEFAULT_WEIGHTS", "addScore", "score", "recordMatches", "useBM25", "useBM25F", "bm25fConfig", "searchPool", "exact", "fieldWeight", "lengthNorm", "pos", "bodyMatches", "allTermsInHeading", "allTermsInBody", "combinedTokens", "proximityBonus", "boostFactor", "popularity", "popularityBoost", "fuzzyDistance", "dist", "normalizedScore", "bodyLower", "bestDist", "bestStart", "substr", "d", "parsed", "filteredScores", "sortedEntries", "aMatches", "bMatches", "topRecords", "typeBoost", "boost", "currentScore", "boostedRecords", "recordIndex", "headingIds", "prefixRecords", "bodyRecords", "serializable", "json", "fieldDocFreq", "queryPopularity", "freq", "headingTrie", "headingIndexMap", "seenRecords", "allSections", "suggestions", "seen", "trieCandidates", "heading", "prefixCandidates", "allCandidates", "selectedId", "n", "counts", "init_search_index", "__esmMin", "cache_exports", "__export", "clearCache", "loadIndex", "loadSiteGraph", "openDB", "saveIndex", "saveSiteGraph", "resolve", "reject", "req", "DB_NAME", "db", "STORE_NAME", "CACHE_VERSION_KEY", "compressData", "data", "compressedStream", "chunks", "reader", "done", "value", "totalLength", "sum", "chunk", "result", "offset", "decompressData", "decompressedStream", "decoder", "index", "metadata", "store", "serializedIndex", "serializeIndex", "compressed", "dataToStore", "ttl", "allRecords", "record", "e", "cached", "deserializeIndex", "graph", "key", "startUrl", "tx", "init_cache", "__esmMin", "init_search_index", "stripTags", "value", "result", "inTag", "i", "char", "generateSelector", "element", "path", "current", "selector", "classes", "siblingIndex", "sibling", "generateStableSelector", "candidates", "push", "escape", "attr", "aria", "role", "name", "extractActionName", "position", "walkComposed", "root", "visit", "iframePath", "elements", "walk", "walkRoot", "child", "frame", "isFocusableAction", "tag", "extractAccessibilityTree", "records", "label", "selectors", "isDestructiveAction", "extractHeadingId", "fullMatch", "text", "idMatch", "hasExplicitId", "findParentSectionId", "html", "headingMatchEnd", "afterHeading", "articleMatch", "headingCache", "extractSections", "html", "url", "headingCache", "cleanHtml", "matches", "headingRegexGlobal", "match", "tag", "text", "headingText", "stripTags", "level", "extractHeadingId", "hasExplicitId", "len", "sections", "i", "heading", "nextHeading", "start", "end", "content", "bodyText", "breadcrumb", "j", "parentSectionId", "findParentSectionId", "selector", "extractActionName", "element", "ariaLabel", "ariaLabelledBy", "labelledElement", "textContent", "title", "isDestructiveAction", "label", "destructiveVerbs", "lowerLabel", "verb", "extractActions", "excludeSelectors", "actions", "doc", "selectors", "elements", "generateStableSelector", "extractFields", "fields", "formElements", "form", "current", "inputs", "input", "id", "labelElement", "parentLabel", "inputElement", "extractHiddenContent", "d", "el", "extractLinks", "links", "anchors", "anchor", "href", "linkText", "resolvedUrl", "resolveUrl", "isExternal", "extractFiles", "extensions", "files", "fileExtensions", "e", "ext", "extractMedia", "media", "images", "img", "alt", "caption", "figure", "figcaption", "textToIndex", "mediaElements", "transcript", "tracks", "track", "src", "extractStructuredData", "structured", "jsonLdScripts", "script", "data", "item", "faqItems", "index", "question", "answer", "type", "name", "description", "TRACKING_PARAMS", "stripTrackingParams", "urlObj", "params", "param", "key", "value", "normalizeUrl", "normalized", "canonicalMatch", "canonicalUrl", "base", "init_search_index", "getSnippet", "text", "query", "lowerText", "lowerQuery", "idx", "start", "end", "appendHighlightedText", "parent", "trimmed", "before", "match", "after", "mark", "getResultTypeIconNode", "type", "svgString", "getResultTypeIcon", "getResultTypeLabel", "UIRenderer", "open", "cb", "placeholder", "currentMode", "onModeChange", "host", "appendHost", "shadow", "parsedDoc", "settingsToggle", "mainView", "settingsView", "tabs", "tab", "t", "themeControl", "modeControl", "hotkeyControl", "actionsModeControl", "agentInspectorToggle", "btnRebuildIndex", "originalText", "btnCopyConfig", "config", "json", "focusInHandler", "event", "first", "last", "target", "focusableSelectors", "element", "mainContent", "selectedIndex", "selected", "allResults", "block", "query", "results", "onResultMouseEnter", "onResultClick", "countEl", "div", "counts", "result", "type", "getResultTypeLabel", "countParts", "count", "fragment", "index", "isSelected", "snippet", "getSnippet", "typeLabel", "isAction", "isSamePage", "canExecuteHere", "btn", "typeDiv", "iconSpan", "getResultTypeIconNode", "labelSpan", "breadcrumbDiv", "headingDiv", "appendHighlightedText", "answerDiv", "qStrong", "ansText", "truncatedAns", "snippetDiv", "actionSpan", "handleMouseEnter", "button", "idx", "el", "i", "handleClick", "message", "toast", "data", "diagPages", "diagInteractive", "diagFiles", "cat", "badge", "VisualInspector", "records", "container", "currentUrl", "pageRecords", "r", "docScrollTop", "docScrollLeft", "record", "element", "rect", "top", "left", "overlay", "isAction", "color", "badge", "init_search_index", "import_meta", "Indexer", "config", "createSearchIndex", "index", "html", "url", "doc", "extractHiddenContent", "rootElement", "scopeElement", "htmlToProcess", "sections", "extractSections", "actions", "extractActions", "fields", "extractFields", "links", "extractLinks", "files", "extractFiles", "media", "extractMedia", "structured", "extractStructuredData", "combined", "onReady", "showToast", "response", "json", "error", "loadIndex", "saveIndex", "ttl", "cached", "cachedMetadata", "e", "pageHashes", "urls", "maxPages", "fetchedSections", "addToIndex", "versionHash", "newPageMetadata", "metadata", "candidates", "allUrls", "seenUrls", "candidate", "xml", "sitemapRegex", "match", "childSitemapUrl", "resolvedUrl", "locRegex", "pageUrl", "hash", "i", "char", "content", "base", "configured", "value", "origin", "cacheKey", "cacheEntry", "robotsUrl", "robotsText", "disallowed", "lines", "currentUserAgent", "line", "trimmedLine", "userAgentMatch", "disallowMatch", "path", "disallowedPath", "delay", "timeSinceLastCrawl", "resolve", "visited", "queue", "normalizeUrl", "concurrency", "batch", "normalizedUrlStr", "l", "u", "arr", "sitemapUrl", "results", "idx", "fetchBatch", "cachedPageInfo", "headResponse", "currentEtag", "currentLastModified", "pageResponse", "pageSections", "workerUrl", "workerCount", "reject", "workers", "completedWorkers", "shards", "worker", "workerId", "shard", "messageHandler", "result", "workerIndex", "errorHandler", "id", "array", "count", "shardSize", "start", "end", "hasErrors", "ActionExecutor", "result", "closeCallback", "currentUrl", "targetUrl", "heading", "element", "fragment", "headingText", "normalized", "headings", "selector", "url", "parsed", "pane", "tabId", "tabButton", "button", "target", "error", "raw", "deferredTarget", "findTarget", "selectorElement", "attempt", "start", "observer", "actionsMode", "showToast", "clickEvent", "deferredAction", "deferredActionStr", "ConfigReader", "config", "dataset", "datasetRecord", "key", "value", "tokenizePipeline", "pipeline", "stopWords", "token", "synonyms", "overrides", "ConfigApplier", "host", "cfg", "Agent", "index", "inspector", "actionsMode", "selector", "resolved", "element", "before", "clickEvent", "retry", "value", "descriptor", "url", "target", "timeout", "resolve", "options", "records", "extractAccessibilityTree", "record", "quietMs", "quietTimer", "observer", "done", "style", "rect", "maxPages", "nextText", "v", "merged", "page", "next", "text", "r", "candidates", "candidate", "match", "iframePath", "find", "root", "direct", "host", "found", "current", "position", "onRoute", "method", "historyMethod", "args", "result", "results", "normalized", "steps", "maxRetries", "retryDelay", "stopOnError", "i", "step", "attempt", "success", "error", "checkReady", "cookies", "cookie", "name", "storage", "key", "ReefSearch", "createSearchIndex", "VisualInspector", "ConfigReader", "Indexer", "UIRenderer", "ActionExecutor", "key", "val", "active", "placeholder", "currentMode", "mode", "input", "host", "root", "event", "originatedInInput", "results", "match", "categories", "currentCat", "idx", "nextIdx", "nextCat", "tabEl", "el", "error", "all", "pages", "r", "interactive", "files", "links", "rawResults", "searchSections", "category", "filtered", "query", "suggestion", "findClosestWord", "list", "makeEmpty", "text", "div", "strong", "countEl", "index", "s", "map", "result", "i", "msg", "ConfigApplier", "hotkey", "handlers", "k", "headless", "scheme", "theme", "fontFamily", "callback", "resolve", "originalOnReady", "getAllSections", "records", "addToIndex", "limit", "options", "searchWithPagination", "getTotalResultCount", "suggest", "facets", "trackQuery", "n", "getPopularQueries", "recordId", "record", "value", "element", "inputElement", "descriptor", "changeEvent", "Agent", "definition", "agent", "steps", "force", "reef", "ReefSearch"] +} diff --git a/extension/index.html b/extension/index.html new file mode 100644 index 0000000..dc6a776 --- /dev/null +++ b/extension/index.html @@ -0,0 +1,1156 @@ + + + + + + + + + Reef Search Extension - Browser Companion + + + + +
+
+ +
Alpha
+
Reef Search
+
+ +
+ + + +
+
+
+
+
+
+
Browser extension — v2.4.0
+

Search tabs, page content, and commands.

+

+ Zero-dependency extension for open tab titles, deep page text, + and quick browser commands. Built with long nights and a lot of coffee. +

+
+ +
+ + + +
+
+
+

Install the extension

+

Load the unpacked package into Chrome and pin it to the toolbar.

+
    +
  1. + 01 + Open Chrome and go to chrome://extensions +
  2. +
  3. + 02 + Enable Developer mode in the top-right corner +
  4. +
  5. + 03 + Click Load unpacked and select the extension folder +
  6. +
  7. + 04 + Pin the extension using the 📌 icon on the toolbar +
  8. +
  9. + 05 + Refresh the browser +
  10. +
+
+
+
+
1
+ chrome://extensions — Extensions page +
+
+
2
+ Developer mode — Toggle in top-right +
+
+
3
+ Load unpacked — Select the folder (unzip it first) +
+
+
4
+ Pin — Attach to toolbar +
+
+
5
+ Refresh — Update the browser +
+
+
+
+
+
+ +
+
+
Extension internals — v2.4.0
+

Security & Privacy

+

+ Reef for Browsers is designed with privacy and security in mind. + All data processing happens locally on your device. + Nothing is uploaded to external servers. +

+ +
+
i
+

+ No telemetry, no external APIs. + Reef does not send any page content, search queries, or usage data to external servers. + All indexing, search, and storage happens entirely within your browser. +

+
+ +
+

Data Collection

+

The extension collects and indexes the following data from pages you visit:

+
+
+

Page Structure

+

Headings (h1–h6), section text, breadcrumbs, and HTML selectors. Scripts, styles, and noscript tags are stripped before indexing.

+
+
+

Actions & Form Fields

+

Buttons, links, form inputs, and their labels. Field values may be captured and stored to support fill workflows.

+
+
+

Media

+

Image alt text, captions, video/audio titles, and transcript availability. Actual media files are not downloaded.

+
+
+

Structured Data

+

JSON-LD FAQ pages, article metadata, and descriptions parsed from structured markup.

+
+
+

+ Always excluded: Password inputs, payment fields, SSN fields, and elements matching your configured exclusion selectors. +

+
+ +
+

Data Storage

+

All data is stored locally using browser storage APIs:

+
    +
  • IndexedDB — Compressed (gzip) search indices, cached page data, and site graphs. Stored under the extension's private origin.
  • +
  • chrome.storage.local — Settings, query history, popular queries, bookmarks, snippets, and notes. Up to 5MB+ with unlimitedStorage permission.
  • +
  • Memory — Active tab indexes, spotlight results, and cache (cleared on browser restart).
  • +
+
+ +
+

Permissions

+

The extension requests the following permissions and their justifications:

+
    +
  • activeTab — Allows temporary access to the active tab to index content and execute user-initiated actions.
  • +
  • scripting — Allows injection of the content script to extract page content and interact with the DOM.
  • +
  • storage — Persists search indices, settings, and library data between sessions.
  • +
  • tabs — Lists open tabs and switches between them for the Spotlight search feature.
  • +
  • contextMenus — Adds right-click options for bookmarking, saving snippets, and adding notes.
  • +
  • unlimitedStorage — Allows storing large search indices without the standard 5MB quota.
  • +
  • host: <all_urls> — Injects content scripts on every website so pages can be indexed when activated by the user.
  • +
+
+ +
+

Privacy Controls

+

You have full control over what Reef accesses and indexes:

+
+
+

Exclusion Selectors

+

Define CSS selectors for elements Reef must never index or interact with. For example: .private, #secret-area.

+
+
+

Blocked Domains

+

Completely disable Reef on specific domains. One domain per line. Useful for banking, healthcare, or internal sites.

+
+
+

Destructive Action Confirmation

+

Require explicit confirmation before executing actions like delete, remove, checkout, or submit order.

+
+
+

Action Mode

+

Set to "Navigate Only" to prevent Reef from clicking buttons or typing into fields. Only link navigation is allowed.

+
+
+
+ +
+

How to Remove Your Data

+

You can permanently delete all data stored by Reef:

+
+
+

Clear Cache

+

Removes all indexed page data from IndexedDB. Pages will be re-indexed on next visit if requested.

+
+
+

Clear History

+

Deletes all tracked search queries and popularity data from chrome.storage.local.

+
+
+

Reset to Defaults

+

Clears all settings, cached data, and library items. This cannot be undone.

+
+
+

Uninstall

+

Removing the extension from Chrome deletes all stored data including IndexedDB and chrome.storage.local entries.

+
+
+
+ +
+

Security Considerations

+

Be aware of the following security characteristics:

+
    +
  • DOM Access — Content scripts run in the context of every webpage and can read/modify the DOM.
  • +
  • Form Values — Input field values may be captured and stored to enable autofill-type workflows.
  • +
  • Cookie Access — The extension's Agent layer can read cookies via the document API for session snapshots.
  • +
  • History Access — Tab titles and URLs are read to build the cross-tab search index.
  • +
  • Local Storage — The extension can read localStorage and make snapshots for session restoration.
  • +
  • History API Override — The extension overrides history.pushState/replaceState to observe route changes.
  • +
  • Cross-Origin Context — Content scripts inherit the origin of the page they run on within the extension's isolated world.
  • +
+
+ +
+

Data Retention

+
    +
  • Indexed Cache — Cached pages persist indefinitely until manually cleared or overwritten by newer versions.
  • +
  • Query History — Last 100 unique queries are retained for suggestion features.
  • +
  • Popularity Data — Per-query popularity counts are retained persistently.
  • +
  • Library Items — Bookmarks, snippets, notes, and recents persist until manually deleted.
  • +
  • Settings — Persist indefinitely in chrome.storage.local.
  • +
+
+
+
+ +
+
+
01
+

Tab search

+

Match titles and URLs across every open tab, instantly.

+
+
+
02
+

Deep content

+

Index headings, snippets, and form fields on the active page.

+
+
+
03
+

Quick commands

+

Duplicate, mute, or copy the URL of any tab from the keyboard.

+
+
+ +
+

Frequently asked questions

+
+
+ Does Reef work offline? + + +
+
+

Yes. Indexing, search, and storage all run locally in the browser, so search still works without an internet connection.

+
+
+
+
+ Will this slow down my browser? + + +
+
+

Indexing only happens on tabs you activate, and cached data is compressed with gzip to keep memory and disk usage low.

+
+
+
+
+ Can I exclude sensitive pages? + + +
+
+

Yes. Add CSS exclusion selectors or block entire domains from the settings panel, useful for banking or healthcare sites.

+
+
+
+
+ Which browsers are supported? + + +
+
+

Reef is built for Chromium-based browsers, including Chrome, Edge, Brave, and Arc, using standard extension APIs.

+
+
+
+ + +
+ + + + \ No newline at end of file diff --git a/index.html b/index.html index c68032f..8788309 100644 --- a/index.html +++ b/index.html @@ -32,7 +32,7 @@ --> - + Extension diff --git a/plugin.zip b/plugin.zip deleted file mode 100644 index 9a9f519..0000000 Binary files a/plugin.zip and /dev/null differ diff --git a/reef-extension-chrome/_locales/en/messages.json b/reef-extension-chrome/_locales/en/messages.json deleted file mode 100644 index 0b5bd20..0000000 --- a/reef-extension-chrome/_locales/en/messages.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "appName": { - "message": "Reef for Browsers", - "description": "The name of the extension" - }, - "appDescription": { - "message": "Search, bookmark, and automate any page. Library of bookmarks, snippets, notes, and a tab-wide finder — all client-side.", - "description": "Description of the extension" - } -} diff --git a/reef-extension-chrome/background.js b/reef-extension-chrome/background.js deleted file mode 100644 index 76d9c21..0000000 --- a/reef-extension-chrome/background.js +++ /dev/null @@ -1,3175 +0,0 @@ -var __defProp = Object.defineProperty; -var __getOwnPropNames = Object.getOwnPropertyNames; -var __esm = (fn, res, err) => function __init() { - if (err) throw err[0]; - try { - return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; - } catch (e) { - throw err = [e], e; - } -}; -var __export = (target, all) => { - for (var name in all) - __defProp(target, name, { get: all[name], enumerable: true }); -}; - -// ../src/search-index.ts -var search_index_exports = {}; -__export(search_index_exports, { - addSectionsToIndex: () => addSectionsToIndex, - addToIndex: () => addToIndex, - createSearchIndex: () => createSearchIndex, - deserializeIndex: () => deserializeIndex, - facets: () => facets, - findClosestWord: () => findClosestWord, - getAllSections: () => getAllSections, - getPopularQueries: () => getPopularQueries, - getSnippet: () => getSnippet, - getTotalResultCount: () => getTotalResultCount, - levenshteinDistance: () => levenshteinDistance, - parseExtendedQuery: () => parseExtendedQuery, - removeFromIndex: () => removeFromIndex, - searchSections: () => searchSections, - searchWithPagination: () => searchWithPagination, - serializeIndex: () => serializeIndex, - suggest: () => suggest, - trackQuery: () => trackQuery, - updateRecord: () => updateRecord -}); -function createTrieNode() { - return { - children: /* @__PURE__ */ new Map(), - records: [] - }; -} -function trieInsert(root, text, record) { - let current = root; - for (let i = 0; i < text.length; i++) { - const char = text[i]; - if (!current.children.has(char)) { - current.children.set(char, createTrieNode()); - } - current = current.children.get(char); - if (!current.records.some((r) => r.id === record.id)) { - current.records.push(record); - } - } -} -function trieDelete(root, text, recordId) { - let current = root; - const path = []; - for (let i = 0; i < text.length; i++) { - const char = text[i]; - if (!current.children.has(char)) { - return; - } - path.push({ node: current, char }); - current = current.children.get(char); - } - const nodesToClean = []; - let tempCurrent = root; - for (let i = 0; i < text.length; i++) { - const char = text[i]; - if (tempCurrent.children.has(char)) { - tempCurrent = tempCurrent.children.get(char); - tempCurrent.records = tempCurrent.records.filter((r) => r.id !== recordId); - nodesToClean.push(tempCurrent); - } - } - for (const node of nodesToClean) { - if (node.records.length === 0 && node.children.size === 0) { - } - } -} -function triePrefixQuery(root, prefix) { - let current = root; - for (let i = 0; i < prefix.length; i++) { - const char = prefix[i]; - if (!current.children.has(char)) { - return []; - } - current = current.children.get(char); - } - return [...current.records]; -} -function invalidateAffectedQueries(index, recordId, headingText, bodyText) { - const recordTokens = /* @__PURE__ */ new Set(); - headingText.toLowerCase().split(/\s+/).forEach((token) => { - if (token.length >= 2) recordTokens.add(token); - }); - bodyText.toLowerCase().split(/\s+/).forEach((token) => { - if (token.length >= 2) recordTokens.add(token); - }); - const queriesToInvalidate = /* @__PURE__ */ new Set(); - for (const [query, entry] of index.queryCache.entries()) { - if (entry.resultIds.includes(recordId)) { - queriesToInvalidate.add(query); - continue; - } - const queryLower = query.toLowerCase(); - const queryTokens = queryLower.split(/\s+/).filter((t) => t.length >= 2); - for (const queryToken of queryTokens) { - if (recordTokens.has(queryToken)) { - queriesToInvalidate.add(query); - break; - } - } - } - if (queriesToInvalidate.size > 0) { - for (const query of queriesToInvalidate) { - index.queryCache.delete(query); - } - } else { - index.queryCache.clear(); - } -} -function jaccardSimilarity(tokens1, tokens2) { - if (tokens1.size === 0 || tokens2.size === 0) return 0; - let intersection = 0; - for (const token of tokens1) { - if (tokens2.has(token)) intersection++; - } - const union = tokens1.size + tokens2.size - intersection; - return intersection / union; -} -function extractTokensForMMR(text) { - const tokens = text.toLowerCase().split(/\s+/); - return new Set(tokens.filter((t) => t.length > 2)); -} -function applyMMR(scoredEntries, lambda = 0.5) { - if (scoredEntries.length <= 1) return scoredEntries; - const results = []; - const selectedTokens = []; - const sortedByScore = [...scoredEntries].sort((a, b) => b[1] - a[1]); - results.push(sortedByScore[0]); - selectedTokens.push(extractTokensForMMR(sortedByScore[0][0].headingText + " " + sortedByScore[0][0].bodyText)); - for (let i = 1; i < sortedByScore.length; i++) { - const [record, originalScore] = sortedByScore[i]; - const recordText = record.headingText + " " + record.bodyText; - const recordTokens = extractTokensForMMR(recordText); - let maxSimilarity = 0; - for (const selectedTokenSet of selectedTokens) { - const similarity = jaccardSimilarity(recordTokens, selectedTokenSet); - maxSimilarity = Math.max(maxSimilarity, similarity); - } - const mmrScore = (1 - lambda) * originalScore + lambda * (1 - maxSimilarity); - let inserted = false; - for (let j = 0; j < results.length; j++) { - if (mmrScore > results[j][1]) { - results.splice(j, 0, [record, mmrScore]); - selectedTokens.splice(j, 0, recordTokens); - inserted = true; - break; - } - } - if (!inserted) { - results.push([record, mmrScore]); - selectedTokens.push(recordTokens); - } - } - return results; -} -function createSearchIndex() { - return { - headingIndex: /* @__PURE__ */ new Map(), - headingIds: /* @__PURE__ */ new Map(), - bodyIndex: /* @__PURE__ */ new Map(), - allSections: [], - queryCache: /* @__PURE__ */ new Map(), - popularQueries: [], - docFrequency: /* @__PURE__ */ new Map(), - fieldDocFreq: { - headingText: /* @__PURE__ */ new Map(), - bodyText: /* @__PURE__ */ new Map(), - label: /* @__PURE__ */ new Map(), - breadcrumb: /* @__PURE__ */ new Map() - }, - totalDocs: 0, - queryPopularity: /* @__PURE__ */ new Map(), - version: 1, - headingTrie: createTrieNode() - }; -} -function addToIndex(index, records, tokenizePipeline) { - index.totalDocs += records.length; - for (const record of records) { - const headingLower = record.headingText.toLowerCase(); - if (headingLower.length >= 2) { - const existing = index.headingIds.get(headingLower) ?? []; - existing.push(record); - index.headingIds.set(headingLower, existing); - const normalizedHeading2 = headingLower.normalize("NFD").replace(/[\u0300-\u036f]/g, ""); - if (normalizedHeading2 !== headingLower) { - const normalizedExisting = index.headingIds.get(normalizedHeading2) ?? []; - normalizedExisting.push(record); - index.headingIds.set(normalizedHeading2, normalizedExisting); - } - } - for (let i = 2; i <= headingLower.length; i++) { - const prefix = headingLower.slice(0, i); - const existing = index.headingIndex.get(prefix) ?? []; - existing.push(record); - index.headingIndex.set(prefix, existing); - } - const normalizedHeading = headingLower.normalize("NFD").replace(/[\u0300-\u036f]/g, ""); - for (let i = 2; i <= normalizedHeading.length; i++) { - const prefix = normalizedHeading.slice(0, i); - if (prefix !== headingLower.slice(0, i)) { - const existing = index.headingIndex.get(prefix) ?? []; - existing.push(record); - index.headingIndex.set(prefix, existing); - } - } - trieInsert(index.headingTrie, headingLower, record); - if (normalizedHeading !== headingLower) { - trieInsert(index.headingTrie, normalizedHeading, record); - } - const bodyLower = record.bodyText.toLowerCase(); - const bodyWords = bodyLower.split(/\s+/); - for (const word of bodyWords) { - if (word.length >= 3) { - const existing = index.bodyIndex.get(word) ?? []; - existing.push(record); - index.bodyIndex.set(word, existing); - const normalizedWord = word.normalize("NFD").replace(/[\u0300-\u036f]/g, ""); - if (normalizedWord !== word) { - const normalizedExisting = index.bodyIndex.get(normalizedWord) ?? []; - normalizedExisting.push(record); - index.bodyIndex.set(normalizedWord, normalizedExisting); - } - } - } - for (const word of bodyWords) { - if (word.length >= 2) { - const currentFreq = index.docFrequency.get(word) ?? 0; - index.docFrequency.set(word, currentFreq + 1); - const currentBodyFreq = index.fieldDocFreq.bodyText.get(word) ?? 0; - index.fieldDocFreq.bodyText.set(word, currentBodyFreq + 1); - } - } - const headingWords = headingLower.split(/\s+/); - for (const word of headingWords) { - if (word.length >= 2) { - const currentHeadingFreq = index.fieldDocFreq.headingText.get(word) ?? 0; - index.fieldDocFreq.headingText.set(word, currentHeadingFreq + 1); - } - } - if (record.label) { - const labelLower = record.label.toLowerCase(); - const labelWords = labelLower.split(/\s+/); - for (const word of labelWords) { - if (word.length >= 2) { - const currentLabelFreq = index.fieldDocFreq.label.get(word) ?? 0; - index.fieldDocFreq.label.set(word, currentLabelFreq + 1); - } - } - } - if (record.breadcrumb) { - const breadcrumbLower = record.breadcrumb.toLowerCase(); - const breadcrumbWords = breadcrumbLower.split(/\s+/); - for (const word of breadcrumbWords) { - if (word.length >= 2) { - const currentBreadcrumbFreq = index.fieldDocFreq.breadcrumb.get(word) ?? 0; - index.fieldDocFreq.breadcrumb.set(word, currentBreadcrumbFreq + 1); - } - } - } - if (record.label) { - const labelLower = record.label.toLowerCase(); - if (labelLower.length >= 2) { - const existing = index.bodyIndex.get(labelLower) ?? []; - existing.push(record); - index.bodyIndex.set(labelLower, existing); - } - } - if (record.type === "structured" && record.structuredData) { - if (record.structuredData.question) { - const questionWords = record.structuredData.question.toLowerCase().split(/\s+/); - for (const word of questionWords) { - if (word.length >= 3) { - const existing = index.bodyIndex.get(word) ?? []; - existing.push(record); - index.bodyIndex.set(word, existing); - } - } - } - if (record.structuredData.answer) { - const answerWords = record.structuredData.answer.toLowerCase().split(/\s+/); - for (const word of answerWords) { - if (word.length >= 3) { - const existing = index.bodyIndex.get(word) ?? []; - existing.push(record); - index.bodyIndex.set(word, existing); - } - } - } - } - } - index.allSections.push(...records); -} -function getAllSections(index) { - return index.allSections; -} -function searchWithPagination(query, index, options = {}) { - const pageSize = Math.max(1, options.pageSize ?? options.limit ?? 20); - const offset = options.cursor ? Math.max(0, Number.parseInt(options.cursor, 10) || 0) : 0; - const ranked = searchSections(query, index, { ...options, limit: Number.MAX_SAFE_INTEGER }); - const results = ranked.slice(offset, offset + pageSize).map( - (record) => "score" in record ? record : { record, score: 0 } - ); - const nextOffset = offset + results.length; - return { - results, - total: ranked.length, - hasMore: nextOffset < ranked.length, - ...nextOffset < ranked.length ? { nextCursor: String(nextOffset) } : {} - }; -} -function getTotalResultCount(query, index, options = {}) { - return searchSections(query, index, { ...options, limit: Number.MAX_SAFE_INTEGER }).length; -} -function levenshteinDistance(a, b) { - const m = a.length; - const n = b.length; - if (m === 0) return n; - if (n === 0) return m; - const dp = new Array(n + 1); - for (let j = 0; j <= n; j++) dp[j] = j; - for (let i = 1; i <= m; i++) { - let prev = dp[0]; - dp[0] = i; - for (let j = 1; j <= n; j++) { - const temp = dp[j]; - dp[j] = Math.min( - dp[j] + 1, - dp[j - 1] + 1, - prev + (a[i - 1] !== b[j - 1] ? 1 : 0) - ); - prev = temp; - } - } - return dp[n]; -} -function findClosestWord(query, index, maxDistance = 2) { - const term = query.trim().toLowerCase(); - if (!term || term.length < 2) return null; - let closest = null; - const checkWord = (word) => { - if (!word || Math.abs(word.length - term.length) > maxDistance) return; - const distance = levenshteinDistance(word, term); - const current = closest; - if (distance <= maxDistance && (!current || distance < current.distance)) { - closest = { word, distance }; - } - }; - for (const key of index.headingIds.keys()) { - checkWord(key); - } - for (const key of index.bodyIndex.keys()) { - checkWord(key); - } - return closest?.word ?? null; -} -function getLengthNorm(textLength, avgLength = 50) { - const norm = 1 - Math.exp(-textLength / avgLength); - return Math.max(0, Math.min(1, norm)); -} -function findMatchPositions(text, query) { - const positions = []; - if (!query.trim() || !text) return positions; - const lowerText = text.toLowerCase(); - const lowerQuery = query.toLowerCase(); - let idx = 0; - while ((idx = lowerText.indexOf(lowerQuery, idx)) !== -1) { - positions.push({ start: idx, end: idx + query.length }); - idx += query.length; - } - return positions; -} -function parseExtendedQuery(query) { - const tokens = tokenizeExtendedQuery(query); - return buildQueryTree(tokens); -} -function tokenizeExtendedQuery(query) { - const tokens = []; - let rest = query; - const exactRegex = /'([^']+)'/g; - const excludeRegex = /!(\S+)/g; - const prefixRegex = /\^(\S+)/g; - const suffixRegex = /(\S+)\$/g; - while (rest.length > 0) { - let matched = false; - const exactMatch = exactRegex.exec(rest); - if (exactMatch && exactMatch.index === 0) { - tokens.push({ type: "exact", value: exactMatch[1] }); - rest = rest.slice(exactMatch[0].length); - matched = true; - continue; - } - const excludeMatch = excludeRegex.exec(rest); - if (excludeMatch && excludeMatch.index === 0) { - tokens.push({ type: "exclude", value: excludeMatch[1] }); - rest = rest.slice(excludeMatch[0].length); - matched = true; - continue; - } - const prefixMatch = prefixRegex.exec(rest); - if (prefixMatch && prefixMatch.index === 0) { - tokens.push({ type: "prefix", value: prefixMatch[1] }); - rest = rest.slice(prefixMatch[0].length); - matched = true; - continue; - } - const suffixMatch = suffixRegex.exec(rest); - if (suffixMatch && suffixMatch.index === 0) { - tokens.push({ type: "suffix", value: suffixMatch[1] }); - rest = rest.slice(suffixMatch[0].length); - matched = true; - continue; - } - const pipeMatch = rest.match(/^\|/); - if (pipeMatch) { - tokens.push({ type: "or", value: "|" }); - rest = rest.slice(1); - matched = true; - continue; - } - const spaceMatch = rest.match(/^\s+/); - if (spaceMatch && !matched) { - rest = rest.slice(spaceMatch[0].length); - continue; - } - const wordMatch = rest.match(/^(\S+)/); - if (wordMatch) { - const fieldMatch = wordMatch[1].match(/^([\w-]+):(.+)$/); - tokens.push({ type: "term", value: fieldMatch ? `${fieldMatch[1]}:${fieldMatch[2]}` : wordMatch[1] }); - rest = rest.slice(wordMatch[0].length); - matched = true; - } - } - return tokens; -} -function buildQueryTree(tokens) { - const result = { type: "and", children: [] }; - let currentOr = null; - for (const token of tokens) { - if (token.type === "or") { - if (!currentOr) { - currentOr = { type: "or", children: [] }; - result.children.push(currentOr); - } - } else { - if (currentOr) { - result.children.push(currentOr); - currentOr = null; - } - result.children.push({ type: token.type, value: token.value }); - } - } - if (currentOr) { - result.children.push(currentOr); - } - return result; -} -function getFuzzyCandidates(query, index, distance) { - const candidates = /* @__PURE__ */ new Set(); - const queryLen = query.length; - for (const [key, records] of index.headingIds) { - if (Math.abs(key.length - queryLen) <= distance) { - for (const record of records) { - candidates.add(record); - } - } - } - for (const [key, records] of index.bodyIndex) { - if (Math.abs(key.length - queryLen) <= distance) { - for (const record of records) { - candidates.add(record); - } - } - } - return candidates; -} -function matchExtendedNode(node, record) { - switch (node.type) { - case "term": - const fieldMatch = node.value.match(/^([\w-]+):(.+)$/); - const searchTerm = (fieldMatch ? fieldMatch[2] : node.value).toLowerCase(); - const field = fieldMatch?.[1].toLowerCase(); - const values = field === "title" || field === "heading" ? [record.headingText] : field === "body" ? [record.bodyText] : field === "label" ? [record.label ?? ""] : field === "breadcrumb" ? [record.breadcrumb] : field === "type" ? [record.type] : [record.headingText, record.bodyText, record.label ?? "", record.breadcrumb]; - return values.some((value) => value.toLowerCase().includes(searchTerm)); - case "exact": - const exactTerm = node.value.toLowerCase(); - const phraseTokens = tokenizeForPhraseMatching(exactTerm); - const headingTokens = tokenizeForPhraseMatching(record.headingText); - const bodyTokens = tokenizeForPhraseMatching(record.bodyText); - return hasPhraseMatch(headingTokens, phraseTokens) || hasPhraseMatch(bodyTokens, phraseTokens); - case "exclude": - const excludeTerm = node.value.toLowerCase(); - return !record.headingText.toLowerCase().includes(excludeTerm) && !record.bodyText.toLowerCase().includes(excludeTerm); - case "prefix": - const prefixTerm = node.value.toLowerCase(); - return record.headingText.toLowerCase().startsWith(prefixTerm) || record.bodyText.toLowerCase().startsWith(prefixTerm); - case "suffix": - const suffixTerm = node.value.toLowerCase(); - return record.headingText.toLowerCase().endsWith(suffixTerm) || record.bodyText.toLowerCase().endsWith(suffixTerm); - case "and": - return node.children.every((child) => matchExtendedNode(child, record)); - case "or": - return node.children.some((child) => matchExtendedNode(child, record)); - } -} -function bm25Score(termFreq, docFreq, totalDocs, docLength, avgDocLength, k1 = 1.5, b = 0.75) { - const idf = Math.log((totalDocs - docFreq + 0.5) / (docFreq + 0.5) + 1); - const norm = 1 - b + b * (docLength / avgDocLength); - return idf * (termFreq * (k1 + 1) / (termFreq + k1 * norm)); -} -function bm25fScore(record, queryTerms, index, weights, k1 = 1.5, b = 0.75) { - const avgDocLength = getAvgDocLength(index); - const totalDocs = index.totalDocs; - const docLength = record.headingText.length * (weights.headingText || 1) + record.bodyText.length * (weights.bodyText || 1) + (record.label ? record.label.length * (weights.label || 1) : 0) + (record.breadcrumb ? record.breadcrumb.length * (weights.breadcrumb || 1) : 0); - let totalScore = 0; - for (const term of queryTerms) { - const termLower = term.toLowerCase(); - let weightedTermFreq = 0; - let combinedDocFreq = 0; - if (weights.headingText) { - const headingText = record.headingText.toLowerCase(); - const headingTermFreq = countTermFrequency(headingText, termLower); - const headingDocFreq = index.fieldDocFreq.headingText.get(termLower) || 1; - weightedTermFreq += headingTermFreq * (weights.headingText || 1); - combinedDocFreq = Math.max(combinedDocFreq, headingDocFreq); - } - if (weights.bodyText) { - const bodyText = record.bodyText.toLowerCase(); - const bodyTermFreq = countTermFrequency(bodyText, termLower); - const bodyDocFreq = index.fieldDocFreq.bodyText.get(termLower) || 1; - weightedTermFreq += bodyTermFreq * (weights.bodyText || 1); - combinedDocFreq = Math.max(combinedDocFreq, bodyDocFreq); - } - if (record.label && weights.label) { - const labelText = record.label.toLowerCase(); - const labelTermFreq = countTermFrequency(labelText, termLower); - const labelDocFreq = index.fieldDocFreq.label.get(termLower) || 1; - weightedTermFreq += labelTermFreq * (weights.label || 1); - combinedDocFreq = Math.max(combinedDocFreq, labelDocFreq); - } - if (record.breadcrumb && weights.breadcrumb) { - const breadcrumbText = record.breadcrumb.toLowerCase(); - const breadcrumbTermFreq = countTermFrequency(breadcrumbText, termLower); - const breadcrumbDocFreq = index.fieldDocFreq.breadcrumb.get(termLower) || 1; - weightedTermFreq += breadcrumbTermFreq * (weights.breadcrumb || 1); - combinedDocFreq = Math.max(combinedDocFreq, breadcrumbDocFreq); - } - if (weightedTermFreq > 0 && combinedDocFreq > 0) { - const idf = Math.log((totalDocs - combinedDocFreq + 0.5) / (combinedDocFreq + 0.5) + 1); - const norm = 1 - b + b * (docLength / avgDocLength); - const bm25f = idf * (weightedTermFreq * (k1 + 1) / (weightedTermFreq + k1 * norm)); - totalScore += bm25f; - } - } - return totalScore; -} -function countTermFrequency(text, term) { - if (!text || !term) return 0; - const words = text.split(/\s+/); - let count = 0; - for (const word of words) { - if (word === term) count++; - } - return count; -} -function tokenizeForPhraseMatching(text) { - if (!text) return []; - return text.toLowerCase().split(/\s+/).filter((t) => t.length > 0); -} -function hasPhraseMatch(textTokens, phraseTokens) { - if (phraseTokens.length === 0) return true; - if (phraseTokens.length > textTokens.length) return false; - for (let i = 0; i <= textTokens.length - phraseTokens.length; i++) { - let match = true; - for (let j = 0; j < phraseTokens.length; j++) { - if (textTokens[i + j] !== phraseTokens[j]) { - match = false; - break; - } - } - if (match) return true; - } - return false; -} -function getSnippet(record, matches, contextChars = 60) { - if (!record || !matches || matches.length === 0) return ""; - const text = record.bodyText || record.headingText || ""; - if (!text) return ""; - if (matches.length === 0) { - return text.slice(0, contextChars * 2) + (text.length > contextChars * 2 ? "..." : ""); - } - const sortedMatches = [...matches].sort((a, b) => a.start - b.start); - let bestMatch = sortedMatches[0]; - for (const match of sortedMatches) { - if (match.end - match.start > bestMatch.end - bestMatch.start) { - bestMatch = match; - } - } - const matchStart = bestMatch.start; - const matchEnd = bestMatch.end; - let start = Math.max(0, matchStart - contextChars); - let end = Math.min(text.length, matchEnd + contextChars); - if (start === 0) { - end = Math.min(text.length, matchEnd + contextChars * 2); - } - if (end === text.length) { - start = Math.max(0, matchStart - contextChars * 2); - } - const snippetText = text.slice(start, end); - const prefix = start > 0 ? "..." : ""; - const suffix = end < text.length ? "..." : ""; - const snippetMatchStart = matchStart - start; - const snippetMatchEnd = matchEnd - start; - const beforeMatch = snippetText.slice(0, snippetMatchStart); - const matchText = snippetText.slice(snippetMatchStart, snippetMatchEnd); - const afterMatch = snippetText.slice(snippetMatchEnd); - return prefix + beforeMatch + "" + matchText + "" + afterMatch + suffix; -} -function calculateProximityBonus(textTokens, queryTokens) { - if (queryTokens.length <= 1) return 1; - const termPositions = /* @__PURE__ */ new Map(); - for (let i = 0; i < textTokens.length; i++) { - const token = textTokens[i]; - if (queryTokens.includes(token)) { - if (!termPositions.has(token)) { - termPositions.set(token, []); - } - termPositions.get(token).push(i); - } - } - let totalProximity = 0; - let pairCount = 0; - for (let i = 0; i < queryTokens.length; i++) { - for (let j = i + 1; j < queryTokens.length; j++) { - const term1 = queryTokens[i]; - const term2 = queryTokens[j]; - const positions1 = termPositions.get(term1) || []; - const positions2 = termPositions.get(term2) || []; - if (positions1.length === 0 || positions2.length === 0) continue; - let minDistance = Infinity; - for (const pos1 of positions1) { - for (const pos2 of positions2) { - const distance = Math.abs(pos1 - pos2); - if (distance < minDistance) { - minDistance = distance; - } - } - } - if (minDistance !== Infinity) { - const bonus = Math.max(1, 2 - minDistance * 0.1); - totalProximity += bonus; - pairCount++; - } - } - } - return pairCount > 0 ? 1 + (totalProximity - pairCount) / pairCount : 1; -} -function getAvgDocLength(index) { - if (index.allSections.length === 0) return 50; - const total = index.allSections.reduce((sum, r) => sum + r.bodyText.length + r.headingText.length, 0); - return total / index.allSections.length; -} -function getCachedShortlist(query, index) { - const cached = index.queryCache.get(query); - if (cached) { - const candidates = /* @__PURE__ */ new Set(); - for (const id of cached.resultIds) { - const record = index.allSections.find((r) => r.id === id); - if (record) candidates.add(record); - } - return candidates; - } - return null; -} -function cacheShortlist(query, records, index) { - const entry = { - resultIds: records.map((r) => r.id), - timestamp: Date.now() - }; - index.queryCache.set(query, entry); - if (index.queryCache.size > 100) { - const firstKey = index.queryCache.keys().next().value; - if (firstKey !== void 0) { - index.queryCache.delete(firstKey); - } - } -} -function searchSections(query, index, limitOrOptions) { - let limit = 8; - let options; - if (typeof limitOrOptions === "number") { - limit = limitOrOptions; - } else if (limitOrOptions) { - options = limitOrOptions; - limit = limitOrOptions.limit ?? 8; - } - const q = query.trim(); - if (!q) { - return index.allSections.slice(0, limit); - } - const normalizedQ = q.normalize("NFD").replace(/[\u0300-\u036f]/g, ""); - const scores = /* @__PURE__ */ new Map(); - const matches = /* @__PURE__ */ new Map(); - const weights = { ...DEFAULT_WEIGHTS, ...options?.weights ?? {} }; - const avgLength = getAvgDocLength(index); - const addScore = (record, score, key, start, end) => { - scores.set(record, (scores.get(record) ?? 0) + score); - const recordMatches = matches.get(record) ?? []; - recordMatches.push({ key, start, end }); - matches.set(record, recordMatches); - }; - const useBM25 = options?.scoringAlgorithm === "bm25"; - const useBM25F = options?.scoringAlgorithm === "bm25f"; - const bm25fConfig = options?.bm25fOptions || { k1: 1.5, b: 0.75 }; - const cached = getCachedShortlist(normalizedQ.toLowerCase(), index); - const searchPool = cached ? [...cached] : index.allSections; - const exact = index.headingIds.get(normalizedQ.toLowerCase()) ?? index.headingIds.get(q.toLowerCase()); - if (exact) { - for (const record of exact) { - const fieldWeight = weights.headingText ?? 1; - const lengthNorm = getLengthNorm(record.headingText.length); - let score; - if (useBM25) { - const termFreq = 1; - const docFreq = index.docFrequency.get(normalizedQ.toLowerCase()) ?? 1; - score = bm25Score(termFreq, docFreq, index.totalDocs, record.headingText.length + record.bodyText.length, avgLength) * 100; - } else { - score = 100 * fieldWeight * (1 + lengthNorm); - } - addScore(record, score, "headingText", 0, record.headingText.length); - } - } - const prefix = index.headingIndex.get(normalizedQ.toLowerCase()) ?? index.headingIndex.get(q.toLowerCase()); - if (prefix) { - for (const record of prefix) { - if (scores.has(record)) continue; - const fieldWeight = weights.headingText ?? 1; - const lengthNorm = getLengthNorm(record.headingText.length); - let score; - if (useBM25) { - const termFreq = 1; - const docFreq = index.docFrequency.get(normalizedQ.toLowerCase()) ?? 1; - score = bm25Score(termFreq, docFreq, index.totalDocs, record.headingText.length + record.bodyText.length, avgLength) * 50; - } else { - score = 50 * fieldWeight * (1 + lengthNorm); - } - const positions = findMatchPositions(record.headingText, q); - if (positions.length > 0) { - const pos = positions[0]; - addScore(record, score, "headingText", pos.start, pos.end); - } else { - addScore(record, score, "headingText", 0, Math.min(q.length, record.headingText.length)); - } - } - } - const words = normalizedQ.toLowerCase().split(/\s+/); - for (const word of words) { - const bodyMatches = index.bodyIndex.get(word); - if (bodyMatches) { - for (const record of bodyMatches) { - if (scores.has(record)) continue; - const fieldWeight = weights.bodyText ?? 1; - const lengthNorm = getLengthNorm(record.bodyText.length); - let score; - if (useBM25) { - const termFreq = 1; - const docFreq = index.docFrequency.get(word) ?? 1; - score = bm25Score(termFreq, docFreq, index.totalDocs, record.headingText.length + record.bodyText.length, avgLength) * 20; - } else { - score = 20 * fieldWeight * (1 + lengthNorm); - } - const positions = findMatchPositions(record.bodyText, q); - if (positions.length > 0) { - const pos = positions[0]; - addScore(record, score, "bodyText", pos.start, pos.end); - } else { - addScore(record, score, "bodyText", 0, Math.min(word.length, record.bodyText.length)); - } - } - } - } - if (useBM25F && scores.size === 0) { - const queryTerms = normalizedQ.toLowerCase().split(/\s+/); - for (const record of index.allSections) { - const score = bm25fScore(record, queryTerms, index, weights, bm25fConfig.k1, bm25fConfig.b); - if (score > 0) { - addScore(record, score * 100, "bm25f", 0, Math.min(normalizedQ.length, record.headingText.length + record.bodyText.length)); - } - } - } - if (scores.size > 0) { - const queryTerms = normalizedQ.toLowerCase().split(/\s+/); - if (queryTerms.length > 1) { - for (const [record, score] of scores.entries()) { - const headingTokens = tokenizeForPhraseMatching(record.headingText); - const bodyTokens = tokenizeForPhraseMatching(record.bodyText); - const allTermsInHeading = queryTerms.every((term) => headingTokens.includes(term)); - const allTermsInBody = queryTerms.every((term) => bodyTokens.includes(term)); - if (allTermsInHeading || allTermsInBody) { - const combinedTokens = [...headingTokens, ...bodyTokens]; - const proximityBonus = calculateProximityBonus(combinedTokens, queryTerms); - scores.set(record, score * proximityBonus); - } - if (hasPhraseMatch(headingTokens, queryTerms) || hasPhraseMatch(bodyTokens, queryTerms)) { - scores.set(record, score * 1.5); - } - } - } - } - if (options?.trackPopularity) { - const boostFactor = options.popularityBoost || 1.2; - for (const [record, score] of scores.entries()) { - const key = `${normalizedQ.toLowerCase()}||${record.id}`; - const popularity = index.queryPopularity.get(key); - if (popularity) { - const popularityBoost = 1 + (popularity.count - 1) * 0.1; - scores.set(record, score * popularityBoost * boostFactor); - } - } - } - if (options?.fuzzy) { - const fuzzyDistance = options.fuzzyDistance ?? 2; - if (scores.size === 0) { - const candidates = getFuzzyCandidates(q.toLowerCase(), index, 1); - for (const record of candidates) { - if (scores.has(record)) continue; - const headingLower = record.headingText.toLowerCase(); - const headingMatch = findMatchPositions(headingLower, q); - if (headingMatch.length === 0) { - const dist = levenshteinDistance(headingLower, q); - if (dist <= 1) { - const normalizedScore = (1 - dist / Math.max(q.length, headingLower.length)) * 60; - const fieldWeight = weights.headingText ?? 1; - const lengthNorm = getLengthNorm(record.headingText.length); - addScore(record, normalizedScore * fieldWeight * (1 + lengthNorm), "headingText", 0, Math.min(q.length, headingLower.length)); - } - } - if (scores.has(record)) continue; - const bodyLower = record.bodyText.toLowerCase(); - let bestDist = Infinity; - let bestStart = 0; - for (let i = 0; i <= bodyLower.length - q.length; i++) { - const substr = bodyLower.slice(i, i + q.length); - const dist = levenshteinDistance(q, substr); - if (dist < bestDist) { - bestDist = dist; - bestStart = i; - } - } - if (bestDist <= 1) { - const normalizedScore = (1 - bestDist / Math.max(q.length, bodyLower.length)) * 40; - const fieldWeight = weights.bodyText ?? 1; - const lengthNorm = getLengthNorm(record.bodyText.length); - addScore(record, normalizedScore * fieldWeight * (1 + lengthNorm), "bodyText", bestStart, bestStart + q.length); - } - } - } - if (scores.size === 0) { - const candidates = getFuzzyCandidates(q.toLowerCase(), index, 2); - for (const record of candidates) { - if (scores.has(record)) continue; - const headingLower = record.headingText.toLowerCase(); - const dist = levenshteinDistance(headingLower, q); - if (dist <= 2) { - const normalizedScore = (1 - dist / Math.max(q.length, headingLower.length)) * 30; - const fieldWeight = weights.headingText ?? 1; - const lengthNorm = getLengthNorm(record.headingText.length); - addScore(record, normalizedScore * fieldWeight * (1 + lengthNorm), "headingText", 0, Math.min(q.length, headingLower.length)); - } - if (scores.has(record)) continue; - const bodyLower = record.bodyText.toLowerCase(); - let bestDist = Infinity; - let bestStart = 0; - for (let i = 0; i <= bodyLower.length - q.length; i++) { - const substr = bodyLower.slice(i, i + q.length); - const d = levenshteinDistance(q, substr); - if (d < bestDist) { - bestDist = d; - bestStart = i; - } - } - if (bestDist <= 2) { - const normalizedScore = (1 - bestDist / Math.max(q.length, bodyLower.length)) * 20; - const fieldWeight = weights.bodyText ?? 1; - const lengthNorm = getLengthNorm(record.bodyText.length); - addScore(record, normalizedScore * fieldWeight * (1 + lengthNorm), "bodyText", bestStart, bestStart + q.length); - } - } - } - } - if ((options?.extended || /\b(?:title|heading|body|label|breadcrumb|type):\S+/i.test(q)) && scores.size === 0) { - const parsed = parseExtendedQuery(q); - for (const record of index.allSections) { - if (matchExtendedNode(parsed, record)) { - const fieldWeight = weights.headingText ?? 1; - const lengthNorm = getLengthNorm(record.headingText.length); - const score = 50 * fieldWeight * (1 + lengthNorm); - addScore(record, score, "headingText", 0, Math.min(q.length, record.headingText.length)); - } - } - } - let filteredScores = [...scores.entries()]; - if (options?.filter) { - filteredScores = filteredScores.filter(([record]) => options.filter(record)); - } - let sortedEntries = filteredScores.sort((a, b) => b[1] - a[1]); - if (options?.sortFn) { - sortedEntries = sortedEntries.sort((a, b) => { - const aMatches = matches.get(a[0]) ?? []; - const bMatches = matches.get(b[0]) ?? []; - return options.sortFn({ record: a[0], score: a[1], matches: aMatches }, { record: b[0], score: b[1], matches: bMatches }); - }); - } - if (options?.diversify) { - const lambda = options.mmrLambda ?? 0.5; - sortedEntries = applyMMR(sortedEntries, lambda); - } - const topRecords = sortedEntries.slice(0, limit).map(([record]) => record); - if (options?.typeWeights) { - const typeBoost = options.typeWeights; - for (const record of topRecords) { - const boost = typeBoost[record.type] ?? 1; - const currentScore = scores.get(record); - if (currentScore !== void 0 && boost !== 1) { - scores.set(record, currentScore * boost); - } - } - sortedEntries = [...scores.entries()].sort((a, b) => b[1] - a[1]); - const boostedRecords = sortedEntries.slice(0, limit).map(([record]) => record); - cacheShortlist(q.toLowerCase(), boostedRecords, index); - if (options?.includeScore || options?.includeMatches) { - return boostedRecords.map((record) => { - const recordMatches = matches.get(record) ?? []; - const normalizedScore = scores.get(record) ?? 0; - const maxScore = useBM25 ? 100 : 200; - const score = Math.max(0, Math.min(1, 1 - normalizedScore / maxScore)); - return { - record, - score, - matches: options.includeMatches ? recordMatches : void 0 - }; - }); - } - return boostedRecords; - } - cacheShortlist(q.toLowerCase(), topRecords, index); - if (options?.includeScore || options?.includeMatches) { - return topRecords.map((record) => { - const recordMatches = matches.get(record) ?? []; - const normalizedScore = scores.get(record) ?? 0; - const maxScore = useBM25 ? 100 : 200; - const score = Math.max(0, Math.min(1, 1 - normalizedScore / maxScore)); - return { - record, - score, - matches: options.includeMatches ? recordMatches : void 0 - }; - }); - } - return topRecords; -} -function removeFromIndex(index, id) { - const recordIndex = index.allSections.findIndex((r) => r.id === id); - if (recordIndex === -1) return; - const record = index.allSections[recordIndex]; - index.allSections.splice(recordIndex, 1); - index.totalDocs = Math.max(0, index.totalDocs - 1); - invalidateAffectedQueries(index, record.id, record.headingText, record.bodyText); - const headingLower = record.headingText.toLowerCase(); - const headingIds = index.headingIds.get(headingLower); - if (headingIds) { - const idx = headingIds.findIndex((r) => r.id === id); - if (idx !== -1) headingIds.splice(idx, 1); - if (headingIds.length === 0) index.headingIds.delete(headingLower); - } - for (let i = 2; i <= headingLower.length; i++) { - const prefix = headingLower.slice(0, i); - const prefixRecords = index.headingIndex.get(prefix); - if (prefixRecords) { - const idx = prefixRecords.findIndex((r) => r.id === id); - if (idx !== -1) prefixRecords.splice(idx, 1); - if (prefixRecords.length === 0) index.headingIndex.delete(prefix); - } - } - trieDelete(index.headingTrie, headingLower, id); - const normalizedHeading = record.headingText.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); - if (normalizedHeading !== headingLower) { - trieDelete(index.headingTrie, normalizedHeading, id); - } - const bodyWords = record.bodyText.toLowerCase().split(/\s+/); - for (const word of bodyWords) { - if (word.length < 3) continue; - const bodyRecords = index.bodyIndex.get(word); - if (bodyRecords) { - const idx = bodyRecords.findIndex((r) => r.id === id); - if (idx !== -1) bodyRecords.splice(idx, 1); - if (bodyRecords.length === 0) index.bodyIndex.delete(word); - } - } -} -function updateRecord(index, record) { - removeFromIndex(index, record.id); - addToIndex(index, [record]); - invalidateAffectedQueries(index, record.id, record.headingText, record.bodyText); -} -function serializeIndex(index) { - const serializable = { - allSections: index.allSections, - headingIndex: Array.from(index.headingIndex.entries()), - headingIds: Array.from(index.headingIds.entries()), - bodyIndex: Array.from(index.bodyIndex.entries()), - docFrequency: Array.from(index.docFrequency.entries()), - fieldDocFreq: { - headingText: Array.from(index.fieldDocFreq.headingText.entries()), - bodyText: Array.from(index.fieldDocFreq.bodyText.entries()), - label: Array.from(index.fieldDocFreq.label.entries()), - breadcrumb: Array.from(index.fieldDocFreq.breadcrumb.entries()) - }, - totalDocs: index.totalDocs, - popularQueries: index.popularQueries, - queryPopularity: Array.from(index.queryPopularity.entries()), - version: index.version - }; - return JSON.stringify(serializable); -} -function deserializeIndex(json) { - const parsed = JSON.parse(json); - const fieldDocFreq = { - headingText: /* @__PURE__ */ new Map(), - bodyText: /* @__PURE__ */ new Map(), - label: /* @__PURE__ */ new Map(), - breadcrumb: /* @__PURE__ */ new Map() - }; - const queryPopularity = /* @__PURE__ */ new Map(); - if (parsed.queryPopularity && Array.isArray(parsed.queryPopularity)) { - for (const [key, value] of parsed.queryPopularity) { - if (typeof value === "object" && value !== null) { - queryPopularity.set(key, value); - } - } - } - if (!parsed.fieldDocFreq) { - if (parsed.docFrequency) { - for (const [term, freq] of parsed.docFrequency) { - fieldDocFreq.bodyText.set(term, freq); - fieldDocFreq.headingText.set(term, Math.floor(freq * 0.3)); - fieldDocFreq.label.set(term, Math.floor(freq * 0.1)); - fieldDocFreq.breadcrumb.set(term, Math.floor(freq * 0.1)); - } - } - } else { - fieldDocFreq.headingText = new Map(parsed.fieldDocFreq.headingText ?? []); - fieldDocFreq.bodyText = new Map(parsed.fieldDocFreq.bodyText ?? []); - fieldDocFreq.label = new Map(parsed.fieldDocFreq.label ?? []); - fieldDocFreq.breadcrumb = new Map(parsed.fieldDocFreq.breadcrumb ?? []); - } - const headingTrie = createTrieNode(); - if (parsed.headingIndex && Array.isArray(parsed.headingIndex)) { - const headingIndexMap = new Map(parsed.headingIndex); - const seenRecords = /* @__PURE__ */ new Set(); - for (const [prefix, records] of headingIndexMap.entries()) { - if (prefix.length >= 2 && Array.isArray(records)) { - for (const record of records) { - if (seenRecords.has(record.id)) continue; - seenRecords.add(record.id); - const headingLower = record.headingText.toLowerCase(); - trieInsert(headingTrie, headingLower, record); - const normalizedHeading = headingLower.normalize("NFD").replace(/[\u0300-\u036f]/g, ""); - if (normalizedHeading !== headingLower) { - trieInsert(headingTrie, normalizedHeading, record); - } - } - } - } - } else { - const allSections = parsed.allSections ?? []; - for (const record of allSections) { - const headingLower = record.headingText.toLowerCase(); - trieInsert(headingTrie, headingLower, record); - const normalizedHeading = headingLower.normalize("NFD").replace(/[\u0300-\u036f]/g, ""); - if (normalizedHeading !== headingLower) { - trieInsert(headingTrie, normalizedHeading, record); - } - } - } - return { - allSections: parsed.allSections ?? [], - headingIndex: new Map(parsed.headingIndex ?? []), - headingIds: new Map(parsed.headingIds ?? []), - bodyIndex: new Map(parsed.bodyIndex ?? []), - queryCache: /* @__PURE__ */ new Map(), - popularQueries: parsed.popularQueries ?? [], - docFrequency: new Map(parsed.docFrequency ?? []), - fieldDocFreq, - totalDocs: parsed.totalDocs ?? 0, - queryPopularity, - version: parsed.version ?? 1, - headingTrie - }; -} -function suggest(query, index, limit = 10) { - const q = query.trim().toLowerCase(); - if (!q || q.length < 2) return []; - const suggestions = []; - const seen = /* @__PURE__ */ new Set(); - const trieCandidates = triePrefixQuery(index.headingTrie, q); - for (const record of trieCandidates) { - if (suggestions.length >= limit) break; - const heading = record.headingText; - if (!seen.has(heading)) { - seen.add(heading); - suggestions.push(heading); - } - } - if (suggestions.length < limit) { - const candidates = index.headingIndex.get(q) ?? []; - const prefixCandidates = index.headingIds.get(q) ?? []; - const allCandidates = [...candidates, ...prefixCandidates]; - for (const record of allCandidates) { - if (suggestions.length >= limit) break; - const heading = record.headingText; - if (!seen.has(heading)) { - seen.add(heading); - suggestions.push(heading); - } - } - } - if (suggestions.length < limit) { - for (const [key, records] of index.headingIds) { - if (suggestions.length >= limit) break; - if (key.includes(q) && !seen.has(records[0].headingText)) { - seen.add(records[0].headingText); - suggestions.push(records[0].headingText); - } - } - } - return suggestions.slice(0, limit); -} -function facets(index) { - const result = { - section: 0, - action: 0, - field: 0, - link: 0, - file: 0, - media: 0, - structured: 0 - }; - for (const record of index.allSections) { - result[record.type] = (result[record.type] ?? 0) + 1; - } - return result; -} -function trackQuery(index, query, selectedId) { - const q = query.trim(); - if (!q) return; - if (index.popularQueries.length >= 100) { - index.popularQueries.shift(); - } - index.popularQueries.push(q); - if (selectedId) { - const key = `${q}||${selectedId}`; - const existing = index.queryPopularity.get(key); - if (existing) { - existing.count++; - } else { - index.queryPopularity.set(key, { - query: q, - recordId: selectedId, - count: 1 - }); - } - } -} -function getPopularQueries(index, n = 5) { - const counts = /* @__PURE__ */ new Map(); - for (const q of index.popularQueries) { - counts.set(q, (counts.get(q) ?? 0) + 1); - } - return [...counts.entries()].sort((a, b) => b[1] - a[1]).slice(0, n).map(([q]) => q); -} -var addSectionsToIndex, DEFAULT_WEIGHTS; -var init_search_index = __esm({ - "../src/search-index.ts"() { - "use strict"; - addSectionsToIndex = addToIndex; - DEFAULT_WEIGHTS = { - headingText: 2, - bodyText: 1, - label: 1.5, - breadcrumb: 0.5 - }; - } -}); - -// ../src/cache.ts -var cache_exports = {}; -__export(cache_exports, { - clearCache: () => clearCache, - loadIndex: () => loadIndex, - loadSiteGraph: () => loadSiteGraph, - openDB: () => openDB, - saveIndex: () => saveIndex, - saveSiteGraph: () => saveSiteGraph -}); -async function openDB() { - return new Promise((resolve, reject) => { - const req = indexedDB.open(DB_NAME, 1); - req.onsuccess = () => resolve(req.result); - req.onerror = () => reject(req.error); - req.onupgradeneeded = () => { - const db = req.result; - if (!db.objectStoreNames.contains(STORE_NAME)) { - db.createObjectStore(STORE_NAME, { keyPath: CACHE_VERSION_KEY }); - } - }; - }); -} -async function compressData(data) { - if (typeof window === "undefined" || !window.CompressionStream) { - return { compressed: false, data }; - } - try { - const blob = new Blob([data]).stream(); - const compressedStream = blob.pipeThrough(new CompressionStream("gzip")); - const chunks = []; - const reader = compressedStream.getReader(); - while (true) { - const { done, value } = await reader.read(); - if (done) break; - chunks.push(new Uint8Array(value)); - } - const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0); - const result = new Uint8Array(totalLength); - let offset = 0; - for (const chunk of chunks) { - result.set(chunk, offset); - offset += chunk.length; - } - return { compressed: true, data: result.buffer }; - } catch { - return { compressed: false, data }; - } -} -async function decompressData(data) { - if (typeof data === "string") { - return data; - } - if (typeof window === "undefined" || !window.DecompressionStream) { - throw new Error("Compressed data but no decompression available"); - } - try { - const blob = new Blob([data]).stream(); - const decompressedStream = blob.pipeThrough(new DecompressionStream("gzip")); - const chunks = []; - const reader = decompressedStream.getReader(); - const decoder = new TextDecoder(); - while (true) { - const { done, value } = await reader.read(); - if (done) break; - chunks.push(decoder.decode(value, { stream: true })); - } - return chunks.join("") + decoder.decode(); - } catch { - throw new Error("Failed to decompress data"); - } -} -async function saveIndex(index, metadata) { - const db = await openDB(); - const tx = db.transaction(STORE_NAME, "readwrite"); - const store = tx.objectStore(STORE_NAME); - const serializedIndex = serializeIndex(index); - const { compressed, data } = await compressData(serializedIndex); - const dataToStore = { - [CACHE_VERSION_KEY]: metadata.versionHash, - index: data, - metadata, - compressed - }; - await new Promise((resolve, reject) => { - const req = store.put(dataToStore); - req.onsuccess = () => resolve(void 0); - req.onerror = () => reject(req.error); - }); -} -async function loadIndex(ttl) { - const db = await openDB(); - const tx = db.transaction(STORE_NAME, "readonly"); - const store = tx.objectStore(STORE_NAME); - const allRecords = await new Promise((resolve, reject) => { - const req = store.getAll(); - req.onsuccess = () => resolve(req.result); - req.onerror = () => reject(req.error); - }); - if (!allRecords.length) return null; - const record = allRecords[0]; - let serializedIndex = record.index; - if (record.compressed) { - try { - serializedIndex = await decompressData(record.index); - } catch (e) { - console.warn("[reef] decompression failed, trying uncompressed fallback:", e); - if (typeof record.index === "string") { - serializedIndex = record.index; - } else { - return null; - } - } - } else if (typeof record.index === "string") { - serializedIndex = record.index; - } else { - return null; - } - const cached = deserializeIndex(serializedIndex); - const metadata = record.metadata; - if (ttl && metadata.buildTime) { - const age = Date.now() - metadata.buildTime; - if (age > ttl) { - return null; - } - } - return { index: cached, metadata }; -} -async function clearCache() { - const db = await openDB(); - const tx = db.transaction(STORE_NAME, "readwrite"); - const store = tx.objectStore(STORE_NAME); - await new Promise((resolve, reject) => { - const req = store.clear(); - req.onsuccess = () => resolve(void 0); - req.onerror = () => reject(req.error); - }); -} -async function saveSiteGraph(graph, key = "site-graph") { - const db = await openDB(); - const tx = db.transaction(STORE_NAME, "readwrite"); - tx.objectStore(STORE_NAME).put({ [CACHE_VERSION_KEY]: `${key}:${graph.startUrl}`, graph, metadata: { versionHash: key, buildTime: Date.now(), pageMetadata: {} } }); -} -async function loadSiteGraph(startUrl, key = "site-graph") { - const db = await openDB(); - const tx = db.transaction(STORE_NAME, "readonly"); - return new Promise((resolve) => { - const req = tx.objectStore(STORE_NAME).get(`${key}:${startUrl}`); - req.onsuccess = () => resolve(req.result?.graph ?? null); - req.onerror = () => resolve(null); - }); -} -var DB_NAME, STORE_NAME, CACHE_VERSION_KEY; -var init_cache = __esm({ - "../src/cache.ts"() { - "use strict"; - init_search_index(); - DB_NAME = "reef-index"; - STORE_NAME = "indices"; - CACHE_VERSION_KEY = "version"; - } -}); - -// src/background.ts -init_search_index(); - -// ../src/extraction.ts -function stripTags(value) { - let result = ""; - let inTag = false; - for (let i = 0; i < value.length; i++) { - const char = value[i]; - if (char === "<" && inTag === false) { - inTag = true; - } else if (char === ">" && inTag === true) { - inTag = false; - } else if (inTag === false) { - result += char; - } - } - return result.replace(/\s+/g, " ").trim(); -} -function generateSelector(element) { - const path = []; - let current = element; - while (current && current.nodeType === Node.ELEMENT_NODE) { - let selector = current.tagName.toLowerCase(); - if (current.id) { - selector += `#${current.id}`; - path.unshift(selector); - break; - } else if (current.className) { - const classes = current.className.trim().split(/\s+/); - if (classes.length) { - selector += `.${classes.join(".")}`; - } - } - let siblingIndex = 1; - let sibling = current.previousElementSibling; - while (sibling) { - if (sibling.tagName === current.tagName) { - siblingIndex++; - } - sibling = sibling.previousElementSibling; - } - if (siblingIndex > 1) { - selector += `:nth-child(${siblingIndex})`; - } - path.unshift(selector); - current = current.parentElement; - } - return path.length > 0 ? path.join(" > ") : element.tagName.toLowerCase(); -} -function generateStableSelector(element) { - const candidates = []; - const push = (value) => { - if (value && !candidates.includes(value)) candidates.push(value); - }; - const escape = (value) => typeof CSS !== "undefined" && CSS.escape ? CSS.escape(value) : value.replace(/(["\\])/g, "\\$1"); - for (const attr of ["data-testid", "data-test", "data-agent-id", "id"]) { - const value = element.getAttribute(attr); - if (value) push(attr === "id" ? `#${escape(value)}` : `[${attr}="${escape(value)}"]`); - } - const aria = element.getAttribute("aria-label"); - if (aria) push(`[aria-label="${escape(aria)}"]`); - const role = element.getAttribute("role"); - const name = extractActionName(element); - if (role && name) push(`[role="${escape(role)}"][aria-label="${escape(name)}"]`); - if (role) push(`[role="${escape(role)}"]`); - push(generateSelector(element)); - let sibling = element.previousElementSibling; - let position = 1; - while (sibling) { - if (sibling.tagName === element.tagName) position++; - sibling = sibling.previousElementSibling; - } - push(`xpath=//${element.tagName.toLowerCase()}[${position}]`); - return candidates; -} -function extractHeadingId(fullMatch, text) { - const idMatch = fullMatch.match(/\bid=["']([^"']+)['"]/i); - if (idMatch?.[1]) return idMatch[1]; - const stripped = text.toLowerCase().replace(/[^a-z0-9]+/g, "-"); - return stripped || Math.random().toString(36).slice(2); -} -function hasExplicitId(fullMatch) { - return /\bid=["'][^"']+["']/i.test(fullMatch); -} -function findParentSectionId(html, headingMatchEnd) { - const afterHeading = html.slice(headingMatchEnd, headingMatchEnd + 500); - const idMatch = afterHeading.match(/]*id="([^"]+)"/i); - if (idMatch?.[1]) return idMatch[1]; - const articleMatch = afterHeading.match(/]*id="([^"]+)"/i); - if (articleMatch?.[1]) return articleMatch[1]; - return null; -} -var headingCache = /* @__PURE__ */ new Map(); -function extractSections(html, url) { - if (headingCache.has(url)) { - return headingCache.get(url); - } - const cleanHtml = html.replace(//gi, " ").replace(//gi, " ").replace(//gi, " ").replace(//g, " "); - const matches = []; - const headingRegexGlobal = /<(h[1-6])[^>]*>([\s\S]*?)<\/h[1-6]>/gi; - let match; - headingRegexGlobal.lastIndex = 0; - while ((match = headingRegexGlobal.exec(cleanHtml)) !== null) { - const [, tag, text] = match; - const headingText = stripTags(text); - const level = parseInt(tag[1], 10); - matches.push({ - level, - index: match.index, - text: headingText, - id: extractHeadingId(match[0], headingText), - hasRealId: hasExplicitId(match[0]) - }); - } - const len = matches.length; - const sections = new Array(len); - for (let i = 0; i < len; i++) { - const heading = matches[i]; - const nextHeading = matches[i + 1]; - const start = heading.index + heading.text.length; - const end = nextHeading?.index ?? cleanHtml.length; - const content = cleanHtml.slice(start, end); - const bodyText = stripTags(content).replace(/\s+/g, " ").trim(); - let breadcrumb = ""; - for (let j = 0; j <= i; j++) { - if (j > 0) breadcrumb += " \u203A "; - breadcrumb += matches[j].text; - } - const parentSectionId = heading.hasRealId ? null : findParentSectionId(cleanHtml, heading.index + heading.text.length); - const selector = heading.hasRealId ? "#" + heading.id : parentSectionId ? "#" + parentSectionId : void 0; - sections[i] = { - id: `${url}#${heading.id}`, - url: `${url}#${heading.id}`, - headingText: heading.text, - headingId: heading.id, - breadcrumb, - bodyText, - type: "section", - selector - }; - } - headingCache.set(url, sections); - return sections; -} -function extractActionName(element) { - const ariaLabel = element.getAttribute("aria-label"); - if (ariaLabel?.trim()) return ariaLabel.trim(); - const ariaLabelledBy = element.getAttribute("aria-labelledby"); - if (ariaLabelledBy) { - const labelledElement = document.getElementById(ariaLabelledBy); - if (labelledElement?.textContent?.trim()) { - return labelledElement.textContent.trim(); - } - } - const textContent = element.textContent?.trim(); - if (textContent) return textContent; - const title = element.getAttribute("title"); - if (title?.trim()) return title.trim(); - return null; -} -function isDestructiveAction(label) { - const destructiveVerbs = [ - "delete", - "remove", - "cancel subscription", - "unsubscribe", - "pay", - "checkout", - "submit order", - "confirm" - ]; - const lowerLabel = label.toLowerCase(); - return destructiveVerbs.some((verb) => lowerLabel.includes(verb)); -} -function extractActions(html, url, excludeSelectors) { - const actions = []; - const doc = new DOMParser().parseFromString(html, "text/html"); - const selectors = [ - "button", - '[role="button"]', - 'input[type="button"]', - 'input[type="submit"]', - "summary", - "[data-reef-action]" - ]; - const elements = Array.from(doc.querySelectorAll(selectors.join(","))); - for (const element of elements) { - if (excludeSelectors && element.matches(excludeSelectors)) continue; - const label = extractActionName(element); - if (!label) continue; - const selectors2 = generateStableSelector(element); - actions.push({ - id: `${url}#action-${actions.length}`, - url, - headingText: label, - headingId: `action-${actions.length}`, - breadcrumb: "", - bodyText: label, - type: "action", - selector: selectors2[0], - selectors: selectors2, - destructive: isDestructiveAction(label), - label - }); - } - return actions; -} -function extractFields(html, url) { - const fields = []; - const doc = new DOMParser().parseFromString(html, "text/html"); - const formElements = Array.from(doc.querySelectorAll("form")); - for (const form of formElements) { - let breadcrumb = ""; - let current = form.parentElement; - while (current && current !== doc.body) { - if (current.matches('h1, h2, h3, h4, h5, h6, article, section, [role="main"], main')) { - const headingText = current.textContent?.trim() || ""; - if (headingText) { - breadcrumb = headingText; - } - break; - } - current = current.parentElement; - } - const inputs = Array.from(form.querySelectorAll("input, textarea, select")); - for (const input of inputs) { - if (input.matches('input[type="hidden"], input[type="button"], input[type="submit"], input[type="reset"]')) { - continue; - } - let label = ""; - const id = input.id; - if (id) { - const labelElement = doc.querySelector(`label[for="${id}"]`); - if (labelElement) { - label = labelElement.textContent?.trim() || ""; - } - } - if (!label) { - const parentLabel = input.closest("label"); - if (parentLabel) { - label = parentLabel.textContent?.trim() || ""; - const inputElement2 = input; - if (label && inputElement2.value && label.includes(inputElement2.value)) { - label = label.replace(inputElement2.value, "").trim(); - } - } - } - if (!label) { - const inputElement2 = input; - const placeholder = "placeholder" in inputElement2 ? inputElement2.placeholder : ""; - label = placeholder || input.getAttribute("aria-label") || ""; - } - if (!label) continue; - const selectors = generateStableSelector(input); - const inputElement = input; - fields.push({ - id: `${url}#field-${fields.length}`, - url, - headingText: label, - headingId: `field-${fields.length}`, - breadcrumb, - bodyText: label, - type: "field", - selector: selectors[0], - selectors, - label, - value: inputElement.value - }); - } - } - return fields; -} -function extractHiddenContent(doc) { - doc.querySelectorAll("details").forEach((d) => { - if (!d.hasAttribute("open")) d.setAttribute("data-reef-was-closed", "true"); - d.setAttribute("open", ""); - }); - doc.querySelectorAll('[aria-hidden="false"]').forEach((el) => { - el.removeAttribute("aria-hidden"); - }); -} -function extractLinks(html, url) { - const links = []; - const doc = new DOMParser().parseFromString(html, "text/html"); - const anchors = Array.from(doc.querySelectorAll("a[href]")); - for (const anchor of anchors) { - if (anchor.hasAttribute("rel") && anchor.getAttribute("rel")?.toLowerCase().includes("nofollow")) continue; - const href = anchor.getAttribute("href"); - if (!href) continue; - if (href === "#" || href.startsWith("javascript:")) continue; - const linkText = anchor.textContent?.trim() || ""; - if (!linkText) continue; - const resolvedUrl = resolveUrl(href, url); - const isExternal = !resolvedUrl.startsWith(window.location.origin); - const selectors = generateStableSelector(anchor); - links.push({ - id: `${url}#link-${links.length}`, - url: resolvedUrl, - headingText: linkText, - headingId: `link-${links.length}`, - breadcrumb: "", - bodyText: linkText, - type: isExternal ? "link" : "section", - selector: selectors[0], - selectors - }); - } - return links; -} -function extractFiles(html, url, extensions) { - const files = []; - const doc = new DOMParser().parseFromString(html, "text/html"); - const fileExtensions = extensions?.split(",").map((e) => e.trim().toLowerCase()) ?? ["pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "zip", "csv"]; - const anchors = Array.from(doc.querySelectorAll("a[href]")); - for (const anchor of anchors) { - const href = anchor.getAttribute("href"); - if (!href) continue; - const isFile = fileExtensions.some( - (ext) => href.toLowerCase().endsWith(`.${ext}`) || href.toLowerCase().endsWith(`.${ext}?`) || href.toLowerCase().endsWith(`.${ext}#`) - ); - if (!isFile) continue; - const linkText = anchor.textContent?.trim() || href.split("/").pop() || ""; - if (!linkText) continue; - const resolvedUrl = resolveUrl(href, url); - const selectors = generateStableSelector(anchor); - files.push({ - id: `${url}#file-${files.length}`, - url: resolvedUrl, - headingText: linkText, - headingId: `file-${files.length}`, - breadcrumb: "", - bodyText: linkText, - type: "file", - selector: selectors[0], - selectors - }); - } - return files; -} -function extractMedia(html, url) { - const media = []; - const doc = new DOMParser().parseFromString(html, "text/html"); - const images = Array.from(doc.querySelectorAll("img")); - for (const img of images) { - const alt = img.alt.trim(); - if (!alt) continue; - let caption = ""; - const figure = img.closest("figure"); - if (figure) { - const figcaption = figure.querySelector("figcaption"); - if (figcaption) { - caption = figcaption.textContent?.trim() || ""; - } - } - const textToIndex = caption ? `${alt} ${caption}` : alt; - if (!textToIndex.trim()) continue; - const selectors = generateStableSelector(img); - media.push({ - id: `${url}#media-image-${media.length}`, - url, - headingText: alt, - headingId: `media-image-${media.length}`, - breadcrumb: "", - bodyText: textToIndex, - type: "media", - selector: selectors[0], - selectors - }); - } - const mediaElements = Array.from(doc.querySelectorAll("video, audio")); - for (const element of mediaElements) { - const title = element.getAttribute("title") || element.getAttribute("aria-label") || ""; - if (!title) continue; - let transcript = ""; - const tracks = Array.from(element.querySelectorAll('track[kind="captions"], track[kind="subtitles"]')); - for (const track of tracks) { - const src = track.getAttribute("src"); - if (src) { - transcript += `[Transcript available: ${src}] `; - } - } - const textToIndex = transcript ? `${title} ${transcript}` : title; - if (!textToIndex.trim()) continue; - const selectors = generateStableSelector(element); - media.push({ - id: `${url}#media-${media.length}`, - url, - headingText: title, - headingId: `media-${media.length}`, - breadcrumb: "", - bodyText: textToIndex, - type: "media", - selector: selectors[0], - selectors, - transcript: transcript.trim() - }); - } - return media; -} -function extractStructuredData(html, url) { - const structured = []; - const doc = new DOMParser().parseFromString(html, "text/html"); - const jsonLdScripts = Array.from(doc.querySelectorAll('script[type="application/ld+json"]')); - for (const script of jsonLdScripts) { - try { - const data = JSON.parse(script.textContent || "{}"); - if (Array.isArray(data) ? data.some((item) => item["@type"] === "FAQPage") : data["@type"] === "FAQPage") { - const faqItems = Array.isArray(data) ? data.flatMap((item) => item.mainEntity || []) : data.mainEntity || []; - for (const [index, question] of faqItems.entries()) { - if (!question || !question.name) continue; - const answer = question.acceptedAnswer?.text || question.suggestedAnswer?.text || ""; - if (!answer) continue; - const textToIndex = `${question.name} ${answer}`; - structured.push({ - id: `${url}#structured-faq-${index}`, - url, - headingText: question.name, - headingId: `structured-faq-${index}`, - breadcrumb: "", - bodyText: textToIndex, - type: "structured", - structuredData: { question: question.name, answer } - }); - } - } else if (data["@type"]) { - const type = data["@type"]; - const name = data.name || data.headline || ""; - const description = data.description || ""; - if (!name && !description) continue; - const textToIndex = `${name} ${description}`.trim(); - if (!textToIndex) continue; - structured.push({ - id: `${url}#structured-${type.toLowerCase()}-${structured.length}`, - url, - headingText: name || "Structured Data", - headingId: `structured-${type.toLowerCase()}-${structured.length}`, - breadcrumb: "", - bodyText: textToIndex, - type: "structured", - structuredData: data - }); - } - } catch (e) { - continue; - } - } - return structured; -} -var TRACKING_PARAMS = /* @__PURE__ */ new Set([ - "utm_source", - "utm_medium", - "utm_campaign", - "utm_term", - "utm_content", - "gclid", - "fbclid", - "mc_cid", - "mc_eid", - "mc_id", - "referrer", - "ref", - "source", - "campaign", - "click_id" -]); -function stripTrackingParams(url) { - try { - const urlObj = new URL(url); - const params = new URLSearchParams(urlObj.search); - for (const param of TRACKING_PARAMS) { - params.delete(param); - } - for (const [key, value] of params.entries()) { - if (!value) { - params.delete(key); - } - } - urlObj.search = params.toString(); - return urlObj.toString(); - } catch { - return url; - } -} -function normalizeUrl(url, html) { - if (!url) return url; - let normalized = url.replace(/\/$/, ""); - normalized = stripTrackingParams(normalized); - if (html) { - const canonicalMatch = html.match(/]*rel=["']canonical["'][^>]*href=["']([^"']+)["']/i); - if (canonicalMatch?.[1]) { - const canonicalUrl = stripTrackingParams(canonicalMatch[1]); - if (canonicalUrl !== normalized) { - return canonicalUrl.replace(/\/$/, ""); - } - } - } - return normalized; -} -function resolveUrl(value, base) { - if (!value) return base; - try { - return new URL(value, base).toString(); - } catch { - return value; - } -} - -// ../src/indexing/indexer.ts -init_search_index(); -var Indexer = class { - constructor(config) { - this.index = createSearchIndex(); - this.robotsCache = /* @__PURE__ */ new Map(); - this.lastCrawlTime = 0; - this.config = config; - } - getIndex() { - return this.index; - } - setIndex(index) { - this.index = index; - } - extractAllContent(html, url) { - const doc = new DOMParser().parseFromString(html, "text/html"); - if (this.config.indexHidden) { - extractHiddenContent(doc); - } - let rootElement = doc; - if (this.config.scope) { - const scopeElement = doc.querySelector(this.config.scope); - if (scopeElement) { - rootElement = scopeElement; - } - } - const htmlToProcess = new XMLSerializer().serializeToString(rootElement); - const sections = extractSections(htmlToProcess, url); - const actions = this.config.indexActions ? extractActions(htmlToProcess, url, this.config.excludeAction) : []; - const fields = this.config.indexActions ? extractFields(htmlToProcess, url) : []; - const links = extractLinks(htmlToProcess, url); - const files = extractFiles(htmlToProcess, url, this.config.fileExtensions); - const media = this.config.indexMedia ? extractMedia(htmlToProcess, url) : []; - const structured = this.config.indexStructuredData ? extractStructuredData(htmlToProcess, url) : []; - const combined = []; - combined.push(...sections, ...actions, ...fields, ...links, ...files, ...media, ...structured); - return combined; - } - async boot(onReady, showToast) { - if (this.config.prebuiltIndexUrl) { - try { - const response = await fetch(this.config.prebuiltIndexUrl); - if (response.ok) { - const json = await response.text(); - this.index = (await Promise.resolve().then(() => (init_search_index(), search_index_exports))).deserializeIndex(json); - console.info(`[reef] loaded prebuilt index with ${this.index.allSections.length} sections`); - onReady(); - return; - } - } catch (error) { - console.warn("[reef] prebuilt index fetch failed, falling back to crawling", error); - } - } - try { - const { loadIndex: loadIndex2, saveIndex: saveIndex2 } = await Promise.resolve().then(() => (init_cache(), cache_exports)); - const ttl = this.config.ttl; - const cached = await loadIndex2(ttl); - if (cached?.index && cached.index.allSections.length > 0) { - this.index = cached.index; - console.info(`[reef] loaded cached index with ${this.index.allSections.length} sections`); - onReady(); - return; - } - } catch (error) { - console.warn("[reef] cache load failed, continuing with fresh index", error); - } - let cachedMetadata = null; - try { - const { loadIndex: loadIndex2 } = await Promise.resolve().then(() => (init_cache(), cache_exports)); - cachedMetadata = await loadIndex2(); - } catch (e) { - console.warn("[reef] failed to load cached metadata for incremental crawling", e); - } - const pageHashes = cachedMetadata?.metadata?.pageMetadata ?? {}; - const urls = await this.fetchSitemapUrls(); - const maxPages = this.config.maxPages ?? 500; - if (urls.length === 0) { - this.crawlSameOrigin(onReady); - return; - } - if (this.config.useWorkerIndexing) { - await this.fetchPagesWithWorker(urls.slice(0, maxPages), urls[0], onReady); - } else { - const fetchedSections = await this.fetchPagesParallel(urls.slice(0, maxPages), urls[0], pageHashes); - if (fetchedSections.length) { - addToIndex(this.index, fetchedSections, this.config.tokenizePipeline); - console.info(`[reef] indexed ${fetchedSections.length} sections`); - } - try { - const { saveIndex: saveIndex2 } = await Promise.resolve().then(() => (init_cache(), cache_exports)); - const versionHash = this.computeVersionHash(urls); - const newPageMetadata = {}; - for (const url of urls.slice(0, maxPages)) { - newPageMetadata[url] = pageHashes[url] ?? { timestamp: Date.now() }; - } - const metadata = { - versionHash, - buildTime: Date.now(), - pageMetadata: newPageMetadata - }; - await saveIndex2(this.index, metadata); - } catch (e) { - console.warn("[reef] cache save failed", e); - } - onReady(); - return; - } - this.crawlSameOrigin(onReady); - } - async fetchSitemapUrls() { - const candidates = this.getSitemapCandidates(); - const allUrls = []; - const seenUrls = /* @__PURE__ */ new Set(); - for (const candidate of candidates) { - try { - await this.processSitemap(candidate, allUrls, seenUrls); - } catch (e) { - console.warn("[reef] failed to process sitemap:", candidate, e); - continue; - } - } - return allUrls; - } - async processSitemap(url, allUrls, seenUrls) { - if (seenUrls.has(url)) return; - seenUrls.add(url); - try { - const response = await fetch(url); - if (!response.ok) return; - const xml = await response.text(); - const isSitemapIndex = xml.includes(""); - if (isSitemapIndex) { - const sitemapRegex = /\s*(.*?)<\/loc>\s*<\/sitemap>/g; - let match; - while ((match = sitemapRegex.exec(xml)) !== null) { - const childSitemapUrl = match[1].trim(); - const resolvedUrl = this.resolveUrl(childSitemapUrl, url); - await this.processSitemap(resolvedUrl, allUrls, seenUrls); - } - } else { - const locRegex = /(.*?)<\/loc>/g; - let match; - while ((match = locRegex.exec(xml)) !== null) { - const pageUrl = match[1].trim(); - const resolvedUrl = this.resolveUrl(pageUrl, url); - if (!seenUrls.has(resolvedUrl)) { - allUrls.push(resolvedUrl); - seenUrls.add(resolvedUrl); - } - } - } - } catch (e) { - console.warn("[reef] failed to process sitemap:", url, e); - } - } - computeVersionHash(urls) { - let hash = 0; - for (const url of urls) { - for (let i = 0; i < url.length; i++) { - const char = url.charCodeAt(i); - hash = (hash << 5) - hash + char; - hash = hash & hash; - } - } - return Math.abs(hash).toString(36); - } - // Simple hash function for content hashing - hashContent(content) { - let hash = 0; - for (let i = 0; i < Math.min(content.length, 1e4); i++) { - const char = content.charCodeAt(i); - hash = (hash << 5) - hash + char; - hash = hash & hash; - } - return Math.abs(hash).toString(36); - } - getSitemapCandidates() { - const base = window.location.href; - const configured = this.config.sitemap ?? "sitemap.xml"; - const candidates = [this.resolveUrl(configured, base)]; - if (!configured.startsWith("/")) { - candidates.push(this.resolveUrl("./sitemap.xml", base)); - } else if (configured === "/sitemap.xml") { - candidates.push(this.resolveUrl("sitemap.xml", base)); - } - return [...new Set(candidates)]; - } - resolveUrl(value, base) { - if (!value) return base; - try { - return new URL(value, base).toString(); - } catch { - return value; - } - } - // Fetch and parse robots.txt for a given origin - async fetchRobotsTxt(origin) { - const cacheKey = origin; - const cacheEntry = this.robotsCache.get(cacheKey); - if (cacheEntry && Date.now() - cacheEntry.timestamp < 36e5) { - return cacheEntry.disallowed; - } - try { - const robotsUrl = new URL("/robots.txt", origin).toString(); - const response = await fetch(robotsUrl); - if (!response.ok) { - this.robotsCache.set(cacheKey, { disallowed: /* @__PURE__ */ new Set(), timestamp: Date.now() }); - return /* @__PURE__ */ new Set(); - } - const robotsText = await response.text(); - const disallowed = this.parseRobotsTxt(robotsText); - this.robotsCache.set(cacheKey, { disallowed, timestamp: Date.now() }); - return disallowed; - } catch (e) { - console.warn("[reef] failed to fetch robots.txt:", e); - this.robotsCache.set(cacheKey, { disallowed: /* @__PURE__ */ new Set(), timestamp: Date.now() }); - return /* @__PURE__ */ new Set(); - } - } - // Parse robots.txt content and extract disallowed paths - parseRobotsTxt(robotsText) { - const disallowed = /* @__PURE__ */ new Set(); - const lines = robotsText.split("\n"); - let currentUserAgent = ""; - for (const line of lines) { - const trimmedLine = line.trim(); - if (!trimmedLine || trimmedLine.startsWith("#")) continue; - const userAgentMatch = trimmedLine.match(/^User-agent:\s*(.+)$/i); - if (userAgentMatch) { - currentUserAgent = userAgentMatch[1].trim(); - continue; - } - const disallowMatch = trimmedLine.match(/^Disallow:\s*(.+)$/i); - if (disallowMatch && currentUserAgent === "*") { - const path = disallowMatch[1].trim(); - if (path) { - disallowed.add(path); - } - } - } - return disallowed; - } - // Check if a URL is allowed by robots.txt - isUrlAllowed(url, origin) { - const cacheEntry = this.robotsCache.get(origin); - if (!cacheEntry) return true; - const disallowed = cacheEntry.disallowed; - if (disallowed.size === 0) return true; - try { - const urlObj = new URL(url); - const path = urlObj.pathname; - for (const disallowedPath of disallowed) { - if (path.startsWith(disallowedPath)) { - return false; - } - } - } catch { - return true; - } - return true; - } - // Apply crawl delay between batches - async applyCrawlDelay() { - const delay = this.config.crawlDelay ?? 0; - if (delay <= 0) return; - const now = Date.now(); - const timeSinceLastCrawl = now - this.lastCrawlTime; - if (timeSinceLastCrawl < delay) { - await new Promise((resolve) => setTimeout(resolve, delay - timeSinceLastCrawl)); - } - this.lastCrawlTime = Date.now(); - } - crawlSameOrigin(onReady) { - const origin = window.location.origin; - const visited = /* @__PURE__ */ new Set(); - const queue = [normalizeUrl(window.location.href)]; - const maxPages = this.config.maxPages ?? 500; - const concurrency = this.config.maxPages ? Math.min(this.config.maxPages, 6) : 6; - const processQueue = async () => { - await this.fetchRobotsTxt(origin); - while (queue.length && visited.size < maxPages) { - await this.applyCrawlDelay(); - const batch = queue.splice(0, concurrency); - for (const url of batch) { - const normalizedUrlStr = normalizeUrl(url); - if (visited.has(normalizedUrlStr) || !normalizedUrlStr.startsWith(origin)) continue; - if (!this.isUrlAllowed(normalizedUrlStr, origin)) { - console.info(`[reef] skipping disallowed URL: ${normalizedUrlStr}`); - continue; - } - visited.add(normalizedUrlStr); - try { - const response = await fetch(normalizedUrlStr); - if (!response.ok) continue; - const html = await response.text(); - const content = this.extractAllContent(html, normalizedUrlStr); - addToIndex(this.index, content, this.config.tokenizePipeline); - const links = extractLinks(html, normalizedUrlStr).filter((l) => l.url.startsWith(origin)).map((l) => normalizeUrl(l.url)).filter((u, i, arr) => arr.indexOf(u) === i); - queue.push(...links); - } catch (e) { - continue; - } - } - } - console.info(`[reef] indexed ${visited.size} pages via same-origin crawl`); - onReady(); - }; - processQueue(); - } - async fetchPagesParallel(urls, sitemapUrl, pageHashes = {}) { - const concurrency = 6; - const sections = []; - const results = new Array(urls.length); - let idx = 0; - const fetchBatch = async () => { - while (idx < urls.length) { - const i = idx++; - const pageUrl = this.resolveUrl(urls[i], sitemapUrl); - try { - const cachedPageInfo = pageHashes[pageUrl]; - if (cachedPageInfo) { - const headResponse = await fetch(pageUrl, { method: "HEAD" }); - if (headResponse.ok) { - const currentEtag = headResponse.headers.get("ETag"); - const currentLastModified = headResponse.headers.get("Last-Modified"); - if (cachedPageInfo.etag && currentEtag === cachedPageInfo.etag || cachedPageInfo.lastModified && currentLastModified === cachedPageInfo.lastModified) { - console.info(`[reef] skipping unchanged page: ${pageUrl}`); - results[i] = null; - return; - } - } - } - const pageResponse = await fetch(pageUrl); - if (pageResponse.ok) { - const html = await pageResponse.text(); - if (cachedPageInfo?.contentHash) { - const currentContentHash = this.hashContent(html); - if (currentContentHash === cachedPageInfo.contentHash) { - console.info(`[reef] skipping unchanged page (content hash match): ${pageUrl}`); - results[i] = null; - return; - } - } - const pageSections = this.extractAllContent(html, pageUrl); - results[i] = pageSections; - pageHashes[pageUrl] = { - etag: pageResponse.headers.get("ETag"), - lastModified: pageResponse.headers.get("Last-Modified"), - contentHash: this.hashContent(html), - timestamp: Date.now() - }; - } - } catch { - results[i] = null; - } - } - }; - await Promise.all([...Array(concurrency)].map(() => fetchBatch())); - for (let i = 0; i < results.length; i++) { - if (results[i]) { - sections.push(...results[i] ?? []); - } - } - return sections; - } - async fetchPagesWithWorker(urls, sitemapUrl, onReady) { - const workerUrl = new URL("../worker.js", import.meta.url).href; - const workerCount = Math.min(navigator.hardwareConcurrency || 4, 4); - return new Promise((resolve, reject) => { - const workers = []; - const results = /* @__PURE__ */ new Map(); - let completedWorkers = 0; - const shards = this.shardArray(urls, workerCount); - for (let i = 0; i < workerCount; i++) { - const worker = new Worker(workerUrl); - workers.push(worker); - const workerId = i; - const shard = shards[i] || []; - const messageHandler = (e) => { - const { result, error, json, workerIndex } = e.data; - if (workerIndex !== workerId) return; - if (error) { - console.error(`[reef] worker ${workerId} error:`, error); - results.set(workerId, { result: "error", error }); - } else if (json) { - results.set(workerId, { result: "ok", error: "" }); - } - completedWorkers++; - if (completedWorkers === workerCount) { - this.mergeWorkerResults(workers, results, onReady, resolve, reject); - } - }; - const errorHandler = (e) => { - console.error(`[reef] worker ${workerId} error:`, e.error); - results.set(workerId, { result: "error", error: e.error?.toString() || "Unknown error" }); - completedWorkers++; - if (completedWorkers === workerCount) { - this.mergeWorkerResults(workers, results, onReady, resolve, reject); - } - }; - worker.addEventListener("message", messageHandler); - worker.addEventListener("error", errorHandler); - const config = { - scope: this.config.scope, - indexActions: this.config.indexActions, - indexMedia: this.config.indexMedia, - indexStructuredData: this.config.indexStructuredData, - indexHidden: this.config.indexHidden, - excludeAction: this.config.excludeAction, - fileExtensions: this.config.fileExtensions - }; - const id = Date.now() + i; - worker.postMessage({ - id, - workerIndex: i, - action: "indexPages", - payload: { - pages: shard.map((url) => [this.resolveUrl(url, sitemapUrl), ""]), - // URL and empty HTML initially - config, - shardIndex: i, - totalShards: workerCount - } - }); - } - }); - } - // Helper to shard an array into N parts - shardArray(array, count) { - const shards = []; - const shardSize = Math.ceil(array.length / count); - for (let i = 0; i < count; i++) { - const start = i * shardSize; - const end = start + shardSize; - shards.push(array.slice(start, end)); - } - return shards; - } - // Merge results from all workers - async mergeWorkerResults(workers, results, onReady, resolve, reject) { - for (const worker of workers) { - worker.terminate(); - } - let hasErrors = false; - for (const [workerId, result] of results) { - if (result.error) { - hasErrors = true; - break; - } - } - if (hasErrors) { - console.warn("[reef] worker pool had errors, falling back to single-threaded"); - try { - const fetchedSections = await this.fetchPagesParallel( - Array.from(results.keys()).map((i) => this.resolveUrl("", "")), - // Empty for fallback - "", - {} - ); - onReady(); - resolve(fetchedSections); - } catch (e) { - reject(e); - } - return; - } - console.warn("[reef] worker pool: TODO implement proper merging of worker results"); - onReady(); - resolve([]); - } -}; - -// src/storage.ts -var KEY = { - bookmarks: "reef.bookmarks", - snippets: "reef.snippets", - pageNotes: "reef.pageNotes", - recents: "reef.recents" -}; -function genId() { - return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`; -} -function storageApi() { - if (typeof chrome !== "undefined" && chrome.storage?.local) { - return chrome.storage.local; - } - return null; -} -async function getArray(key) { - const api = storageApi(); - if (!api) return []; - const data = await api.get([key]); - return data[key] || []; -} -async function setArray(key, value) { - const api = storageApi(); - if (!api) return; - await api.set({ [key]: value }); -} -async function listBookmarks(query = "", tags = []) { - const all = await getArray(KEY.bookmarks); - return filterItems(all, query, tags); -} -async function createBookmark(input) { - const now = Date.now(); - const bookmark = { ...input, id: genId(), createdAt: now, updatedAt: now }; - const all = await getArray(KEY.bookmarks); - all.unshift(bookmark); - await setArray(KEY.bookmarks, all); - return bookmark; -} -async function updateBookmark(id, patch) { - const all = await getArray(KEY.bookmarks); - const idx = all.findIndex((b) => b.id === id); - if (idx < 0) return null; - all[idx] = { ...all[idx], ...patch, id, updatedAt: Date.now() }; - await setArray(KEY.bookmarks, all); - return all[idx]; -} -async function deleteBookmark(id) { - const all = await getArray(KEY.bookmarks); - const next = all.filter((b) => b.id !== id); - if (next.length === all.length) return false; - await setArray(KEY.bookmarks, next); - return true; -} -async function listSnippets(query = "", tags = []) { - const all = await getArray(KEY.snippets); - return filterItems(all, query, tags); -} -async function createSnippet(input) { - const now = Date.now(); - const snippet = { ...input, id: genId(), createdAt: now, updatedAt: now }; - const all = await getArray(KEY.snippets); - all.unshift(snippet); - await setArray(KEY.snippets, all); - return snippet; -} -async function updateSnippet(id, patch) { - const all = await getArray(KEY.snippets); - const idx = all.findIndex((s) => s.id === id); - if (idx < 0) return null; - all[idx] = { ...all[idx], ...patch, id, updatedAt: Date.now() }; - await setArray(KEY.snippets, all); - return all[idx]; -} -async function deleteSnippet(id) { - const all = await getArray(KEY.snippets); - const next = all.filter((s) => s.id !== id); - if (next.length === all.length) return false; - await setArray(KEY.snippets, next); - return true; -} -async function getPageNote(url) { - const all = await getArray(KEY.pageNotes); - return all.find((n) => n.url === url) || null; -} -async function listPageNotes(query = "") { - const all = await getArray(KEY.pageNotes); - if (!query) return all; - const q = query.toLowerCase(); - return all.filter( - (n) => n.text.toLowerCase().includes(q) || n.title.toLowerCase().includes(q) || n.url.toLowerCase().includes(q) - ); -} -async function setPageNote(url, text, title) { - const all = await getArray(KEY.pageNotes); - const existing = all.findIndex((n) => n.url === url); - const note = { url, text, title, updatedAt: Date.now() }; - if (existing >= 0) all[existing] = note; - else all.unshift(note); - await setArray(KEY.pageNotes, all); - return note; -} -async function deletePageNote(url) { - const all = await getArray(KEY.pageNotes); - const next = all.filter((n) => n.url !== url); - if (next.length === all.length) return false; - await setArray(KEY.pageNotes, next); - return true; -} -var RECENT_MAX = 30; -async function listRecents() { - return getArray(KEY.recents); -} -async function recordRecent(page) { - const all = await getArray(KEY.recents); - const next = [{ ...page, visitedAt: Date.now() }, ...all.filter((p) => p.url !== page.url)].slice(0, RECENT_MAX); - await setArray(KEY.recents, next); -} -async function clearRecents() { - await setArray(KEY.recents, []); -} -async function allBookmarkTags() { - return collectTags(await getArray(KEY.bookmarks)); -} -async function allSnippetTags() { - return collectTags(await getArray(KEY.snippets)); -} -function filterItems(items, query, tags) { - let out = items; - if (tags.length) { - out = out.filter((item) => tags.every((t) => item.tags.includes(t))); - } - if (query) { - const q = query.toLowerCase(); - out = out.filter((item) => { - const hay = JSON.stringify(item).toLowerCase(); - return hay.includes(q); - }); - } - return out; -} -function collectTags(items) { - const set = /* @__PURE__ */ new Set(); - for (const item of items) for (const tag of item.tags) set.add(tag); - return Array.from(set).sort(); -} - -// src/background.ts -var tabIndices = /* @__PURE__ */ new Map(); -var siteIndices = /* @__PURE__ */ new Map(); -async function getOptions() { - if (typeof chrome === "undefined" || !chrome.storage?.local) { - return { - actionsMode: "execute", - allowDenyList: [], - exclusionSelectors: [], - telemetryEnabled: false, - enableCrossTabCrawl: false - }; - } - const data = await chrome.storage.local.get([ - "actionsMode", - "allowDenyList", - "exclusionSelectors", - "telemetryEnabled", - "enableCrossTabCrawl" - ]); - return { - actionsMode: data.actionsMode || "execute", - allowDenyList: data.allowDenyList || [], - exclusionSelectors: data.exclusionSelectors || [], - telemetryEnabled: data.telemetryEnabled || false, - enableCrossTabCrawl: data.enableCrossTabCrawl || false - }; -} -async function getOrFetchTabIndex(tabId, forceRefresh = false) { - if (!forceRefresh && tabIndices.has(tabId)) { - return tabIndices.get(tabId); - } - try { - const response = await chrome.tabs.sendMessage(tabId, { type: "GET_MANIFEST" }); - if (response && response.success && response.manifest) { - const index = createSearchIndex(); - addToIndex(index, response.manifest.records); - const state = { - index, - manifest: response.manifest, - lastUpdated: Date.now() - }; - tabIndices.set(tabId, state); - const options = await getOptions(); - if (options.enableCrossTabCrawl && response.manifest.url) { - try { - const urlObj = new URL(response.manifest.url); - const origin = urlObj.origin; - let siteIndex = siteIndices.get(origin); - if (!siteIndex) { - siteIndex = createSearchIndex(); - siteIndices.set(origin, siteIndex); - } - addToIndex(siteIndex, response.manifest.records); - } catch { - } - } - try { - const tab = await chrome.tabs.get(tabId); - if (tab.url && tab.title) { - await recordRecent({ - url: tab.url, - title: tab.title, - favicon: tab.favIconUrl, - recordCount: response.manifest.records.length - }); - } - } catch { - } - return state; - } - } catch (err) { - console.warn(`[Reef Background] Failed to fetch manifest from tab ${tabId}:`, err); - } - return null; -} -function ensureContextMenus() { - if (typeof chrome === "undefined" || !chrome.contextMenus) return; - const api = chrome.contextMenus; - if (!api) return; - const create = (id, title, contexts) => { - try { - api.create({ id, title, contexts }, () => { - void chrome.runtime.lastError; - }); - } catch { - } - }; - create("reef-bookmark-selection", "Bookmark selection in Reef", ["selection"]); - create("reef-snippet-selection", "Save selection as snippet", ["selection"]); - create("reef-search-selection", 'Search "%s" in Reef', ["selection"]); - create("reef-note-page", "Add note to this page", ["page", "selection"]); - create("reef-bookmark-page", "Bookmark this page in Reef", ["page"]); -} -if (typeof chrome !== "undefined") { - if (chrome.runtime?.onInstalled) { - chrome.runtime.onInstalled.addListener(() => ensureContextMenus()); - } - if (chrome.runtime?.onStartup) { - chrome.runtime.onStartup.addListener(() => ensureContextMenus()); - } - ensureContextMenus(); -} -if (typeof chrome !== "undefined" && chrome.contextMenus?.onClicked) { - chrome.contextMenus.onClicked.addListener(async (info, tab) => { - try { - if (info.menuItemId === "reef-bookmark-selection" && info.selectionText && tab?.id !== void 0) { - await chrome.tabs.sendMessage(tab.id, { type: "REEF_BOOKMARK_SELECTION", text: info.selectionText }); - return; - } - if (info.menuItemId === "reef-snippet-selection" && info.selectionText && tab?.id !== void 0) { - await chrome.tabs.sendMessage(tab.id, { type: "REEF_SNIPPET_SELECTION", text: info.selectionText }); - return; - } - if (info.menuItemId === "reef-search-selection" && info.selectionText && tab?.id !== void 0) { - await chrome.tabs.sendMessage(tab.id, { type: "REEF_OPEN_POPUP_QUERY", query: info.selectionText }); - chrome.action?.openPopup?.(); - return; - } - if (info.menuItemId === "reef-note-page" && tab?.id !== void 0) { - await chrome.tabs.sendMessage(tab.id, { type: "REEF_OPEN_NOTE_FOR_PAGE" }); - chrome.action?.openPopup?.(); - return; - } - if (info.menuItemId === "reef-bookmark-page" && tab?.id !== void 0) { - await chrome.tabs.sendMessage(tab.id, { type: "REEF_BOOKMARK_PAGE" }); - return; - } - } catch (err) { - console.warn("[Reef Background] Context menu action failed:", err); - } - }); -} -if (typeof chrome !== "undefined" && chrome.omnibox) { - chrome.omnibox.onInputChanged.addListener(async (text, suggestCallback) => { - const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true }); - if (!activeTab?.id) return; - const state = await getOrFetchTabIndex(activeTab.id); - if (!state) return; - const results = searchSections(text, state.index, { limit: 5 }); - const suggestions = results.map((r) => ({ - content: r.url || r.headingText, - description: `${escapeXml(r.headingText)} - ${escapeXml(r.bodyText?.slice(0, 60) || "")}` - })); - suggestCallback(suggestions); - }); - chrome.omnibox.onInputEntered.addListener(async (text, disposition) => { - const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true }); - if (!activeTab?.id) return; - const state = await getOrFetchTabIndex(activeTab.id); - if (!state) return; - const results = searchSections(text, state.index, { limit: 1 }); - if (results.length > 0) { - const record = results[0]; - await chrome.tabs.sendMessage(activeTab.id, { - type: "EXECUTE_ACTION", - record, - actionType: record.type === "field" ? "type" : "click" - }); - } - }); -} -function escapeXml(str) { - return str.replace(/[<>&'"]/g, (c) => { - switch (c) { - case "<": - return "<"; - case ">": - return ">"; - case "&": - return "&"; - case "'": - return "'"; - case '"': - return """; - default: - return c; - } - }); -} -function escapeRegex(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); -} -function scoreTitle(title, q) { - if (!q) return 0; - const t = title.toLowerCase(); - if (t === q) return 60; - if (t.startsWith(q)) return 35; - const idx = t.indexOf(q); - if (idx >= 0) return 15 + Math.max(0, 10 - Math.floor(idx / 8)); - if (new RegExp(`\\b${escapeRegex(q)}`, "i").test(title)) return 12; - return 0; -} -function searchSiteContent(query, limit = 10) { - const results = []; - for (const [origin, index] of siteIndices) { - try { - const hits = searchSections(query, index, { limit: 3, fuzzy: true }); - for (const hit of hits) { - results.push({ - url: hit.url || origin, - headingText: hit.headingText, - bodyText: (hit.bodyText || "").slice(0, 120), - selector: hit.selector, - type: hit.type, - score: 5, - sourceOrigin: origin - }); - } - } catch { - } - } - results.sort((a, b) => b.score - a.score); - return results.slice(0, limit); -} -function looksLikeUrl(q) { - return /^(https?:\/\/|www\.)/i.test(q) || /^[\w-]+(\.[\w-]+)+(\/\S*)?$/i.test(q) && q.includes("."); -} -async function searchOpenTabs(query, limit = 25) { - if (!query.trim() || typeof chrome === "undefined" || !chrome.tabs) return { items: [] }; - const q = query.toLowerCase(); - const tabs = await chrome.tabs.query({}); - let currentWindowId; - try { - const [cw] = await chrome.windows.getCurrent(); - currentWindowId = cw?.id; - } catch { - } - const fetchPromises = []; - for (const tab of tabs) { - if (!tab.id || !tab.url || !tab.title) continue; - if (tab.url.startsWith("chrome://") || tab.url.startsWith("about:") || tab.url.startsWith("moz-extension:")) continue; - if (!tabIndices.has(tab.id)) { - fetchPromises.push( - getOrFetchTabIndex(tab.id).then(() => { - }) - ); - } - } - if (fetchPromises.length > 0) { - await Promise.race([ - Promise.allSettled(fetchPromises), - new Promise((resolve) => setTimeout(resolve, 3e3)) - ]); - } - const matches = []; - for (const tab of tabs) { - if (!tab.id || !tab.url || !tab.title) continue; - if (tab.url.startsWith("chrome://") || tab.url.startsWith("about:") || tab.url.startsWith("moz-extension:")) continue; - let score = 0; - const matchedRecords = []; - const title = tab.title; - const url = tab.url; - score += scoreTitle(title, q); - if (url.toLowerCase().includes(q)) score += 5; - if (typeof currentWindowId === "number" && tab.windowId === currentWindowId) score += 3; - const state = tabIndices.get(tab.id); - if (state) { - try { - const hits = searchSections(query, state.index, { limit: 3, fuzzy: true }); - if (hits.length) { - score += 8; - matchedRecords.push(...hits); - } - } catch { - } - } else if (score === 0) { - continue; - } - if (score > 0) { - matches.push({ tab, score, matchedRecords }); - } - } - matches.sort((a, b) => b.score - a.score); - let items = matches.slice(0, limit).map((m) => ({ - tabId: m.tab.id, - title: m.tab.title, - url: m.tab.url, - favIconUrl: m.tab.favIconUrl, - windowId: m.tab.windowId, - score: m.score, - matchedRecords: m.matchedRecords.map((r) => ({ - headingText: r.headingText, - bodyText: (r.bodyText || "").slice(0, 120), - selector: r.selector, - type: r.type - })) - })); - let suggestion; - for (const [, state] of tabIndices) { - const word = findClosestWord(query, state.index, 2); - if (word && word !== query.toLowerCase()) { - suggestion = word; - break; - } - } - let autocorrected = false; - if (items.length === 0 && suggestion) { - const correctedMatches = []; - for (const tab of tabs) { - if (!tab.id || !tab.url || !tab.title) continue; - if (tab.url.startsWith("chrome://") || tab.url.startsWith("about:") || tab.url.startsWith("moz-extension:")) continue; - const state = tabIndices.get(tab.id); - if (!state) continue; - try { - const hits = searchSections(suggestion, state.index, { limit: 3, fuzzy: true }); - if (hits.length) { - correctedMatches.push({ tab, score: 6 + hits.length, matchedRecords: hits }); - } - } catch { - } - } - if (correctedMatches.length > 0) { - correctedMatches.sort((a, b) => b.score - a.score); - items = correctedMatches.slice(0, limit).map((m) => ({ - tabId: m.tab.id, - title: m.tab.title, - url: m.tab.url, - favIconUrl: m.tab.favIconUrl, - windowId: m.tab.windowId, - score: m.score, - matchedRecords: m.matchedRecords.map((r) => ({ - headingText: r.headingText, - bodyText: (r.bodyText || "").slice(0, 120), - selector: r.selector, - type: r.type - })) - })); - autocorrected = true; - } - } - if (items.length === 0) { - try { - const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true }); - if (activeTab?.id) { - const reefResponse = await chrome.tabs.sendMessage(activeTab.id, { - type: "REEF_LOCAL_SEARCH", - query, - options: { limit: 5 } - }); - if (reefResponse?.success && reefResponse.results?.length > 0) { - if (!suggestion && reefResponse.suggestion) suggestion = reefResponse.suggestion; - if (reefResponse.autocorrected) { - items = reefResponse.results.slice(0, 5).map((r) => ({ - tabId: activeTab.id, - title: activeTab.title || "", - url: activeTab.url || "", - favIconUrl: activeTab.favIconUrl || "", - windowId: activeTab.windowId, - score: 5, - matchedRecords: [{ headingText: r.headingText, bodyText: (r.bodyText || "").slice(0, 120), selector: r.selector, type: r.type }] - })); - autocorrected = true; - } - } - } - } catch { - } - } - const siteResults = searchSiteContent(query, 5); - const actions = []; - if (looksLikeUrl(query.trim())) { - let navUrl = query.trim(); - if (!/^https?:\/\//i.test(navUrl)) navUrl = "https://" + navUrl; - actions.push({ type: "open-url", title: `Open ${navUrl}`, url: navUrl }); - } - actions.push({ type: "search-web", title: `Search the web for "${query}"`, url: `https://www.google.com/search?q=${encodeURIComponent(query)}` }); - return { items, suggestion, autocorrected, siteResults, actions }; -} -async function listOpenTabs() { - if (typeof chrome === "undefined" || !chrome.tabs) return { items: [] }; - const tabs = await chrome.tabs.query({}); - const items = []; - for (const tab of tabs) { - if (!tab.id || !tab.url || !tab.title) continue; - if (tab.url.startsWith("chrome://") || tab.url.startsWith("about:") || tab.url.startsWith("moz-extension:")) continue; - items.push({ - tabId: tab.id, - title: tab.title, - url: tab.url, - favIconUrl: tab.favIconUrl || "", - windowId: tab.windowId, - score: 0, - matchedRecords: [] - }); - } - return { items }; -} -if (typeof chrome !== "undefined" && chrome.runtime?.onMessage) { - chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { - (async () => { - try { - if (message.type === "SEARCH_CURRENT_TAB") { - const tabId = message.tabId || sender.tab?.id; - if (!tabId) { - sendResponse({ success: false, error: "no-tab-id" }); - return; - } - const state = await getOrFetchTabIndex(tabId, message.forceRefresh); - if (!state) { - sendResponse({ success: false, error: "failed-to-index-tab" }); - return; - } - const options = message.searchOptions || {}; - const query = message.query || ""; - const paginated = searchWithPagination(query, state.index, options); - const bgSuggestions = suggest(query, state.index); - let results = paginated.results.map((sr) => sr.record ?? sr); - let suggestion; - let autocorrected = false; - let reefSuggestions = []; - try { - const reefResponse = await chrome.tabs.sendMessage(tabId, { - type: "REEF_LOCAL_SEARCH", - query, - options: { limit: options.pageSize ?? 20 } - }); - if (reefResponse?.success) { - reefSuggestions = reefResponse.suggestions || []; - if (results.length === 0 && reefResponse.autocorrected && reefResponse.results?.length > 0) { - results = reefResponse.results; - suggestion = reefResponse.suggestion; - autocorrected = true; - } - if (!suggestion && reefResponse.suggestion) { - suggestion = reefResponse.suggestion; - } - } - } catch { - } - const mergedSuggestions = [...new Set([...bgSuggestions, ...reefSuggestions])].slice(0, 10); - sendResponse({ - success: true, - results, - total: paginated.total, - hasMore: paginated.hasMore, - suggestions: mergedSuggestions, - suggestion, - autocorrected, - manifest: state.manifest - }); - return; - } - if (message.type === "EXECUTE_TAB_ACTION") { - const tabId = message.tabId || sender.tab?.id; - if (!tabId) { - sendResponse({ success: false, error: "no-tab-id" }); - return; - } - const opts = await getOptions(); - const response = await chrome.tabs.sendMessage(tabId, { - type: "EXECUTE_ACTION", - record: message.record, - actionType: message.actionType, - value: message.value, - options: { - actionsMode: opts.actionsMode, - exclusionSelectors: opts.exclusionSelectors - } - }); - sendResponse(response); - return; - } - if (message.type === "CRAWL_SITE_CROSS_TAB") { - const tabId = message.tabId || sender.tab?.id; - if (!tabId) { - sendResponse({ success: false, error: "no-tab-id" }); - return; - } - const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); - if (!tab?.url) { - sendResponse({ success: false, error: "no-tab-url" }); - return; - } - const indexer = new Indexer({ scope: "body", indexActions: true }); - await indexer.crawlSameOrigin(() => { - const crawledIndex = indexer.getIndex(); - siteIndices.set(new URL(tab.url).origin, crawledIndex); - sendResponse({ success: true }); - }); - return; - } - if (message.type === "LIBRARY_BOOKMARK_LIST") { - sendResponse({ success: true, items: await listBookmarks(message.query || "", message.tags || []) }); - return; - } - if (message.type === "LIBRARY_BOOKMARK_CREATE") { - const bookmark = await createBookmark(message.data); - sendResponse({ success: true, item: bookmark }); - return; - } - if (message.type === "LIBRARY_BOOKMARK_UPDATE") { - const item = await updateBookmark(message.id, message.data || {}); - sendResponse({ success: !!item, item }); - return; - } - if (message.type === "LIBRARY_BOOKMARK_DELETE") { - sendResponse({ success: await deleteBookmark(message.id) }); - return; - } - if (message.type === "LIBRARY_SNIPPET_LIST") { - sendResponse({ success: true, items: await listSnippets(message.query || "", message.tags || []) }); - return; - } - if (message.type === "LIBRARY_SNIPPET_CREATE") { - const snippet = await createSnippet(message.data); - sendResponse({ success: true, item: snippet }); - return; - } - if (message.type === "LIBRARY_SNIPPET_UPDATE") { - const item = await updateSnippet(message.id, message.data || {}); - sendResponse({ success: !!item, item }); - return; - } - if (message.type === "LIBRARY_SNIPPET_DELETE") { - sendResponse({ success: await deleteSnippet(message.id) }); - return; - } - if (message.type === "LIBRARY_NOTE_GET") { - sendResponse({ success: true, item: await getPageNote(message.url) }); - return; - } - if (message.type === "LIBRARY_NOTE_LIST") { - sendResponse({ success: true, items: await listPageNotes(message.query || "") }); - return; - } - if (message.type === "LIBRARY_NOTE_SET") { - const note = await setPageNote(message.url, message.text, message.title || ""); - sendResponse({ success: true, item: note }); - return; - } - if (message.type === "LIBRARY_NOTE_DELETE") { - sendResponse({ success: await deletePageNote(message.url) }); - return; - } - if (message.type === "LIBRARY_RECENTS_LIST") { - sendResponse({ success: true, items: await listRecents() }); - return; - } - if (message.type === "LIBRARY_RECENTS_CLEAR") { - await clearRecents(); - sendResponse({ success: true }); - return; - } - if (message.type === "LIBRARY_TAGS") { - sendResponse({ - success: true, - bookmarkTags: await allBookmarkTags(), - snippetTags: await allSnippetTags() - }); - return; - } - if (message.type === "TAB_SEARCH" || message.type === "SPOTLIGHT_SEARCH") { - const result = await searchOpenTabs(message.query || "", message.limit || 50); - sendResponse({ - success: true, - items: result.items, - suggestion: result.suggestion, - autocorrected: result.autocorrected, - siteResults: result.siteResults, - actions: result.actions - }); - return; - } - if (message.type === "TAB_SWITCH") { - if (typeof message.tabId === "number" && chrome.tabs) { - await chrome.tabs.update(message.tabId, { active: true }); - if (typeof message.windowId === "number" && chrome.windows) { - await chrome.windows.update(message.windowId, { focused: true }); - } - sendResponse({ success: true }); - } else { - sendResponse({ success: false, error: "invalid-tab-id" }); - } - return; - } - if (message.type === "SPOTLIGHT_OPEN_RECORD") { - if (typeof message.tabId === "number" && message.record && chrome.tabs) { - try { - await chrome.tabs.sendMessage(message.tabId, { - type: "HIGHLIGHT_RECORD", - record: message.record - }); - sendResponse({ success: true }); - } catch { - sendResponse({ success: false, error: "failed-to-highlight" }); - } - } else { - sendResponse({ success: false, error: "invalid-spotlight-open-record" }); - } - return; - } - if (message.type === "SPOTLIGHT_OPEN_NEW_TAB") { - if (message.url && chrome.tabs?.create) { - await chrome.tabs.create({ url: message.url }); - sendResponse({ success: true }); - } else { - sendResponse({ success: false, error: "invalid-url" }); - } - return; - } - if (message.type === "SPOTLIGHT_CRAWL_SITE") { - if (!message.origin) { - sendResponse({ success: false, error: "no-origin" }); - return; - } - try { - const allTabs = await chrome.tabs.query({}); - const seedTab = allTabs.find((t) => t.url && t.id && new URL(t.url).origin === message.origin); - if (seedTab?.id) { - const resp = await chrome.tabs.sendMessage(seedTab.id, { type: "GET_MANIFEST" }); - if (resp?.success?.manifest) { - let siteIndex = siteIndices.get(message.origin); - if (!siteIndex) { - siteIndex = createSearchIndex(); - siteIndices.set(message.origin, siteIndex); - } - addToIndex(siteIndex, resp.manifest.records); - sendResponse({ success: true, recordCount: resp.manifest.records.length }); - } else { - sendResponse({ success: false, error: "no-manifest" }); - } - } else { - sendResponse({ success: false, error: "no-tab-for-origin" }); - } - } catch (err) { - sendResponse({ success: false, error: err?.message || String(err) }); - } - return; - } - if (message.type === "LIBRARY_OPEN_RECENT") { - if (message.url && chrome.tabs?.create) { - await chrome.tabs.create({ url: message.url }); - sendResponse({ success: true }); - } else { - sendResponse({ success: false, error: "invalid-url" }); - } - return; - } - if (message.type === "LIST_OPEN_TABS") { - const result = await listOpenTabs(); - sendResponse({ - success: true, - items: result.items - }); - return; - } - sendResponse({ success: false, error: "unsupported-background-message" }); - } catch (err) { - sendResponse({ success: false, error: err?.message || String(err) }); - } - })(); - return true; - }); -} -if (typeof chrome !== "undefined" && chrome.tabs?.onRemoved) { - chrome.tabs.onRemoved.addListener((tabId) => { - tabIndices.delete(tabId); - }); -} -if (typeof chrome !== "undefined" && chrome.commands?.onCommand) { - chrome.commands.onCommand.addListener(async (command) => { - if (command !== "open-spotlight") return; - if (!chrome.tabs?.query) return; - try { - const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true }); - if (!activeTab?.id) return; - await chrome.tabs.sendMessage(activeTab.id, { type: "SHOW_SPOTLIGHT" }); - } catch { - } - }); -} -//# sourceMappingURL=background.js.map diff --git a/reef-extension-chrome/background.js.map b/reef-extension-chrome/background.js.map deleted file mode 100644 index 506c456..0000000 --- a/reef-extension-chrome/background.js.map +++ /dev/null @@ -1,7 +0,0 @@ -{ - "version": 3, - "sources": ["../../src/search-index.ts", "../../src/cache.ts", "../src/background.ts", "../../src/extraction.ts", "../../src/indexing/indexer.ts", "../src/storage.ts"], - "sourcesContent": ["/**\n * @file Search index data structure and search algorithms.\n * Provides in-memory search indexing with fuzzy matching and result ranking.\n */\n\nimport { IndexRecord, SearchOptions, ScoredRecord, MatchSpan, TokenFilter, ReefConfig, SearchPage } from './types.js';\n\n// Default stop words for English\nconst STOP_WORDS = new Set([\n 'a', 'an', 'the', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by',\n 'from', 'up', 'about', 'into', 'through', 'during', 'before', 'after', 'above', 'below',\n 'is', 'are', 'was', 'were', 'be', 'been', 'being', 'have', 'has', 'had', 'do', 'does',\n 'did', 'will', 'would', 'should', 'could', 'may', 'might', 'must', 'shall', 'can', 'it',\n 'its', 'this', 'that', 'these', 'those', 'i', 'you', 'he', 'she', 'we', 'they', 'what',\n 'which', 'who', 'whom', 'when', 'where', 'why', 'how', 'all', 'each', 'every', 'both',\n 'few', 'more', 'most', 'other', 'some', 'such', 'no', 'nor', 'not', 'only', 'own',\n 'same', 'so', 'than', 'too', 'very', 'just', 'also', 'now', 'here', 'there',\n]);\n\n// Default tokenizer pipeline\nfunction createDefaultTokenizePipeline(): TokenFilter[] {\n return [\n (token: string) => {\n if (STOP_WORDS.has(token)) return null;\n return token;\n },\n (token: string) => {\n return token.normalize('NFD').replace(/[\\u0300-\\u036f]/g, '');\n },\n (token: string) => {\n if (token.length <= 2) return token;\n if (/ing$/.test(token)) return token.slice(0, -3);\n if (/ed$/.test(token)) return token.slice(0, -2);\n if (/es$/.test(token) && token.length > 3) return token.slice(0, -2);\n if (/s$/.test(token) && token.length > 3) return token.slice(0, -1);\n return token;\n },\n ];\n}\n\n// Apply tokenization pipeline to text and return array of tokens\nfunction tokenizeText(text: string, pipeline?: TokenFilter[]): string[] {\n if (!text) return [];\n\n let tokens = text.toLowerCase().split(/[^a-z0-9]+/i).filter(t => t.length > 0);\n const filters = pipeline ?? createDefaultTokenizePipeline();\n\n const result: string[] = [];\n for (const token of tokens) {\n let processed: string | null = token;\n for (const filter of filters) {\n if (processed === null) break;\n processed = filter(processed);\n }\n if (processed !== null) {\n result.push(processed);\n }\n }\n return result;\n}\n\n// Search cache entry for LRU\ninterface CacheEntry {\n resultIds: string[];\n timestamp: number;\n}\n\n// Per-field document frequency tracking for BM25F\ninterface FieldDocFreq {\n headingText: Map;\n bodyText: Map;\n label: Map;\n breadcrumb: Map;\n}\n\n// Query popularity tracking entry\ninterface PopularityEntry {\n query: string;\n recordId: string;\n count: number;\n}\n\n// Trie node for autocomplete\ninterface TrieNode {\n children: Map;\n records: IndexRecord[]; // Records that have this prefix\n}\n\nexport interface SearchIndex {\n headingIndex: Map;\n headingIds: Map;\n bodyIndex: Map;\n allSections: IndexRecord[];\n queryCache: Map;\n popularQueries: string[];\n docFrequency: Map;\n fieldDocFreq: FieldDocFreq;\n totalDocs: number;\n queryPopularity: Map;\n version: number;\n headingTrie: TrieNode; // Trie for efficient prefix lookups\n}\n\n// Create a new trie node\nfunction createTrieNode(): TrieNode {\n return {\n children: new Map(),\n records: [],\n };\n}\n\n// Insert a string into the trie\nfunction trieInsert(root: TrieNode, text: string, record: IndexRecord): void {\n let current = root;\n \n for (let i = 0; i < text.length; i++) {\n const char = text[i];\n if (!current.children.has(char)) {\n current.children.set(char, createTrieNode());\n }\n current = current.children.get(char)!;\n \n // Add record to this prefix node\n if (!current.records.some(r => r.id === record.id)) {\n current.records.push(record);\n }\n }\n}\n\n// Delete a string from the trie\nfunction trieDelete(root: TrieNode, text: string, recordId: string): void {\n let current = root;\n const path: {node: TrieNode, char: string}[] = [];\n \n // Traverse to find the string\n for (let i = 0; i < text.length; i++) {\n const char = text[i];\n if (!current.children.has(char)) {\n return; // String not in trie\n }\n path.push({node: current, char});\n current = current.children.get(char)!;\n }\n \n // Remove the record from all prefix nodes\n const nodesToClean: TrieNode[] = [];\n let tempCurrent = root;\n \n for (let i = 0; i < text.length; i++) {\n const char = text[i];\n if (tempCurrent.children.has(char)) {\n tempCurrent = tempCurrent.children.get(char)!;\n // Remove the record from this node\n tempCurrent.records = tempCurrent.records.filter(r => r.id !== recordId);\n nodesToClean.push(tempCurrent);\n }\n }\n \n // Clean up empty nodes (optional optimization)\n for (const node of nodesToClean) {\n if (node.records.length === 0 && node.children.size === 0) {\n // This node can be removed, but we'd need to track parent references\n // For now, just leave empty nodes as they don't affect functionality\n }\n }\n}\n\n// Query the trie for prefix matches\nfunction triePrefixQuery(root: TrieNode, prefix: string): IndexRecord[] {\n let current = root;\n \n for (let i = 0; i < prefix.length; i++) {\n const char = prefix[i];\n if (!current.children.has(char)) {\n return []; // Prefix not found\n }\n current = current.children.get(char)!;\n }\n \n // Return all records that have this prefix\n return [...current.records];\n}\n\n// Count total nodes in the trie (for memory comparison)\nfunction countTrieNodes(root: TrieNode): number {\n let count = 1; // Count current node\n for (const child of root.children.values()) {\n count += countTrieNodes(child);\n }\n return count;\n}\n\n// Helper function for finer-grained cache invalidation\nfunction invalidateAffectedQueries(index: SearchIndex, recordId: string, headingText: string, bodyText: string): void {\n // Tokenize the record's heading and body text\n const recordTokens = new Set();\n \n // Add heading tokens\n headingText.toLowerCase().split(/\\s+/).forEach(token => {\n if (token.length >= 2) recordTokens.add(token);\n });\n \n // Add body tokens\n bodyText.toLowerCase().split(/\\s+/).forEach(token => {\n if (token.length >= 2) recordTokens.add(token);\n });\n \n // Find queries that need to be invalidated\n const queriesToInvalidate = new Set();\n \n for (const [query, entry] of index.queryCache.entries()) {\n // Check if this query's results include the mutated record\n if (entry.resultIds.includes(recordId)) {\n queriesToInvalidate.add(query);\n continue;\n }\n \n // Check if query terms overlap with record tokens\n const queryLower = query.toLowerCase();\n const queryTokens = queryLower.split(/\\s+/).filter(t => t.length >= 2);\n \n for (const queryToken of queryTokens) {\n if (recordTokens.has(queryToken)) {\n queriesToInvalidate.add(query);\n break;\n }\n }\n }\n \n // Remove invalidated queries from cache\n if (queriesToInvalidate.size > 0) {\n for (const query of queriesToInvalidate) {\n index.queryCache.delete(query);\n }\n } else {\n // If we can't determine affected queries (e.g., empty cache or no matches),\n // fall back to clearing the entire cache\n index.queryCache.clear();\n }\n}\n\n// Calculate Jaccard similarity between two sets of tokens\nfunction jaccardSimilarity(tokens1: Set, tokens2: Set): number {\n if (tokens1.size === 0 || tokens2.size === 0) return 0;\n \n let intersection = 0;\n for (const token of tokens1) {\n if (tokens2.has(token)) intersection++;\n }\n \n const union = tokens1.size + tokens2.size - intersection;\n return intersection / union;\n}\n\n// Extract tokens from text for MMR similarity comparison\nfunction extractTokensForMMR(text: string): Set {\n const tokens = text.toLowerCase().split(/\\s+/);\n return new Set(tokens.filter(t => t.length > 2)); // Filter out very short tokens\n}\n\n// Maximal Marginal Relevance (MMR) re-ranking\n// Diversifies results by penalizing documents similar to already-selected ones\nfunction applyMMR(\n scoredEntries: Array<[IndexRecord, number]>, \n lambda: number = 0.5\n): Array<[IndexRecord, number]> {\n if (scoredEntries.length <= 1) return scoredEntries;\n \n const results: Array<[IndexRecord, number]> = [];\n const selectedTokens: Set[] = [];\n \n // Sort by score first (descending)\n const sortedByScore = [...scoredEntries].sort((a, b) => b[1] - a[1]);\n \n // Select first result (highest score)\n results.push(sortedByScore[0]);\n selectedTokens.push(extractTokensForMMR(sortedByScore[0][0].headingText + ' ' + sortedByScore[0][0].bodyText));\n \n // Select remaining results using MMR\n for (let i = 1; i < sortedByScore.length; i++) {\n const [record, originalScore] = sortedByScore[i];\n const recordText = record.headingText + ' ' + record.bodyText;\n const recordTokens = extractTokensForMMR(recordText);\n \n // Calculate similarity to already-selected results\n let maxSimilarity = 0;\n for (const selectedTokenSet of selectedTokens) {\n const similarity = jaccardSimilarity(recordTokens, selectedTokenSet);\n maxSimilarity = Math.max(maxSimilarity, similarity);\n }\n \n // MMR score: (1 - lambda) * relevance + lambda * (1 - maxSimilarity)\n const mmrScore = (1 - lambda) * originalScore + lambda * (1 - maxSimilarity);\n \n // Insert in the right position\n let inserted = false;\n for (let j = 0; j < results.length; j++) {\n if (mmrScore > results[j][1]) {\n results.splice(j, 0, [record, mmrScore]);\n selectedTokens.splice(j, 0, recordTokens);\n inserted = true;\n break;\n }\n }\n \n if (!inserted) {\n results.push([record, mmrScore]);\n selectedTokens.push(recordTokens);\n }\n }\n \n return results;\n}\n\nexport function createSearchIndex(): SearchIndex {\n return {\n headingIndex: new Map(),\n headingIds: new Map(),\n bodyIndex: new Map(),\n allSections: [],\n queryCache: new Map(),\n popularQueries: [],\n docFrequency: new Map(),\n fieldDocFreq: {\n headingText: new Map(),\n bodyText: new Map(),\n label: new Map(),\n breadcrumb: new Map(),\n },\n totalDocs: 0,\n queryPopularity: new Map(),\n version: 1,\n headingTrie: createTrieNode(),\n };\n}\n\nexport function addToIndex(index: SearchIndex, records: IndexRecord[], tokenizePipeline?: TokenFilter[]): void {\n index.totalDocs += records.length;\n\n for (const record of records) {\n // Index by exact heading text (backward compatible)\n const headingLower = record.headingText.toLowerCase();\n if (headingLower.length >= 2) {\n const existing = index.headingIds.get(headingLower) ?? [];\n existing.push(record);\n index.headingIds.set(headingLower, existing);\n\n // Also index normalized version for diacritic matching\n const normalizedHeading = headingLower.normalize('NFD').replace(/[\\u0300-\\u036f]/g, '');\n if (normalizedHeading !== headingLower) {\n const normalizedExisting = index.headingIds.get(normalizedHeading) ?? [];\n normalizedExisting.push(record);\n index.headingIds.set(normalizedHeading, normalizedExisting);\n }\n }\n\n // Index by heading prefixes (also normalized) - keep for backward compatibility\n for (let i = 2; i <= headingLower.length; i++) {\n const prefix = headingLower.slice(0, i);\n const existing = index.headingIndex.get(prefix) ?? [];\n existing.push(record);\n index.headingIndex.set(prefix, existing);\n }\n const normalizedHeading = headingLower.normalize('NFD').replace(/[\\u0300-\\u036f]/g, '');\n for (let i = 2; i <= normalizedHeading.length; i++) {\n const prefix = normalizedHeading.slice(0, i);\n if (prefix !== headingLower.slice(0, i)) {\n const existing = index.headingIndex.get(prefix) ?? [];\n existing.push(record);\n index.headingIndex.set(prefix, existing);\n }\n }\n\n // Insert into trie for efficient prefix lookups\n trieInsert(index.headingTrie, headingLower, record);\n if (normalizedHeading !== headingLower) {\n trieInsert(index.headingTrie, normalizedHeading, record);\n }\n\n // Index body words (backward compatible, simple split)\n const bodyLower = record.bodyText.toLowerCase();\n const bodyWords = bodyLower.split(/\\s+/);\n for (const word of bodyWords) {\n if (word.length >= 3) {\n const existing = index.bodyIndex.get(word) ?? [];\n existing.push(record);\n index.bodyIndex.set(word, existing);\n\n // Also index normalized version\n const normalizedWord = word.normalize('NFD').replace(/[\\u0300-\\u036f]/g, '');\n if (normalizedWord !== word) {\n const normalizedExisting = index.bodyIndex.get(normalizedWord) ?? [];\n normalizedExisting.push(record);\n index.bodyIndex.set(normalizedWord, normalizedExisting);\n }\n }\n }\n\n // Track doc frequency for BM25 (use body words for now)\n for (const word of bodyWords) {\n if (word.length >= 2) {\n const currentFreq = index.docFrequency.get(word) ?? 0;\n index.docFrequency.set(word, currentFreq + 1);\n \n // Track per-field doc frequency for BM25F\n const currentBodyFreq = index.fieldDocFreq.bodyText.get(word) ?? 0;\n index.fieldDocFreq.bodyText.set(word, currentBodyFreq + 1);\n }\n }\n\n // Track per-field doc frequency for heading\n const headingWords = headingLower.split(/\\s+/);\n for (const word of headingWords) {\n if (word.length >= 2) {\n const currentHeadingFreq = index.fieldDocFreq.headingText.get(word) ?? 0;\n index.fieldDocFreq.headingText.set(word, currentHeadingFreq + 1);\n }\n }\n\n // Track per-field doc frequency for label if present\n if (record.label) {\n const labelLower = record.label.toLowerCase();\n const labelWords = labelLower.split(/\\s+/);\n for (const word of labelWords) {\n if (word.length >= 2) {\n const currentLabelFreq = index.fieldDocFreq.label.get(word) ?? 0;\n index.fieldDocFreq.label.set(word, currentLabelFreq + 1);\n }\n }\n }\n\n // Track per-field doc frequency for breadcrumb if present\n if (record.breadcrumb) {\n const breadcrumbLower = record.breadcrumb.toLowerCase();\n const breadcrumbWords = breadcrumbLower.split(/\\s+/);\n for (const word of breadcrumbWords) {\n if (word.length >= 2) {\n const currentBreadcrumbFreq = index.fieldDocFreq.breadcrumb.get(word) ?? 0;\n index.fieldDocFreq.breadcrumb.set(word, currentBreadcrumbFreq + 1);\n }\n }\n }\n\n // Also index the label if present\n if (record.label) {\n const labelLower = record.label.toLowerCase();\n if (labelLower.length >= 2) {\n const existing = index.bodyIndex.get(labelLower) ?? [];\n existing.push(record);\n index.bodyIndex.set(labelLower, existing);\n }\n }\n\n if (record.type === 'structured' && record.structuredData) {\n if (record.structuredData.question) {\n const questionWords = record.structuredData.question.toLowerCase().split(/\\s+/);\n for (const word of questionWords) {\n if (word.length >= 3) {\n const existing = index.bodyIndex.get(word) ?? [];\n existing.push(record);\n index.bodyIndex.set(word, existing);\n }\n }\n }\n if (record.structuredData.answer) {\n const answerWords = record.structuredData.answer.toLowerCase().split(/\\s+/);\n for (const word of answerWords) {\n if (word.length >= 3) {\n const existing = index.bodyIndex.get(word) ?? [];\n existing.push(record);\n index.bodyIndex.set(word, existing);\n }\n }\n }\n }\n }\n\n // Also add records to allSections\n index.allSections.push(...records);\n}\n\nexport const addSectionsToIndex = addToIndex;\n\nexport function getAllSections(index: SearchIndex): IndexRecord[] {\n return index.allSections;\n}\n\n/** Return a stable cursor page over the current ranking. */\nexport function searchWithPagination(\n query: string,\n index: SearchIndex,\n options: SearchOptions & { cursor?: string; pageSize?: number } = {}\n): SearchPage {\n const pageSize = Math.max(1, options.pageSize ?? options.limit ?? 20);\n const offset = options.cursor ? Math.max(0, Number.parseInt(options.cursor, 10) || 0) : 0;\n const ranked = searchSections(query, index, { ...options, limit: Number.MAX_SAFE_INTEGER }) as ScoredRecord[] | IndexRecord[];\n const results = ranked.slice(offset, offset + pageSize).map((record) =>\n 'score' in record ? record : { record, score: 0 }\n ) as ScoredRecord[];\n const nextOffset = offset + results.length;\n return {\n results,\n total: ranked.length,\n hasMore: nextOffset < ranked.length,\n ...(nextOffset < ranked.length ? { nextCursor: String(nextOffset) } : {}),\n };\n}\n\nexport function getTotalResultCount(query: string, index: SearchIndex, options: SearchOptions = {}): number {\n return (searchSections(query, index, { ...options, limit: Number.MAX_SAFE_INTEGER }) as unknown[]).length;\n}\n\nexport function levenshteinDistance(a: string, b: string): number {\n const m = a.length;\n const n = b.length;\n if (m === 0) return n;\n if (n === 0) return m;\n\n const dp: number[] = new Array(n + 1);\n for (let j = 0; j <= n; j++) dp[j] = j;\n\n for (let i = 1; i <= m; i++) {\n let prev = dp[0];\n dp[0] = i;\n for (let j = 1; j <= n; j++) {\n const temp = dp[j];\n dp[j] = Math.min(\n dp[j] + 1,\n dp[j - 1] + 1,\n prev + (a[i - 1] !== b[j - 1] ? 1 : 0)\n );\n prev = temp;\n }\n }\n return dp[n];\n}\n\nexport function findClosestWord(query: string, index: SearchIndex, maxDistance = 2): string | null {\n const term = query.trim().toLowerCase();\n if (!term || term.length < 2) return null;\n\n let closest: { word: string; distance: number } | null = null;\n\n const checkWord = (word: string) => {\n if (!word || Math.abs(word.length - term.length) > maxDistance) return;\n const distance = levenshteinDistance(word, term);\n const current = closest as { word: string; distance: number } | null;\n if (distance <= maxDistance && (!current || distance < current.distance)) {\n closest = { word, distance };\n }\n };\n\n for (const key of index.headingIds.keys()) {\n checkWord(key);\n }\n for (const key of index.bodyIndex.keys()) {\n checkWord(key);\n }\n\n return (closest as { word: string; distance: number } | null)?.word ?? null;\n}\n\n// Default field weights (matching Fuse.js convention where higher = more important)\nconst DEFAULT_WEIGHTS: Record = {\n headingText: 2,\n bodyText: 1,\n label: 1.5,\n breadcrumb: 0.5,\n};\n\n// Calculate field length normalization factor (shorter matches = better)\nfunction getLengthNorm(textLength: number, avgLength = 50): number {\n const norm = 1 - Math.exp(-textLength / avgLength);\n return Math.max(0, Math.min(1, norm));\n}\n\n// Find all match positions in a string for a query\nfunction findMatchPositions(text: string, query: string): Array<{ start: number; end: number }> {\n const positions: Array<{ start: number; end: number }> = [];\n if (!query.trim() || !text) return positions;\n\n const lowerText = text.toLowerCase();\n const lowerQuery = query.toLowerCase();\n let idx = 0;\n\n while ((idx = lowerText.indexOf(lowerQuery, idx)) !== -1) {\n positions.push({ start: idx, end: idx + query.length });\n idx += query.length;\n }\n\n return positions;\n}\n\n// Parse extended search query syntax\nexport type QueryNode =\n | { type: 'term'; value: string; field?: string }\n | { type: 'exact'; value: string }\n | { type: 'exclude'; value: string }\n | { type: 'prefix'; value: string }\n | { type: 'suffix'; value: string }\n | { type: 'or'; children: QueryNode[] }\n | { type: 'and'; children: QueryNode[] };\n\nexport function parseExtendedQuery(query: string): QueryNode {\n const tokens = tokenizeExtendedQuery(query);\n return buildQueryTree(tokens);\n}\n\nfunction tokenizeExtendedQuery(query: string): Array<{ type: string; value: string }> {\n const tokens: Array<{ type: string; value: string }> = [];\n\n let rest = query;\n const exactRegex = /'([^']+)'/g;\n const excludeRegex = /!(\\S+)/g;\n const prefixRegex = /\\^(\\S+)/g;\n const suffixRegex = /(\\S+)\\$/g;\n\n while (rest.length > 0) {\n let matched = false;\n\n const exactMatch = exactRegex.exec(rest);\n if (exactMatch && exactMatch.index === 0) {\n tokens.push({ type: 'exact', value: exactMatch[1] });\n rest = rest.slice(exactMatch[0].length);\n matched = true;\n continue;\n }\n\n const excludeMatch = excludeRegex.exec(rest);\n if (excludeMatch && excludeMatch.index === 0) {\n tokens.push({ type: 'exclude', value: excludeMatch[1] });\n rest = rest.slice(excludeMatch[0].length);\n matched = true;\n continue;\n }\n\n const prefixMatch = prefixRegex.exec(rest);\n if (prefixMatch && prefixMatch.index === 0) {\n tokens.push({ type: 'prefix', value: prefixMatch[1] });\n rest = rest.slice(prefixMatch[0].length);\n matched = true;\n continue;\n }\n\n const suffixMatch = suffixRegex.exec(rest);\n if (suffixMatch && suffixMatch.index === 0) {\n tokens.push({ type: 'suffix', value: suffixMatch[1] });\n rest = rest.slice(suffixMatch[0].length);\n matched = true;\n continue;\n }\n\n const pipeMatch = rest.match(/^\\|/);\n if (pipeMatch) {\n tokens.push({ type: 'or', value: '|' });\n rest = rest.slice(1);\n matched = true;\n continue;\n }\n\n const spaceMatch = rest.match(/^\\s+/);\n if (spaceMatch && !matched) {\n rest = rest.slice(spaceMatch[0].length);\n continue;\n }\n\n const wordMatch = rest.match(/^(\\S+)/);\n if (wordMatch) {\n const fieldMatch = wordMatch[1].match(/^([\\w-]+):(.+)$/);\n tokens.push({ type: 'term', value: fieldMatch ? `${fieldMatch[1]}:${fieldMatch[2]}` : wordMatch[1] });\n rest = rest.slice(wordMatch[0].length);\n matched = true;\n }\n }\n\n return tokens;\n}\n\nfunction buildQueryTree(tokens: Array<{ type: string; value: string }>): QueryNode {\n const result: QueryNode = { type: 'and', children: [] };\n let currentOr: QueryNode | null = null;\n\n for (const token of tokens) {\n if (token.type === 'or') {\n if (!currentOr) {\n currentOr = { type: 'or', children: [] };\n result.children.push(currentOr);\n }\n } else {\n if (currentOr) {\n result.children.push(currentOr);\n currentOr = null;\n }\n result.children.push({ type: token.type as any, value: token.value });\n }\n }\n\n if (currentOr) {\n result.children.push(currentOr);\n }\n\n return result;\n}\n\n// Check if a query matches a record via indexed terms with fuzzy tolerance\nfunction getFuzzyCandidates(query: string, index: SearchIndex, distance: number): Set {\n const candidates = new Set();\n const queryLen = query.length;\n\n for (const [key, records] of index.headingIds) {\n if (Math.abs(key.length - queryLen) <= distance) {\n for (const record of records) {\n candidates.add(record);\n }\n }\n }\n\n for (const [key, records] of index.bodyIndex) {\n if (Math.abs(key.length - queryLen) <= distance) {\n for (const record of records) {\n candidates.add(record);\n }\n }\n }\n\n return candidates;\n}\n\n// Extended search matching for a single query node\nfunction matchExtendedNode(node: QueryNode, record: IndexRecord): boolean {\n switch (node.type) {\n case 'term':\n const fieldMatch = node.value.match(/^([\\w-]+):(.+)$/);\n const searchTerm = (fieldMatch ? fieldMatch[2] : node.value).toLowerCase();\n const field = fieldMatch?.[1].toLowerCase();\n const values = field === 'title' || field === 'heading' ? [record.headingText]\n : field === 'body' ? [record.bodyText]\n : field === 'label' ? [record.label ?? '']\n : field === 'breadcrumb' ? [record.breadcrumb]\n : field === 'type' ? [record.type]\n : [record.headingText, record.bodyText, record.label ?? '', record.breadcrumb];\n return values.some(value => value.toLowerCase().includes(searchTerm));\n\n case 'exact':\n const exactTerm = node.value.toLowerCase();\n // Use proper phrase matching instead of string includes\n const phraseTokens = tokenizeForPhraseMatching(exactTerm);\n const headingTokens = tokenizeForPhraseMatching(record.headingText);\n const bodyTokens = tokenizeForPhraseMatching(record.bodyText);\n return hasPhraseMatch(headingTokens, phraseTokens) || hasPhraseMatch(bodyTokens, phraseTokens);\n\n case 'exclude':\n const excludeTerm = node.value.toLowerCase();\n return !record.headingText.toLowerCase().includes(excludeTerm) && !record.bodyText.toLowerCase().includes(excludeTerm);\n\n case 'prefix':\n const prefixTerm = node.value.toLowerCase();\n return record.headingText.toLowerCase().startsWith(prefixTerm) || record.bodyText.toLowerCase().startsWith(prefixTerm);\n\n case 'suffix':\n const suffixTerm = node.value.toLowerCase();\n return record.headingText.toLowerCase().endsWith(suffixTerm) || record.bodyText.toLowerCase().endsWith(suffixTerm);\n\n case 'and':\n return node.children.every(child => matchExtendedNode(child, record));\n\n case 'or':\n return node.children.some(child => matchExtendedNode(child, record));\n }\n}\n\n// Calculate BM25 score for a term\nfunction bm25Score(\n termFreq: number,\n docFreq: number,\n totalDocs: number,\n docLength: number,\n avgDocLength: number,\n k1 = 1.5,\n b = 0.75\n): number {\n const idf = Math.log((totalDocs - docFreq + 0.5) / (docFreq + 0.5) + 1);\n const norm = 1 - b + b * (docLength / avgDocLength);\n return idf * ((termFreq * (k1 + 1)) / (termFreq + k1 * norm));\n}\n\n// Calculate BM25F score - field-weighted BM25\n// BM25F combines per-field weighted term frequency into a single pseudo-frequency\n// before applying the BM25 saturation function\nfunction bm25fScore(\n record: IndexRecord,\n queryTerms: string[],\n index: SearchIndex,\n weights: Record,\n k1 = 1.5,\n b = 0.75\n): number {\n const avgDocLength = getAvgDocLength(index);\n const totalDocs = index.totalDocs;\n \n // Calculate document length (combined length of all searchable fields)\n const docLength = (\n record.headingText.length * (weights.headingText || 1) +\n record.bodyText.length * (weights.bodyText || 1) +\n (record.label ? record.label.length * (weights.label || 1) : 0) +\n (record.breadcrumb ? record.breadcrumb.length * (weights.breadcrumb || 1) : 0)\n );\n \n let totalScore = 0;\n \n // For each query term, calculate BM25F score across all fields\n for (const term of queryTerms) {\n const termLower = term.toLowerCase();\n \n // Calculate per-field term frequencies and doc frequencies\n let weightedTermFreq = 0;\n let combinedDocFreq = 0;\n \n // Check heading field\n if (weights.headingText) {\n const headingText = record.headingText.toLowerCase();\n const headingTermFreq = countTermFrequency(headingText, termLower);\n const headingDocFreq = index.fieldDocFreq.headingText.get(termLower) || 1;\n \n weightedTermFreq += headingTermFreq * (weights.headingText || 1);\n combinedDocFreq = Math.max(combinedDocFreq, headingDocFreq);\n }\n \n // Check body field\n if (weights.bodyText) {\n const bodyText = record.bodyText.toLowerCase();\n const bodyTermFreq = countTermFrequency(bodyText, termLower);\n const bodyDocFreq = index.fieldDocFreq.bodyText.get(termLower) || 1;\n \n weightedTermFreq += bodyTermFreq * (weights.bodyText || 1);\n combinedDocFreq = Math.max(combinedDocFreq, bodyDocFreq);\n }\n \n // Check label field\n if (record.label && weights.label) {\n const labelText = record.label.toLowerCase();\n const labelTermFreq = countTermFrequency(labelText, termLower);\n const labelDocFreq = index.fieldDocFreq.label.get(termLower) || 1;\n \n weightedTermFreq += labelTermFreq * (weights.label || 1);\n combinedDocFreq = Math.max(combinedDocFreq, labelDocFreq);\n }\n \n // Check breadcrumb field\n if (record.breadcrumb && weights.breadcrumb) {\n const breadcrumbText = record.breadcrumb.toLowerCase();\n const breadcrumbTermFreq = countTermFrequency(breadcrumbText, termLower);\n const breadcrumbDocFreq = index.fieldDocFreq.breadcrumb.get(termLower) || 1;\n \n weightedTermFreq += breadcrumbTermFreq * (weights.breadcrumb || 1);\n combinedDocFreq = Math.max(combinedDocFreq, breadcrumbDocFreq);\n }\n \n // Apply BM25 formula to the weighted term frequency\n if (weightedTermFreq > 0 && combinedDocFreq > 0) {\n const idf = Math.log((totalDocs - combinedDocFreq + 0.5) / (combinedDocFreq + 0.5) + 1);\n const norm = 1 - b + b * (docLength / avgDocLength);\n const bm25f = idf * ((weightedTermFreq * (k1 + 1)) / (weightedTermFreq + k1 * norm));\n totalScore += bm25f;\n }\n }\n \n return totalScore;\n}\n\n// Helper function to count term frequency in text\nfunction countTermFrequency(text: string, term: string): number {\n if (!text || !term) return 0;\n const words = text.split(/\\s+/);\n let count = 0;\n for (const word of words) {\n if (word === term) count++;\n }\n return count;\n}\n\n// Tokenize text using the same pipeline as the index\nfunction tokenizeForPhraseMatching(text: string): string[] {\n if (!text) return [];\n // Use simple split for phrase matching (no stop word removal or stemming)\n return text.toLowerCase().split(/\\s+/).filter(t => t.length > 0);\n}\n\n// Check if a phrase (array of tokens) exists as contiguous sequence in text\nfunction hasPhraseMatch(textTokens: string[], phraseTokens: string[]): boolean {\n if (phraseTokens.length === 0) return true;\n if (phraseTokens.length > textTokens.length) return false;\n \n for (let i = 0; i <= textTokens.length - phraseTokens.length; i++) {\n let match = true;\n for (let j = 0; j < phraseTokens.length; j++) {\n if (textTokens[i + j] !== phraseTokens[j]) {\n match = false;\n break;\n }\n }\n if (match) return true;\n }\n return false;\n}\n\n// Snippet/highlight generation helper\n/**\n * Generate a text snippet around match positions with highlights\n * @param record The index record containing the text\n * @param matches Array of MatchSpan objects with start/end positions\n * @param contextChars Number of characters to include around each match (default 60)\n * @returns Truncated excerpt with match boundaries marked using tags\n */\nexport function getSnippet(record: IndexRecord, matches: MatchSpan[], contextChars = 60): string {\n if (!record || !matches || matches.length === 0) return '';\n \n const text = record.bodyText || record.headingText || '';\n if (!text) return '';\n \n // If no specific matches, return first part of text\n if (matches.length === 0) {\n return text.slice(0, contextChars * 2) + (text.length > contextChars * 2 ? '...' : '');\n }\n \n // Sort matches by start position\n const sortedMatches = [...matches].sort((a, b) => a.start - b.start);\n \n // Find the best match (longest match or first one)\n let bestMatch = sortedMatches[0];\n for (const match of sortedMatches) {\n if (match.end - match.start > bestMatch.end - bestMatch.start) {\n bestMatch = match;\n }\n }\n \n const matchStart = bestMatch.start;\n const matchEnd = bestMatch.end;\n \n // Calculate snippet boundaries\n let start = Math.max(0, matchStart - contextChars);\n let end = Math.min(text.length, matchEnd + contextChars);\n \n // Adjust start if we're at the beginning\n if (start === 0) {\n end = Math.min(text.length, matchEnd + contextChars * 2);\n }\n \n // Adjust end if we're at the end\n if (end === text.length) {\n start = Math.max(0, matchStart - contextChars * 2);\n }\n \n const snippetText = text.slice(start, end);\n \n // Add ellipsis if we're not at the beginning or end\n const prefix = start > 0 ? '...' : '';\n const suffix = end < text.length ? '...' : '';\n \n // Find the match position within the snippet\n const snippetMatchStart = matchStart - start;\n const snippetMatchEnd = matchEnd - start;\n \n // Create the highlighted snippet\n const beforeMatch = snippetText.slice(0, snippetMatchStart);\n const matchText = snippetText.slice(snippetMatchStart, snippetMatchEnd);\n const afterMatch = snippetText.slice(snippetMatchEnd);\n \n return prefix + beforeMatch + '' + matchText + '' + afterMatch + suffix;\n}\n\n// Calculate proximity score for multiple terms in text\n// Returns a bonus multiplier based on how close the terms are to each other\nfunction calculateProximityBonus(textTokens: string[], queryTokens: string[]): number {\n if (queryTokens.length <= 1) return 1.0; // No bonus for single term\n \n // Find all positions where each query term appears\n const termPositions: Map = new Map();\n for (let i = 0; i < textTokens.length; i++) {\n const token = textTokens[i];\n if (queryTokens.includes(token)) {\n if (!termPositions.has(token)) {\n termPositions.set(token, []);\n }\n termPositions.get(token)!.push(i);\n }\n }\n \n // For each query term, find its closest neighbor\n let totalProximity = 0;\n let pairCount = 0;\n \n for (let i = 0; i < queryTokens.length; i++) {\n for (let j = i + 1; j < queryTokens.length; j++) {\n const term1 = queryTokens[i];\n const term2 = queryTokens[j];\n \n const positions1 = termPositions.get(term1) || [];\n const positions2 = termPositions.get(term2) || [];\n \n if (positions1.length === 0 || positions2.length === 0) continue;\n \n // Find minimum distance between any occurrence of term1 and term2\n let minDistance = Infinity;\n for (const pos1 of positions1) {\n for (const pos2 of positions2) {\n const distance = Math.abs(pos1 - pos2);\n if (distance < minDistance) {\n minDistance = distance;\n }\n }\n }\n \n if (minDistance !== Infinity) {\n // Convert distance to bonus: closer terms get higher bonus\n // Max bonus of 2.0 for adjacent terms (distance = 1)\n // Linear falloff: bonus = 2.0 - (distance * 0.1)\n const bonus = Math.max(1.0, 2.0 - (minDistance * 0.1));\n totalProximity += bonus;\n pairCount++;\n }\n }\n }\n \n // Return average bonus across all term pairs\n return pairCount > 0 ? 1.0 + (totalProximity - pairCount) / pairCount : 1.0;\n}\n\n// Calculate average document length from index\nfunction getAvgDocLength(index: SearchIndex): number {\n if (index.allSections.length === 0) return 50;\n const total = index.allSections.reduce((sum, r) => sum + r.bodyText.length + r.headingText.length, 0);\n return total / index.allSections.length;\n}\n\n// Get cached shortlist if available\nfunction getCachedShortlist(query: string, index: SearchIndex): Set | null {\n const cached = index.queryCache.get(query);\n if (cached) {\n const candidates = new Set();\n for (const id of cached.resultIds) {\n const record = index.allSections.find(r => r.id === id);\n if (record) candidates.add(record);\n }\n return candidates;\n }\n return null;\n}\n\n// Cache shortlist for future queries\nfunction cacheShortlist(query: string, records: IndexRecord[], index: SearchIndex): void {\n const entry: CacheEntry = {\n resultIds: records.map(r => r.id),\n timestamp: Date.now()\n };\n index.queryCache.set(query, entry);\n\n // Limit cache size\n if (index.queryCache.size > 100) {\n const firstKey = index.queryCache.keys().next().value;\n if (firstKey !== undefined) {\n index.queryCache.delete(firstKey);\n }\n }\n}\n\n// Backward-compatible searchSections - old signature preserved\nexport function searchSections(\n query: string,\n index: SearchIndex,\n limitOrOptions?: number | SearchOptions\n): IndexRecord[] | ScoredRecord[] {\n let limit = 8;\n let options: SearchOptions | undefined;\n\n if (typeof limitOrOptions === 'number') {\n limit = limitOrOptions;\n } else if (limitOrOptions) {\n options = limitOrOptions;\n limit = limitOrOptions.limit ?? 8;\n }\n\n const q = query.trim();\n\n if (!q) {\n return index.allSections.slice(0, limit);\n }\n\n // Normalize diacritics in query for matching\n const normalizedQ = q.normalize('NFD').replace(/[\\u0300-\\u036f]/g, '');\n\n const scores = new Map();\n const matches = new Map();\n const weights = { ...DEFAULT_WEIGHTS, ...(options?.weights ?? {}) };\n const avgLength = getAvgDocLength(index);\n\n const addScore = (record: IndexRecord, score: number, key: string, start: number, end: number) => {\n scores.set(record, (scores.get(record) ?? 0) + score);\n const recordMatches = matches.get(record) ?? [];\n recordMatches.push({ key, start, end });\n matches.set(record, recordMatches);\n };\n\n const useBM25 = options?.scoringAlgorithm === 'bm25';\n const useBM25F = options?.scoringAlgorithm === 'bm25f';\n \n // Get BM25F config options or use defaults\n const bm25fConfig = options?.bm25fOptions || { k1: 1.5, b: 0.75 };\n\n // Check query cache first\n const cached = getCachedShortlist(normalizedQ.toLowerCase(), index);\n const searchPool: IndexRecord[] = cached\n ? [...cached]\n : index.allSections;\n\n // Exact matches on headingText (try both original and normalized)\n const exact = index.headingIds.get(normalizedQ.toLowerCase()) ?? index.headingIds.get(q.toLowerCase());\n if (exact) {\n for (const record of exact) {\n const fieldWeight = weights.headingText ?? 1;\n const lengthNorm = getLengthNorm(record.headingText.length);\n let score: number;\n if (useBM25) {\n const termFreq = 1;\n const docFreq = index.docFrequency.get(normalizedQ.toLowerCase()) ?? 1;\n score = bm25Score(termFreq, docFreq, index.totalDocs, record.headingText.length + record.bodyText.length, avgLength) * 100;\n } else {\n score = 100 * fieldWeight * (1 + lengthNorm);\n }\n addScore(record, score, 'headingText', 0, record.headingText.length);\n }\n }\n\n// Prefix matches on headingText (try both)\n const prefix = index.headingIndex.get(normalizedQ.toLowerCase()) ?? index.headingIndex.get(q.toLowerCase());\n if (prefix) {\n for (const record of prefix) {\n if (scores.has(record)) continue;\n const fieldWeight = weights.headingText ?? 1;\n const lengthNorm = getLengthNorm(record.headingText.length);\n let score: number;\n if (useBM25) {\n const termFreq = 1;\n const docFreq = index.docFrequency.get(normalizedQ.toLowerCase()) ?? 1;\n score = bm25Score(termFreq, docFreq, index.totalDocs, record.headingText.length + record.bodyText.length, avgLength) * 50;\n } else {\n score = 50 * fieldWeight * (1 + lengthNorm);\n }\n const positions = findMatchPositions(record.headingText, q);\n if (positions.length > 0) {\n const pos = positions[0];\n addScore(record, score, 'headingText', pos.start, pos.end);\n } else {\n addScore(record, score, 'headingText', 0, Math.min(q.length, record.headingText.length));\n }\n }\n }\n\n // Word matches in bodyText (try both)\n const words = normalizedQ.toLowerCase().split(/\\s+/);\n for (const word of words) {\n const bodyMatches = index.bodyIndex.get(word);\n if (bodyMatches) {\n for (const record of bodyMatches) {\n if (scores.has(record)) continue;\n const fieldWeight = weights.bodyText ?? 1;\n const lengthNorm = getLengthNorm(record.bodyText.length);\n let score: number;\n if (useBM25) {\n const termFreq = 1;\n const docFreq = index.docFrequency.get(word) ?? 1;\n score = bm25Score(termFreq, docFreq, index.totalDocs, record.headingText.length + record.bodyText.length, avgLength) * 20;\n } else {\n score = 20 * fieldWeight * (1 + lengthNorm);\n }\n const positions = findMatchPositions(record.bodyText, q);\n if (positions.length > 0) {\n const pos = positions[0];\n addScore(record, score, 'bodyText', pos.start, pos.end);\n} else {\n addScore(record, score, 'bodyText', 0, Math.min(word.length, record.bodyText.length));\n }\n }\n }\n }\n\n // BM25F scoring - score all records using field-weighted BM25\n if (useBM25F && scores.size === 0) {\n const queryTerms = normalizedQ.toLowerCase().split(/\\s+/);\n for (const record of index.allSections) {\n const score = bm25fScore(record, queryTerms, index, weights, bm25fConfig.k1, bm25fConfig.b);\n if (score > 0) {\n addScore(record, score * 100, 'bm25f', 0, Math.min(normalizedQ.length, record.headingText.length + record.bodyText.length));\n }\n }\n }\n\n // Apply proximity bonus for multi-term queries\n if (scores.size > 0) {\n const queryTerms = normalizedQ.toLowerCase().split(/\\s+/);\n if (queryTerms.length > 1) {\n for (const [record, score] of scores.entries()) {\n const headingTokens = tokenizeForPhraseMatching(record.headingText);\n const bodyTokens = tokenizeForPhraseMatching(record.bodyText);\n \n // Check if all query terms appear in either heading or body\n const allTermsInHeading = queryTerms.every(term => headingTokens.includes(term));\n const allTermsInBody = queryTerms.every(term => bodyTokens.includes(term));\n \n if (allTermsInHeading || allTermsInBody) {\n // Calculate proximity bonus\n const combinedTokens = [...headingTokens, ...bodyTokens];\n const proximityBonus = calculateProximityBonus(combinedTokens, queryTerms);\n \n // Apply bonus to existing score\n scores.set(record, score * proximityBonus);\n }\n \n // Check for exact phrase match and give additional bonus\n if (hasPhraseMatch(headingTokens, queryTerms) || hasPhraseMatch(bodyTokens, queryTerms)) {\n scores.set(record, score * 1.5); // 50% bonus for exact phrase match\n }\n }\n }\n }\n\n // Apply popularity boosts if enabled\n if (options?.trackPopularity) {\n const boostFactor = options.popularityBoost || 1.2; // Default 20% boost\n for (const [record, score] of scores.entries()) {\n const key = `${normalizedQ.toLowerCase()}||${record.id}`;\n const popularity = index.queryPopularity.get(key);\n if (popularity) {\n // Apply multiplicative boost based on popularity count\n const popularityBoost = 1 + ((popularity.count - 1) * 0.1); // 10% per additional click, max at 2x\n scores.set(record, score * popularityBoost * boostFactor);\n }\n }\n }\n\n // Staged fuzzy search - exact \u2192 1-typo \u2192 2-typo, short-circuit on first hit\n if (options?.fuzzy) {\n const fuzzyDistance = options.fuzzyDistance ?? 2;\n\n // Stage 1: Exact matches (already done above)\n // Stage 2: 1-typo matches on indexed candidates\n if (scores.size === 0) {\n const candidates = getFuzzyCandidates(q.toLowerCase(), index, 1);\n for (const record of candidates) {\n if (scores.has(record)) continue;\n\n const headingLower = record.headingText.toLowerCase();\n const headingMatch = findMatchPositions(headingLower, q);\n if (headingMatch.length === 0) {\n const dist = levenshteinDistance(headingLower, q);\n if (dist <= 1) {\n const normalizedScore = (1 - dist / Math.max(q.length, headingLower.length)) * 60;\n const fieldWeight = weights.headingText ?? 1;\n const lengthNorm = getLengthNorm(record.headingText.length);\n addScore(record, normalizedScore * fieldWeight * (1 + lengthNorm), 'headingText', 0, Math.min(q.length, headingLower.length));\n }\n }\n\n if (scores.has(record)) continue;\n\n const bodyLower = record.bodyText.toLowerCase();\n let bestDist = Infinity;\n let bestStart = 0;\n for (let i = 0; i <= bodyLower.length - q.length; i++) {\n const substr = bodyLower.slice(i, i + q.length);\n const dist = levenshteinDistance(q, substr);\n if (dist < bestDist) {\n bestDist = dist;\n bestStart = i;\n }\n }\n if (bestDist <= 1) {\n const normalizedScore = (1 - bestDist / Math.max(q.length, bodyLower.length)) * 40;\n const fieldWeight = weights.bodyText ?? 1;\n const lengthNorm = getLengthNorm(record.bodyText.length);\n addScore(record, normalizedScore * fieldWeight * (1 + lengthNorm), 'bodyText', bestStart, bestStart + q.length);\n }\n }\n }\n\n // Stage 3: 2-typo matches\n if (scores.size === 0) {\n const candidates = getFuzzyCandidates(q.toLowerCase(), index, 2);\n for (const record of candidates) {\n if (scores.has(record)) continue;\n\n const headingLower = record.headingText.toLowerCase();\n const dist = levenshteinDistance(headingLower, q);\n if (dist <= 2) {\n const normalizedScore = (1 - dist / Math.max(q.length, headingLower.length)) * 30;\n const fieldWeight = weights.headingText ?? 1;\n const lengthNorm = getLengthNorm(record.headingText.length);\n addScore(record, normalizedScore * fieldWeight * (1 + lengthNorm), 'headingText', 0, Math.min(q.length, headingLower.length));\n }\n\n if (scores.has(record)) continue;\n\n const bodyLower = record.bodyText.toLowerCase();\n let bestDist = Infinity;\n let bestStart = 0;\n for (let i = 0; i <= bodyLower.length - q.length; i++) {\n const substr = bodyLower.slice(i, i + q.length);\n const d = levenshteinDistance(q, substr);\n if (d < bestDist) {\n bestDist = d;\n bestStart = i;\n }\n }\n if (bestDist <= 2) {\n const normalizedScore = (1 - bestDist / Math.max(q.length, bodyLower.length)) * 20;\n const fieldWeight = weights.bodyText ?? 1;\n const lengthNorm = getLengthNorm(record.bodyText.length);\n addScore(record, normalizedScore * fieldWeight * (1 + lengthNorm), 'bodyText', bestStart, bestStart + q.length);\n }\n }\n }\n }\n\n // Extended query syntax\n if ((options?.extended || /\\b(?:title|heading|body|label|breadcrumb|type):\\S+/i.test(q)) && scores.size === 0) {\n const parsed = parseExtendedQuery(q);\n for (const record of index.allSections) {\n if (matchExtendedNode(parsed, record)) {\n const fieldWeight = weights.headingText ?? 1;\n const lengthNorm = getLengthNorm(record.headingText.length);\n const score = 50 * fieldWeight * (1 + lengthNorm);\n addScore(record, score, 'headingText', 0, Math.min(q.length, record.headingText.length));\n }\n }\n }\n\n // Filtering\n let filteredScores = [...scores.entries()];\n if (options?.filter) {\n filteredScores = filteredScores.filter(([record]) => options!.filter!(record));\n }\n\n // Sort results\n let sortedEntries = filteredScores.sort((a, b) => b[1] - a[1]);\n if (options?.sortFn) {\n sortedEntries = sortedEntries.sort((a, b) => {\n const aMatches = matches.get(a[0]) ?? [];\n const bMatches = matches.get(b[0]) ?? [];\n return options!.sortFn!({ record: a[0], score: a[1], matches: aMatches }, { record: b[0], score: b[1], matches: bMatches });\n });\n }\n\n // Apply MMR (Maximal Marginal Relevance) for result diversity if enabled\n if (options?.diversify) {\n const lambda = options.mmrLambda ?? 0.5; // Default 50% diversity\n sortedEntries = applyMMR(sortedEntries, lambda);\n }\n\n const topRecords = sortedEntries.slice(0, limit).map(([record]) => record);\n\n // Apply type-based ranking boost\n if (options?.typeWeights) {\n const typeBoost = options.typeWeights;\n for (const record of topRecords) {\n const boost = typeBoost[record.type] ?? 1;\n const currentScore = scores.get(record);\n if (currentScore !== undefined && boost !== 1) {\n scores.set(record, currentScore * boost);\n }\n }\n sortedEntries = [...scores.entries()].sort((a, b) => b[1] - a[1]);\n const boostedRecords = sortedEntries.slice(0, limit).map(([record]) => record);\n \n // Cache the boosted shortlist\n cacheShortlist(q.toLowerCase(), boostedRecords, index);\n \n if (options?.includeScore || options?.includeMatches) {\n return boostedRecords.map(record => {\n const recordMatches = matches.get(record) ?? [];\n const normalizedScore = scores.get(record) ?? 0;\n const maxScore = useBM25 ? 100 : 200;\n const score = Math.max(0, Math.min(1, 1 - normalizedScore / maxScore));\n return {\n record,\n score,\n matches: options.includeMatches ? recordMatches : undefined,\n };\n });\n }\n return boostedRecords;\n }\n\n // Cache the shortlist\n cacheShortlist(q.toLowerCase(), topRecords, index);\n\n // Return format based on options\n if (options?.includeScore || options?.includeMatches) {\n return topRecords.map(record => {\n const recordMatches = matches.get(record) ?? [];\n const normalizedScore = scores.get(record) ?? 0;\n const maxScore = useBM25 ? 100 : 200;\n const score = Math.max(0, Math.min(1, 1 - normalizedScore / maxScore));\n return {\n record,\n score,\n matches: options.includeMatches ? recordMatches : undefined,\n };\n });\n }\n\n return topRecords;\n}\n\n// remove a record from the index by its id\nexport function removeFromIndex(index: SearchIndex, id: string): void {\n const recordIndex = index.allSections.findIndex(r => r.id === id);\n if (recordIndex === -1) return;\n\n const record = index.allSections[recordIndex];\n index.allSections.splice(recordIndex, 1);\n index.totalDocs = Math.max(0, index.totalDocs - 1);\n\n // Invalidate only queries affected by this mutation\n invalidateAffectedQueries(index, record.id, record.headingText, record.bodyText);\n\n const headingLower = record.headingText.toLowerCase();\n const headingIds = index.headingIds.get(headingLower);\n if (headingIds) {\n const idx = headingIds.findIndex(r => r.id === id);\n if (idx !== -1) headingIds.splice(idx, 1);\n if (headingIds.length === 0) index.headingIds.delete(headingLower);\n }\n\n for (let i = 2; i <= headingLower.length; i++) {\n const prefix = headingLower.slice(0, i);\n const prefixRecords = index.headingIndex.get(prefix);\n if (prefixRecords) {\n const idx = prefixRecords.findIndex(r => r.id === id);\n if (idx !== -1) prefixRecords.splice(idx, 1);\n if (prefixRecords.length === 0) index.headingIndex.delete(prefix);\n }\n }\n\n // Remove from trie\n trieDelete(index.headingTrie, headingLower, id);\n const normalizedHeading = record.headingText.toLowerCase().normalize('NFD').replace(/[\\u0300-\\u036f]/g, '');\n if (normalizedHeading !== headingLower) {\n trieDelete(index.headingTrie, normalizedHeading, id);\n }\n\n const bodyWords = record.bodyText.toLowerCase().split(/\\s+/);\n for (const word of bodyWords) {\n if (word.length < 3) continue;\n const bodyRecords = index.bodyIndex.get(word);\n if (bodyRecords) {\n const idx = bodyRecords.findIndex(r => r.id === id);\n if (idx !== -1) bodyRecords.splice(idx, 1);\n if (bodyRecords.length === 0) index.bodyIndex.delete(word);\n }\n }\n}\n\n// Update an existing record in the index\nexport function updateRecord(index: SearchIndex, record: IndexRecord): void {\n // First, remove the old record and invalidate affected queries\n removeFromIndex(index, record.id);\n \n // Then add the updated record\n addToIndex(index, [record]);\n \n // Since we've replaced the record, we need to invalidate queries that might now match\n // the updated content (the remove already handled invalidation, but add might introduce new matches)\n // For simplicity, we'll invalidate queries that match the new record's tokens\n invalidateAffectedQueries(index, record.id, record.headingText, record.bodyText);\n}\n\n// Serialize index to JSON string\n// Note: trie is not serialized as it can be rebuilt from headingIndex on load\nexport function serializeIndex(index: SearchIndex): string {\n const serializable = {\n allSections: index.allSections,\n headingIndex: Array.from(index.headingIndex.entries()),\n headingIds: Array.from(index.headingIds.entries()),\n bodyIndex: Array.from(index.bodyIndex.entries()),\n docFrequency: Array.from(index.docFrequency.entries()),\n fieldDocFreq: {\n headingText: Array.from(index.fieldDocFreq.headingText.entries()),\n bodyText: Array.from(index.fieldDocFreq.bodyText.entries()),\n label: Array.from(index.fieldDocFreq.label.entries()),\n breadcrumb: Array.from(index.fieldDocFreq.breadcrumb.entries()),\n },\n totalDocs: index.totalDocs,\n popularQueries: index.popularQueries,\n queryPopularity: Array.from(index.queryPopularity.entries()),\n version: index.version,\n };\n return JSON.stringify(serializable);\n}\n\n// Deserialize index from JSON string with migration support\nexport function deserializeIndex(json: string): SearchIndex {\n const parsed = JSON.parse(json);\n \n // Migrate old index format to new format with fieldDocFreq\n const fieldDocFreq: FieldDocFreq = {\n headingText: new Map(),\n bodyText: new Map(),\n label: new Map(),\n breadcrumb: new Map(),\n };\n \n // Load query popularity data if present\n const queryPopularity = new Map();\n if (parsed.queryPopularity && Array.isArray(parsed.queryPopularity)) {\n for (const [key, value] of parsed.queryPopularity) {\n if (typeof value === 'object' && value !== null) {\n queryPopularity.set(key, value as PopularityEntry);\n }\n }\n }\n \n // If old format (no fieldDocFreq), we need to rebuild it from docFrequency\n // This is a one-time migration that will happen on first load\n if (!parsed.fieldDocFreq) {\n // We can't perfectly reconstruct fieldDocFreq from the old format,\n // but we can approximate by using docFrequency for all fields\n if (parsed.docFrequency) {\n for (const [term, freq] of parsed.docFrequency) {\n // Distribute the frequency across fields (this is an approximation)\n fieldDocFreq.bodyText.set(term, freq);\n fieldDocFreq.headingText.set(term, Math.floor(freq * 0.3));\n fieldDocFreq.label.set(term, Math.floor(freq * 0.1));\n fieldDocFreq.breadcrumb.set(term, Math.floor(freq * 0.1));\n }\n }\n } else {\n // New format - load fieldDocFreq from parsed data\n fieldDocFreq.headingText = new Map(parsed.fieldDocFreq.headingText ?? []);\n fieldDocFreq.bodyText = new Map(parsed.fieldDocFreq.bodyText ?? []);\n fieldDocFreq.label = new Map(parsed.fieldDocFreq.label ?? []);\n fieldDocFreq.breadcrumb = new Map(parsed.fieldDocFreq.breadcrumb ?? []);\n }\n \n // Rebuild trie from headingIndex data for backward compatibility\n const headingTrie = createTrieNode();\n \n // If we have headingIndex data, rebuild the trie\n if (parsed.headingIndex && Array.isArray(parsed.headingIndex)) {\n const headingIndexMap = new Map(parsed.headingIndex as [string, IndexRecord[]][]);\n const seenRecords = new Set();\n for (const [prefix, records] of headingIndexMap.entries()) {\n if (prefix.length >= 2 && Array.isArray(records)) { // Only prefixes of length 2+ as per original implementation\n for (const record of records) {\n if (seenRecords.has(record.id)) continue;\n seenRecords.add(record.id);\n // Insert each record's full heading into the trie\n const headingLower = record.headingText.toLowerCase();\n trieInsert(headingTrie, headingLower, record);\n \n // Also insert normalized version\n const normalizedHeading = headingLower.normalize('NFD').replace(/[\\u0300-\\u036f]/g, '');\n if (normalizedHeading !== headingLower) {\n trieInsert(headingTrie, normalizedHeading, record);\n }\n }\n }\n }\n } else {\n // If no headingIndex, rebuild from allSections\n const allSections = parsed.allSections ?? [];\n for (const record of allSections) {\n const headingLower = record.headingText.toLowerCase();\n trieInsert(headingTrie, headingLower, record);\n \n const normalizedHeading = headingLower.normalize('NFD').replace(/[\\u0300-\\u036f]/g, '');\n if (normalizedHeading !== headingLower) {\n trieInsert(headingTrie, normalizedHeading, record);\n }\n }\n }\n \n return {\n allSections: parsed.allSections ?? [],\n headingIndex: new Map(parsed.headingIndex ?? []),\n headingIds: new Map(parsed.headingIds ?? []),\n bodyIndex: new Map(parsed.bodyIndex ?? []),\n queryCache: new Map(),\n popularQueries: parsed.popularQueries ?? [],\n docFrequency: new Map(parsed.docFrequency ?? []),\n fieldDocFreq,\n totalDocs: parsed.totalDocs ?? 0,\n queryPopularity,\n version: parsed.version ?? 1,\n headingTrie,\n };\n}\n\n// Suggest function for autocomplete - returns unique headings matching query prefix\nexport function suggest(query: string, index: SearchIndex, limit: number = 10): string[] {\n const q = query.trim().toLowerCase();\n if (!q || q.length < 2) return [];\n\n const suggestions: string[] = [];\n const seen = new Set();\n\n // Get candidates from trie (new efficient method)\n const trieCandidates = triePrefixQuery(index.headingTrie, q);\n for (const record of trieCandidates) {\n if (suggestions.length >= limit) break;\n const heading = record.headingText;\n if (!seen.has(heading)) {\n seen.add(heading);\n suggestions.push(heading);\n }\n }\n\n // Fallback to old method for backward compatibility if trie doesn't have results\n if (suggestions.length < limit) {\n const candidates = index.headingIndex.get(q) ?? [];\n const prefixCandidates = index.headingIds.get(q) ?? [];\n\n const allCandidates = [...candidates, ...prefixCandidates];\n for (const record of allCandidates) {\n if (suggestions.length >= limit) break;\n const heading = record.headingText;\n if (!seen.has(heading)) {\n seen.add(heading);\n suggestions.push(heading);\n }\n }\n }\n\n // If not enough from prefix, try fuzzy match on indexed keys\n if (suggestions.length < limit) {\n for (const [key, records] of index.headingIds) {\n if (suggestions.length >= limit) break;\n if (key.includes(q) && !seen.has(records[0].headingText)) {\n seen.add(records[0].headingText);\n suggestions.push(records[0].headingText);\n }\n }\n }\n\n return suggestions.slice(0, limit);\n}\n\n// Facets function - returns counts per record type\nexport function facets(index: SearchIndex): Record {\n const result: Record = {\n section: 0,\n action: 0,\n field: 0,\n link: 0,\n file: 0,\n media: 0,\n structured: 0,\n };\n\n for (const record of index.allSections) {\n result[record.type] = (result[record.type] ?? 0) + 1;\n }\n\n return result;\n}\n\n// Track query for popularity analytics\nexport function trackQuery(index: SearchIndex, query: string, selectedId?: string): void {\n const q = query.trim();\n if (!q) return;\n\n // Add to ring buffer (keep last 100)\n if (index.popularQueries.length >= 100) {\n index.popularQueries.shift();\n }\n index.popularQueries.push(q);\n\n // If a selected record ID is provided and popularity tracking is enabled,\n // store the (query, recordId) pair for future ranking boosts\n if (selectedId) {\n const key = `${q}||${selectedId}`; // Use double pipe as separator\n const existing = index.queryPopularity.get(key);\n if (existing) {\n existing.count++;\n } else {\n index.queryPopularity.set(key, {\n query: q,\n recordId: selectedId,\n count: 1,\n });\n }\n }\n}\n\n// Get popular queries - returns most frequently searched terms\nexport function getPopularQueries(index: SearchIndex, n: number = 5): string[] {\n const counts = new Map();\n for (const q of index.popularQueries) {\n counts.set(q, (counts.get(q) ?? 0) + 1);\n }\n\n return [...counts.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, n)\n .map(([q]) => q);\n}\n\nexport type { IndexRecord, TokenFilter };\n", "/**\n * @file IndexedDB persistent cache for search index.\n * Stores serialized index with metadata for TTL-based expiration.\n */\n\nimport { SearchIndex, serializeIndex, deserializeIndex } from './search-index.js';\nimport { CacheMetadata, SiteGraph } from './types.js';\n\n// Feature detection for compression streams\ndeclare global {\n interface Window {\n CompressionStream?: typeof CompressionStream;\n DecompressionStream?: typeof DecompressionStream;\n }\n}\n\nconst DB_NAME = 'reef-index';\nconst STORE_NAME = 'indices';\nconst CACHE_VERSION_KEY = 'version';\n\nexport async function openDB(): Promise {\n return new Promise((resolve, reject) => {\n const req = indexedDB.open(DB_NAME, 1);\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n req.onupgradeneeded = () => {\n const db = req.result;\n if (!db.objectStoreNames.contains(STORE_NAME)) {\n db.createObjectStore(STORE_NAME, { keyPath: CACHE_VERSION_KEY });\n }\n };\n });\n}\n\n// Compress data using gzip if available\nasync function compressData(data: string): Promise<{ compressed: boolean; data: ArrayBuffer | string }> {\n if (typeof window === 'undefined' || !window.CompressionStream) {\n return { compressed: false, data };\n }\n \n try {\n const blob = new Blob([data]).stream();\n const compressedStream = blob.pipeThrough(new CompressionStream('gzip'));\n const chunks: Uint8Array[] = [];\n const reader = compressedStream.getReader();\n \n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(new Uint8Array(value));\n }\n \n // Concatenate chunks into a single ArrayBuffer\n const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n for (const chunk of chunks) {\n result.set(chunk, offset);\n offset += chunk.length;\n }\n \n return { compressed: true, data: result.buffer };\n } catch {\n return { compressed: false, data };\n }\n}\n\n// Decompress data using gzip if available\nasync function decompressData(data: ArrayBuffer | string): Promise {\n if (typeof data === 'string') {\n return data; // Not compressed\n }\n \n if (typeof window === 'undefined' || !window.DecompressionStream) {\n throw new Error('Compressed data but no decompression available');\n }\n \n try {\n const blob = new Blob([data]).stream();\n const decompressedStream = blob.pipeThrough(new DecompressionStream('gzip'));\n const chunks: string[] = [];\n const reader = decompressedStream.getReader();\n const decoder = new TextDecoder();\n \n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(decoder.decode(value, { stream: true }));\n }\n \n return chunks.join('') + decoder.decode();\n } catch {\n throw new Error('Failed to decompress data');\n }\n}\n\n// Serialize index to JSON string\nexport async function saveIndex(index: SearchIndex, metadata: CacheMetadata): Promise {\n const db = await openDB();\n const tx = db.transaction(STORE_NAME, 'readwrite');\n const store = tx.objectStore(STORE_NAME);\n \n const serializedIndex = serializeIndex(index);\n const { compressed, data } = await compressData(serializedIndex);\n \n const dataToStore = {\n [CACHE_VERSION_KEY]: metadata.versionHash,\n index: data,\n metadata,\n compressed\n };\n \n await new Promise((resolve, reject) => {\n const req = store.put(dataToStore);\n req.onsuccess = () => resolve(undefined);\n req.onerror = () => reject(req.error);\n });\n}\n\nexport async function loadIndex(ttl?: number): Promise<{ index: SearchIndex; metadata: CacheMetadata } | null> {\n const db = await openDB();\n const tx = db.transaction(STORE_NAME, 'readonly');\n const store = tx.objectStore(STORE_NAME);\n \n const allRecords = await new Promise((resolve, reject) => {\n const req = store.getAll();\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n });\n\n if (!allRecords.length) return null;\n \n const record = allRecords[0] as { [key: string]: any };\n let serializedIndex = record.index;\n \n // Decompress if needed\n if (record.compressed) {\n try {\n serializedIndex = await decompressData(record.index);\n } catch (e) {\n console.warn('[reef] decompression failed, trying uncompressed fallback:', e);\n // If decompression fails, try as string (fallback for old caches)\n if (typeof record.index === 'string') {\n serializedIndex = record.index;\n } else {\n return null; // Can't read this cache\n }\n }\n } else if (typeof record.index === 'string') {\n serializedIndex = record.index;\n } else {\n // Can't handle this format\n return null;\n }\n \n const cached = deserializeIndex(serializedIndex);\n const metadata: CacheMetadata = record.metadata;\n\n if (ttl && metadata.buildTime) {\n const age = Date.now() - metadata.buildTime;\n if (age > ttl) {\n return null;\n }\n }\n\n return { index: cached, metadata };\n}\n\nexport async function clearCache(): Promise {\n const db = await openDB();\n const tx = db.transaction(STORE_NAME, 'readwrite');\n const store = tx.objectStore(STORE_NAME);\n await new Promise((resolve, reject) => {\n const req = store.clear();\n req.onsuccess = () => resolve(undefined);\n req.onerror = () => reject(req.error);\n });\n}\n\nexport async function saveSiteGraph(graph: SiteGraph, key = 'site-graph'): Promise {\n const db = await openDB();\n const tx = db.transaction(STORE_NAME, 'readwrite');\n tx.objectStore(STORE_NAME).put({ [CACHE_VERSION_KEY]: `${key}:${graph.startUrl}`, graph, metadata: { versionHash: key, buildTime: Date.now(), pageMetadata: {} } });\n}\n\nexport async function loadSiteGraph(startUrl: string, key = 'site-graph'): Promise {\n const db = await openDB();\n const tx = db.transaction(STORE_NAME, 'readonly');\n return new Promise(resolve => { const req = tx.objectStore(STORE_NAME).get(`${key}:${startUrl}`); req.onsuccess = () => resolve(req.result?.graph ?? null); req.onerror = () => resolve(null); });\n}\n", "/**\n * @file Background Service Worker for Reef for Browsers.\n * Manages search index state per tab, storage persistence, message routing,\n * omnibox keyword handler, optional cross-tab site crawl, and the Library\n * (bookmarks / snippets / notes / recents) plus tab search.\n */\n\nimport { createSearchIndex, addToIndex, searchSections, searchWithPagination, suggest, findClosestWord } from '../../src/search-index.js';\nimport type { SearchIndex } from '../../src/search-index.js';\nimport type { IndexRecord, SearchOptions } from '../../src/types.js';\nimport type { AgentManifest } from '../../src/agent-ready.js';\nimport { Indexer } from '../../src/indexing/indexer.js';\nimport {\n listBookmarks, createBookmark, updateBookmark, deleteBookmark,\n listSnippets, createSnippet, updateSnippet, deleteSnippet,\n getPageNote, setPageNote, deletePageNote, listPageNotes,\n listRecents, recordRecent, clearRecents,\n allBookmarkTags, allSnippetTags,\n type Bookmark, type Snippet, type PageNote,\n} from './storage.js';\n\ninterface TabIndexState {\n index: SearchIndex;\n manifest?: AgentManifest;\n lastUpdated: number;\n}\n\nconst tabIndices = new Map();\nconst siteIndices = new Map();\n\n// \u2500\u2500\u2500 OPTIONS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nasync function getOptions() {\n if (typeof chrome === 'undefined' || !chrome.storage?.local) {\n return {\n actionsMode: 'execute',\n allowDenyList: [],\n exclusionSelectors: [],\n telemetryEnabled: false,\n enableCrossTabCrawl: false,\n };\n }\n const data = await chrome.storage.local.get([\n 'actionsMode', 'allowDenyList', 'exclusionSelectors',\n 'telemetryEnabled', 'enableCrossTabCrawl',\n ]);\n return {\n actionsMode: data.actionsMode || 'execute',\n allowDenyList: data.allowDenyList || [],\n exclusionSelectors: data.exclusionSelectors || [],\n telemetryEnabled: data.telemetryEnabled || false,\n enableCrossTabCrawl: data.enableCrossTabCrawl || false,\n };\n}\n\n// \u2500\u2500\u2500 TAB INDEX \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nasync function getOrFetchTabIndex(tabId: number, forceRefresh = false): Promise {\n if (!forceRefresh && tabIndices.has(tabId)) {\n return tabIndices.get(tabId)!;\n }\n\n try {\n const response: any = await chrome.tabs.sendMessage(tabId, { type: 'GET_MANIFEST' });\n if (response && response.success && response.manifest) {\n const index = createSearchIndex();\n addToIndex(index, response.manifest.records);\n\n const state: TabIndexState = {\n index,\n manifest: response.manifest,\n lastUpdated: Date.now(),\n };\n tabIndices.set(tabId, state);\n\n const options = await getOptions();\n if (options.enableCrossTabCrawl && response.manifest.url) {\n try {\n const urlObj = new URL(response.manifest.url);\n const origin = urlObj.origin;\n let siteIndex = siteIndices.get(origin);\n if (!siteIndex) {\n siteIndex = createSearchIndex();\n siteIndices.set(origin, siteIndex);\n }\n addToIndex(siteIndex, response.manifest.records);\n } catch {\n // Invalid URL\n }\n }\n\n // Record recent page\n try {\n const tab = await chrome.tabs.get(tabId);\n if (tab.url && tab.title) {\n await recordRecent({\n url: tab.url,\n title: tab.title,\n favicon: tab.favIconUrl,\n recordCount: response.manifest.records.length,\n });\n }\n } catch {\n // Tab may have closed\n }\n\n return state;\n }\n } catch (err) {\n console.warn(`[Reef Background] Failed to fetch manifest from tab ${tabId}:`, err);\n }\n\n return null;\n}\n\n// \u2500\u2500\u2500 CONTEXT MENU \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction ensureContextMenus() {\n if (typeof chrome === 'undefined' || !chrome.contextMenus) return;\n const api = (chrome as any).contextMenus;\n if (!api) return;\n\n const create = (id: string, title: string, contexts: chrome.contextMenus.ContextType[]) => {\n try {\n api.create({ id, title, contexts }, () => {\n // Ignore \"already exists\" errors when service worker wakes up\n void chrome.runtime.lastError;\n });\n } catch {\n // ignore\n }\n };\n\n create('reef-bookmark-selection', 'Bookmark selection in Reef', ['selection']);\n create('reef-snippet-selection', 'Save selection as snippet', ['selection']);\n create('reef-search-selection', 'Search \"%s\" in Reef', ['selection']);\n create('reef-note-page', 'Add note to this page', ['page', 'selection']);\n create('reef-bookmark-page', 'Bookmark this page in Reef', ['page']);\n}\n\nif (typeof chrome !== 'undefined') {\n if (chrome.runtime?.onInstalled) {\n chrome.runtime.onInstalled.addListener(() => ensureContextMenus());\n }\n if (chrome.runtime?.onStartup) {\n chrome.runtime.onStartup.addListener(() => ensureContextMenus());\n }\n // MV3 service workers may wake up without onInstalled firing\n ensureContextMenus();\n}\n\nif (typeof chrome !== 'undefined' && (chrome as any).contextMenus?.onClicked) {\n (chrome as any).contextMenus.onClicked.addListener(async (info: any, tab?: chrome.tabs.Tab) => {\n try {\n if (info.menuItemId === 'reef-bookmark-selection' && info.selectionText && tab?.id !== undefined) {\n await chrome.tabs.sendMessage(tab.id, { type: 'REEF_BOOKMARK_SELECTION', text: info.selectionText });\n return;\n }\n if (info.menuItemId === 'reef-snippet-selection' && info.selectionText && tab?.id !== undefined) {\n await chrome.tabs.sendMessage(tab.id, { type: 'REEF_SNIPPET_SELECTION', text: info.selectionText });\n return;\n }\n if (info.menuItemId === 'reef-search-selection' && info.selectionText && tab?.id !== undefined) {\n await chrome.tabs.sendMessage(tab.id, { type: 'REEF_OPEN_POPUP_QUERY', query: info.selectionText });\n chrome.action?.openPopup?.();\n return;\n }\n if (info.menuItemId === 'reef-note-page' && tab?.id !== undefined) {\n await chrome.tabs.sendMessage(tab.id, { type: 'REEF_OPEN_NOTE_FOR_PAGE' });\n chrome.action?.openPopup?.();\n return;\n }\n if (info.menuItemId === 'reef-bookmark-page' && tab?.id !== undefined) {\n await chrome.tabs.sendMessage(tab.id, { type: 'REEF_BOOKMARK_PAGE' });\n return;\n }\n } catch (err) {\n console.warn('[Reef Background] Context menu action failed:', err);\n }\n });\n}\n\n// \u2500\u2500\u2500 OMNIBOX \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nif (typeof chrome !== 'undefined' && chrome.omnibox) {\n chrome.omnibox.onInputChanged.addListener(async (text, suggestCallback) => {\n const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });\n if (!activeTab?.id) return;\n\n const state = await getOrFetchTabIndex(activeTab.id);\n if (!state) return;\n\n const results = searchSections(text, state.index, { limit: 5 }) as IndexRecord[];\n const suggestions = results.map(r => ({\n content: r.url || r.headingText,\n description: `${escapeXml(r.headingText)} - ${escapeXml(r.bodyText?.slice(0, 60) || '')}`,\n }));\n suggestCallback(suggestions);\n });\n\n chrome.omnibox.onInputEntered.addListener(async (text, disposition) => {\n const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });\n if (!activeTab?.id) return;\n\n const state = await getOrFetchTabIndex(activeTab.id);\n if (!state) return;\n\n const results = searchSections(text, state.index, { limit: 1 }) as IndexRecord[];\n if (results.length > 0) {\n const record = results[0];\n await chrome.tabs.sendMessage(activeTab.id, {\n type: 'EXECUTE_ACTION',\n record,\n actionType: record.type === 'field' ? 'type' : 'click',\n });\n }\n });\n}\n\nfunction escapeXml(str: string): string {\n return str.replace(/[<>&'\"]/g, c => {\n switch (c) {\n case '<': return '<';\n case '>': return '>';\n case '&': return '&';\n case '\\'': return ''';\n case '\"': return '"';\n default: return c;\n }\n });\n}\n\nfunction escapeRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n// Title scoring for Spotlight-style ranking. Higher is more relevant.\nfunction scoreTitle(title: string, q: string): number {\n if (!q) return 0;\n const t = title.toLowerCase();\n if (t === q) return 60; // exact match\n if (t.startsWith(q)) return 35; // title starts with query\n const idx = t.indexOf(q);\n if (idx >= 0) return 15 + Math.max(0, 10 - Math.floor(idx / 8)); // contains, earlier = better\n if (new RegExp(`\\\\b${escapeRegex(q)}`, 'i').test(title)) return 12; // word boundary\n return 0;\n}\n\n// \u2500\u2500\u2500 SITE CONTENT SEARCH \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction searchSiteContent(query: string, limit = 10): Array<{\n url: string;\n headingText: string;\n bodyText: string;\n selector?: string;\n type: string;\n score: number;\n sourceOrigin: string;\n}> {\n const results: Array<{\n url: string;\n headingText: string;\n bodyText: string;\n selector?: string;\n type: string;\n score: number;\n sourceOrigin: string;\n }> = [];\n\n for (const [origin, index] of siteIndices) {\n try {\n const hits = searchSections(query, index, { limit: 3, fuzzy: true }) as IndexRecord[];\n for (const hit of hits) {\n results.push({\n url: hit.url || origin,\n headingText: hit.headingText,\n bodyText: (hit.bodyText || '').slice(0, 120),\n selector: hit.selector,\n type: hit.type,\n score: 5,\n sourceOrigin: origin,\n });\n }\n } catch {\n // ignore\n }\n }\n\n results.sort((a, b) => b.score - a.score);\n return results.slice(0, limit);\n}\n\n// Check if a string looks like a URL the user wants to navigate to\nfunction looksLikeUrl(q: string): boolean {\n return /^(https?:\\/\\/|www\\.)/i.test(q) ||\n (/^[\\w-]+(\\.[\\w-]+)+(\\/\\S*)?$/i.test(q) && q.includes('.'));\n}\n\n// \u2500\u2500\u2500 TAB SEARCH \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nasync function searchOpenTabs(query: string, limit = 25): Promise<{\n items: any[];\n suggestion?: string;\n autocorrected?: boolean;\n siteResults?: any[];\n actions?: any[];\n}> {\n if (!query.trim() || typeof chrome === 'undefined' || !chrome.tabs) return { items: [] };\n const q = query.toLowerCase();\n const tabs = await chrome.tabs.query({});\n\n let currentWindowId: number | undefined;\n try {\n const [cw] = await chrome.windows.getCurrent();\n currentWindowId = cw?.id;\n } catch {\n /* ignore */\n }\n\n // Proactively fetch manifests from tabs that aren't indexed yet\n const fetchPromises: Promise[] = [];\n for (const tab of tabs) {\n if (!tab.id || !tab.url || !tab.title) continue;\n if (tab.url.startsWith('chrome://') || tab.url.startsWith('about:') || tab.url.startsWith('moz-extension:')) continue;\n if (!tabIndices.has(tab.id)) {\n fetchPromises.push(\n getOrFetchTabIndex(tab.id).then(() => { /* indexed or failed */ })\n );\n }\n }\n if (fetchPromises.length > 0) {\n await Promise.race([\n Promise.allSettled(fetchPromises),\n new Promise(resolve => setTimeout(resolve, 3000)),\n ]);\n }\n\n const matches: { tab: chrome.tabs.Tab; score: number; matchedRecords: IndexRecord[] }[] = [];\n\n for (const tab of tabs) {\n if (!tab.id || !tab.url || !tab.title) continue;\n if (tab.url.startsWith('chrome://') || tab.url.startsWith('about:') || tab.url.startsWith('moz-extension:')) continue;\n\n let score = 0;\n const matchedRecords: IndexRecord[] = [];\n const title = tab.title;\n const url = tab.url;\n\n score += scoreTitle(title, q);\n if (url.toLowerCase().includes(q)) score += 5;\n if (typeof currentWindowId === 'number' && tab.windowId === currentWindowId) score += 3;\n\n const state = tabIndices.get(tab.id);\n if (state) {\n try {\n const hits = searchSections(query, state.index, { limit: 3, fuzzy: true }) as IndexRecord[];\n if (hits.length) {\n score += 8;\n matchedRecords.push(...hits);\n }\n } catch {\n // ignore\n }\n } else if (score === 0) {\n continue;\n }\n\n if (score > 0) {\n matches.push({ tab, score, matchedRecords });\n }\n }\n\n matches.sort((a, b) => b.score - a.score);\n\n let items = matches.slice(0, limit).map(m => ({\n tabId: m.tab.id,\n title: m.tab.title,\n url: m.tab.url,\n favIconUrl: m.tab.favIconUrl,\n windowId: m.tab.windowId,\n score: m.score,\n matchedRecords: m.matchedRecords.map(r => ({\n headingText: r.headingText,\n bodyText: (r.bodyText || '').slice(0, 120),\n selector: r.selector,\n type: r.type,\n })),\n }));\n\n // Generate \"did you mean\" suggestion from all available indices\n let suggestion: string | undefined;\n for (const [, state] of tabIndices) {\n const word = findClosestWord(query, state.index, 2);\n if (word && word !== query.toLowerCase()) {\n suggestion = word;\n break;\n }\n }\n\n // Autocorrect: if no tab results but we have a suggestion, re-search with corrected query\n let autocorrected = false;\n if (items.length === 0 && suggestion) {\n const correctedMatches: { tab: chrome.tabs.Tab; score: number; matchedRecords: IndexRecord[] }[] = [];\n for (const tab of tabs) {\n if (!tab.id || !tab.url || !tab.title) continue;\n if (tab.url.startsWith('chrome://') || tab.url.startsWith('about:') || tab.url.startsWith('moz-extension:')) continue;\n const state = tabIndices.get(tab.id);\n if (!state) continue;\n try {\n const hits = searchSections(suggestion, state.index, { limit: 3, fuzzy: true }) as IndexRecord[];\n if (hits.length) {\n correctedMatches.push({ tab, score: 6 + hits.length, matchedRecords: hits });\n }\n } catch { /* ignore */ }\n }\n if (correctedMatches.length > 0) {\n correctedMatches.sort((a, b) => b.score - a.score);\n items = correctedMatches.slice(0, limit).map(m => ({\n tabId: m.tab.id,\n title: m.tab.title,\n url: m.tab.url,\n favIconUrl: m.tab.favIconUrl,\n windowId: m.tab.windowId,\n score: m.score,\n matchedRecords: m.matchedRecords.map(r => ({\n headingText: r.headingText,\n bodyText: (r.bodyText || '').slice(0, 120),\n selector: r.selector,\n type: r.type,\n })),\n }));\n autocorrected = true;\n }\n }\n\n // Search site content across crawled site indices\n const siteResults = searchSiteContent(query, 5);\n\n // Build action items (new tab actions)\n const actions: any[] = [];\n if (looksLikeUrl(query.trim())) {\n let navUrl = query.trim();\n if (!/^https?:\\/\\//i.test(navUrl)) navUrl = 'https://' + navUrl;\n actions.push({ type: 'open-url', title: `Open ${navUrl}`, url: navUrl });\n }\n actions.push({ type: 'search-web', title: `Search the web for \"${query}\"`, url: `https://www.google.com/search?q=${encodeURIComponent(query)}` });\n\n return { items, suggestion, autocorrected, siteResults, actions };\n}\n\n// \u2500\u2500\u2500 MESSAGE ROUTER \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nif (typeof chrome !== 'undefined' && chrome.runtime?.onMessage) {\n chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {\n (async () => {\n try {\n // Existing search\n if (message.type === 'SEARCH_CURRENT_TAB') {\n const tabId = message.tabId || sender.tab?.id;\n if (!tabId) { sendResponse({ success: false, error: 'no-tab-id' }); return; }\n\n const state = await getOrFetchTabIndex(tabId, message.forceRefresh);\n if (!state) { sendResponse({ success: false, error: 'failed-to-index-tab' }); return; }\n\n const options: SearchOptions = message.searchOptions || {};\n const paginated = searchWithPagination(message.query || '', state.index, options);\n const suggestions = suggest(message.query || '', state.index);\n const results = paginated.results.map((sr: any) => sr.record ?? sr);\n\n sendResponse({\n success: true,\n results,\n total: paginated.total,\n hasMore: paginated.hasMore,\n suggestions,\n manifest: state.manifest,\n });\n return;\n }\n\n if (message.type === 'EXECUTE_TAB_ACTION') {\n const tabId = message.tabId || sender.tab?.id;\n if (!tabId) { sendResponse({ success: false, error: 'no-tab-id' }); return; }\n const opts = await getOptions();\n const response = await chrome.tabs.sendMessage(tabId, {\n type: 'EXECUTE_ACTION',\n record: message.record,\n actionType: message.actionType,\n value: message.value,\n options: {\n actionsMode: opts.actionsMode,\n exclusionSelectors: opts.exclusionSelectors,\n },\n });\n sendResponse(response);\n return;\n }\n\n if (message.type === 'CRAWL_SITE_CROSS_TAB') {\n const tabId = message.tabId || sender.tab?.id;\n if (!tabId) { sendResponse({ success: false, error: 'no-tab-id' }); return; }\n const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });\n if (!tab?.url) { sendResponse({ success: false, error: 'no-tab-url' }); return; }\n const indexer = new Indexer({ scope: 'body', indexActions: true });\n await indexer.crawlSameOrigin(() => {\n const crawledIndex = indexer.getIndex();\n siteIndices.set(new URL(tab.url!).origin, crawledIndex);\n sendResponse({ success: true });\n });\n return;\n }\n\n // Library \u2014 bookmarks\n if (message.type === 'LIBRARY_BOOKMARK_LIST') {\n sendResponse({ success: true, items: await listBookmarks(message.query || '', message.tags || []) });\n return;\n }\n if (message.type === 'LIBRARY_BOOKMARK_CREATE') {\n const bookmark = await createBookmark(message.data as Omit);\n sendResponse({ success: true, item: bookmark });\n return;\n }\n if (message.type === 'LIBRARY_BOOKMARK_UPDATE') {\n const item = await updateBookmark(message.id, message.data || {});\n sendResponse({ success: !!item, item });\n return;\n }\n if (message.type === 'LIBRARY_BOOKMARK_DELETE') {\n sendResponse({ success: await deleteBookmark(message.id) });\n return;\n }\n\n // Library \u2014 snippets\n if (message.type === 'LIBRARY_SNIPPET_LIST') {\n sendResponse({ success: true, items: await listSnippets(message.query || '', message.tags || []) });\n return;\n }\n if (message.type === 'LIBRARY_SNIPPET_CREATE') {\n const snippet = await createSnippet(message.data as Omit);\n sendResponse({ success: true, item: snippet });\n return;\n }\n if (message.type === 'LIBRARY_SNIPPET_UPDATE') {\n const item = await updateSnippet(message.id, message.data || {});\n sendResponse({ success: !!item, item });\n return;\n }\n if (message.type === 'LIBRARY_SNIPPET_DELETE') {\n sendResponse({ success: await deleteSnippet(message.id) });\n return;\n }\n\n // Library \u2014 page notes\n if (message.type === 'LIBRARY_NOTE_GET') {\n sendResponse({ success: true, item: await getPageNote(message.url) });\n return;\n }\n if (message.type === 'LIBRARY_NOTE_LIST') {\n sendResponse({ success: true, items: await listPageNotes(message.query || '') });\n return;\n }\n if (message.type === 'LIBRARY_NOTE_SET') {\n const note = await setPageNote(message.url, message.text, message.title || '');\n sendResponse({ success: true, item: note });\n return;\n }\n if (message.type === 'LIBRARY_NOTE_DELETE') {\n sendResponse({ success: await deletePageNote(message.url) });\n return;\n }\n\n // Library \u2014 recents\n if (message.type === 'LIBRARY_RECENTS_LIST') {\n sendResponse({ success: true, items: await listRecents() });\n return;\n }\n if (message.type === 'LIBRARY_RECENTS_CLEAR') {\n await clearRecents();\n sendResponse({ success: true });\n return;\n }\n\n // Library \u2014 tags\n if (message.type === 'LIBRARY_TAGS') {\n sendResponse({\n success: true,\n bookmarkTags: await allBookmarkTags(),\n snippetTags: await allSnippetTags(),\n });\n return;\n }\n\n // Tab search\n if (message.type === 'TAB_SEARCH' || message.type === 'SPOTLIGHT_SEARCH') {\n const result = await searchOpenTabs(message.query || '', message.limit || 50);\n sendResponse({\n success: true,\n items: result.items,\n suggestion: result.suggestion,\n autocorrected: result.autocorrected,\n siteResults: result.siteResults,\n actions: result.actions,\n });\n return;\n }\n\n // Switch to a specific tab (used from popup)\n if (message.type === 'TAB_SWITCH') {\n if (typeof message.tabId === 'number' && chrome.tabs) {\n await chrome.tabs.update(message.tabId, { active: true });\n if (typeof message.windowId === 'number' && chrome.windows) {\n await chrome.windows.update(message.windowId, { focused: true });\n }\n sendResponse({ success: true });\n } else {\n sendResponse({ success: false, error: 'invalid-tab-id' });\n }\n return;\n }\n\n // Spotlight: forward a HIGHLIGHT_RECORD to the target tab (called after TAB_SWITCH)\n if (message.type === 'SPOTLIGHT_OPEN_RECORD') {\n if (typeof message.tabId === 'number' && message.record && chrome.tabs) {\n try {\n await chrome.tabs.sendMessage(message.tabId, {\n type: 'HIGHLIGHT_RECORD',\n record: message.record,\n });\n sendResponse({ success: true });\n } catch {\n sendResponse({ success: false, error: 'failed-to-highlight' });\n }\n } else {\n sendResponse({ success: false, error: 'invalid-spotlight-open-record' });\n }\n return;\n }\n\n // Spotlight: open a URL in a new tab (action items)\n if (message.type === 'SPOTLIGHT_OPEN_NEW_TAB') {\n if (message.url && chrome.tabs?.create) {\n await chrome.tabs.create({ url: message.url });\n sendResponse({ success: true });\n } else {\n sendResponse({ success: false, error: 'invalid-url' });\n }\n return;\n }\n\n // Spotlight: crawl a site's content for cross-site search\n if (message.type === 'SPOTLIGHT_CRAWL_SITE') {\n if (!message.origin) { sendResponse({ success: false, error: 'no-origin' }); return; }\n try {\n const allTabs = await chrome.tabs.query({});\n const seedTab = allTabs.find(t => t.url && t.id && new URL(t.url).origin === message.origin);\n if (seedTab?.id) {\n const resp: any = await chrome.tabs.sendMessage(seedTab.id, { type: 'GET_MANIFEST' });\n if (resp?.success?.manifest) {\n let siteIndex = siteIndices.get(message.origin);\n if (!siteIndex) {\n siteIndex = createSearchIndex();\n siteIndices.set(message.origin, siteIndex);\n }\n addToIndex(siteIndex, resp.manifest.records);\n sendResponse({ success: true, recordCount: resp.manifest.records.length });\n } else {\n sendResponse({ success: false, error: 'no-manifest' });\n }\n } else {\n sendResponse({ success: false, error: 'no-tab-for-origin' });\n }\n } catch (err: any) {\n sendResponse({ success: false, error: err?.message || String(err) });\n }\n return;\n }\n\n // Library: open a recent page\n if (message.type === 'LIBRARY_OPEN_RECENT') {\n if (message.url && chrome.tabs?.create) {\n await chrome.tabs.create({ url: message.url });\n sendResponse({ success: true });\n } else {\n sendResponse({ success: false, error: 'invalid-url' });\n }\n return;\n }\n\n sendResponse({ success: false, error: 'unsupported-background-message' });\n } catch (err: any) {\n sendResponse({ success: false, error: err?.message || String(err) });\n }\n })();\n return true;\n });\n}\n\n// \u2500\u2500\u2500 TAB CLEANUP \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nif (typeof chrome !== 'undefined' && chrome.tabs?.onRemoved) {\n chrome.tabs.onRemoved.addListener(tabId => {\n tabIndices.delete(tabId);\n });\n}\n\n// \u2500\u2500\u2500 COMMANDS (Spotlight shortcut) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nif (typeof chrome !== 'undefined' && chrome.commands?.onCommand) {\n chrome.commands.onCommand.addListener(async (command) => {\n if (command !== 'open-spotlight') return;\n if (!chrome.tabs?.query) return;\n try {\n const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });\n if (!activeTab?.id) return;\n // Pages without a content script (chrome://, file:// without permission, etc.) will throw \u2014 fail silently\n await chrome.tabs.sendMessage(activeTab.id, { type: 'SHOW_SPOTLIGHT' });\n } catch {\n /* no content script on this page */\n }\n });\n}\n", "/**\n * @file Content extraction functions for indexing web pages.\n * Extracts sections, actions, fields, links, files, media, and structured data from HTML.\n */\n\nimport { IndexRecord } from './types.js';\n\nexport function stripTags(value: string): string {\n let result = '';\n let inTag = false;\n for (let i = 0; i < value.length; i++) {\n const char = value[i];\n if (char === '<' && inTag === false) {\n inTag = true;\n } else if (char === '>' && inTag === true) {\n inTag = false;\n } else if (inTag === false) {\n result += char;\n }\n }\n return result.replace(/\\s+/g, ' ').trim();\n}\n\nexport function generateSelector(element: Element): string {\n const path: string[] = [];\n let current: Element | null = element;\n\n while (current && current.nodeType === Node.ELEMENT_NODE) {\n let selector = current.tagName.toLowerCase();\n if (current.id) {\n selector += `#${current.id}`;\n path.unshift(selector);\n break;\n } else if (current.className) {\n const classes = current.className.trim().split(/\\s+/);\n if (classes.length) {\n selector += `.${classes.join('.')}`;\n }\n }\n\n let siblingIndex = 1;\n let sibling = current.previousElementSibling;\n while (sibling) {\n if (sibling.tagName === current.tagName) {\n siblingIndex++;\n }\n sibling = sibling.previousElementSibling;\n }\n if (siblingIndex > 1) {\n selector += `:nth-child(${siblingIndex})`;\n }\n\n path.unshift(selector);\n current = current.parentElement;\n }\n\n return path.length > 0 ? path.join(' > ') : element.tagName.toLowerCase();\n}\n\n/** Return resilient selectors from most semantic to least semantic. */\nexport function generateStableSelector(element: Element): string[] {\n const candidates: string[] = [];\n const push = (value: string) => { if (value && !candidates.includes(value)) candidates.push(value); };\n const escape = (value: string) => typeof CSS !== 'undefined' && CSS.escape ? CSS.escape(value) : value.replace(/([\"\\\\])/g, '\\\\$1');\n for (const attr of ['data-testid', 'data-test', 'data-agent-id', 'id']) {\n const value = element.getAttribute(attr);\n if (value) push(attr === 'id' ? `#${escape(value)}` : `[${attr}=\"${escape(value)}\"]`);\n }\n const aria = element.getAttribute('aria-label');\n if (aria) push(`[aria-label=\"${escape(aria)}\"]`);\n const role = element.getAttribute('role');\n const name = extractActionName(element);\n if (role && name) push(`[role=\"${escape(role)}\"][aria-label=\"${escape(name)}\"]`);\n if (role) push(`[role=\"${escape(role)}\"]`);\n push(generateSelector(element));\n // XPath is intentionally last: it survives class churn but is less readable.\n let sibling = element.previousElementSibling;\n let position = 1;\n while (sibling) { if (sibling.tagName === element.tagName) position++; sibling = sibling.previousElementSibling; }\n push(`xpath=//${element.tagName.toLowerCase()}[${position}]`);\n return candidates;\n}\n\nfunction walkComposed(root: Document | Element | ShadowRoot, visit: (element: Element, iframePath: number[]) => void, iframePath: number[] = []): void {\n const elements = root instanceof Document ? Array.from(root.documentElement ? [root.documentElement] : []) : [root as Element];\n const walk = (element: Element, path: number[]) => {\n visit(element, path);\n if (element.shadowRoot) walkRoot(element.shadowRoot, path);\n for (const child of Array.from(element.children)) walk(child, path);\n if (element.tagName.toLowerCase() === 'iframe') {\n try { const frame = (element as HTMLIFrameElement).contentDocument; if (frame) walkRoot(frame, [...path, Array.from(element.parentElement?.children || []).indexOf(element)]); } catch { /* cross-origin */ }\n }\n };\n const walkRoot = (value: Document | Element | ShadowRoot, path: number[]) => {\n if (value instanceof Document) { if (value.documentElement) walk(value.documentElement, path); }\n else if (value instanceof ShadowRoot) for (const child of Array.from(value.children)) walk(child, path);\n else walk(value, path);\n };\n for (const element of elements) walk(element, iframePath);\n}\n\nfunction isFocusableAction(element: Element): boolean {\n const tag = element.tagName.toLowerCase();\n return ['a', 'button', 'input', 'textarea', 'select', 'summary'].includes(tag) ||\n ['button', 'link', 'tab', 'menuitem', 'checkbox', 'radio', 'combobox', 'option'].includes(element.getAttribute('role') || '') ||\n element.hasAttribute('contenteditable') || (element.hasAttribute('tabindex') && Number(element.getAttribute('tabindex')) >= 0) ||\n element.hasAttribute('data-reef-action');\n}\n\n/** Extract actionable custom elements from a live composed DOM, including open shadow roots and same-origin frames. */\nexport function extractAccessibilityTree(root: Document | Element | ShadowRoot = document): IndexRecord[] {\n const records: IndexRecord[] = [];\n walkComposed(root, (element, iframePath) => {\n if (!isFocusableAction(element)) return;\n const label = extractActionName(element);\n if (!label) return;\n const selectors = generateStableSelector(element);\n records.push({ id: `${typeof location !== 'undefined' ? location.href : ''}#accessibility-${records.length}`, url: typeof location !== 'undefined' ? location.href : '', headingText: label, headingId: `accessibility-${records.length}`, breadcrumb: '', bodyText: label, type: element.matches('input,textarea,select,[contenteditable]') ? 'field' : 'action', selector: selectors[0], selectors, iframePath, label, destructive: isDestructiveAction(label) });\n });\n return records;\n}\n\nexport function extractHeadingId(fullMatch: string, text: string): string {\n const idMatch = fullMatch.match(/\\bid=[\"']([^\"']+)['\"]/i);\n if (idMatch?.[1]) return idMatch[1];\n const stripped = text.toLowerCase().replace(/[^a-z0-9]+/g, '-');\n return stripped || Math.random().toString(36).slice(2);\n}\n\nexport function hasExplicitId(fullMatch: string): boolean {\n return /\\bid=[\"'][^\"']+[\"']/i.test(fullMatch);\n}\n\nexport function findParentSectionId(html: string, headingMatchEnd: number): string | null {\n const afterHeading = html.slice(headingMatchEnd, headingMatchEnd + 500);\n const idMatch = afterHeading.match(/]*id=\"([^\"]+)\"/i);\n if (idMatch?.[1]) return idMatch[1];\n const articleMatch = afterHeading.match(/]*id=\"([^\"]+)\"/i);\n if (articleMatch?.[1]) return articleMatch[1];\n return null;\n}\n\nconst headingCache = new Map();\n\nexport function chunkBodyText(bodyText: string, chunkSize: number, record: IndexRecord): IndexRecord[] {\n if (chunkSize <= 0 || bodyText.length <= chunkSize) {\n return [record];\n }\n \n const chunks: IndexRecord[] = [];\n const overlap = Math.floor(chunkSize * 0.2); // 20% overlap\n \n for (let i = 0; i < bodyText.length; i += chunkSize - overlap) {\n const chunkEnd = Math.min(i + chunkSize, bodyText.length);\n const chunkText = bodyText.slice(i, chunkEnd);\n \n // Create a new record for this chunk\n const chunkRecord: IndexRecord = {\n ...record,\n id: `${record.id}-chunk-${Math.floor(i / chunkSize)}`,\n url: `${record.url}-chunk-${Math.floor(i / chunkSize)}`,\n bodyText: chunkText,\n headingText: `${record.headingText} (chunk ${Math.floor(i / chunkSize) + 1})`,\n headingId: `${record.headingId}-chunk-${Math.floor(i / chunkSize)}`,\n };\n \n chunks.push(chunkRecord);\n }\n \n return chunks;\n}\n\nexport function extractSections(html: string, url: string): IndexRecord[] {\n if (headingCache.has(url)) {\n return headingCache.get(url)!;\n }\n\n const cleanHtml = html\n .replace(//gi, ' ')\n .replace(//gi, ' ')\n .replace(//gi, ' ')\n .replace(//g, ' ');\n\n const matches: Array<{ level: number; index: number; text: string; id: string; hasRealId: boolean }> = [];\n const headingRegexGlobal = /<(h[1-6])[^>]*>([\\s\\S]*?)<\\/h[1-6]>/gi;\n let match: RegExpExecArray | null;\n\n headingRegexGlobal.lastIndex = 0;\n while ((match = headingRegexGlobal.exec(cleanHtml)) !== null) {\n const [, tag, text] = match;\n const headingText = stripTags(text);\n const level = parseInt(tag[1], 10);\n matches.push({\n level,\n index: match.index,\n text: headingText,\n id: extractHeadingId(match[0], headingText),\n hasRealId: hasExplicitId(match[0]),\n });\n }\n\n const len = matches.length;\n const sections: IndexRecord[] = new Array(len);\n\n for (let i = 0; i < len; i++) {\n const heading = matches[i];\n const nextHeading = matches[i + 1];\n const start = heading.index + heading.text.length;\n const end = nextHeading?.index ?? cleanHtml.length;\n const content = cleanHtml.slice(start, end);\n const bodyText = stripTags(content).replace(/\\s+/g, ' ').trim();\n\n let breadcrumb = '';\n for (let j = 0; j <= i; j++) {\n if (j > 0) breadcrumb += ' \u203A ';\n breadcrumb += matches[j].text;\n }\n\n const parentSectionId = heading.hasRealId ? null : findParentSectionId(cleanHtml, heading.index + heading.text.length);\n const selector = heading.hasRealId\n ? '#' + heading.id\n : parentSectionId\n ? '#' + parentSectionId\n : undefined;\n\n sections[i] = {\n id: `${url}#${heading.id}`,\n url: `${url}#${heading.id}`,\n headingText: heading.text,\n headingId: heading.id,\n breadcrumb,\n bodyText,\n type: 'section',\n selector,\n };\n }\n\n headingCache.set(url, sections);\n return sections;\n}\n\nexport function extractActionName(element: Element): string | null {\n const ariaLabel = element.getAttribute('aria-label');\n if (ariaLabel?.trim()) return ariaLabel.trim();\n\n const ariaLabelledBy = element.getAttribute('aria-labelledby');\n if (ariaLabelledBy) {\n const labelledElement = document.getElementById(ariaLabelledBy);\n if (labelledElement?.textContent?.trim()) {\n return labelledElement.textContent.trim();\n }\n }\n\n const textContent = element.textContent?.trim();\n if (textContent) return textContent;\n\n const title = element.getAttribute('title');\n if (title?.trim()) return title.trim();\n\n return null;\n}\n\nexport function isDestructiveAction(label: string): boolean {\n const destructiveVerbs = [\n 'delete', 'remove', 'cancel subscription', 'unsubscribe',\n 'pay', 'checkout', 'submit order', 'confirm'\n ];\n const lowerLabel = label.toLowerCase();\n return destructiveVerbs.some(verb => lowerLabel.includes(verb));\n}\n\nexport function extractActions(html: string, url: string, excludeSelectors?: string): IndexRecord[] {\n const actions: IndexRecord[] = [];\n const doc = new DOMParser().parseFromString(html, 'text/html');\n\n const selectors = [\n 'button',\n '[role=\"button\"]',\n 'input[type=\"button\"]',\n 'input[type=\"submit\"]',\n 'summary',\n '[data-reef-action]'\n ];\n\n const elements = Array.from(doc.querySelectorAll(selectors.join(',')));\n\n for (const element of elements) {\n if (excludeSelectors && element.matches(excludeSelectors)) continue;\n\n const label = extractActionName(element);\n if (!label) continue;\n\n const selectors = generateStableSelector(element);\n\n actions.push({\n id: `${url}#action-${actions.length}`,\n url: url,\n headingText: label,\n headingId: `action-${actions.length}`,\n breadcrumb: '',\n bodyText: label,\n type: 'action',\n selector: selectors[0], selectors,\n destructive: isDestructiveAction(label),\n label\n });\n }\n\n return actions;\n}\n\nexport function extractFields(html: string, url: string): IndexRecord[] {\n const fields: IndexRecord[] = [];\n const doc = new DOMParser().parseFromString(html, 'text/html');\n\n const formElements = Array.from(doc.querySelectorAll('form'));\n\n for (const form of formElements) {\n let breadcrumb = '';\n let current: Element | null = form.parentElement;\n while (current && current !== doc.body) {\n if (current.matches('h1, h2, h3, h4, h5, h6, article, section, [role=\"main\"], main')) {\n const headingText = current.textContent?.trim() || '';\n if (headingText) {\n breadcrumb = headingText;\n }\n break;\n }\n current = current.parentElement;\n }\n\n const inputs = Array.from(form.querySelectorAll('input, textarea, select'));\n\n for (const input of inputs) {\n if (input.matches('input[type=\"hidden\"], input[type=\"button\"], input[type=\"submit\"], input[type=\"reset\"]')) {\n continue;\n }\n\n let label = '';\n const id = input.id;\n if (id) {\n const labelElement = doc.querySelector(`label[for=\"${id}\"]`);\n if (labelElement) {\n label = labelElement.textContent?.trim() || '';\n }\n }\n\n if (!label) {\n const parentLabel = input.closest('label');\n if (parentLabel) {\n label = parentLabel.textContent?.trim() || '';\n const inputElement = input as HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement;\n if (label && inputElement.value && label.includes(inputElement.value)) {\n label = label.replace(inputElement.value, '').trim();\n }\n }\n }\n\n if (!label) {\n const inputElement = input as HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement;\n const placeholder = 'placeholder' in inputElement ? inputElement.placeholder : '';\n label = placeholder || input.getAttribute('aria-label') || '';\n }\n\n if (!label) continue;\n\n const selectors = generateStableSelector(input);\n const inputElement = input as HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement;\n\n fields.push({\n id: `${url}#field-${fields.length}`,\n url: url,\n headingText: label,\n headingId: `field-${fields.length}`,\n breadcrumb,\n bodyText: label,\n type: 'field',\n selector: selectors[0], selectors,\n label,\n value: inputElement.value\n });\n }\n }\n\n return fields;\n}\n\nexport function extractHiddenContent(doc: Document): void {\n doc.querySelectorAll('details').forEach(d => {\n if (!d.hasAttribute('open')) d.setAttribute('data-reef-was-closed', 'true');\n d.setAttribute('open', '');\n });\n doc.querySelectorAll('[aria-hidden=\"false\"]').forEach(el => {\n el.removeAttribute('aria-hidden');\n });\n}\n\nexport function extractLinks(html: string, url: string): IndexRecord[] {\n const links: IndexRecord[] = [];\n const doc = new DOMParser().parseFromString(html, 'text/html');\n\n const anchors = Array.from(doc.querySelectorAll('a[href]'));\n\n for (const anchor of anchors) {\n if (anchor.hasAttribute('rel') && anchor.getAttribute('rel')?.toLowerCase().includes('nofollow')) continue;\n\n const href = anchor.getAttribute('href');\n if (!href) continue;\n\n if (href === '#' || href.startsWith('javascript:')) continue;\n\n const linkText = anchor.textContent?.trim() || '';\n if (!linkText) continue;\n\n const resolvedUrl = resolveUrl(href, url);\n const isExternal = !resolvedUrl.startsWith(window.location.origin);\n const selectors = generateStableSelector(anchor);\n\n links.push({\n id: `${url}#link-${links.length}`,\n url: resolvedUrl,\n headingText: linkText,\n headingId: `link-${links.length}`,\n breadcrumb: '',\n bodyText: linkText,\n type: isExternal ? 'link' : 'section',\n selector: selectors[0], selectors\n });\n }\n\n return links;\n}\n\nexport function extractFiles(html: string, url: string, extensions?: string): IndexRecord[] {\n const files: IndexRecord[] = [];\n const doc = new DOMParser().parseFromString(html, 'text/html');\n\n const fileExtensions = extensions?.split(',').map(e => e.trim().toLowerCase()) ??\n ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'zip', 'csv'];\n\n const anchors = Array.from(doc.querySelectorAll('a[href]'));\n\n for (const anchor of anchors) {\n const href = anchor.getAttribute('href');\n if (!href) continue;\n\n const isFile = fileExtensions.some(ext =>\n href.toLowerCase().endsWith(`.${ext}`) ||\n href.toLowerCase().endsWith(`.${ext}?`) ||\n href.toLowerCase().endsWith(`.${ext}#`)\n );\n\n if (!isFile) continue;\n\n const linkText = anchor.textContent?.trim() || href.split('/').pop() || '';\n if (!linkText) continue;\n\n const resolvedUrl = resolveUrl(href, url);\n const selectors = generateStableSelector(anchor);\n\n files.push({\n id: `${url}#file-${files.length}`,\n url: resolvedUrl,\n headingText: linkText,\n headingId: `file-${files.length}`,\n breadcrumb: '',\n bodyText: linkText,\n type: 'file',\n selector: selectors[0], selectors\n });\n }\n\n return files;\n}\n\nexport function extractMedia(html: string, url: string): IndexRecord[] {\n const media: IndexRecord[] = [];\n const doc = new DOMParser().parseFromString(html, 'text/html');\n\n const images = Array.from(doc.querySelectorAll('img'));\n for (const img of images) {\n const alt = img.alt.trim();\n if (!alt) continue;\n\n let caption = '';\n const figure = img.closest('figure');\n if (figure) {\n const figcaption = figure.querySelector('figcaption');\n if (figcaption) {\n caption = figcaption.textContent?.trim() || '';\n }\n }\n\n const textToIndex = caption ? `${alt} ${caption}` : alt;\n if (!textToIndex.trim()) continue;\n\n const selectors = generateStableSelector(img);\n\n media.push({\n id: `${url}#media-image-${media.length}`,\n url: url,\n headingText: alt,\n headingId: `media-image-${media.length}`,\n breadcrumb: '',\n bodyText: textToIndex,\n type: 'media',\n selector: selectors[0], selectors\n });\n }\n\n const mediaElements = Array.from(doc.querySelectorAll('video, audio'));\n for (const element of mediaElements) {\n const title = element.getAttribute('title') || element.getAttribute('aria-label') || '';\n if (!title) continue;\n\n let transcript = '';\n const tracks = Array.from(element.querySelectorAll('track[kind=\"captions\"], track[kind=\"subtitles\"]'));\n for (const track of tracks) {\n const src = track.getAttribute('src');\n if (src) {\n transcript += `[Transcript available: ${src}] `;\n }\n }\n\n const textToIndex = transcript ? `${title} ${transcript}` : title;\n if (!textToIndex.trim()) continue;\n\n const selectors = generateStableSelector(element);\n\n media.push({\n id: `${url}#media-${media.length}`,\n url: url,\n headingText: title,\n headingId: `media-${media.length}`,\n breadcrumb: '',\n bodyText: textToIndex,\n type: 'media',\n selector: selectors[0], selectors,\n transcript: transcript.trim()\n });\n }\n\n return media;\n}\n\nexport function extractStructuredData(html: string, url: string): IndexRecord[] {\n const structured: IndexRecord[] = [];\n const doc = new DOMParser().parseFromString(html, 'text/html');\n\n const jsonLdScripts = Array.from(doc.querySelectorAll('script[type=\"application/ld+json\"]'));\n for (const script of jsonLdScripts) {\n try {\n const data = JSON.parse(script.textContent || '{}');\n\n if (Array.isArray(data) ? data.some((item: any) => item['@type'] === 'FAQPage') :\n data['@type'] === 'FAQPage') {\n const faqItems = Array.isArray(data) ?\n data.flatMap((item: any) => item.mainEntity || []) :\n data.mainEntity || [];\n\n for (const [index, question] of faqItems.entries()) {\n if (!question || !question.name) continue;\n\n const answer = question.acceptedAnswer?.text ||\n question.suggestedAnswer?.text ||\n '';\n\n if (!answer) continue;\n\n const textToIndex = `${question.name} ${answer}`;\n\n structured.push({\n id: `${url}#structured-faq-${index}`,\n url: url,\n headingText: question.name,\n headingId: `structured-faq-${index}`,\n breadcrumb: '',\n bodyText: textToIndex,\n type: 'structured',\n structuredData: { question: question.name, answer }\n });\n }\n }\n else if (data['@type']) {\n const type = data['@type'];\n const name = data.name || data.headline || '';\n const description = data.description || '';\n\n if (!name && !description) continue;\n\n const textToIndex = `${name} ${description}`.trim();\n if (!textToIndex) continue;\n\n structured.push({\n id: `${url}#structured-${type.toLowerCase()}-${structured.length}`,\n url: url,\n headingText: name || 'Structured Data',\n headingId: `structured-${type.toLowerCase()}-${structured.length}`,\n breadcrumb: '',\n bodyText: textToIndex,\n type: 'structured',\n structuredData: data\n });\n }\n } catch (e) {\n continue;\n }\n }\n\n return structured;\n}\n\n// Known tracking parameters to strip\nconst TRACKING_PARAMS = new Set([\n 'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'gclid', 'fbclid', 'mc_cid', 'mc_eid', 'mc_id',\n 'referrer', 'ref', 'source', 'campaign', 'click_id'\n]);\n\n// Strip known tracking parameters from URL\nfunction stripTrackingParams(url: string): string {\n try {\n const urlObj = new URL(url);\n const params = new URLSearchParams(urlObj.search);\n \n // Remove tracking parameters\n for (const param of TRACKING_PARAMS) {\n params.delete(param);\n }\n \n // Remove empty parameters\n for (const [key, value] of params.entries()) {\n if (!value) {\n params.delete(key);\n }\n }\n \n // Reconstruct URL\n urlObj.search = params.toString();\n return urlObj.toString();\n } catch {\n return url;\n }\n}\n\n// Normalize URL: strip trailing slash, sort/drop known tracking query params, resolve to canonical if present\nexport function normalizeUrl(url: string, html?: string): string {\n if (!url) return url;\n \n // Strip trailing slash from pathname\n let normalized = url.replace(/\\/$/, '');\n \n // Strip tracking parameters\n normalized = stripTrackingParams(normalized);\n \n // Check for canonical URL in HTML if provided\n if (html) {\n const canonicalMatch = html.match(/]*rel=[\"']canonical[\"'][^>]*href=[\"']([^\"']+)[\"']/i);\n if (canonicalMatch?.[1]) {\n const canonicalUrl = stripTrackingParams(canonicalMatch[1]);\n // If canonical URL is different, use it\n if (canonicalUrl !== normalized) {\n return canonicalUrl.replace(/\\/$/, '');\n }\n }\n }\n \n return normalized;\n}\n\nexport function resolveUrl(value: string, base: string): string {\n if (!value) return base;\n try {\n return new URL(value, base).toString();\n } catch {\n return value;\n }\n}\n", "/**\n * @file Indexing and crawling functionality for the search index.\n * Handles sitemap fetching, page crawling, and worker-based indexing.\n */\n\nimport {\n extractSections,\n extractActions,\n extractFields,\n extractLinks,\n extractFiles,\n extractMedia,\n extractStructuredData,\n extractHiddenContent,\n normalizeUrl,\n} from '../extraction.js';\nimport {\n createSearchIndex,\n addToIndex,\n type SearchIndex,\n type IndexRecord,\n type TokenFilter,\n} from '../search-index.js';\nimport type { ReefConfig } from '../types.js';\n\nexport class Indexer {\n private index: SearchIndex = createSearchIndex();\n private config: ReefConfig;\n private robotsCache: Map; timestamp: number }> = new Map();\n private lastCrawlTime: number = 0;\n\n constructor(config: ReefConfig) {\n this.config = config;\n }\n\n getIndex(): SearchIndex { return this.index; }\n setIndex(index: SearchIndex): void { this.index = index; }\n\n private extractAllContent(html: string, url: string): IndexRecord[] {\n const doc = new DOMParser().parseFromString(html, 'text/html');\n\n if (this.config.indexHidden) {\n extractHiddenContent(doc);\n }\n\n let rootElement: Element | Document = doc;\n if (this.config.scope) {\n const scopeElement = doc.querySelector(this.config.scope);\n if (scopeElement) {\n rootElement = scopeElement;\n }\n }\n\n const htmlToProcess = new XMLSerializer().serializeToString(rootElement as Element);\n\n const sections = extractSections(htmlToProcess, url);\n const actions = this.config.indexActions ? extractActions(htmlToProcess, url, this.config.excludeAction) : [];\n const fields = this.config.indexActions ? extractFields(htmlToProcess, url) : [];\n const links = extractLinks(htmlToProcess, url);\n const files = extractFiles(htmlToProcess, url, this.config.fileExtensions);\n const media = this.config.indexMedia ? extractMedia(htmlToProcess, url) : [];\n const structured = this.config.indexStructuredData ? extractStructuredData(htmlToProcess, url) : [];\n\n const combined: IndexRecord[] = [];\n combined.push(...sections, ...actions, ...fields, ...links, ...files, ...media, ...structured);\n return combined;\n }\n\n async boot(\n onReady: () => void,\n showToast?: (msg: string) => void\n ): Promise {\n // Try prebuilt index first if configured\n if (this.config.prebuiltIndexUrl) {\n try {\n const response = await fetch(this.config.prebuiltIndexUrl);\n if (response.ok) {\n const json = await response.text();\n this.index = (await import('../search-index.js')).deserializeIndex(json);\n console.info(`[reef] loaded prebuilt index with ${this.index.allSections.length} sections`);\n onReady();\n return;\n }\n } catch (error) {\n console.warn('[reef] prebuilt index fetch failed, falling back to crawling', error);\n }\n }\n\n // Try IndexedDB cache first\n try {\n const { loadIndex, saveIndex } = await import('../cache.js');\n const ttl = this.config.ttl;\n const cached = await loadIndex(ttl);\n if (cached?.index && cached.index.allSections.length > 0) {\n this.index = cached.index;\n console.info(`[reef] loaded cached index with ${this.index.allSections.length} sections`);\n onReady();\n return;\n }\n } catch (error) {\n console.warn('[reef] cache load failed, continuing with fresh index', error);\n }\n\n // Try to load existing page metadata from cache first for incremental crawling\n let cachedMetadata: { metadata: any } | null = null;\n try {\n const { loadIndex } = await import('../cache.js');\n cachedMetadata = await loadIndex();\n } catch (e) {\n console.warn('[reef] failed to load cached metadata for incremental crawling', e);\n }\n\n const pageHashes = cachedMetadata?.metadata?.pageMetadata ?? {};\n\n // Use the new recursive sitemap processing\n const urls = await this.fetchSitemapUrls();\n const maxPages = this.config.maxPages ?? 500;\n\n if (urls.length === 0) {\n // Fallback to same-origin crawling if no sitemaps found\n this.crawlSameOrigin(onReady);\n return;\n }\n\n if (this.config.useWorkerIndexing) {\n // TODO: Implement incremental crawling for worker-based indexing\n await this.fetchPagesWithWorker(urls.slice(0, maxPages), urls[0], onReady);\n } else {\n const fetchedSections = await this.fetchPagesParallel(urls.slice(0, maxPages), urls[0], pageHashes);\n if (fetchedSections.length) {\n addToIndex(this.index, fetchedSections, this.config.tokenizePipeline);\n console.info(`[reef] indexed ${fetchedSections.length} sections`);\n }\n // Save to cache with updated page metadata\n try {\n const { saveIndex } = await import('../cache.js');\n const versionHash = this.computeVersionHash(urls);\n const newPageMetadata: Record = {};\n \n // Update metadata for crawled pages\n for (const url of urls.slice(0, maxPages)) {\n newPageMetadata[url] = pageHashes[url] ?? { timestamp: Date.now() };\n }\n \n const metadata = {\n versionHash,\n buildTime: Date.now(),\n pageMetadata: newPageMetadata\n };\n await saveIndex(this.index, metadata);\n } catch (e) {\n console.warn('[reef] cache save failed', e);\n }\n onReady();\n return;\n }\n\n this.crawlSameOrigin(onReady);\n }\n\n async fetchSitemapUrls(): Promise {\n const candidates = this.getSitemapCandidates();\n const allUrls: string[] = [];\n const seenUrls = new Set();\n\n for (const candidate of candidates) {\n try {\n await this.processSitemap(candidate, allUrls, seenUrls);\n } catch (e) {\n console.warn('[reef] failed to process sitemap:', candidate, e);\n continue;\n }\n }\n\n return allUrls;\n }\n\n private async processSitemap(url: string, allUrls: string[], seenUrls: Set): Promise {\n if (seenUrls.has(url)) return;\n seenUrls.add(url);\n\n try {\n const response = await fetch(url);\n if (!response.ok) return;\n const xml = await response.text();\n\n // Check if this is a sitemap index\n const isSitemapIndex = xml.includes('');\n \n if (isSitemapIndex) {\n // Parse child sitemaps from sitemap index\n const sitemapRegex = /\\s*(.*?)<\\/loc>\\s*<\\/sitemap>/g;\n let match: RegExpExecArray | null;\n \n while ((match = sitemapRegex.exec(xml)) !== null) {\n const childSitemapUrl = match[1].trim();\n const resolvedUrl = this.resolveUrl(childSitemapUrl, url);\n await this.processSitemap(resolvedUrl, allUrls, seenUrls);\n }\n } else {\n // Parse URLs from regular sitemap\n const locRegex = /(.*?)<\\/loc>/g;\n let match: RegExpExecArray | null;\n while ((match = locRegex.exec(xml)) !== null) {\n const pageUrl = match[1].trim();\n const resolvedUrl = this.resolveUrl(pageUrl, url);\n if (!seenUrls.has(resolvedUrl)) {\n allUrls.push(resolvedUrl);\n seenUrls.add(resolvedUrl);\n }\n }\n }\n } catch (e) {\n console.warn('[reef] failed to process sitemap:', url, e);\n }\n }\n\n private computeVersionHash(urls: string[]): string {\n let hash = 0;\n for (const url of urls) {\n for (let i = 0; i < url.length; i++) {\n const char = url.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash;\n }\n }\n return Math.abs(hash).toString(36);\n }\n\n // Simple hash function for content hashing\n private hashContent(content: string): string {\n let hash = 0;\n for (let i = 0; i < Math.min(content.length, 10000); i++) { // Only hash first 10k chars for performance\n const char = content.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash;\n }\n return Math.abs(hash).toString(36);\n }\n\n getSitemapCandidates(): string[] {\n const base = window.location.href;\n const configured = this.config.sitemap ?? 'sitemap.xml';\n const candidates = [this.resolveUrl(configured, base)];\n\n if (!configured.startsWith('/')) {\n candidates.push(this.resolveUrl('./sitemap.xml', base));\n } else if (configured === '/sitemap.xml') {\n candidates.push(this.resolveUrl('sitemap.xml', base));\n }\n\n return [...new Set(candidates)];\n }\n\n private resolveUrl(value: string, base: string): string {\n if (!value) return base;\n try {\n return new URL(value, base).toString();\n } catch {\n return value;\n }\n }\n\n // Fetch and parse robots.txt for a given origin\n private async fetchRobotsTxt(origin: string): Promise> {\n const cacheKey = origin;\n const cacheEntry = this.robotsCache.get(cacheKey);\n \n // Return cached entry if it's still fresh (cache for 1 hour)\n if (cacheEntry && Date.now() - cacheEntry.timestamp < 3600000) {\n return cacheEntry.disallowed;\n }\n\n try {\n const robotsUrl = new URL('/robots.txt', origin).toString();\n const response = await fetch(robotsUrl);\n \n if (!response.ok) {\n // If we can't fetch robots.txt, assume crawling is allowed\n this.robotsCache.set(cacheKey, { disallowed: new Set(), timestamp: Date.now() });\n return new Set();\n }\n\n const robotsText = await response.text();\n const disallowed = this.parseRobotsTxt(robotsText);\n \n this.robotsCache.set(cacheKey, { disallowed, timestamp: Date.now() });\n return disallowed;\n } catch (e) {\n console.warn('[reef] failed to fetch robots.txt:', e);\n // On error, assume crawling is allowed\n this.robotsCache.set(cacheKey, { disallowed: new Set(), timestamp: Date.now() });\n return new Set();\n }\n }\n\n // Parse robots.txt content and extract disallowed paths\n private parseRobotsTxt(robotsText: string): Set {\n const disallowed = new Set();\n const lines = robotsText.split('\\n');\n \n let currentUserAgent = '';\n \n for (const line of lines) {\n const trimmedLine = line.trim();\n if (!trimmedLine || trimmedLine.startsWith('#')) continue;\n \n const userAgentMatch = trimmedLine.match(/^User-agent:\\s*(.+)$/i);\n if (userAgentMatch) {\n currentUserAgent = userAgentMatch[1].trim();\n continue;\n }\n\n const disallowMatch = trimmedLine.match(/^Disallow:\\s*(.+)$/i);\n if (disallowMatch && currentUserAgent === '*') {\n const path = disallowMatch[1].trim();\n if (path) {\n disallowed.add(path);\n }\n }\n }\n \n return disallowed;\n }\n\n // Check if a URL is allowed by robots.txt\n private isUrlAllowed(url: string, origin: string): boolean {\n // If we don't have cached robots.txt for this origin, assume it's allowed\n const cacheEntry = this.robotsCache.get(origin);\n if (!cacheEntry) return true;\n \n const disallowed = cacheEntry.disallowed;\n if (disallowed.size === 0) return true;\n \n // Check if the URL path starts with any disallowed path\n try {\n const urlObj = new URL(url);\n const path = urlObj.pathname;\n \n for (const disallowedPath of disallowed) {\n if (path.startsWith(disallowedPath)) {\n return false;\n }\n }\n } catch {\n return true; // If URL parsing fails, assume allowed\n }\n \n return true;\n }\n\n // Apply crawl delay between batches\n private async applyCrawlDelay(): Promise {\n const delay = this.config.crawlDelay ?? 0;\n if (delay <= 0) return;\n \n const now = Date.now();\n const timeSinceLastCrawl = now - this.lastCrawlTime;\n \n if (timeSinceLastCrawl < delay) {\n await new Promise(resolve => setTimeout(resolve, delay - timeSinceLastCrawl));\n }\n \n this.lastCrawlTime = Date.now();\n }\n\n private crawlSameOrigin(onReady: () => void): void {\n const origin = window.location.origin;\n const visited = new Set();\n const queue: string[] = [normalizeUrl(window.location.href)];\n const maxPages = this.config.maxPages ?? 500;\n const concurrency = this.config.maxPages ? Math.min(this.config.maxPages, 6) : 6;\n\n const processQueue = async () => {\n // Fetch robots.txt first\n await this.fetchRobotsTxt(origin);\n\n while (queue.length && visited.size < maxPages) {\n // Apply crawl delay between batches\n await this.applyCrawlDelay();\n \n const batch = queue.splice(0, concurrency);\n \n for (const url of batch) {\n const normalizedUrlStr = normalizeUrl(url);\n if (visited.has(normalizedUrlStr) || !normalizedUrlStr.startsWith(origin)) continue;\n \n // Check robots.txt\n if (!this.isUrlAllowed(normalizedUrlStr, origin)) {\n console.info(`[reef] skipping disallowed URL: ${normalizedUrlStr}`);\n continue;\n }\n \n visited.add(normalizedUrlStr);\n\n try {\n const response = await fetch(normalizedUrlStr);\n if (!response.ok) continue;\n const html = await response.text();\n const content = this.extractAllContent(html, normalizedUrlStr);\n addToIndex(this.index, content, this.config.tokenizePipeline);\n\n const links = extractLinks(html, normalizedUrlStr)\n .filter(l => l.url.startsWith(origin))\n .map(l => normalizeUrl(l.url))\n .filter((u, i, arr) => arr.indexOf(u) === i);\n queue.push(...links);\n } catch (e) {\n continue;\n }\n }\n }\n console.info(`[reef] indexed ${visited.size} pages via same-origin crawl`);\n onReady();\n };\n\n processQueue();\n }\n\n private async fetchPagesParallel(urls: string[], sitemapUrl: string, pageHashes: Record = {}): Promise {\n const concurrency = 6;\n const sections: IndexRecord[] = [];\n const results: (IndexRecord[] | null)[] = new Array(urls.length);\n\n let idx = 0;\n const fetchBatch = async () => {\n while (idx < urls.length) {\n const i = idx++;\n const pageUrl = this.resolveUrl(urls[i], sitemapUrl);\n try {\n // Check if we have cached info for this URL\n const cachedPageInfo = pageHashes[pageUrl];\n \n // Skip if content hasn't changed (incremental crawling)\n if (cachedPageInfo) {\n const headResponse = await fetch(pageUrl, { method: 'HEAD' });\n if (headResponse.ok) {\n const currentEtag = headResponse.headers.get('ETag');\n const currentLastModified = headResponse.headers.get('Last-Modified');\n \n // Check if content is unchanged\n if ((cachedPageInfo.etag && currentEtag === cachedPageInfo.etag) ||\n (cachedPageInfo.lastModified && currentLastModified === cachedPageInfo.lastModified)) {\n console.info(`[reef] skipping unchanged page: ${pageUrl}`);\n results[i] = null; // Skip this page\n return;\n }\n }\n }\n \n // Fetch the full page content\n const pageResponse = await fetch(pageUrl);\n if (pageResponse.ok) {\n const html = await pageResponse.text();\n \n // Check content hash if we have it\n if (cachedPageInfo?.contentHash) {\n const currentContentHash = this.hashContent(html);\n if (currentContentHash === cachedPageInfo.contentHash) {\n console.info(`[reef] skipping unchanged page (content hash match): ${pageUrl}`);\n results[i] = null; // Skip this page\n return;\n }\n }\n \n const pageSections = this.extractAllContent(html, pageUrl);\n results[i] = pageSections;\n \n // Update page metadata with new hash/etag info\n pageHashes[pageUrl] = {\n etag: pageResponse.headers.get('ETag'),\n lastModified: pageResponse.headers.get('Last-Modified'),\n contentHash: this.hashContent(html),\n timestamp: Date.now()\n };\n }\n } catch {\n results[i] = null;\n }\n }\n };\n\n await Promise.all([...Array(concurrency)].map(() => fetchBatch()));\n\n for (let i = 0; i < results.length; i++) {\n if (results[i]) {\n sections.push(...(results[i] ?? []));\n }\n }\n\n return sections;\n }\n\n private async fetchPagesWithWorker(urls: string[], sitemapUrl: string, onReady: () => void): Promise {\n const workerUrl = new URL('../worker.js', import.meta.url).href;\n const workerCount = Math.min(navigator.hardwareConcurrency || 4, 4);\n \n return new Promise((resolve, reject) => {\n const workers: Worker[] = [];\n const results: Map = new Map();\n let completedWorkers = 0;\n \n // Shard the URLs across workers\n const shards = this.shardArray(urls, workerCount);\n \n for (let i = 0; i < workerCount; i++) {\n const worker = new Worker(workerUrl);\n workers.push(worker);\n \n const workerId = i;\n const shard = shards[i] || [];\n \n const messageHandler = (e: MessageEvent) => {\n const { result, error, json, workerIndex } = e.data as { \n result: string; \n error: string; \n json: string; \n workerIndex: number \n };\n \n if (workerIndex !== workerId) return; // Not for this worker\n \n if (error) {\n console.error(`[reef] worker ${workerId} error:`, error);\n results.set(workerId, { result: 'error', error });\n } else if (json) {\n results.set(workerId, { result: 'ok', error: '' });\n // Store the serialized index from this worker\n // We'll merge them after all workers complete\n }\n \n completedWorkers++;\n \n if (completedWorkers === workerCount) {\n // All workers completed, merge results\n this.mergeWorkerResults(workers, results, onReady, resolve, reject);\n }\n };\n \n const errorHandler = (e: ErrorEvent) => {\n console.error(`[reef] worker ${workerId} error:`, e.error);\n results.set(workerId, { result: 'error', error: e.error?.toString() || 'Unknown error' });\n completedWorkers++;\n \n if (completedWorkers === workerCount) {\n this.mergeWorkerResults(workers, results, onReady, resolve, reject);\n }\n };\n \n worker.addEventListener('message', messageHandler);\n worker.addEventListener('error', errorHandler);\n \n const config = {\n scope: this.config.scope,\n indexActions: this.config.indexActions,\n indexMedia: this.config.indexMedia,\n indexStructuredData: this.config.indexStructuredData,\n indexHidden: this.config.indexHidden,\n excludeAction: this.config.excludeAction,\n fileExtensions: this.config.fileExtensions,\n };\n \n const id = Date.now() + i; // Unique ID for each worker\n worker.postMessage({\n id,\n workerIndex: i,\n action: 'indexPages',\n payload: {\n pages: shard.map(url => [this.resolveUrl(url, sitemapUrl), '']), // URL and empty HTML initially\n config,\n shardIndex: i,\n totalShards: workerCount\n },\n });\n }\n });\n }\n \n // Helper to shard an array into N parts\n private shardArray(array: T[], count: number): T[][] {\n const shards: T[][] = [];\n const shardSize = Math.ceil(array.length / count);\n \n for (let i = 0; i < count; i++) {\n const start = i * shardSize;\n const end = start + shardSize;\n shards.push(array.slice(start, end));\n }\n \n return shards;\n }\n \n // Merge results from all workers\n private async mergeWorkerResults(\n workers: Worker[], \n results: Map, \n onReady: () => void, \n resolve: (value: IndexRecord[] | PromiseLike) => void, \n reject: (reason?: any) => void\n ): Promise {\n // Clean up workers\n for (const worker of workers) {\n worker.terminate();\n }\n \n // Check for errors\n let hasErrors = false;\n for (const [workerId, result] of results) {\n if (result.error) {\n hasErrors = true;\n break;\n }\n }\n \n if (hasErrors) {\n // If any worker failed, fall back to single-threaded approach\n console.warn('[reef] worker pool had errors, falling back to single-threaded');\n try {\n const fetchedSections = await this.fetchPagesParallel(\n Array.from(results.keys()).map(i => this.resolveUrl('', '')), // Empty for fallback\n '',\n {}\n );\n onReady();\n resolve(fetchedSections);\n } catch (e) {\n reject(e);\n }\n return;\n }\n \n // All workers completed successfully\n // For now, fall back to single-threaded as the worker communication \n // would need significant changes to return partial indices\n console.warn('[reef] worker pool: TODO implement proper merging of worker results');\n onReady();\n resolve([]);\n }\n}", "/**\n * @file Local library storage for Reef for Browsers.\n * Persists user Bookmarks, Snippets, PageNotes, and Recent pages\n * in chrome.storage.local. No remote sync, no telemetry.\n */\n\nexport interface Bookmark {\n id: string;\n url: string;\n title: string;\n selectedText?: string;\n selector?: string;\n contextBefore?: string;\n contextAfter?: string;\n note: string;\n tags: string[];\n favicon?: string;\n createdAt: number;\n updatedAt: number;\n}\n\nexport interface Snippet {\n id: string;\n text: string;\n title: string;\n tags: string[];\n source?: { url: string; title: string };\n createdAt: number;\n updatedAt: number;\n}\n\nexport interface PageNote {\n url: string;\n text: string;\n title: string;\n updatedAt: number;\n}\n\nexport interface RecentPage {\n url: string;\n title: string;\n favicon?: string;\n visitedAt: number;\n recordCount: number;\n}\n\nconst KEY = {\n bookmarks: 'reef.bookmarks',\n snippets: 'reef.snippets',\n pageNotes: 'reef.pageNotes',\n recents: 'reef.recents',\n} as const;\n\nfunction genId(): string {\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n}\n\nfunction storageApi(): typeof chrome.storage.local | null {\n if (typeof chrome !== 'undefined' && chrome.storage?.local) {\n return chrome.storage.local;\n }\n return null;\n}\n\nasync function getArray(key: string): Promise {\n const api = storageApi();\n if (!api) return [];\n const data = await api.get([key]);\n return (data[key] as T[]) || [];\n}\n\nasync function setArray(key: string, value: T[]): Promise {\n const api = storageApi();\n if (!api) return;\n await api.set({ [key]: value });\n}\n\n// \u2500\u2500\u2500 BOOKMARKS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport async function listBookmarks(query = '', tags: string[] = []): Promise {\n const all = await getArray(KEY.bookmarks);\n return filterItems(all, query, tags);\n}\n\nexport async function createBookmark(input: Omit): Promise {\n const now = Date.now();\n const bookmark: Bookmark = { ...input, id: genId(), createdAt: now, updatedAt: now };\n const all = await getArray(KEY.bookmarks);\n all.unshift(bookmark);\n await setArray(KEY.bookmarks, all);\n return bookmark;\n}\n\nexport async function updateBookmark(id: string, patch: Partial): Promise {\n const all = await getArray(KEY.bookmarks);\n const idx = all.findIndex(b => b.id === id);\n if (idx < 0) return null;\n all[idx] = { ...all[idx], ...patch, id, updatedAt: Date.now() };\n await setArray(KEY.bookmarks, all);\n return all[idx];\n}\n\nexport async function deleteBookmark(id: string): Promise {\n const all = await getArray(KEY.bookmarks);\n const next = all.filter(b => b.id !== id);\n if (next.length === all.length) return false;\n await setArray(KEY.bookmarks, next);\n return true;\n}\n\n// \u2500\u2500\u2500 SNIPPETS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport async function listSnippets(query = '', tags: string[] = []): Promise {\n const all = await getArray(KEY.snippets);\n return filterItems(all, query, tags);\n}\n\nexport async function createSnippet(input: Omit): Promise {\n const now = Date.now();\n const snippet: Snippet = { ...input, id: genId(), createdAt: now, updatedAt: now };\n const all = await getArray(KEY.snippets);\n all.unshift(snippet);\n await setArray(KEY.snippets, all);\n return snippet;\n}\n\nexport async function updateSnippet(id: string, patch: Partial): Promise {\n const all = await getArray(KEY.snippets);\n const idx = all.findIndex(s => s.id === id);\n if (idx < 0) return null;\n all[idx] = { ...all[idx], ...patch, id, updatedAt: Date.now() };\n await setArray(KEY.snippets, all);\n return all[idx];\n}\n\nexport async function deleteSnippet(id: string): Promise {\n const all = await getArray(KEY.snippets);\n const next = all.filter(s => s.id !== id);\n if (next.length === all.length) return false;\n await setArray(KEY.snippets, next);\n return true;\n}\n\n// \u2500\u2500\u2500 PAGE NOTES \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport async function getPageNote(url: string): Promise {\n const all = await getArray(KEY.pageNotes);\n return all.find(n => n.url === url) || null;\n}\n\nexport async function listPageNotes(query = ''): Promise {\n const all = await getArray(KEY.pageNotes);\n if (!query) return all;\n const q = query.toLowerCase();\n return all.filter(n =>\n n.text.toLowerCase().includes(q) ||\n n.title.toLowerCase().includes(q) ||\n n.url.toLowerCase().includes(q)\n );\n}\n\nexport async function setPageNote(url: string, text: string, title: string): Promise {\n const all = await getArray(KEY.pageNotes);\n const existing = all.findIndex(n => n.url === url);\n const note: PageNote = { url, text, title, updatedAt: Date.now() };\n if (existing >= 0) all[existing] = note;\n else all.unshift(note);\n await setArray(KEY.pageNotes, all);\n return note;\n}\n\nexport async function deletePageNote(url: string): Promise {\n const all = await getArray(KEY.pageNotes);\n const next = all.filter(n => n.url !== url);\n if (next.length === all.length) return false;\n await setArray(KEY.pageNotes, next);\n return true;\n}\n\n// \u2500\u2500\u2500 RECENT PAGES \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst RECENT_MAX = 30;\n\nexport async function listRecents(): Promise {\n return getArray(KEY.recents);\n}\n\nexport async function recordRecent(page: Omit): Promise {\n const all = await getArray(KEY.recents);\n const next = [{ ...page, visitedAt: Date.now() }, ...all.filter(p => p.url !== page.url)].slice(0, RECENT_MAX);\n await setArray(KEY.recents, next);\n}\n\nexport async function clearRecents(): Promise {\n await setArray(KEY.recents, []);\n}\n\n// \u2500\u2500\u2500 TAGS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport async function allBookmarkTags(): Promise {\n return collectTags(await getArray(KEY.bookmarks));\n}\n\nexport async function allSnippetTags(): Promise {\n return collectTags(await getArray(KEY.snippets));\n}\n\n// \u2500\u2500\u2500 HELPERS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction filterItems(items: T[], query: string, tags: string[]): T[] {\n let out = items;\n if (tags.length) {\n out = out.filter(item => tags.every(t => item.tags.includes(t)));\n }\n if (query) {\n const q = query.toLowerCase();\n out = out.filter(item => {\n const hay = JSON.stringify(item).toLowerCase();\n return hay.includes(q);\n });\n }\n return out;\n}\n\nfunction collectTags(items: T[]): string[] {\n const set = new Set();\n for (const item of items) for (const tag of item.tags) set.add(tag);\n return Array.from(set).sort();\n}\n\nexport function parseTagsInput(raw: string): string[] {\n return Array.from(new Set(\n raw\n .split(/[,\\n]/)\n .map(s => s.trim().replace(/^#/, ''))\n .filter(Boolean)\n ));\n}\n"], - "mappings": "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", - "names": ["normalizedHeading", "selectors", "inputElement", "loadIndex", "saveIndex"] -} diff --git a/reef-extension-chrome/content.js b/reef-extension-chrome/content.js deleted file mode 100644 index 8f9bdde..0000000 --- a/reef-extension-chrome/content.js +++ /dev/null @@ -1,2489 +0,0 @@ -"use strict"; -(() => { - // ../src/extraction.ts - function stripTags(value) { - let result = ""; - let inTag = false; - for (let i = 0; i < value.length; i++) { - const char = value[i]; - if (char === "<" && inTag === false) { - inTag = true; - } else if (char === ">" && inTag === true) { - inTag = false; - } else if (inTag === false) { - result += char; - } - } - return result.replace(/\s+/g, " ").trim(); - } - function generateSelector(element) { - const path = []; - let current = element; - while (current && current.nodeType === Node.ELEMENT_NODE) { - let selector = current.tagName.toLowerCase(); - if (current.id) { - selector += `#${current.id}`; - path.unshift(selector); - break; - } else if (current.className) { - const classes = current.className.trim().split(/\s+/); - if (classes.length) { - selector += `.${classes.join(".")}`; - } - } - let siblingIndex = 1; - let sibling = current.previousElementSibling; - while (sibling) { - if (sibling.tagName === current.tagName) { - siblingIndex++; - } - sibling = sibling.previousElementSibling; - } - if (siblingIndex > 1) { - selector += `:nth-child(${siblingIndex})`; - } - path.unshift(selector); - current = current.parentElement; - } - return path.length > 0 ? path.join(" > ") : element.tagName.toLowerCase(); - } - function generateStableSelector(element) { - const candidates = []; - const push = (value) => { - if (value && !candidates.includes(value)) candidates.push(value); - }; - const escape = (value) => typeof CSS !== "undefined" && CSS.escape ? CSS.escape(value) : value.replace(/(["\\])/g, "\\$1"); - for (const attr of ["data-testid", "data-test", "data-agent-id", "id"]) { - const value = element.getAttribute(attr); - if (value) push(attr === "id" ? `#${escape(value)}` : `[${attr}="${escape(value)}"]`); - } - const aria = element.getAttribute("aria-label"); - if (aria) push(`[aria-label="${escape(aria)}"]`); - const role = element.getAttribute("role"); - const name = extractActionName(element); - if (role && name) push(`[role="${escape(role)}"][aria-label="${escape(name)}"]`); - if (role) push(`[role="${escape(role)}"]`); - push(generateSelector(element)); - let sibling = element.previousElementSibling; - let position = 1; - while (sibling) { - if (sibling.tagName === element.tagName) position++; - sibling = sibling.previousElementSibling; - } - push(`xpath=//${element.tagName.toLowerCase()}[${position}]`); - return candidates; - } - function walkComposed(root, visit, iframePath = []) { - const elements = root instanceof Document ? Array.from(root.documentElement ? [root.documentElement] : []) : [root]; - const walk = (element, path) => { - visit(element, path); - if (element.shadowRoot) walkRoot(element.shadowRoot, path); - for (const child of Array.from(element.children)) walk(child, path); - if (element.tagName.toLowerCase() === "iframe") { - try { - const frame = element.contentDocument; - if (frame) walkRoot(frame, [...path, Array.from(element.parentElement?.children || []).indexOf(element)]); - } catch { - } - } - }; - const walkRoot = (value, path) => { - if (value instanceof Document) { - if (value.documentElement) walk(value.documentElement, path); - } else if (value instanceof ShadowRoot) for (const child of Array.from(value.children)) walk(child, path); - else walk(value, path); - }; - for (const element of elements) walk(element, iframePath); - } - function isFocusableAction(element) { - const tag = element.tagName.toLowerCase(); - return ["a", "button", "input", "textarea", "select", "summary"].includes(tag) || ["button", "link", "tab", "menuitem", "checkbox", "radio", "combobox", "option"].includes(element.getAttribute("role") || "") || element.hasAttribute("contenteditable") || element.hasAttribute("tabindex") && Number(element.getAttribute("tabindex")) >= 0 || element.hasAttribute("data-reef-action"); - } - function extractAccessibilityTree(root = document) { - const records = []; - walkComposed(root, (element, iframePath) => { - if (!isFocusableAction(element)) return; - const label = extractActionName(element); - if (!label) return; - const selectors = generateStableSelector(element); - records.push({ id: `${typeof location !== "undefined" ? location.href : ""}#accessibility-${records.length}`, url: typeof location !== "undefined" ? location.href : "", headingText: label, headingId: `accessibility-${records.length}`, breadcrumb: "", bodyText: label, type: element.matches("input,textarea,select,[contenteditable]") ? "field" : "action", selector: selectors[0], selectors, iframePath, label, destructive: isDestructiveAction(label) }); - }); - return records; - } - function extractHeadingId(fullMatch, text) { - const idMatch = fullMatch.match(/\bid=["']([^"']+)['"]/i); - if (idMatch?.[1]) return idMatch[1]; - const stripped = text.toLowerCase().replace(/[^a-z0-9]+/g, "-"); - return stripped || Math.random().toString(36).slice(2); - } - function hasExplicitId(fullMatch) { - return /\bid=["'][^"']+["']/i.test(fullMatch); - } - function findParentSectionId(html, headingMatchEnd) { - const afterHeading = html.slice(headingMatchEnd, headingMatchEnd + 500); - const idMatch = afterHeading.match(/]*id="([^"]+)"/i); - if (idMatch?.[1]) return idMatch[1]; - const articleMatch = afterHeading.match(/]*id="([^"]+)"/i); - if (articleMatch?.[1]) return articleMatch[1]; - return null; - } - var headingCache = /* @__PURE__ */ new Map(); - function extractSections(html, url) { - if (headingCache.has(url)) { - return headingCache.get(url); - } - const cleanHtml = html.replace(//gi, " ").replace(//gi, " ").replace(//gi, " ").replace(//g, " "); - const matches = []; - const headingRegexGlobal = /<(h[1-6])[^>]*>([\s\S]*?)<\/h[1-6]>/gi; - let match; - headingRegexGlobal.lastIndex = 0; - while ((match = headingRegexGlobal.exec(cleanHtml)) !== null) { - const [, tag, text] = match; - const headingText = stripTags(text); - const level = parseInt(tag[1], 10); - matches.push({ - level, - index: match.index, - text: headingText, - id: extractHeadingId(match[0], headingText), - hasRealId: hasExplicitId(match[0]) - }); - } - const len = matches.length; - const sections = new Array(len); - for (let i = 0; i < len; i++) { - const heading = matches[i]; - const nextHeading = matches[i + 1]; - const start = heading.index + heading.text.length; - const end = nextHeading?.index ?? cleanHtml.length; - const content = cleanHtml.slice(start, end); - const bodyText = stripTags(content).replace(/\s+/g, " ").trim(); - let breadcrumb = ""; - for (let j = 0; j <= i; j++) { - if (j > 0) breadcrumb += " \u203A "; - breadcrumb += matches[j].text; - } - const parentSectionId = heading.hasRealId ? null : findParentSectionId(cleanHtml, heading.index + heading.text.length); - const selector = heading.hasRealId ? "#" + heading.id : parentSectionId ? "#" + parentSectionId : void 0; - sections[i] = { - id: `${url}#${heading.id}`, - url: `${url}#${heading.id}`, - headingText: heading.text, - headingId: heading.id, - breadcrumb, - bodyText, - type: "section", - selector - }; - } - headingCache.set(url, sections); - return sections; - } - function extractActionName(element) { - const ariaLabel = element.getAttribute("aria-label"); - if (ariaLabel?.trim()) return ariaLabel.trim(); - const ariaLabelledBy = element.getAttribute("aria-labelledby"); - if (ariaLabelledBy) { - const labelledElement = document.getElementById(ariaLabelledBy); - if (labelledElement?.textContent?.trim()) { - return labelledElement.textContent.trim(); - } - } - const textContent = element.textContent?.trim(); - if (textContent) return textContent; - const title = element.getAttribute("title"); - if (title?.trim()) return title.trim(); - return null; - } - function isDestructiveAction(label) { - const destructiveVerbs = [ - "delete", - "remove", - "cancel subscription", - "unsubscribe", - "pay", - "checkout", - "submit order", - "confirm" - ]; - const lowerLabel = label.toLowerCase(); - return destructiveVerbs.some((verb) => lowerLabel.includes(verb)); - } - function extractActions(html, url, excludeSelectors) { - const actions = []; - const doc = new DOMParser().parseFromString(html, "text/html"); - const selectors = [ - "button", - '[role="button"]', - 'input[type="button"]', - 'input[type="submit"]', - "summary", - "[data-reef-action]" - ]; - const elements = Array.from(doc.querySelectorAll(selectors.join(","))); - for (const element of elements) { - if (excludeSelectors && element.matches(excludeSelectors)) continue; - const label = extractActionName(element); - if (!label) continue; - const selectors2 = generateStableSelector(element); - actions.push({ - id: `${url}#action-${actions.length}`, - url, - headingText: label, - headingId: `action-${actions.length}`, - breadcrumb: "", - bodyText: label, - type: "action", - selector: selectors2[0], - selectors: selectors2, - destructive: isDestructiveAction(label), - label - }); - } - return actions; - } - function extractFields(html, url) { - const fields = []; - const doc = new DOMParser().parseFromString(html, "text/html"); - const formElements = Array.from(doc.querySelectorAll("form")); - for (const form of formElements) { - let breadcrumb = ""; - let current = form.parentElement; - while (current && current !== doc.body) { - if (current.matches('h1, h2, h3, h4, h5, h6, article, section, [role="main"], main')) { - const headingText = current.textContent?.trim() || ""; - if (headingText) { - breadcrumb = headingText; - } - break; - } - current = current.parentElement; - } - const inputs = Array.from(form.querySelectorAll("input, textarea, select")); - for (const input of inputs) { - if (input.matches('input[type="hidden"], input[type="button"], input[type="submit"], input[type="reset"]')) { - continue; - } - let label = ""; - const id = input.id; - if (id) { - const labelElement = doc.querySelector(`label[for="${id}"]`); - if (labelElement) { - label = labelElement.textContent?.trim() || ""; - } - } - if (!label) { - const parentLabel = input.closest("label"); - if (parentLabel) { - label = parentLabel.textContent?.trim() || ""; - const inputElement2 = input; - if (label && inputElement2.value && label.includes(inputElement2.value)) { - label = label.replace(inputElement2.value, "").trim(); - } - } - } - if (!label) { - const inputElement2 = input; - const placeholder = "placeholder" in inputElement2 ? inputElement2.placeholder : ""; - label = placeholder || input.getAttribute("aria-label") || ""; - } - if (!label) continue; - const selectors = generateStableSelector(input); - const inputElement = input; - fields.push({ - id: `${url}#field-${fields.length}`, - url, - headingText: label, - headingId: `field-${fields.length}`, - breadcrumb, - bodyText: label, - type: "field", - selector: selectors[0], - selectors, - label, - value: inputElement.value - }); - } - } - return fields; - } - function extractLinks(html, url) { - const links = []; - const doc = new DOMParser().parseFromString(html, "text/html"); - const anchors = Array.from(doc.querySelectorAll("a[href]")); - for (const anchor of anchors) { - if (anchor.hasAttribute("rel") && anchor.getAttribute("rel")?.toLowerCase().includes("nofollow")) continue; - const href = anchor.getAttribute("href"); - if (!href) continue; - if (href === "#" || href.startsWith("javascript:")) continue; - const linkText = anchor.textContent?.trim() || ""; - if (!linkText) continue; - const resolvedUrl = resolveUrl(href, url); - const isExternal = !resolvedUrl.startsWith(window.location.origin); - const selectors = generateStableSelector(anchor); - links.push({ - id: `${url}#link-${links.length}`, - url: resolvedUrl, - headingText: linkText, - headingId: `link-${links.length}`, - breadcrumb: "", - bodyText: linkText, - type: isExternal ? "link" : "section", - selector: selectors[0], - selectors - }); - } - return links; - } - function extractFiles(html, url, extensions) { - const files = []; - const doc = new DOMParser().parseFromString(html, "text/html"); - const fileExtensions = extensions?.split(",").map((e) => e.trim().toLowerCase()) ?? ["pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "zip", "csv"]; - const anchors = Array.from(doc.querySelectorAll("a[href]")); - for (const anchor of anchors) { - const href = anchor.getAttribute("href"); - if (!href) continue; - const isFile = fileExtensions.some( - (ext) => href.toLowerCase().endsWith(`.${ext}`) || href.toLowerCase().endsWith(`.${ext}?`) || href.toLowerCase().endsWith(`.${ext}#`) - ); - if (!isFile) continue; - const linkText = anchor.textContent?.trim() || href.split("/").pop() || ""; - if (!linkText) continue; - const resolvedUrl = resolveUrl(href, url); - const selectors = generateStableSelector(anchor); - files.push({ - id: `${url}#file-${files.length}`, - url: resolvedUrl, - headingText: linkText, - headingId: `file-${files.length}`, - breadcrumb: "", - bodyText: linkText, - type: "file", - selector: selectors[0], - selectors - }); - } - return files; - } - function extractMedia(html, url) { - const media = []; - const doc = new DOMParser().parseFromString(html, "text/html"); - const images = Array.from(doc.querySelectorAll("img")); - for (const img of images) { - const alt = img.alt.trim(); - if (!alt) continue; - let caption = ""; - const figure = img.closest("figure"); - if (figure) { - const figcaption = figure.querySelector("figcaption"); - if (figcaption) { - caption = figcaption.textContent?.trim() || ""; - } - } - const textToIndex = caption ? `${alt} ${caption}` : alt; - if (!textToIndex.trim()) continue; - const selectors = generateStableSelector(img); - media.push({ - id: `${url}#media-image-${media.length}`, - url, - headingText: alt, - headingId: `media-image-${media.length}`, - breadcrumb: "", - bodyText: textToIndex, - type: "media", - selector: selectors[0], - selectors - }); - } - const mediaElements = Array.from(doc.querySelectorAll("video, audio")); - for (const element of mediaElements) { - const title = element.getAttribute("title") || element.getAttribute("aria-label") || ""; - if (!title) continue; - let transcript = ""; - const tracks = Array.from(element.querySelectorAll('track[kind="captions"], track[kind="subtitles"]')); - for (const track of tracks) { - const src = track.getAttribute("src"); - if (src) { - transcript += `[Transcript available: ${src}] `; - } - } - const textToIndex = transcript ? `${title} ${transcript}` : title; - if (!textToIndex.trim()) continue; - const selectors = generateStableSelector(element); - media.push({ - id: `${url}#media-${media.length}`, - url, - headingText: title, - headingId: `media-${media.length}`, - breadcrumb: "", - bodyText: textToIndex, - type: "media", - selector: selectors[0], - selectors, - transcript: transcript.trim() - }); - } - return media; - } - function extractStructuredData(html, url) { - const structured = []; - const doc = new DOMParser().parseFromString(html, "text/html"); - const jsonLdScripts = Array.from(doc.querySelectorAll('script[type="application/ld+json"]')); - for (const script of jsonLdScripts) { - try { - const data = JSON.parse(script.textContent || "{}"); - if (Array.isArray(data) ? data.some((item) => item["@type"] === "FAQPage") : data["@type"] === "FAQPage") { - const faqItems = Array.isArray(data) ? data.flatMap((item) => item.mainEntity || []) : data.mainEntity || []; - for (const [index, question] of faqItems.entries()) { - if (!question || !question.name) continue; - const answer = question.acceptedAnswer?.text || question.suggestedAnswer?.text || ""; - if (!answer) continue; - const textToIndex = `${question.name} ${answer}`; - structured.push({ - id: `${url}#structured-faq-${index}`, - url, - headingText: question.name, - headingId: `structured-faq-${index}`, - breadcrumb: "", - bodyText: textToIndex, - type: "structured", - structuredData: { question: question.name, answer } - }); - } - } else if (data["@type"]) { - const type = data["@type"]; - const name = data.name || data.headline || ""; - const description = data.description || ""; - if (!name && !description) continue; - const textToIndex = `${name} ${description}`.trim(); - if (!textToIndex) continue; - structured.push({ - id: `${url}#structured-${type.toLowerCase()}-${structured.length}`, - url, - headingText: name || "Structured Data", - headingId: `structured-${type.toLowerCase()}-${structured.length}`, - breadcrumb: "", - bodyText: textToIndex, - type: "structured", - structuredData: data - }); - } - } catch (e) { - continue; - } - } - return structured; - } - function resolveUrl(value, base) { - if (!value) return base; - try { - return new URL(value, base).toString(); - } catch { - return value; - } - } - - // ../src/agent.ts - var Agent = class { - constructor(index, inspector, actionsMode = "execute") { - this.actionCount = 0; - this.lastRoute = typeof window !== "undefined" ? window.location.href : ""; - this.lastAction = { success: false, reason: "no-action" }; - this.index = index; - this.inspector = inspector; - this.options = typeof actionsMode === "string" ? { actionsMode } : actionsMode; - this.actionsMode = this.options.actionsMode ?? "execute"; - this.installRouteObserver(); - } - async click(selector) { - if (typeof selector !== "string" && selector.destructive && (this.actionsMode === "navigate-only" || this.options.destructive === false)) throw new Error("destructive-action-blocked"); - this.guardAction(); - const resolved = await this.resolveSelector(selector); - if (!resolved.success) { - if (typeof selector !== "string" && this.actionsMode === "navigate-only") return this; - throw new Error(resolved.reason || "Click failed"); - } - const element = resolved.element; - this.ensureVisible(element ?? null); - const before = this.fingerprint(); - if (element && typeof MouseEvent !== "undefined") { - const clickEvent = new MouseEvent("click", { - bubbles: true, - cancelable: true, - view: typeof window !== "undefined" ? window : void 0 - }); - element.dispatchEvent(clickEvent); - } - await this.waitForStable({ quietMs: 50, timeout: 1e3 }); - if (!this.changedSince(before) && typeof selector !== "string" && selector.selectors?.length) { - const retry = await this.resolveSelector({ ...selector, selectors: selector.selectors.slice(1) }); - if (retry.success && retry.element && retry.element !== element) retry.element.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true })); - } - this.lastAction = { success: true, changed: this.changedSince(before), url: typeof window !== "undefined" ? window.location.href : void 0, element }; - return this; - } - async type(selector, value) { - this.guardAction(); - const resolved = await this.resolveSelector(selector); - if (!resolved.success) { - throw new Error(resolved.reason || "Type failed"); - } - const element = resolved.element; - this.ensureVisible(element); - if (element && "value" in element) { - const descriptor = Object.getOwnPropertyDescriptor(element, "value"); - if (descriptor && descriptor.set) { - descriptor.set.call(element, value); - } else { - element.value = value; - } - element.dispatchEvent(new Event("input", { bubbles: true })); - element.dispatchEvent(new Event("change", { bubbles: true })); - } - this.lastAction = { success: true, changed: true, url: typeof window !== "undefined" ? window.location.href : void 0, element: element ?? void 0 }; - return this; - } - async submit(selector) { - let element = null; - if (selector) { - const resolved = await this.resolveSelector(selector); - if (resolved.success && resolved.element) { - element = resolved.element; - } - } else if (typeof document !== "undefined") { - element = document.querySelector("form") || document.querySelector('button[type="submit"]') || document.querySelector('input[type="submit"]'); - } - if (element) { - if (element instanceof HTMLFormElement) { - element.dispatchEvent(new Event("submit", { bubbles: true })); - } else { - const clickEvent = new MouseEvent("click", { - bubbles: true, - cancelable: true, - view: typeof window !== "undefined" ? window : void 0 - }); - element.dispatchEvent(clickEvent); - } - this.lastAction = { success: true, changed: true, url: typeof window !== "undefined" ? window.location.href : void 0, element }; - } - return this; - } - getLastActionResult() { - return { ...this.lastAction }; - } - async navigate(url) { - if ((url.startsWith("http://") || url.startsWith("https://") || url.startsWith("/")) && typeof window !== "undefined") { - const target = new URL(url, window.location.href).toString(); - const sameOrigin = new URL(target).origin === window.location.origin; - if (sameOrigin && target.split("#")[0] === window.location.href.split("#")[0] && window.history.pushState) { - window.history.pushState({}, "", target); - window.dispatchEvent(new PopStateEvent("popstate")); - await this.waitForStable(); - } else window.location.href = target; - } - return this; - } - async back() { - if (typeof window !== "undefined") { - window.history.back(); - } - return this; - } - async forward() { - if (typeof window !== "undefined") { - window.history.forward(); - } - return this; - } - async wait(timeout = 1e3) { - await new Promise((resolve) => setTimeout(resolve, timeout)); - return this; - } - async extract(selector) { - const resolved = await this.resolveSelector(selector); - if (!resolved.success) { - throw new Error(resolved.reason || "Extract failed"); - } - const element = resolved.element; - if (!element) { - throw new Error("Element not found"); - } - if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) { - return element.value; - } - return element.textContent?.trim() || ""; - } - async observe(options) { - if (typeof document === "undefined") return []; - const records = extractAccessibilityTree(options?.root ?? document).filter((record) => { - if (options?.includeHidden) return true; - const element = this.resolveRecordElement(record); - return !!element && (!options?.inViewport && options?.inViewport !== false ? this.isVisible(element) : this.isVisible(element)); - }); - this.inspector.setRecords(records); - return records; - } - async waitForStable(options) { - if (typeof document === "undefined" || typeof MutationObserver === "undefined") return this; - const quietMs = options?.quietMs ?? 250; - const timeout = options?.timeout ?? 5e3; - await new Promise((resolve) => { - let quietTimer; - const observer = new MutationObserver(() => { - clearTimeout(quietTimer); - quietTimer = setTimeout(done, quietMs); - }); - const done = () => { - clearTimeout(quietTimer); - observer.disconnect(); - resolve(); - }; - observer.observe(document, { subtree: true, childList: true, attributes: true, characterData: true }); - quietTimer = setTimeout(done, quietMs); - setTimeout(done, timeout); - }); - return this; - } - isVisible(element) { - if (!element) return false; - const style = typeof window !== "undefined" ? window.getComputedStyle(element) : null; - if (style && (style.display === "none" || style.visibility === "hidden" || style.opacity === "0")) return false; - const rect = element.getBoundingClientRect(); - return rect.width > 0 && rect.height > 0 && (typeof window === "undefined" || rect.bottom >= 0 && rect.right >= 0 && rect.top <= window.innerHeight && rect.left <= window.innerWidth); - } - async exhaustPagination(options) { - const maxPages = options?.maxPages ?? 10; - const nextText = (options?.nextText ?? ["next", "older", "more"]).map((v) => v.toLowerCase()); - const merged = /* @__PURE__ */ new Map(); - for (let page = 0; page < maxPages && this.actionCount < (options?.maxActionsPerRun ?? this.options.maxActionsPerRun ?? Infinity); page++) { - const records = await this.observe({ inViewport: false, includeHidden: true }); - records.forEach((record) => merged.set(`${record.label}:${record.type}`, record)); - const next = records.find((record) => record.type === "action" && !record.destructive && nextText.some((text) => (record.label || "").toLowerCase().includes(text))); - if (next) await this.click(next); - else if (options?.scroll !== false && typeof window !== "undefined") { - window.scrollTo(0, document.body.scrollHeight); - await this.waitForStable(); - } else break; - if (!next && options?.scroll === false) break; - } - return [...merged.values()]; - } - async resolveSelector(selector) { - let element = null; - if (typeof selector === "string") { - element = this.queryComposed(selector); - } else if (selector) { - const record = this.index.allSections.find((r) => r.id === selector.id); - const candidates = record?.selectors?.length ? record.selectors : record?.selector ? [record.selector] : []; - for (const candidate of candidates) { - element = this.queryComposed(candidate, record?.iframePath); - if (element) break; - } - if (!element) element = this.resolveRecordElement(selector); - if (!element && selector.headingText) { - const match = await this.findActionable(selector.headingText); - if (match && match.id !== selector.id) return this.resolveSelector(match); - } - } - if (!element) { - return { success: false, reason: "element-not-found" }; - } - return { success: true, element }; - } - queryComposed(selector, iframePath) { - if (typeof document === "undefined") return null; - const find = (root) => { - if (selector.startsWith("xpath=")) { - try { - return document.evaluate(selector.slice(6), root, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; - } catch { - return null; - } - } - const direct = root.querySelector(selector); - if (direct) return direct; - for (const host of Array.from(root.querySelectorAll("*"))) if (host.shadowRoot) { - const found = find(host.shadowRoot); - if (found) return found; - } - return null; - }; - if (!iframePath?.length) return find(document); - let current = document; - for (const position of iframePath) { - const frame = current?.querySelectorAll("iframe")[position]; - current = frame?.contentDocument ?? null; - } - return current ? find(current) : null; - } - resolveRecordElement(record) { - const candidates = record.selectors?.length ? record.selectors : record.selector ? [record.selector] : []; - for (const selector of candidates) { - const found = this.queryComposed(selector, record.iframePath); - if (found) return found; - } - return null; - } - ensureVisible(element) { - if (element && !this.isVisible(element)) element.scrollIntoView({ block: "center", inline: "nearest" }); - } - fingerprint() { - return `${typeof window !== "undefined" ? window.location.href : ""}|${typeof document !== "undefined" ? document.body?.textContent?.length : 0}`; - } - changedSince(before) { - return this.fingerprint() !== before; - } - guardAction() { - this.actionCount++; - if (this.actionCount > (this.options.maxActionsPerRun ?? Infinity)) throw new Error("max-actions-per-run-exceeded"); - if (this.options.rateLimitMs) return; - } - installRouteObserver() { - if (typeof window === "undefined") return; - const onRoute = () => { - this.lastRoute = window.location.href; - void this.observe({ inViewport: false }); - }; - window.addEventListener("popstate", onRoute); - window.addEventListener("hashchange", onRoute); - for (const method of ["pushState", "replaceState"]) { - const historyMethod = window.history[method].bind(window.history); - window.history[method] = (...args) => { - const result = historyMethod(...args); - onRoute(); - return result; - }; - } - } - async findActionable(text) { - const results = this.index.allSections.filter( - (r) => r.type === "action" || r.type === "field" - ); - const normalized = text.toLowerCase().trim(); - for (const record of results) { - if (record.headingText.toLowerCase().includes(normalized) || record.label?.toLowerCase().includes(normalized)) { - return record; - } - } - return null; - } - async executeWorkflow(steps, options) { - const maxRetries = options?.maxRetries ?? 0; - const retryDelay = options?.retryDelay ?? 500; - const stopOnError = options?.stopOnError ?? true; - for (let i = 0; i < steps.length; i++) { - const step = steps[i]; - options?.onStepStart?.(step, i); - let attempt = 0; - let success = false; - while (attempt <= maxRetries && !success) { - try { - switch (step.action) { - case "click": - if (step.selector) { - await this.click(step.selector); - } else if (step.recordId) { - const record = this.index.allSections.find((r) => r.id === step.recordId); - if (record) await this.click(record); - } - success = true; - break; - case "type": - if (step.selector) { - await this.type(step.selector, step.value || ""); - } else if (step.recordId) { - const record = this.index.allSections.find((r) => r.id === step.recordId); - if (record) await this.type(record, step.value || ""); - } - success = true; - break; - case "navigate": - if (step.url) { - await this.navigate(step.url); - await this.waitForNavigation(); - } - success = true; - break; - case "extract": - success = true; - break; - case "submit": - await this.submit(step.selector); - success = true; - break; - case "back": - await this.back(); - success = true; - break; - case "forward": - await this.forward(); - success = true; - break; - case "wait": - if (step.timeout) { - await this.wait(step.timeout); - } - success = true; - break; - } - options?.onStepComplete?.(step, i, success); - } catch (error) { - attempt++; - if (attempt > maxRetries) { - options?.onStepError?.(step, i, error); - if (stopOnError) { - throw error; - } - } else { - await new Promise((resolve) => setTimeout(resolve, retryDelay)); - } - } - } - } - } - async waitForNavigation() { - await new Promise((resolve) => { - if (typeof document !== "undefined") { - const checkReady = () => { - if (document.readyState === "complete") { - resolve(); - } else { - setTimeout(checkReady, 100); - } - }; - checkReady(); - } else { - resolve(); - } - }); - } - getSession() { - return { - id: this.generateSessionId(), - url: typeof window !== "undefined" ? window.location.href : "about:blank", - timestamp: Date.now(), - cookies: this.getCookies(), - localStorage: this.getLocalStorageSnapshot() - }; - } - generateSessionId() { - return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15); - } - getCookies() { - const cookies = {}; - if (typeof document !== "undefined") { - document.cookie.split(";").forEach((cookie) => { - const [name, value] = cookie.trim().split("="); - if (name) cookies[name] = value || ""; - }); - } - return cookies; - } - getLocalStorageSnapshot() { - const storage = {}; - try { - if (typeof localStorage !== "undefined") { - for (let i = 0; i < localStorage.length; i++) { - const key = localStorage.key(i); - if (key) { - storage[key] = localStorage.getItem(key) || ""; - } - } - } - } catch (e) { - } - return storage; - } - }; - - // ../src/search-index.ts - function createTrieNode() { - return { - children: /* @__PURE__ */ new Map(), - records: [] - }; - } - function createSearchIndex() { - return { - headingIndex: /* @__PURE__ */ new Map(), - headingIds: /* @__PURE__ */ new Map(), - bodyIndex: /* @__PURE__ */ new Map(), - allSections: [], - queryCache: /* @__PURE__ */ new Map(), - popularQueries: [], - docFrequency: /* @__PURE__ */ new Map(), - fieldDocFreq: { - headingText: /* @__PURE__ */ new Map(), - bodyText: /* @__PURE__ */ new Map(), - label: /* @__PURE__ */ new Map(), - breadcrumb: /* @__PURE__ */ new Map() - }, - totalDocs: 0, - queryPopularity: /* @__PURE__ */ new Map(), - version: 1, - headingTrie: createTrieNode() - }; - } - - // src/spotlight.ts - var HOST_ID = "reef-spotlight-host"; - var MAX_ROWS_DEFAULT = 50; - var DEBOUNCE_DEFAULT = 80; - var MAX_RECENTS_DEFAULT = 8; - var STYLES = ` -:host { all: initial; } -*, *::before, *::after { box-sizing: border-box; } - -.backdrop { - position: fixed; - inset: 0; - background: rgba(15, 17, 21, 0.42); - -webkit-backdrop-filter: blur(8px); - backdrop-filter: blur(8px); - z-index: 2147483647; - display: flex; - align-items: flex-start; - justify-content: center; - padding-top: 14vh; - animation: reef-spotlight-fade 120ms ease-out; -} -.card { - width: min(640px, 92vw); - max-height: min(560px, 70vh); - background: #ffffff; - color: #111111; - border-radius: 14px; - box-shadow: 0 24px 64px rgba(0, 0, 0, 0.32), 0 4px 12px rgba(0, 0, 0, 0.18); - display: flex; - flex-direction: column; - overflow: hidden; - animation: reef-spotlight-slide 140ms cubic-bezier(0.2, 0.8, 0.2, 1); - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif; -} -.input-row { - display: flex; - align-items: center; - gap: 10px; - padding: 14px 18px; - border-bottom: 1px solid #ececec; -} -.input-row .glyph { - width: 18px; - height: 18px; - flex: 0 0 18px; - color: #6b7280; -} -.input-row input { - flex: 1 1 auto; - font-size: 18px; - line-height: 24px; - border: none; - outline: none; - background: transparent; - color: inherit; - font-family: inherit; - min-width: 0; -} -.input-row input::placeholder { color: #9ca3af; } -.input-row .esc { - font-size: 11px; - color: #6b7280; - border: 1px solid #e5e7eb; - border-radius: 4px; - padding: 1px 6px; - background: #fafafa; - user-select: none; -} -.results { - flex: 1 1 auto; - overflow-y: auto; - padding: 6px 0; - scrollbar-width: thin; -} -.empty { - padding: 28px 20px; - text-align: center; - color: #6b7280; - font-size: 14px; - line-height: 1.5; -} -.empty .suggestion { - color: #2563eb; - font-weight: 600; - text-decoration: underline; - cursor: pointer; -} -.empty .suggestion:hover { - color: #1d4ed8; -} -.row { - display: grid; - grid-template-columns: 18px 1fr auto; - align-items: center; - gap: 10px; - padding: 8px 16px; - cursor: pointer; - border-left: 2px solid transparent; - user-select: none; -} -.row[aria-selected="true"] { - background: #f3f4f6; - border-left-color: #111111; -} -.row .favicon { - width: 16px; - height: 16px; - border-radius: 2px; - background: #f3f4f6; - object-fit: contain; -} -.row .main { min-width: 0; } -.row .title { - font-size: 14px; - font-weight: 600; - line-height: 1.3; - color: #111111; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} -.row .title mark { - background: #fde68a; - color: inherit; - padding: 0 1px; - border-radius: 2px; -} -.row .url { - font-size: 12px; - color: #6b7280; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - margin-top: 1px; -} -.row .match { - font-size: 11px; - color: #6b7280; - max-width: 180px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} -.footer { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - padding: 8px 14px; - border-top: 1px solid #ececec; - font-size: 11px; - color: #6b7280; - background: #fafafa; -} -.footer .hints { display: flex; gap: 10px; flex-wrap: wrap; } -.footer .hint kbd { - font-family: inherit; - font-size: 10px; - border: 1px solid #e5e7eb; - background: #ffffff; - border-radius: 3px; - padding: 0 4px; - margin-right: 2px; -} -.footer .brand { font-weight: 600; color: #111111; } - -/* Section headers */ -.section-header { - font-size: 10px; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.8px; - color: #9ca3af; - padding: 8px 16px 4px; - user-select: none; -} - -/* Autocorrect banner */ -.autocorrect-banner { - padding: 8px 16px; - font-size: 12px; - color: #6b7280; - background: #fefce8; - border-bottom: 1px solid #fde68a; -} -.autocorrect-banner strong { color: #111111; } -.autocorrect-banner .autocorrect-orig { - color: #2563eb; - text-decoration: none; - cursor: pointer; -} -.autocorrect-banner .autocorrect-orig:hover { text-decoration: underline; } - -/* Site result rows */ -.row-site .site-icon { - display: flex; - align-items: center; - justify-content: center; - color: #6b7280; - background: #f3f4f6; - border-radius: 2px; -} -.row-site .site-icon svg { stroke: #6366f1; } - -/* Action rows */ -.row-action { opacity: 0.85; } -.row-action:hover { opacity: 1; } -.row-action .action-icon { - display: flex; - align-items: center; - justify-content: center; - background: #f0fdf4; - border-radius: 2px; -} -.row-action .action-icon svg { stroke: #16a34a; } -.row-action .action-title { font-style: italic; } -.row-action .action-badge { - font-size: 10px; - text-transform: uppercase; - letter-spacing: 0.5px; - color: #16a34a; - font-weight: 600; -} - -/* Dark theme */ -:host([data-theme="dark"]) .card { background: #1f2024; color: #e7e7ea; box-shadow: 0 24px 64px rgba(0, 0, 0, 0.55), 0 4px 12px rgba(0, 0, 0, 0.35); } -:host([data-theme="dark"]) .input-row { border-bottom-color: #2c2d31; } -:host([data-theme="dark"]) .input-row .glyph, -:host([data-theme="dark"]) .input-row input::placeholder, -:host([data-theme="dark"]) .row .url, -:host([data-theme="dark"]) .row .match, -:host([data-theme="dark"]) .footer { color: #a1a1aa; } -:host([data-theme="dark"]) .input-row .esc { background: #2c2d31; border-color: #3a3b40; color: #a1a1aa; } -:host([data-theme="dark"]) .row[aria-selected="true"] { background: #2c2d31; border-left-color: #ffffff; } -:host([data-theme="dark"]) .row .title { color: #e7e7ea; } -:host([data-theme="dark"]) .row .favicon { background: #2c2d31; } -:host([data-theme="dark"]) .row .title mark { background: #facc15; color: #111111; } -:host([data-theme="dark"]) .footer { background: #161719; border-top-color: #2c2d31; } -:host([data-theme="dark"]) .footer .brand { color: #e7e7ea; } -:host([data-theme="dark"]) .footer .hint kbd { background: #2c2d31; border-color: #3a3b40; color: #e7e7ea; } -:host([data-theme="dark"]) .empty .suggestion { color: #60a5fa; } -:host([data-theme="dark"]) .empty .suggestion:hover { color: #93bbfd; } -:host([data-theme="dark"]) .section-header { color: #71717a; } -:host([data-theme="dark"]) .autocorrect-banner { background: #422006; border-bottom-color: #854d0e; color: #a1a1aa; } -:host([data-theme="dark"]) .autocorrect-banner strong { color: #e7e7ea; } -:host([data-theme="dark"]) .autocorrect-banner .autocorrect-orig { color: #60a5fa; } -:host([data-theme="dark"]) .row-site .site-icon { background: #2c2d31; } -:host([data-theme="dark"]) .row-action .action-icon { background: #1a2e1a; } -:host([data-theme="dark"]) .row-action .action-badge { color: #4ade80; } - -@keyframes reef-spotlight-fade { from { opacity: 0; } to { opacity: 1; } } -@keyframes reef-spotlight-slide { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: translateY(0); } } -@media (prefers-reduced-motion: reduce) { - .backdrop, .card { animation: none; } -} -`; - function defaultSendMessage(msg) { - return new Promise((resolve) => { - try { - const result = globalThis.chrome?.runtime?.sendMessage(msg, (res) => resolve(res)); - if (result && typeof result.then === "function") { - result.then(resolve, () => resolve(void 0)); - } - } catch { - resolve(void 0); - } - }); - } - async function getStoredTheme() { - try { - const chrome2 = globalThis.chrome; - if (chrome2?.storage?.local?.get) { - const data = await new Promise((resolve) => { - chrome2.storage.local.get(["theme"], (d) => resolve(d)); - }); - const t = data?.theme; - if (t === "light" || t === "dark" || t === "system") return t; - } - } catch { - } - return "system"; - } - function resolveTheme(t) { - if (t === "system") { - try { - return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; - } catch { - return "light"; - } - } - return t; - } - function hostFromUrl(url) { - try { - return new URL(url).host; - } catch { - return url; - } - } - function escapeHtml(s) { - return s.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'"); - } - function highlight(text, query) { - if (!query) return escapeHtml(text); - const safe = escapeHtml(text); - const q = escapeHtml(query); - try { - const re = new RegExp(`(${q.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})`, "ig"); - return safe.replace(re, "$1"); - } catch { - return safe; - } - } - function truncate(s, n) { - if (s.length <= n) return s; - return s.slice(0, n - 1) + "\u2026"; - } - var FALLBACK_FAVICON = "data:image/svg+xml;utf8,"; - function createSpotlight(opts = {}) { - const maxRows = opts.maxRows ?? MAX_ROWS_DEFAULT; - const debounceMs = opts.debounceMs ?? DEBOUNCE_DEFAULT; - const maxRecents = opts.maxRecents ?? MAX_RECENTS_DEFAULT; - const send = opts.sendMessage ?? defaultSendMessage; - let host = null; - let shadow = null; - let card = null; - let input = null; - let results = null; - let footer = null; - let mounted = false; - let open = false; - let currentResults = []; - let unifiedResults = []; - let currentQuery = ""; - let currentSuggestion; - let currentSiteResults = []; - let currentActions = []; - let currentAutocorrected = false; - let currentReefSuggestions = []; - let selectedIndex = 0; - let perTabMatchIndex = /* @__PURE__ */ new Map(); - let debounceHandle = null; - let lastQueryKey = "\0never-queried\0"; - let inFlight = 0; - function mount() { - if (mounted) return; - host = document.createElement("div"); - host.id = HOST_ID; - host.style.position = "fixed"; - host.style.inset = "0"; - host.style.zIndex = "2147483647"; - host.style.display = "none"; - shadow = host.attachShadow({ mode: "open" }); - const style = document.createElement("style"); - style.textContent = STYLES; - shadow.appendChild(style); - const backdrop = document.createElement("div"); - backdrop.className = "backdrop"; - backdrop.addEventListener("mousedown", (e) => { - if (e.target === backdrop) hide(); - }); - card = document.createElement("div"); - card.className = "card"; - card.setAttribute("role", "dialog"); - card.setAttribute("aria-label", "Reef Spotlight"); - card.addEventListener("mousedown", (e) => e.stopPropagation()); - const inputRow = document.createElement("div"); - inputRow.className = "input-row"; - const glyph = document.createElementNS("http://www.w3.org/2000/svg", "svg"); - glyph.setAttribute("class", "glyph"); - glyph.setAttribute("viewBox", "0 0 20 20"); - glyph.setAttribute("fill", "none"); - glyph.setAttribute("stroke", "currentColor"); - glyph.setAttribute("stroke-width", "2"); - const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle"); - circle.setAttribute("cx", "9"); - circle.setAttribute("cy", "9"); - circle.setAttribute("r", "6"); - const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); - line.setAttribute("x1", "13.5"); - line.setAttribute("y1", "13.5"); - line.setAttribute("x2", "18"); - line.setAttribute("y2", "18"); - line.setAttribute("stroke-linecap", "round"); - glyph.appendChild(circle); - glyph.appendChild(line); - inputRow.appendChild(glyph); - input = document.createElement("input"); - input.type = "text"; - input.placeholder = "Search tabs & page content\u2026"; - input.autocomplete = "off"; - input.spellcheck = false; - input.setAttribute("aria-label", "Search tabs and page content"); - input.setAttribute("aria-autocomplete", "list"); - input.setAttribute("aria-controls", "reef-spotlight-results"); - input.addEventListener("input", onInput); - input.addEventListener("keydown", onKeyDown); - inputRow.appendChild(input); - const esc = document.createElement("span"); - esc.className = "esc"; - esc.textContent = "esc"; - inputRow.appendChild(esc); - results = document.createElement("div"); - results.className = "results"; - results.id = "reef-spotlight-results"; - results.setAttribute("role", "listbox"); - results.setAttribute("aria-label", "Tabs"); - results.addEventListener("mousedown", (e) => { - const target = e.target; - const row = target.closest(".row"); - if (!row) return; - e.preventDefault(); - const idx = Number(row.dataset.index); - if (Number.isFinite(idx)) { - selectedIndex = idx; - applySelection(); - openSelected(); - } - }); - footer = document.createElement("div"); - footer.className = "footer"; - const hints = document.createElement("span"); - hints.className = "hints"; - hints.innerHTML = '\u2191\u2193navigate\u21B5openescclosetabcycle matchesCtrl+1\u20139jump'; - footer.appendChild(hints); - const brand = document.createElement("span"); - brand.className = "brand"; - brand.textContent = "Reef Spotlight"; - footer.appendChild(brand); - card.appendChild(inputRow); - card.appendChild(results); - card.appendChild(footer); - backdrop.appendChild(card); - shadow.appendChild(backdrop); - host.addEventListener("keydown", (e) => { - if (e.key === "Escape") { - e.preventDefault(); - e.stopPropagation(); - hide(); - } - }, true); - document.documentElement.appendChild(host); - mounted = true; - } - function setSelected(idx, scroll = false) { - if (!results) return; - if (unifiedResults.length === 0) { - selectedIndex = 0; - return; - } - if (idx < 0) idx = unifiedResults.length - 1; - if (idx >= unifiedResults.length) idx = 0; - selectedIndex = idx; - applySelection(scroll); - } - function applySelection(scroll = false) { - if (!results) return; - const rows = results.querySelectorAll(".row"); - rows.forEach((r) => r.setAttribute("aria-selected", "false")); - const sel = rows[selectedIndex]; - if (sel) { - sel.setAttribute("aria-selected", "true"); - if (scroll && typeof sel.scrollIntoView === "function") { - try { - sel.scrollIntoView({ block: "nearest" }); - } catch { - } - } - } - } - function renderResults(items, query, suggestion, autocorrected, siteResults, actions, reefSuggestions) { - if (!results) return; - currentResults = items; - unifiedResults = []; - selectedIndex = 0; - results.replaceChildren(); - const hasAny = items.length > 0 || siteResults && siteResults.length > 0 || actions && actions.length > 0; - if (!hasAny) { - const empty = document.createElement("div"); - empty.className = "empty"; - if (suggestion && suggestion !== query.toLowerCase() && !autocorrected) { - empty.innerHTML = `No matching tabs found. Did you mean ${escapeHtml(suggestion)}?`; - const link = empty.querySelector(".suggestion"); - if (link) { - link.addEventListener("click", (e) => { - e.preventDefault(); - if (input) { - input.value = suggestion; - currentQuery = suggestion; - onInput(); - } - }); - } - } else { - empty.textContent = query ? "No matching tabs found." : "Start typing to search every open tab."; - } - if (reefSuggestions && reefSuggestions.length > 0) { - const chips = document.createElement("div"); - chips.className = "reef-suggestion-chips"; - chips.style.cssText = "display:flex;flex-wrap:wrap;gap:4px;justify-content:center;margin-top:8px;"; - for (const s of reefSuggestions) { - const chip = document.createElement("button"); - chip.className = "reef-suggestion-chip"; - chip.textContent = s; - chip.style.cssText = "font-size:11px;padding:3px 10px;border-radius:12px;border:1px solid #e5e7eb;background:#fafafa;color:#6b7280;cursor:pointer;"; - chip.addEventListener("click", () => { - if (input) { - input.value = s; - currentQuery = s; - onInput(); - } - }); - chips.appendChild(chip); - } - empty.appendChild(chips); - } - if (actions && actions.length > 0) { - const frag = document.createDocumentFragment(); - renderSectionHeader(frag, "Actions"); - for (const action of actions) { - const idx = unifiedResults.length; - unifiedResults.push({ kind: "action", data: action }); - frag.appendChild(createActionRow(action, idx)); - } - results.appendChild(frag); - } - results.appendChild(empty); - return; - } - const fragment = document.createDocumentFragment(); - if (autocorrected && suggestion) { - const banner = document.createElement("div"); - banner.className = "autocorrect-banner"; - banner.innerHTML = `Showing results for ${escapeHtml(suggestion)}search for "${escapeHtml(query)}" instead`; - const origLink = banner.querySelector(".autocorrect-orig"); - if (origLink) { - origLink.addEventListener("click", (e) => { - e.preventDefault(); - lastQueryKey = "\0force-requery\0"; - if (input) { - input.value = query; - currentQuery = query; - onInput(); - } - }); - } - fragment.appendChild(banner); - } - if (items.length > 0) { - if (siteResults?.length || autocorrected) { - renderSectionHeader(fragment, "Tabs"); - } - const renderCount = Math.min(items.length, maxRows); - for (let i = 0; i < renderCount; i++) { - const item = items[i]; - const idx = unifiedResults.length; - unifiedResults.push({ kind: "tab", data: item }); - fragment.appendChild(createTabRow(item, query, idx)); - } - } - if (siteResults && siteResults.length > 0) { - renderSectionHeader(fragment, "Site Content"); - for (const sr of siteResults) { - const idx = unifiedResults.length; - unifiedResults.push({ kind: "site", data: sr }); - fragment.appendChild(createSiteRow(sr, query, idx)); - } - } - if (actions && actions.length > 0) { - renderSectionHeader(fragment, "New Tab"); - for (const action of actions) { - const idx = unifiedResults.length; - unifiedResults.push({ kind: "action", data: action }); - fragment.appendChild(createActionRow(action, idx)); - } - } - results.appendChild(fragment); - applySelection(); - } - function renderSectionHeader(parent, label) { - const header = document.createElement("div"); - header.className = "section-header"; - header.textContent = label; - parent.appendChild(header); - } - function createTabRow(item, query, idx) { - const row = document.createElement("div"); - row.className = "row"; - row.setAttribute("role", "option"); - row.setAttribute("aria-selected", idx === selectedIndex ? "true" : "false"); - row.dataset.index = String(idx); - row.dataset.tabId = String(item.tabId); - row.dataset.windowId = String(item.windowId); - row.title = item.title; - const fav = document.createElement("img"); - fav.className = "favicon"; - fav.alt = ""; - fav.width = 16; - fav.height = 16; - fav.src = item.favIconUrl || FALLBACK_FAVICON; - fav.addEventListener("error", () => { - fav.src = FALLBACK_FAVICON; - }, { once: true }); - row.appendChild(fav); - const main = document.createElement("div"); - main.className = "main"; - const title = document.createElement("div"); - title.className = "title"; - title.innerHTML = highlight(truncate(item.title, 60), query); - const url = document.createElement("div"); - url.className = "url"; - url.textContent = hostFromUrl(item.url); - main.appendChild(title); - main.appendChild(url); - row.appendChild(main); - const match = item.matchedRecords?.[perTabMatchIndex.get(item.tabId) ?? 0]; - if (match && (match.headingText || match.bodyText)) { - const snippet = document.createElement("div"); - snippet.className = "match"; - const text = (match.headingText ? match.headingText + " \u2014 " : "") + (match.bodyText || ""); - snippet.textContent = truncate(text, 90); - row.appendChild(snippet); - } else { - const placeholder = document.createElement("div"); - row.appendChild(placeholder); - } - return row; - } - function createSiteRow(sr, query, idx) { - const row = document.createElement("div"); - row.className = "row row-site"; - row.setAttribute("role", "option"); - row.setAttribute("aria-selected", idx === selectedIndex ? "true" : "false"); - row.dataset.index = String(idx); - row.title = sr.headingText; - const icon = document.createElement("div"); - icon.className = "favicon site-icon"; - icon.innerHTML = ''; - row.appendChild(icon); - const main = document.createElement("div"); - main.className = "main"; - const title = document.createElement("div"); - title.className = "title"; - title.innerHTML = highlight(truncate(sr.headingText, 60), query); - const url = document.createElement("div"); - url.className = "url"; - url.textContent = sr.sourceOrigin; - main.appendChild(title); - main.appendChild(url); - row.appendChild(main); - const snippet = document.createElement("div"); - snippet.className = "match"; - snippet.textContent = truncate(sr.bodyText, 90); - row.appendChild(snippet); - return row; - } - function createActionRow(action, idx) { - const row = document.createElement("div"); - row.className = "row row-action"; - row.setAttribute("role", "option"); - row.setAttribute("aria-selected", idx === selectedIndex ? "true" : "false"); - row.dataset.index = String(idx); - row.title = action.title; - const icon = document.createElement("div"); - icon.className = "favicon action-icon"; - if (action.type === "search-web") { - icon.innerHTML = ''; - } else { - icon.innerHTML = ''; - } - row.appendChild(icon); - const main = document.createElement("div"); - main.className = "main"; - const title = document.createElement("div"); - title.className = "title action-title"; - title.textContent = action.title; - main.appendChild(title); - row.appendChild(main); - const badge = document.createElement("div"); - badge.className = "match action-badge"; - badge.textContent = "new tab"; - row.appendChild(badge); - return row; - } - function renderRecents(items) { - if (!results) return; - currentResults = items.map((r) => ({ - tabId: -1, - windowId: -1, - title: r.title, - url: r.url, - favIconUrl: r.favicon, - score: 0, - matchedRecords: [], - _isRecent: true - })); - selectedIndex = 0; - results.replaceChildren(); - if (items.length === 0) { - const empty = document.createElement("div"); - empty.className = "empty"; - empty.textContent = "Start typing to search every open tab."; - results.appendChild(empty); - return; - } - const fragment = document.createDocumentFragment(); - items.forEach((r, i) => { - const row = document.createElement("div"); - row.className = "row"; - row.setAttribute("role", "option"); - row.setAttribute("aria-selected", i === 0 ? "true" : "false"); - row.dataset.index = String(i); - row.dataset.recent = "1"; - row.title = r.title; - const fav = document.createElement("img"); - fav.className = "favicon"; - fav.alt = ""; - fav.width = 16; - fav.height = 16; - fav.src = r.favicon || FALLBACK_FAVICON; - fav.addEventListener("error", () => { - fav.src = FALLBACK_FAVICON; - }, { once: true }); - row.appendChild(fav); - const main = document.createElement("div"); - main.className = "main"; - const title = document.createElement("div"); - title.className = "title"; - title.textContent = truncate(r.title, 60); - const url = document.createElement("div"); - url.className = "url"; - url.textContent = hostFromUrl(r.url); - main.appendChild(title); - main.appendChild(url); - row.appendChild(main); - const tag = document.createElement("div"); - tag.className = "match"; - tag.textContent = "recent"; - row.appendChild(tag); - fragment.appendChild(row); - }); - results.appendChild(fragment); - } - async function runQuery(query) { - const key = query.trim(); - if (key === lastQueryKey) return; - lastQueryKey = key; - perTabMatchIndex = /* @__PURE__ */ new Map(); - if (!key) { - currentSuggestion = void 0; - currentSiteResults = []; - currentActions = []; - currentAutocorrected = false; - const recents = await fetchRecents(); - renderRecents(recents); - return; - } - const reqId = ++inFlight; - const res = await send({ type: "SPOTLIGHT_SEARCH", query: key, limit: maxRows }); - if (reqId !== inFlight) return; - const items = res && res.success && Array.isArray(res.items) ? res.items : []; - currentSuggestion = res && res.suggestion || void 0; - currentAutocorrected = !!(res && res.autocorrected); - currentSiteResults = res && Array.isArray(res.siteResults) ? res.siteResults : []; - currentActions = res && Array.isArray(res.actions) ? res.actions : []; - currentReefSuggestions = items.length === 0 ? reefSuggest(key, 5) : []; - renderResults(items, key, currentSuggestion, currentAutocorrected, currentSiteResults, currentActions, currentReefSuggestions); - } - async function fetchRecents() { - try { - const res = await send({ type: "LIBRARY_RECENTS_LIST" }); - const list = res && res.success && Array.isArray(res.items) ? res.items : []; - return list.slice(0, maxRecents).map((r) => ({ - url: r.url, - title: r.title, - favicon: r.favicon, - visitedAt: r.visitedAt ?? 0 - })); - } catch { - return []; - } - } - function onInput() { - if (!input) return; - currentQuery = input.value; - if (debounceHandle) clearTimeout(debounceHandle); - debounceHandle = setTimeout(() => { - debounceHandle = null; - void runQuery(currentQuery); - }, debounceMs); - } - function onKeyDown(e) { - if (!input) return; - const isMac = /Mac|iPod|iPhone|iPad/.test(navigator.platform); - const mod = isMac ? e.metaKey : e.ctrlKey; - if (e.key === "ArrowDown" || mod && (e.key === "n" || e.key === "N")) { - e.preventDefault(); - setSelected(selectedIndex + 1, true); - return; - } - if (e.key === "ArrowUp" || mod && (e.key === "p" || e.key === "P")) { - e.preventDefault(); - setSelected(selectedIndex - 1, true); - return; - } - if (e.key === "Home") { - e.preventDefault(); - setSelected(0, true); - return; - } - if (e.key === "End") { - e.preventDefault(); - setSelected(unifiedResults.length - 1, true); - return; - } - if (e.key === "Enter") { - e.preventDefault(); - openSelected(); - return; - } - if (e.key === "Tab") { - const sel = unifiedResults[selectedIndex]; - if (sel && sel.kind === "tab" && sel.data.matchedRecords && sel.data.matchedRecords.length > 1) { - e.preventDefault(); - const next = ((perTabMatchIndex.get(sel.data.tabId) ?? 0) + 1) % sel.data.matchedRecords.length; - perTabMatchIndex.set(sel.data.tabId, next); - applySelection(); - if (results) { - const row = results.querySelectorAll(".row")[selectedIndex]; - if (row) { - const match = sel.data.matchedRecords[next]; - const snippetEl = row.querySelector(".match"); - if (snippetEl) { - const text = (match.headingText ? match.headingText + " \u2014 " : "") + (match.bodyText || ""); - snippetEl.textContent = truncate(text, 90); - } - } - } - } - return; - } - if (e.key === "Backspace" && e.shiftKey && mod) { - e.preventDefault(); - if (input) { - input.value = ""; - currentQuery = ""; - onInput(); - } - return; - } - if (mod && e.key >= "1" && e.key <= "9") { - e.preventDefault(); - const idx = Number(e.key) - 1; - if (idx < unifiedResults.length) { - setSelected(idx, true); - openSelected(); - } - return; - } - } - async function openSelected() { - const sel = unifiedResults[selectedIndex]; - if (!sel) return; - if (sel.kind === "action") { - try { - await send({ type: "SPOTLIGHT_OPEN_NEW_TAB", url: sel.data.url }); - } catch { - } - hide(); - return; - } - if (sel.kind === "site") { - try { - await send({ type: "SPOTLIGHT_OPEN_NEW_TAB", url: sel.data.url }); - } catch { - } - hide(); - return; - } - const tabHit = sel.data; - if (tabHit._isRecent) { - try { - await send({ type: "LIBRARY_OPEN_RECENT", url: tabHit.url }); - } catch { - } - hide(); - return; - } - const matchIdx = perTabMatchIndex.get(tabHit.tabId) ?? 0; - const record = tabHit.matchedRecords?.[matchIdx]; - try { - await send({ type: "TAB_SWITCH", tabId: tabHit.tabId, windowId: tabHit.windowId }); - if (record) { - await send({ type: "SPOTLIGHT_OPEN_RECORD", tabId: tabHit.tabId, record }); - } - } catch { - } - hide(); - } - async function applyTheme(theme) { - if (!host) return; - const resolved = resolveTheme(theme); - host.setAttribute("data-theme", resolved); - } - async function show() { - mount(); - if (!host || !input) return; - open = true; - host.style.display = "block"; - const t = opts.theme ?? await getStoredTheme(); - await applyTheme(t); - lastQueryKey = "\0never-queried\0"; - perTabMatchIndex = /* @__PURE__ */ new Map(); - currentSuggestion = void 0; - currentSiteResults = []; - currentActions = []; - currentAutocorrected = false; - unifiedResults = []; - input.value = ""; - currentQuery = ""; - await runQuery(""); - requestAnimationFrame(() => { - try { - input?.focus(); - } catch { - } - }); - } - function hide() { - if (!host) return; - open = false; - host.style.display = "none"; - if (debounceHandle) { - clearTimeout(debounceHandle); - debounceHandle = null; - } - } - async function toggle() { - if (open) hide(); - else await show(); - } - function isOpen() { - return open; - } - function destroy() { - if (debounceHandle) clearTimeout(debounceHandle); - if (host && host.parentNode) host.parentNode.removeChild(host); - host = null; - shadow = null; - card = null; - input = null; - results = null; - footer = null; - mounted = false; - open = false; - } - return { show, hide, toggle, isOpen, destroy }; - } - - // src/content.ts - var HARD_EXCLUSION_SELECTOR = 'input[type="password"], input[name*="card" i], input[autocomplete*="cc-" i], input[name*="ssn" i], input[name*="social-security" i], [data-reef-agent="off"], [data-sensitive]'; - function isSensitiveElement(element, customExclusions = []) { - if (element.matches(HARD_EXCLUSION_SELECTOR) || element.closest('[data-reef-agent="off"], [data-sensitive]')) { - return true; - } - return customExclusions.some((selector) => { - try { - return element.matches(selector) || !!element.closest(selector); - } catch { - return false; - } - }); - } - function getAuthoritativeManifest() { - if (typeof window !== "undefined" && window.__reefAgentManifest) { - return window.__reefAgentManifest; - } - const scriptTag = document.querySelector('script[type="application/agent-manifest+json"]'); - if (scriptTag?.textContent) { - try { - return JSON.parse(scriptTag.textContent); - } catch { - } - } - return null; - } - function extractPageManifest(customExclusions = []) { - const authoritative = getAuthoritativeManifest(); - if (authoritative) { - const filteredRecords = authoritative.records.filter((record) => { - if (!record.selector) return true; - try { - const el = document.querySelector(record.selector); - return el ? !isSensitiveElement(el, customExclusions) : true; - } catch { - return true; - } - }); - syncRecordsToReef(filteredRecords); - return { - ...authoritative, - records: filteredRecords - }; - } - const url = location.href; - const html = document.documentElement.outerHTML; - const rawRecords = [ - ...extractSections(html, url), - ...extractActions(html, url), - ...extractFields(html, url), - ...extractLinks(html, url), - ...extractFiles(html, url), - ...extractMedia(html, url), - ...extractStructuredData(html, url), - ...extractAccessibilityTree(document) - ]; - const filtered = rawRecords.filter((record) => { - if (record.selector) { - try { - const element = document.querySelector(record.selector); - if (element && isSensitiveElement(element, customExclusions)) return false; - } catch { - } - } - return true; - }); - const deduped = [ - ...new Map( - filtered.map((record) => [`${record.type}:${record.headingText}:${record.selector || record.url}`, record]) - ).values() - ]; - syncRecordsToReef(deduped); - return { - version: 1, - url, - generatedAt: Date.now(), - records: deduped, - excludedCount: rawRecords.length - deduped.length - }; - } - var dummyInspector = { - activate: () => { - }, - deactivate: () => { - }, - isActive: () => false, - setRecords: () => { - } - }; - var currentAgent = null; - function getOrCreateAgent(actionsMode = "execute") { - const index = createSearchIndex(); - currentAgent = new Agent(index, dummyInspector, { actionsMode }); - return currentAgent; - } - - var reefSynced = false; - var reefLastUrl = ""; - function syncRecordsToReef(records) { - if (typeof window === "undefined" || !window.Reef) return; - const currentUrl = typeof location !== "undefined" ? location.href : ""; - if (reefSynced && reefLastUrl === currentUrl) return; - try { - window.Reef.addCustomRecords(records); - reefSynced = true; - reefLastUrl = currentUrl; - } catch (e) { - console.warn("[reef-ext] failed to sync records to Reef index", e); - } - } - - function reefLocalSearch(query, options) { - if (typeof window === "undefined" || !window.Reef) return { results: [], suggestion: void 0, autocorrected: false }; - if (!query || !query.trim()) return { results: [], suggestion: void 0, autocorrected: false }; - const limit = options?.limit ?? 20; - const results = window.Reef.search(query, { limit, fuzzy: false, includeScore: true }); - if (results.length > 0) { - return { results: results.map((r) => r.record ?? r), suggestion: void 0, autocorrected: false }; - } - const fuzzyResults = window.Reef.search(query, { limit, fuzzy: true, fuzzyDistance: 2, includeScore: true }); - if (fuzzyResults.length > 0) { - const suggestion = fuzzyResults[0]?.record?.headingText ?? void 0; - return { results: fuzzyResults.map((r) => r.record ?? r), suggestion, autocorrected: true }; - } - return { results: [], suggestion: void 0, autocorrected: false }; - } - - function reefSuggest(query, limit) { - if (typeof window === "undefined" || !window.Reef) return []; - try { - return window.Reef.suggest(query, limit ?? 10); - } catch { - return []; - } - } - - if (typeof document !== "undefined") { - document.addEventListener("keydown", (e) => { - const isMac = /Mac|iPod|iPhone|iPad/.test(navigator.platform); - if (!isMac) return; - if (e.metaKey && e.shiftKey && (e.key === "l" || e.key === "L") && !e.ctrlKey && !e.altKey) { - e.preventDefault(); - e.stopPropagation(); - ensureSpotlight().show(); - } - }, true); - } - var spotlightHandle = null; - function ensureSpotlight() { - if (!spotlightHandle) spotlightHandle = createSpotlight(); - return spotlightHandle; - } - if (typeof chrome !== "undefined" && chrome.runtime?.onMessage) { - chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { - (async () => { - try { - if (message.type === "PING") { - sendResponse({ success: true, url: location.href }); - return; - } - if (message.type === "GET_MANIFEST" || message.type === "RESCAN") { - const manifest = extractPageManifest(message.options?.exclusionSelectors || []); - sendResponse({ success: true, manifest }); - return; - } - if (message.type === "EXECUTE_ACTION" && message.record) { - const actionsMode = message.options?.actionsMode || "execute"; - const agent = getOrCreateAgent(actionsMode); - if (message.record.destructive && actionsMode === "navigate-only") { - sendResponse({ success: false, error: "destructive-action-blocked-by-mode" }); - return; - } - if (message.actionType === "click" || message.record.type === "action" || message.record.type === "link") { - if (message.record.selector) { - await agent.click(message.record); - } else if (message.record.url) { - location.href = message.record.url; - } - sendResponse({ success: true, url: location.href }); - return; - } - if (message.actionType === "type" || message.record.type === "field") { - const valueToType = message.value ?? message.record.value ?? ""; - await agent.type(message.record, valueToType); - sendResponse({ success: true }); - return; - } - sendResponse({ success: false, error: "unknown-action-type" }); - return; - } - if (message.type === "HIGHLIGHT_RECORD" && message.record?.selector) { - const el = document.querySelector(message.record.selector); - if (el) { - el.scrollIntoView({ behavior: "smooth", block: "center" }); - const origOutline = el.style.outline; - el.style.outline = "3px solid #00a8b5"; - setTimeout(() => { - el.style.outline = origOutline; - }, 2e3); - } - sendResponse({ success: true }); - return; - } - if (message.type === "REEF_LOCAL_SEARCH") { - const query = message.query || ""; - const opts = message.options || {}; - if (!query.trim()) { - sendResponse({ success: true, results: [], suggestion: void 0, autocorrected: false }); - return; - } - if (!reefSynced) { - extractPageManifest(message.options?.exclusionSelectors || []); - } - const searchResult = reefLocalSearch(query, { limit: opts.limit ?? 20 }); - const suggestions = reefSuggest(query, 8); - sendResponse({ - success: true, - results: searchResult.results, - suggestion: searchResult.suggestion, - autocorrected: searchResult.autocorrected, - suggestions - }); - return; - } - if (message.type === "REEF_SUGGEST") { - const suggestions = reefSuggest(message.query || "", message.limit ?? 10); - sendResponse({ success: true, suggestions }); - return; - } - if (message.type === "REEF_BOOKMARK_SELECTION" && message.text) { - const created = await createBookmarkFromSelection(message.text); - showReefToast(created ? "Bookmarked in Reef" : "Bookmark failed", created ? "success" : "error"); - sendResponse({ success: !!created }); - return; - } - if (message.type === "REEF_SNIPPET_SELECTION" && message.text) { - const created = await createSnippetFromSelection(message.text); - showReefToast(created ? "Saved as snippet" : "Snippet failed", created ? "success" : "error"); - sendResponse({ success: !!created }); - return; - } - if (message.type === "REEF_BOOKMARK_PAGE") { - const created = await createBookmarkFromPage(); - showReefToast(created ? "Page bookmarked" : "Bookmark failed", created ? "success" : "error"); - sendResponse({ success: !!created }); - return; - } - if (message.type === "REEF_SHOW_TOAST" && message.message) { - showReefToast(message.message, message.toastType || "info"); - sendResponse({ success: true }); - return; - } - if (message.type === "REEF_OPEN_POPUP_QUERY" || message.type === "REEF_OPEN_NOTE_FOR_PAGE") { - sendResponse({ success: true }); - return; - } - if (message.type === "SHOW_SPOTLIGHT") { - try { - await ensureSpotlight().show(); - sendResponse({ success: true }); - } catch (err) { - sendResponse({ success: false, error: err?.message || String(err) }); - } - return; - } - if (message.type === "HIDE_SPOTLIGHT") { - spotlightHandle?.hide(); - sendResponse({ success: true }); - return; - } - sendResponse({ success: false, error: "unsupported-message-type" }); - } catch (err) { - sendResponse({ success: false, error: err?.message || String(err) }); - } - })(); - return true; - }); - } - var TOOLBAR_HOST_ID = "reef-selection-toolbar-host"; - function isInsideEditable(target) { - if (!target) return false; - const el = target; - if (!el || !el.closest) return false; - return !!el.closest('input, textarea, [contenteditable="true"], [contenteditable=""], [data-reef-agent="off"], [data-sensitive]'); - } - function getSelectionContext() { - const sel = typeof window !== "undefined" ? window.getSelection() : null; - if (!sel || sel.rangeCount === 0 || sel.isCollapsed) { - return { text: "", rect: null, range: null }; - } - const text = sel.toString().trim(); - if (text.length < 1) return { text: "", rect: null, range: null }; - const range = sel.getRangeAt(0); - const rect = range.getBoundingClientRect(); - return { text, rect, range }; - } - function ensureToolbarHost() { - if (typeof document === "undefined") return null; - let host = document.getElementById(TOOLBAR_HOST_ID); - if (host && host.shadowRoot) return host; - if (!host) { - host = document.createElement("div"); - host.id = TOOLBAR_HOST_ID; - host.setAttribute("data-reef-agent", "on"); - host.style.cssText = "position: fixed; z-index: 2147483646; top: 0; left: 0; display: none; pointer-events: auto;"; - (document.body || document.documentElement).appendChild(host); - } - if (!host.shadowRoot) { - const root = host.attachShadow({ mode: "open" }); - const style = document.createElement("style"); - style.textContent = REEF_TOOLBAR_CSS; - const bar = document.createElement("div"); - bar.className = "bar"; - bar.innerHTML = ` - - - - - `; - root.appendChild(style); - root.appendChild(bar); - bar.addEventListener("mousedown", (e) => { - e.preventDefault(); - e.stopPropagation(); - }); - bar.addEventListener("click", async (e) => { - const target = e.target; - const btn = target.closest(".btn"); - if (!btn) return; - const action = btn.dataset.action; - const text = host._reefText || ""; - hideToolbar(); - if (action === "bookmark") { - const ok = await createBookmarkFromSelection(text); - showReefToast(ok ? "Bookmarked in Reef" : "Bookmark failed", ok ? "success" : "error"); - } else if (action === "snippet") { - const ok = await createSnippetFromSelection(text); - showReefToast(ok ? "Saved as snippet" : "Snippet failed", ok ? "success" : "error"); - } else if (action === "search") { - if (typeof chrome !== "undefined" && chrome.runtime?.sendMessage) { - chrome.runtime.sendMessage({ type: "TAB_SEARCH_PROMPT", query: text }); - } - try { - chrome?.action?.openPopup?.(); - } catch { - } - } else if (action === "copy") { - try { - await navigator.clipboard.writeText(text); - showReefToast("Copied to clipboard", "success"); - } catch { - showReefToast("Copy failed", "error"); - } - } - }); - } - return host; - } - var REEF_TOOLBAR_CSS = ` -:host { all: initial; } -.bar { - display: inline-flex; - align-items: center; - gap: 2px; - background: #111; - color: #fff; - border-radius: 8px; - padding: 4px; - box-shadow: 0 6px 20px rgba(0,0,0,.25); - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif; - font-size: 12px; - line-height: 1; - user-select: none; -} -.btn { - display: inline-flex; - align-items: center; - gap: 4px; - padding: 6px 8px; - background: transparent; - color: #fff; - border: 0; - border-radius: 6px; - cursor: pointer; - font: inherit; - white-space: nowrap; - transition: background .12s; -} -.btn:hover { background: rgba(255,255,255,.12); } -.btn:active { background: rgba(255,255,255,.2); } -.btn svg { display: block; } -`; - function showToolbarAt(rect, text) { - const host = ensureToolbarHost(); - if (!host) return; - host._reefText = text; - host.style.display = "block"; - const bar = host.shadowRoot.querySelector(".bar"); - const margin = 8; - const barHeight = bar ? bar.offsetHeight : 36; - const barWidth = bar ? bar.offsetWidth : 280; - let top = window.scrollY + rect.top - barHeight - margin; - let left = window.scrollX + rect.left + rect.width / 2 - barWidth / 2; - if (rect.top - barHeight - margin < 0) { - top = window.scrollY + rect.bottom + margin; - } - const maxLeft = window.scrollX + window.innerWidth - barWidth - 8; - if (left > maxLeft) left = maxLeft; - if (left < window.scrollX + 8) left = window.scrollX + 8; - host.style.transform = `translate(${left}px, ${top}px)`; - } - function hideToolbar() { - const host = document.getElementById(TOOLBAR_HOST_ID); - if (host) { - host.style.display = "none"; - host._reefText = ""; - } - } - var toolbarTimer = null; - function onSelectionChange() { - if (typeof document === "undefined") return; - const active = document.activeElement; - if (active && isInsideEditable(active)) { - hideToolbar(); - return; - } - const sel = window.getSelection(); - if (!sel || sel.rangeCount === 0 || sel.isCollapsed) { - hideToolbar(); - return; - } - if (isInsideEditable(sel.anchorNode)) { - hideToolbar(); - return; - } - const { text, rect } = getSelectionContext(); - if (!text || text.length < 1 || !rect || rect.width === 0) { - hideToolbar(); - return; - } - if (toolbarTimer) window.clearTimeout(toolbarTimer); - toolbarTimer = window.setTimeout(() => { - const ctx = getSelectionContext(); - if (ctx.text && ctx.rect) showToolbarAt(ctx.rect, ctx.text); - }, 220); - } - function dismissOnOutside() { - document.addEventListener("mousedown", (e) => { - const host = document.getElementById(TOOLBAR_HOST_ID); - if (!host || host.style.display === "none") return; - if (host.contains(e.target)) return; - const sel = window.getSelection(); - if (sel && !sel.isCollapsed) return; - hideToolbar(); - }, true); - document.addEventListener("keydown", (e) => { - if (e.key === "Escape") hideToolbar(); - }); - window.addEventListener("scroll", () => hideToolbar(), { passive: true }); - window.addEventListener("resize", () => hideToolbar()); - } - if (typeof document !== "undefined") { - document.addEventListener("selectionchange", onSelectionChange); - if (document.readyState === "complete" || document.readyState === "interactive") { - dismissOnOutside(); - } else { - document.addEventListener("DOMContentLoaded", dismissOnOutside); - } - } - var TOAST_HOST_ID = "reef-toast-host"; - function showReefToast(message, type = "info") { - if (typeof document === "undefined") return; - let host = document.getElementById(TOAST_HOST_ID); - if (!host) { - host = document.createElement("div"); - host.id = TOAST_HOST_ID; - host.setAttribute("data-reef-agent", "on"); - host.style.cssText = "position: fixed; z-index: 2147483647; top: 16px; right: 16px; display: flex; flex-direction: column; gap: 6px; pointer-events: none;"; - (document.body || document.documentElement).appendChild(host); - } - const shadow = host.shadowRoot || host.attachShadow({ mode: "open" }); - if (!shadow.querySelector("style")) { - const style = document.createElement("style"); - style.textContent = ` - :host { all: initial; } - .toast { - background: ${type === "error" ? "#b91c1c" : type === "success" ? "#047857" : "#111"}; - color: #fff; - padding: 8px 12px; - border-radius: 8px; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif; - font-size: 12px; - line-height: 1.3; - max-width: 320px; - box-shadow: 0 8px 24px rgba(0,0,0,.25); - animation: reef-in .18s ease-out; - pointer-events: auto; - } - @keyframes reef-in { - from { opacity: 0; transform: translateY(-4px); } - to { opacity: 1; transform: translateY(0); } - } - `; - shadow.appendChild(style); - } - const el = document.createElement("div"); - el.className = "toast"; - el.textContent = message; - shadow.appendChild(el); - window.setTimeout(() => { - el.style.transition = "opacity .2s"; - el.style.opacity = "0"; - window.setTimeout(() => el.remove(), 220); - }, 2200); - } - async function createBookmarkFromSelection(text) { - if (typeof chrome === "undefined" || !chrome.runtime?.sendMessage) return false; - const { selectionContext } = captureSelectionContext(); - return new Promise((resolve) => { - chrome.runtime.sendMessage({ - type: "LIBRARY_BOOKMARK_CREATE", - data: { - url: location.href, - title: document.title, - selectedText: text.slice(0, 4e3), - note: "", - tags: [], - contextBefore: selectionContext.before, - contextAfter: selectionContext.after, - favicon: getFavicon() - } - }, (res) => resolve(!!(res && res.success))); - }); - } - async function createSnippetFromSelection(text) { - if (typeof chrome === "undefined" || !chrome.runtime?.sendMessage) return false; - return new Promise((resolve) => { - chrome.runtime.sendMessage({ - type: "LIBRARY_SNIPPET_CREATE", - data: { - text: text.slice(0, 4e3), - title: text.slice(0, 80), - tags: [], - source: { url: location.href, title: document.title } - } - }, (res) => resolve(!!(res && res.success))); - }); - } - async function createBookmarkFromPage() { - if (typeof chrome === "undefined" || !chrome.runtime?.sendMessage) return false; - return new Promise((resolve) => { - chrome.runtime.sendMessage({ - type: "LIBRARY_BOOKMARK_CREATE", - data: { - url: location.href, - title: document.title, - note: "", - tags: [], - favicon: getFavicon() - } - }, (res) => resolve(!!(res && res.success))); - }); - } - function captureSelectionContext() { - try { - const sel = window.getSelection(); - if (!sel || sel.rangeCount === 0) return { before: "", after: "" }; - const range = sel.getRangeAt(0); - const beforeRange = document.createRange(); - beforeRange.setStart(document.body, 0); - beforeRange.setEnd(range.startContainer, range.startOffset); - const afterRange = document.createRange(); - afterRange.setStart(range.endContainer, range.endOffset); - afterRange.setEndAfter(document.body.lastChild || document.body); - return { - before: (beforeRange.toString() || "").slice(-160).trim(), - after: (afterRange.toString() || "").slice(0, 160).trim() - }; - } catch { - return { before: "", after: "" }; - } - } - function getFavicon() { - if (typeof document === "undefined") return void 0; - const link = document.querySelector('link[rel*="icon"]'); - return link?.href || void 0; - } -})(); -//# sourceMappingURL=content.js.map diff --git a/reef-extension-chrome/content.js.map b/reef-extension-chrome/content.js.map deleted file mode 100644 index f038e3f..0000000 --- a/reef-extension-chrome/content.js.map +++ /dev/null @@ -1,7 +0,0 @@ -{ - "version": 3, - "sources": ["../../src/extraction.ts", "../../src/agent.ts", "../../src/search-index.ts", "../src/spotlight.ts", "../src/content.ts"], - "sourcesContent": ["/**\n * @file Content extraction functions for indexing web pages.\n * Extracts sections, actions, fields, links, files, media, and structured data from HTML.\n */\n\nimport { IndexRecord } from './types.js';\n\nexport function stripTags(value: string): string {\n let result = '';\n let inTag = false;\n for (let i = 0; i < value.length; i++) {\n const char = value[i];\n if (char === '<' && inTag === false) {\n inTag = true;\n } else if (char === '>' && inTag === true) {\n inTag = false;\n } else if (inTag === false) {\n result += char;\n }\n }\n return result.replace(/\\s+/g, ' ').trim();\n}\n\nexport function generateSelector(element: Element): string {\n const path: string[] = [];\n let current: Element | null = element;\n\n while (current && current.nodeType === Node.ELEMENT_NODE) {\n let selector = current.tagName.toLowerCase();\n if (current.id) {\n selector += `#${current.id}`;\n path.unshift(selector);\n break;\n } else if (current.className) {\n const classes = current.className.trim().split(/\\s+/);\n if (classes.length) {\n selector += `.${classes.join('.')}`;\n }\n }\n\n let siblingIndex = 1;\n let sibling = current.previousElementSibling;\n while (sibling) {\n if (sibling.tagName === current.tagName) {\n siblingIndex++;\n }\n sibling = sibling.previousElementSibling;\n }\n if (siblingIndex > 1) {\n selector += `:nth-child(${siblingIndex})`;\n }\n\n path.unshift(selector);\n current = current.parentElement;\n }\n\n return path.length > 0 ? path.join(' > ') : element.tagName.toLowerCase();\n}\n\n/** Return resilient selectors from most semantic to least semantic. */\nexport function generateStableSelector(element: Element): string[] {\n const candidates: string[] = [];\n const push = (value: string) => { if (value && !candidates.includes(value)) candidates.push(value); };\n const escape = (value: string) => typeof CSS !== 'undefined' && CSS.escape ? CSS.escape(value) : value.replace(/([\"\\\\])/g, '\\\\$1');\n for (const attr of ['data-testid', 'data-test', 'data-agent-id', 'id']) {\n const value = element.getAttribute(attr);\n if (value) push(attr === 'id' ? `#${escape(value)}` : `[${attr}=\"${escape(value)}\"]`);\n }\n const aria = element.getAttribute('aria-label');\n if (aria) push(`[aria-label=\"${escape(aria)}\"]`);\n const role = element.getAttribute('role');\n const name = extractActionName(element);\n if (role && name) push(`[role=\"${escape(role)}\"][aria-label=\"${escape(name)}\"]`);\n if (role) push(`[role=\"${escape(role)}\"]`);\n push(generateSelector(element));\n // XPath is intentionally last: it survives class churn but is less readable.\n let sibling = element.previousElementSibling;\n let position = 1;\n while (sibling) { if (sibling.tagName === element.tagName) position++; sibling = sibling.previousElementSibling; }\n push(`xpath=//${element.tagName.toLowerCase()}[${position}]`);\n return candidates;\n}\n\nfunction walkComposed(root: Document | Element | ShadowRoot, visit: (element: Element, iframePath: number[]) => void, iframePath: number[] = []): void {\n const elements = root instanceof Document ? Array.from(root.documentElement ? [root.documentElement] : []) : [root as Element];\n const walk = (element: Element, path: number[]) => {\n visit(element, path);\n if (element.shadowRoot) walkRoot(element.shadowRoot, path);\n for (const child of Array.from(element.children)) walk(child, path);\n if (element.tagName.toLowerCase() === 'iframe') {\n try { const frame = (element as HTMLIFrameElement).contentDocument; if (frame) walkRoot(frame, [...path, Array.from(element.parentElement?.children || []).indexOf(element)]); } catch { /* cross-origin */ }\n }\n };\n const walkRoot = (value: Document | Element | ShadowRoot, path: number[]) => {\n if (value instanceof Document) { if (value.documentElement) walk(value.documentElement, path); }\n else if (value instanceof ShadowRoot) for (const child of Array.from(value.children)) walk(child, path);\n else walk(value, path);\n };\n for (const element of elements) walk(element, iframePath);\n}\n\nfunction isFocusableAction(element: Element): boolean {\n const tag = element.tagName.toLowerCase();\n return ['a', 'button', 'input', 'textarea', 'select', 'summary'].includes(tag) ||\n ['button', 'link', 'tab', 'menuitem', 'checkbox', 'radio', 'combobox', 'option'].includes(element.getAttribute('role') || '') ||\n element.hasAttribute('contenteditable') || (element.hasAttribute('tabindex') && Number(element.getAttribute('tabindex')) >= 0) ||\n element.hasAttribute('data-reef-action');\n}\n\n/** Extract actionable custom elements from a live composed DOM, including open shadow roots and same-origin frames. */\nexport function extractAccessibilityTree(root: Document | Element | ShadowRoot = document): IndexRecord[] {\n const records: IndexRecord[] = [];\n walkComposed(root, (element, iframePath) => {\n if (!isFocusableAction(element)) return;\n const label = extractActionName(element);\n if (!label) return;\n const selectors = generateStableSelector(element);\n records.push({ id: `${typeof location !== 'undefined' ? location.href : ''}#accessibility-${records.length}`, url: typeof location !== 'undefined' ? location.href : '', headingText: label, headingId: `accessibility-${records.length}`, breadcrumb: '', bodyText: label, type: element.matches('input,textarea,select,[contenteditable]') ? 'field' : 'action', selector: selectors[0], selectors, iframePath, label, destructive: isDestructiveAction(label) });\n });\n return records;\n}\n\nexport function extractHeadingId(fullMatch: string, text: string): string {\n const idMatch = fullMatch.match(/\\bid=[\"']([^\"']+)['\"]/i);\n if (idMatch?.[1]) return idMatch[1];\n const stripped = text.toLowerCase().replace(/[^a-z0-9]+/g, '-');\n return stripped || Math.random().toString(36).slice(2);\n}\n\nexport function hasExplicitId(fullMatch: string): boolean {\n return /\\bid=[\"'][^\"']+[\"']/i.test(fullMatch);\n}\n\nexport function findParentSectionId(html: string, headingMatchEnd: number): string | null {\n const afterHeading = html.slice(headingMatchEnd, headingMatchEnd + 500);\n const idMatch = afterHeading.match(/]*id=\"([^\"]+)\"/i);\n if (idMatch?.[1]) return idMatch[1];\n const articleMatch = afterHeading.match(/]*id=\"([^\"]+)\"/i);\n if (articleMatch?.[1]) return articleMatch[1];\n return null;\n}\n\nconst headingCache = new Map();\n\nexport function chunkBodyText(bodyText: string, chunkSize: number, record: IndexRecord): IndexRecord[] {\n if (chunkSize <= 0 || bodyText.length <= chunkSize) {\n return [record];\n }\n \n const chunks: IndexRecord[] = [];\n const overlap = Math.floor(chunkSize * 0.2); // 20% overlap\n \n for (let i = 0; i < bodyText.length; i += chunkSize - overlap) {\n const chunkEnd = Math.min(i + chunkSize, bodyText.length);\n const chunkText = bodyText.slice(i, chunkEnd);\n \n // Create a new record for this chunk\n const chunkRecord: IndexRecord = {\n ...record,\n id: `${record.id}-chunk-${Math.floor(i / chunkSize)}`,\n url: `${record.url}-chunk-${Math.floor(i / chunkSize)}`,\n bodyText: chunkText,\n headingText: `${record.headingText} (chunk ${Math.floor(i / chunkSize) + 1})`,\n headingId: `${record.headingId}-chunk-${Math.floor(i / chunkSize)}`,\n };\n \n chunks.push(chunkRecord);\n }\n \n return chunks;\n}\n\nexport function extractSections(html: string, url: string): IndexRecord[] {\n if (headingCache.has(url)) {\n return headingCache.get(url)!;\n }\n\n const cleanHtml = html\n .replace(//gi, ' ')\n .replace(//gi, ' ')\n .replace(//gi, ' ')\n .replace(//g, ' ');\n\n const matches: Array<{ level: number; index: number; text: string; id: string; hasRealId: boolean }> = [];\n const headingRegexGlobal = /<(h[1-6])[^>]*>([\\s\\S]*?)<\\/h[1-6]>/gi;\n let match: RegExpExecArray | null;\n\n headingRegexGlobal.lastIndex = 0;\n while ((match = headingRegexGlobal.exec(cleanHtml)) !== null) {\n const [, tag, text] = match;\n const headingText = stripTags(text);\n const level = parseInt(tag[1], 10);\n matches.push({\n level,\n index: match.index,\n text: headingText,\n id: extractHeadingId(match[0], headingText),\n hasRealId: hasExplicitId(match[0]),\n });\n }\n\n const len = matches.length;\n const sections: IndexRecord[] = new Array(len);\n\n for (let i = 0; i < len; i++) {\n const heading = matches[i];\n const nextHeading = matches[i + 1];\n const start = heading.index + heading.text.length;\n const end = nextHeading?.index ?? cleanHtml.length;\n const content = cleanHtml.slice(start, end);\n const bodyText = stripTags(content).replace(/\\s+/g, ' ').trim();\n\n let breadcrumb = '';\n for (let j = 0; j <= i; j++) {\n if (j > 0) breadcrumb += ' \u203A ';\n breadcrumb += matches[j].text;\n }\n\n const parentSectionId = heading.hasRealId ? null : findParentSectionId(cleanHtml, heading.index + heading.text.length);\n const selector = heading.hasRealId\n ? '#' + heading.id\n : parentSectionId\n ? '#' + parentSectionId\n : undefined;\n\n sections[i] = {\n id: `${url}#${heading.id}`,\n url: `${url}#${heading.id}`,\n headingText: heading.text,\n headingId: heading.id,\n breadcrumb,\n bodyText,\n type: 'section',\n selector,\n };\n }\n\n headingCache.set(url, sections);\n return sections;\n}\n\nexport function extractActionName(element: Element): string | null {\n const ariaLabel = element.getAttribute('aria-label');\n if (ariaLabel?.trim()) return ariaLabel.trim();\n\n const ariaLabelledBy = element.getAttribute('aria-labelledby');\n if (ariaLabelledBy) {\n const labelledElement = document.getElementById(ariaLabelledBy);\n if (labelledElement?.textContent?.trim()) {\n return labelledElement.textContent.trim();\n }\n }\n\n const textContent = element.textContent?.trim();\n if (textContent) return textContent;\n\n const title = element.getAttribute('title');\n if (title?.trim()) return title.trim();\n\n return null;\n}\n\nexport function isDestructiveAction(label: string): boolean {\n const destructiveVerbs = [\n 'delete', 'remove', 'cancel subscription', 'unsubscribe',\n 'pay', 'checkout', 'submit order', 'confirm'\n ];\n const lowerLabel = label.toLowerCase();\n return destructiveVerbs.some(verb => lowerLabel.includes(verb));\n}\n\nexport function extractActions(html: string, url: string, excludeSelectors?: string): IndexRecord[] {\n const actions: IndexRecord[] = [];\n const doc = new DOMParser().parseFromString(html, 'text/html');\n\n const selectors = [\n 'button',\n '[role=\"button\"]',\n 'input[type=\"button\"]',\n 'input[type=\"submit\"]',\n 'summary',\n '[data-reef-action]'\n ];\n\n const elements = Array.from(doc.querySelectorAll(selectors.join(',')));\n\n for (const element of elements) {\n if (excludeSelectors && element.matches(excludeSelectors)) continue;\n\n const label = extractActionName(element);\n if (!label) continue;\n\n const selectors = generateStableSelector(element);\n\n actions.push({\n id: `${url}#action-${actions.length}`,\n url: url,\n headingText: label,\n headingId: `action-${actions.length}`,\n breadcrumb: '',\n bodyText: label,\n type: 'action',\n selector: selectors[0], selectors,\n destructive: isDestructiveAction(label),\n label\n });\n }\n\n return actions;\n}\n\nexport function extractFields(html: string, url: string): IndexRecord[] {\n const fields: IndexRecord[] = [];\n const doc = new DOMParser().parseFromString(html, 'text/html');\n\n const formElements = Array.from(doc.querySelectorAll('form'));\n\n for (const form of formElements) {\n let breadcrumb = '';\n let current: Element | null = form.parentElement;\n while (current && current !== doc.body) {\n if (current.matches('h1, h2, h3, h4, h5, h6, article, section, [role=\"main\"], main')) {\n const headingText = current.textContent?.trim() || '';\n if (headingText) {\n breadcrumb = headingText;\n }\n break;\n }\n current = current.parentElement;\n }\n\n const inputs = Array.from(form.querySelectorAll('input, textarea, select'));\n\n for (const input of inputs) {\n if (input.matches('input[type=\"hidden\"], input[type=\"button\"], input[type=\"submit\"], input[type=\"reset\"]')) {\n continue;\n }\n\n let label = '';\n const id = input.id;\n if (id) {\n const labelElement = doc.querySelector(`label[for=\"${id}\"]`);\n if (labelElement) {\n label = labelElement.textContent?.trim() || '';\n }\n }\n\n if (!label) {\n const parentLabel = input.closest('label');\n if (parentLabel) {\n label = parentLabel.textContent?.trim() || '';\n const inputElement = input as HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement;\n if (label && inputElement.value && label.includes(inputElement.value)) {\n label = label.replace(inputElement.value, '').trim();\n }\n }\n }\n\n if (!label) {\n const inputElement = input as HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement;\n const placeholder = 'placeholder' in inputElement ? inputElement.placeholder : '';\n label = placeholder || input.getAttribute('aria-label') || '';\n }\n\n if (!label) continue;\n\n const selectors = generateStableSelector(input);\n const inputElement = input as HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement;\n\n fields.push({\n id: `${url}#field-${fields.length}`,\n url: url,\n headingText: label,\n headingId: `field-${fields.length}`,\n breadcrumb,\n bodyText: label,\n type: 'field',\n selector: selectors[0], selectors,\n label,\n value: inputElement.value\n });\n }\n }\n\n return fields;\n}\n\nexport function extractHiddenContent(doc: Document): void {\n doc.querySelectorAll('details').forEach(d => {\n if (!d.hasAttribute('open')) d.setAttribute('data-reef-was-closed', 'true');\n d.setAttribute('open', '');\n });\n doc.querySelectorAll('[aria-hidden=\"false\"]').forEach(el => {\n el.removeAttribute('aria-hidden');\n });\n}\n\nexport function extractLinks(html: string, url: string): IndexRecord[] {\n const links: IndexRecord[] = [];\n const doc = new DOMParser().parseFromString(html, 'text/html');\n\n const anchors = Array.from(doc.querySelectorAll('a[href]'));\n\n for (const anchor of anchors) {\n if (anchor.hasAttribute('rel') && anchor.getAttribute('rel')?.toLowerCase().includes('nofollow')) continue;\n\n const href = anchor.getAttribute('href');\n if (!href) continue;\n\n if (href === '#' || href.startsWith('javascript:')) continue;\n\n const linkText = anchor.textContent?.trim() || '';\n if (!linkText) continue;\n\n const resolvedUrl = resolveUrl(href, url);\n const isExternal = !resolvedUrl.startsWith(window.location.origin);\n const selectors = generateStableSelector(anchor);\n\n links.push({\n id: `${url}#link-${links.length}`,\n url: resolvedUrl,\n headingText: linkText,\n headingId: `link-${links.length}`,\n breadcrumb: '',\n bodyText: linkText,\n type: isExternal ? 'link' : 'section',\n selector: selectors[0], selectors\n });\n }\n\n return links;\n}\n\nexport function extractFiles(html: string, url: string, extensions?: string): IndexRecord[] {\n const files: IndexRecord[] = [];\n const doc = new DOMParser().parseFromString(html, 'text/html');\n\n const fileExtensions = extensions?.split(',').map(e => e.trim().toLowerCase()) ??\n ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'zip', 'csv'];\n\n const anchors = Array.from(doc.querySelectorAll('a[href]'));\n\n for (const anchor of anchors) {\n const href = anchor.getAttribute('href');\n if (!href) continue;\n\n const isFile = fileExtensions.some(ext =>\n href.toLowerCase().endsWith(`.${ext}`) ||\n href.toLowerCase().endsWith(`.${ext}?`) ||\n href.toLowerCase().endsWith(`.${ext}#`)\n );\n\n if (!isFile) continue;\n\n const linkText = anchor.textContent?.trim() || href.split('/').pop() || '';\n if (!linkText) continue;\n\n const resolvedUrl = resolveUrl(href, url);\n const selectors = generateStableSelector(anchor);\n\n files.push({\n id: `${url}#file-${files.length}`,\n url: resolvedUrl,\n headingText: linkText,\n headingId: `file-${files.length}`,\n breadcrumb: '',\n bodyText: linkText,\n type: 'file',\n selector: selectors[0], selectors\n });\n }\n\n return files;\n}\n\nexport function extractMedia(html: string, url: string): IndexRecord[] {\n const media: IndexRecord[] = [];\n const doc = new DOMParser().parseFromString(html, 'text/html');\n\n const images = Array.from(doc.querySelectorAll('img'));\n for (const img of images) {\n const alt = img.alt.trim();\n if (!alt) continue;\n\n let caption = '';\n const figure = img.closest('figure');\n if (figure) {\n const figcaption = figure.querySelector('figcaption');\n if (figcaption) {\n caption = figcaption.textContent?.trim() || '';\n }\n }\n\n const textToIndex = caption ? `${alt} ${caption}` : alt;\n if (!textToIndex.trim()) continue;\n\n const selectors = generateStableSelector(img);\n\n media.push({\n id: `${url}#media-image-${media.length}`,\n url: url,\n headingText: alt,\n headingId: `media-image-${media.length}`,\n breadcrumb: '',\n bodyText: textToIndex,\n type: 'media',\n selector: selectors[0], selectors\n });\n }\n\n const mediaElements = Array.from(doc.querySelectorAll('video, audio'));\n for (const element of mediaElements) {\n const title = element.getAttribute('title') || element.getAttribute('aria-label') || '';\n if (!title) continue;\n\n let transcript = '';\n const tracks = Array.from(element.querySelectorAll('track[kind=\"captions\"], track[kind=\"subtitles\"]'));\n for (const track of tracks) {\n const src = track.getAttribute('src');\n if (src) {\n transcript += `[Transcript available: ${src}] `;\n }\n }\n\n const textToIndex = transcript ? `${title} ${transcript}` : title;\n if (!textToIndex.trim()) continue;\n\n const selectors = generateStableSelector(element);\n\n media.push({\n id: `${url}#media-${media.length}`,\n url: url,\n headingText: title,\n headingId: `media-${media.length}`,\n breadcrumb: '',\n bodyText: textToIndex,\n type: 'media',\n selector: selectors[0], selectors,\n transcript: transcript.trim()\n });\n }\n\n return media;\n}\n\nexport function extractStructuredData(html: string, url: string): IndexRecord[] {\n const structured: IndexRecord[] = [];\n const doc = new DOMParser().parseFromString(html, 'text/html');\n\n const jsonLdScripts = Array.from(doc.querySelectorAll('script[type=\"application/ld+json\"]'));\n for (const script of jsonLdScripts) {\n try {\n const data = JSON.parse(script.textContent || '{}');\n\n if (Array.isArray(data) ? data.some((item: any) => item['@type'] === 'FAQPage') :\n data['@type'] === 'FAQPage') {\n const faqItems = Array.isArray(data) ?\n data.flatMap((item: any) => item.mainEntity || []) :\n data.mainEntity || [];\n\n for (const [index, question] of faqItems.entries()) {\n if (!question || !question.name) continue;\n\n const answer = question.acceptedAnswer?.text ||\n question.suggestedAnswer?.text ||\n '';\n\n if (!answer) continue;\n\n const textToIndex = `${question.name} ${answer}`;\n\n structured.push({\n id: `${url}#structured-faq-${index}`,\n url: url,\n headingText: question.name,\n headingId: `structured-faq-${index}`,\n breadcrumb: '',\n bodyText: textToIndex,\n type: 'structured',\n structuredData: { question: question.name, answer }\n });\n }\n }\n else if (data['@type']) {\n const type = data['@type'];\n const name = data.name || data.headline || '';\n const description = data.description || '';\n\n if (!name && !description) continue;\n\n const textToIndex = `${name} ${description}`.trim();\n if (!textToIndex) continue;\n\n structured.push({\n id: `${url}#structured-${type.toLowerCase()}-${structured.length}`,\n url: url,\n headingText: name || 'Structured Data',\n headingId: `structured-${type.toLowerCase()}-${structured.length}`,\n breadcrumb: '',\n bodyText: textToIndex,\n type: 'structured',\n structuredData: data\n });\n }\n } catch (e) {\n continue;\n }\n }\n\n return structured;\n}\n\n// Known tracking parameters to strip\nconst TRACKING_PARAMS = new Set([\n 'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'gclid', 'fbclid', 'mc_cid', 'mc_eid', 'mc_id',\n 'referrer', 'ref', 'source', 'campaign', 'click_id'\n]);\n\n// Strip known tracking parameters from URL\nfunction stripTrackingParams(url: string): string {\n try {\n const urlObj = new URL(url);\n const params = new URLSearchParams(urlObj.search);\n \n // Remove tracking parameters\n for (const param of TRACKING_PARAMS) {\n params.delete(param);\n }\n \n // Remove empty parameters\n for (const [key, value] of params.entries()) {\n if (!value) {\n params.delete(key);\n }\n }\n \n // Reconstruct URL\n urlObj.search = params.toString();\n return urlObj.toString();\n } catch {\n return url;\n }\n}\n\n// Normalize URL: strip trailing slash, sort/drop known tracking query params, resolve to canonical if present\nexport function normalizeUrl(url: string, html?: string): string {\n if (!url) return url;\n \n // Strip trailing slash from pathname\n let normalized = url.replace(/\\/$/, '');\n \n // Strip tracking parameters\n normalized = stripTrackingParams(normalized);\n \n // Check for canonical URL in HTML if provided\n if (html) {\n const canonicalMatch = html.match(/]*rel=[\"']canonical[\"'][^>]*href=[\"']([^\"']+)[\"']/i);\n if (canonicalMatch?.[1]) {\n const canonicalUrl = stripTrackingParams(canonicalMatch[1]);\n // If canonical URL is different, use it\n if (canonicalUrl !== normalized) {\n return canonicalUrl.replace(/\\/$/, '');\n }\n }\n }\n \n return normalized;\n}\n\nexport function resolveUrl(value: string, base: string): string {\n if (!value) return base;\n try {\n return new URL(value, base).toString();\n } catch {\n return value;\n }\n}\n", "import type { IndexRecord, WorkflowStep, AgentSession, WorkflowOptions, ActionResult, AgentOptions, ObservationOptions, StableWaitOptions, PaginationOptions } from './types.js';\nimport type { SearchIndex } from './search-index.js';\nimport { extractAccessibilityTree } from './extraction.js';\n\ntype InspectorInterface = {\n activate: () => void;\n deactivate: () => void;\n isActive: () => boolean;\n setRecords: (records: IndexRecord[]) => void;\n};\n\nexport { type WorkflowStep, type WorkflowOptions, type AgentSession, type ActionResult };\n\nexport class Agent {\n private index: SearchIndex;\n private inspector: InspectorInterface;\n private actionsMode: 'execute' | 'navigate-only';\n private options: AgentOptions;\n private actionCount = 0;\n private lastRoute = typeof window !== 'undefined' ? window.location.href : '';\n private lastAction: ActionResult = { success: false, reason: 'no-action' };\n\n constructor(index: SearchIndex, inspector: InspectorInterface, actionsMode: 'execute' | 'navigate-only' | AgentOptions = 'execute') {\n this.index = index;\n this.inspector = inspector;\n this.options = typeof actionsMode === 'string' ? { actionsMode } : actionsMode;\n this.actionsMode = this.options.actionsMode ?? 'execute';\n this.installRouteObserver();\n }\n\n async click(selector: string | IndexRecord): Promise {\n if (typeof selector !== 'string' && selector.destructive && (this.actionsMode === 'navigate-only' || this.options.destructive === false)) throw new Error('destructive-action-blocked');\n this.guardAction();\n const resolved = await this.resolveSelector(selector);\n if (!resolved.success) {\n if (typeof selector !== 'string' && this.actionsMode === 'navigate-only') return this;\n throw new Error(resolved.reason || 'Click failed');\n }\n\n const element = resolved.element;\n this.ensureVisible(element ?? null);\n const before = this.fingerprint();\n if (element && typeof MouseEvent !== 'undefined') {\n const clickEvent = new MouseEvent('click', {\n bubbles: true,\n cancelable: true,\n view: typeof window !== 'undefined' ? window : undefined,\n });\n element.dispatchEvent(clickEvent);\n }\n await this.waitForStable({ quietMs: 50, timeout: 1000 });\n if (!this.changedSince(before) && typeof selector !== 'string' && selector.selectors?.length) {\n const retry = await this.resolveSelector({ ...selector, selectors: selector.selectors.slice(1) });\n if (retry.success && retry.element && retry.element !== element) retry.element.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));\n }\n this.lastAction = { success: true, changed: this.changedSince(before), url: typeof window !== 'undefined' ? window.location.href : undefined, element };\n return this;\n }\n\n async type(selector: string | IndexRecord, value: string): Promise {\n this.guardAction();\n const resolved = await this.resolveSelector(selector);\n if (!resolved.success) {\n throw new Error(resolved.reason || 'Type failed');\n }\n\n const element = resolved.element as HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | null;\n this.ensureVisible(element);\n if (element && ('value' in element)) {\n const descriptor = Object.getOwnPropertyDescriptor(element, 'value');\n if (descriptor && descriptor.set) {\n descriptor.set.call(element, value);\n } else {\n element.value = value;\n }\n element.dispatchEvent(new Event('input', { bubbles: true }));\n element.dispatchEvent(new Event('change', { bubbles: true }));\n }\n this.lastAction = { success: true, changed: true, url: typeof window !== 'undefined' ? window.location.href : undefined, element: element ?? undefined };\n return this;\n }\n\n async submit(selector?: string | IndexRecord): Promise {\n let element: Element | null = null;\n\n if (selector) {\n const resolved = await this.resolveSelector(selector);\n if (resolved.success && resolved.element) {\n element = resolved.element;\n }\n } else if (typeof document !== 'undefined') {\n element = document.querySelector('form') || document.querySelector('button[type=\"submit\"]') ||\n document.querySelector('input[type=\"submit\"]');\n }\n\n if (element) {\n if (element instanceof HTMLFormElement) {\n element.dispatchEvent(new Event('submit', { bubbles: true }));\n } else {\n const clickEvent = new MouseEvent('click', {\n bubbles: true,\n cancelable: true,\n view: typeof window !== 'undefined' ? window : undefined,\n });\n element.dispatchEvent(clickEvent);\n }\n this.lastAction = { success: true, changed: true, url: typeof window !== 'undefined' ? window.location.href : undefined, element };\n }\n return this;\n }\n\n getLastActionResult(): ActionResult { return { ...this.lastAction }; }\n\n async navigate(url: string): Promise {\n if ((url.startsWith('http://') || url.startsWith('https://') || url.startsWith('/')) && typeof window !== 'undefined') {\n const target = new URL(url, window.location.href).toString();\n const sameOrigin = new URL(target).origin === window.location.origin;\n if (sameOrigin && target.split('#')[0] === window.location.href.split('#')[0] && window.history.pushState) {\n window.history.pushState({}, '', target);\n window.dispatchEvent(new PopStateEvent('popstate'));\n await this.waitForStable();\n } else window.location.href = target;\n }\n return this;\n }\n\n async back(): Promise {\n if (typeof window !== 'undefined') {\n window.history.back();\n }\n return this;\n }\n\n async forward(): Promise {\n if (typeof window !== 'undefined') {\n window.history.forward();\n }\n return this;\n }\n\n async wait(timeout: number = 1000): Promise {\n await new Promise(resolve => setTimeout(resolve, timeout));\n return this;\n }\n\n async extract(selector: string | IndexRecord): Promise {\n const resolved = await this.resolveSelector(selector);\n if (!resolved.success) {\n throw new Error(resolved.reason || 'Extract failed');\n }\n\n const element = resolved.element;\n if (!element) {\n throw new Error('Element not found');\n }\n\n if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {\n return element.value;\n }\n\n return element.textContent?.trim() || '';\n }\n\n async observe(options?: ObservationOptions): Promise {\n if (typeof document === 'undefined') return [];\n const records = extractAccessibilityTree(options?.root ?? document).filter(record => {\n if (options?.includeHidden) return true;\n const element = this.resolveRecordElement(record);\n return !!element && (!options?.inViewport && options?.inViewport !== false ? this.isVisible(element) : this.isVisible(element));\n });\n this.inspector.setRecords(records);\n return records;\n }\n\n async waitForStable(options?: StableWaitOptions): Promise {\n if (typeof document === 'undefined' || typeof MutationObserver === 'undefined') return this;\n const quietMs = options?.quietMs ?? 250;\n const timeout = options?.timeout ?? 5000;\n await new Promise(resolve => {\n let quietTimer: ReturnType;\n const observer = new MutationObserver(() => { clearTimeout(quietTimer); quietTimer = setTimeout(done, quietMs); });\n const done = () => { clearTimeout(quietTimer); observer.disconnect(); resolve(); };\n observer.observe(document, { subtree: true, childList: true, attributes: true, characterData: true });\n quietTimer = setTimeout(done, quietMs);\n setTimeout(done, timeout);\n });\n return this;\n }\n\n isVisible(element: Element | null): boolean {\n if (!element) return false;\n const style = typeof window !== 'undefined' ? window.getComputedStyle(element) : null;\n if (style && (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0')) return false;\n const rect = element.getBoundingClientRect();\n return rect.width > 0 && rect.height > 0 && (typeof window === 'undefined' || (rect.bottom >= 0 && rect.right >= 0 && rect.top <= window.innerHeight && rect.left <= window.innerWidth));\n }\n\n async exhaustPagination(options?: PaginationOptions): Promise {\n const maxPages = options?.maxPages ?? 10;\n const nextText = (options?.nextText ?? ['next', 'older', 'more']).map(v => v.toLowerCase());\n const merged = new Map();\n for (let page = 0; page < maxPages && this.actionCount < (options?.maxActionsPerRun ?? this.options.maxActionsPerRun ?? Infinity); page++) {\n const records = await this.observe({ inViewport: false, includeHidden: true });\n records.forEach(record => merged.set(`${record.label}:${record.type}`, record));\n const next = records.find(record => record.type === 'action' && !record.destructive && nextText.some(text => (record.label || '').toLowerCase().includes(text)));\n if (next) await this.click(next);\n else if (options?.scroll !== false && typeof window !== 'undefined') { window.scrollTo(0, document.body.scrollHeight); await this.waitForStable(); }\n else break;\n if (!next && options?.scroll === false) break;\n }\n return [...merged.values()];\n }\n\n private async resolveSelector(selector: string | IndexRecord): Promise {\n let element: Element | null = null;\n\n if (typeof selector === 'string') {\n element = this.queryComposed(selector);\n } else if (selector) {\n const record = this.index.allSections.find(r => r.id === selector.id);\n const candidates = record?.selectors?.length ? record.selectors : (record?.selector ? [record.selector] : []);\n for (const candidate of candidates) { element = this.queryComposed(candidate, record?.iframePath); if (element) break; }\n if (!element) element = this.resolveRecordElement(selector);\n if (!element && selector.headingText) { const match = await this.findActionable(selector.headingText); if (match && match.id !== selector.id) return this.resolveSelector(match); }\n }\n\n if (!element) {\n return { success: false, reason: 'element-not-found' };\n }\n\n return { success: true, element };\n }\n\n private queryComposed(selector: string, iframePath?: number[]): Element | null {\n if (typeof document === 'undefined') return null;\n const find = (root: Document | ShadowRoot): Element | null => {\n if (selector.startsWith('xpath=')) { try { return document.evaluate(selector.slice(6), root, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue as Element | null; } catch { return null; } }\n const direct = root.querySelector(selector); if (direct) return direct;\n for (const host of Array.from(root.querySelectorAll('*'))) if (host.shadowRoot) { const found = find(host.shadowRoot); if (found) return found; }\n return null;\n };\n if (!iframePath?.length) return find(document);\n let current: Document | null = document;\n for (const position of iframePath) { const frame = current?.querySelectorAll('iframe')[position] as HTMLIFrameElement | undefined; current = frame?.contentDocument ?? null; }\n return current ? find(current) : null;\n }\n\n private resolveRecordElement(record: IndexRecord): Element | null { const candidates = record.selectors?.length ? record.selectors : (record.selector ? [record.selector] : []); for (const selector of candidates) { const found = this.queryComposed(selector, record.iframePath); if (found) return found; } return null; }\n private ensureVisible(element: Element | null): void { if (element && !this.isVisible(element)) element.scrollIntoView({ block: 'center', inline: 'nearest' }); }\n private fingerprint(): string { return `${typeof window !== 'undefined' ? window.location.href : ''}|${typeof document !== 'undefined' ? document.body?.textContent?.length : 0}`; }\n private changedSince(before: string): boolean { return this.fingerprint() !== before; }\n private guardAction(): void { this.actionCount++; if (this.actionCount > (this.options.maxActionsPerRun ?? Infinity)) throw new Error('max-actions-per-run-exceeded'); if (this.options.rateLimitMs) return; }\n private installRouteObserver(): void {\n if (typeof window === 'undefined') return;\n const onRoute = () => { this.lastRoute = window.location.href; void this.observe({ inViewport: false }); };\n window.addEventListener('popstate', onRoute); window.addEventListener('hashchange', onRoute);\n for (const method of ['pushState', 'replaceState'] as const) {\n const historyMethod: (...args: any[]) => any = (window.history[method] as any).bind(window.history);\n (window.history as any)[method] = (...args: any[]) => { const result = historyMethod(...args); onRoute(); return result; };\n }\n }\n\n async findActionable(text: string): Promise {\n const results = this.index.allSections.filter(r =>\n r.type === 'action' || r.type === 'field'\n );\n\n const normalized = text.toLowerCase().trim();\n for (const record of results) {\n if (record.headingText.toLowerCase().includes(normalized) ||\n record.label?.toLowerCase().includes(normalized)) {\n return record;\n }\n }\n return null;\n }\n\n async executeWorkflow(steps: WorkflowStep[], options?: WorkflowOptions): Promise {\n const maxRetries = options?.maxRetries ?? 0;\n const retryDelay = options?.retryDelay ?? 500;\n const stopOnError = options?.stopOnError ?? true;\n\n for (let i = 0; i < steps.length; i++) {\n const step = steps[i];\n options?.onStepStart?.(step, i);\n\n let attempt = 0;\n let success = false;\n\n while (attempt <= maxRetries && !success) {\n try {\nswitch (step.action) {\n case 'click':\n if (step.selector) {\n await this.click(step.selector);\n } else if (step.recordId) {\n const record = this.index.allSections.find(r => r.id === step.recordId);\n if (record) await this.click(record);\n }\n success = true;\n break;\n case 'type':\n if (step.selector) {\n await this.type(step.selector, step.value || '');\n } else if (step.recordId) {\n const record = this.index.allSections.find(r => r.id === step.recordId);\n if (record) await this.type(record, step.value || '');\n }\n success = true;\n break;\n case 'navigate':\n if (step.url) {\n await this.navigate(step.url);\n await this.waitForNavigation();\n }\n success = true;\n break;\n case 'extract':\n success = true;\n break;\n case 'submit':\n await this.submit(step.selector);\n success = true;\n break;\n case 'back':\n await this.back();\n success = true;\n break;\n case 'forward':\n await this.forward();\n success = true;\n break;\n case 'wait':\n if (step.timeout) {\n await this.wait(step.timeout);\n }\n success = true;\n break;\n }\n options?.onStepComplete?.(step, i, success);\n } catch (error) {\n attempt++;\n if (attempt > maxRetries) {\n options?.onStepError?.(step, i, error as Error);\n if (stopOnError) {\n throw error;\n }\n } else {\n await new Promise(resolve => setTimeout(resolve, retryDelay));\n }\n }\n }\n }\n }\n\n private async waitForNavigation(): Promise {\n await new Promise(resolve => {\n if (typeof document !== 'undefined') {\n const checkReady = () => {\n if (document.readyState === 'complete') {\n resolve();\n } else {\n setTimeout(checkReady, 100);\n }\n };\n checkReady();\n } else {\n resolve();\n }\n });\n }\n\n getSession(): AgentSession {\n return {\n id: this.generateSessionId(),\n url: typeof window !== 'undefined' ? window.location.href : 'about:blank',\n timestamp: Date.now(),\n cookies: this.getCookies(),\n localStorage: this.getLocalStorageSnapshot(),\n };\n }\n\n private generateSessionId(): string {\n return Math.random().toString(36).substring(2, 15) +\n Math.random().toString(36).substring(2, 15);\n }\n\n private getCookies(): Record {\n const cookies: Record = {};\n if (typeof document !== 'undefined') {\n document.cookie.split(';').forEach(cookie => {\n const [name, value] = cookie.trim().split('=');\n if (name) cookies[name] = value || '';\n });\n }\n return cookies;\n }\n\n private getLocalStorageSnapshot(): Record {\n const storage: Record = {};\n try {\n if (typeof localStorage !== 'undefined') {\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key) {\n storage[key] = localStorage.getItem(key) || '';\n }\n }\n }\n } catch (e) {\n // localStorage may not be available\n }\n return storage;\n }\n}\n", "/**\n * @file Search index data structure and search algorithms.\n * Provides in-memory search indexing with fuzzy matching and result ranking.\n */\n\nimport { IndexRecord, SearchOptions, ScoredRecord, MatchSpan, TokenFilter, ReefConfig, SearchPage } from './types.js';\n\n// Default stop words for English\nconst STOP_WORDS = new Set([\n 'a', 'an', 'the', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by',\n 'from', 'up', 'about', 'into', 'through', 'during', 'before', 'after', 'above', 'below',\n 'is', 'are', 'was', 'were', 'be', 'been', 'being', 'have', 'has', 'had', 'do', 'does',\n 'did', 'will', 'would', 'should', 'could', 'may', 'might', 'must', 'shall', 'can', 'it',\n 'its', 'this', 'that', 'these', 'those', 'i', 'you', 'he', 'she', 'we', 'they', 'what',\n 'which', 'who', 'whom', 'when', 'where', 'why', 'how', 'all', 'each', 'every', 'both',\n 'few', 'more', 'most', 'other', 'some', 'such', 'no', 'nor', 'not', 'only', 'own',\n 'same', 'so', 'than', 'too', 'very', 'just', 'also', 'now', 'here', 'there',\n]);\n\n// Default tokenizer pipeline\nfunction createDefaultTokenizePipeline(): TokenFilter[] {\n return [\n (token: string) => {\n if (STOP_WORDS.has(token)) return null;\n return token;\n },\n (token: string) => {\n return token.normalize('NFD').replace(/[\\u0300-\\u036f]/g, '');\n },\n (token: string) => {\n if (token.length <= 2) return token;\n if (/ing$/.test(token)) return token.slice(0, -3);\n if (/ed$/.test(token)) return token.slice(0, -2);\n if (/es$/.test(token) && token.length > 3) return token.slice(0, -2);\n if (/s$/.test(token) && token.length > 3) return token.slice(0, -1);\n return token;\n },\n ];\n}\n\n// Apply tokenization pipeline to text and return array of tokens\nfunction tokenizeText(text: string, pipeline?: TokenFilter[]): string[] {\n if (!text) return [];\n\n let tokens = text.toLowerCase().split(/[^a-z0-9]+/i).filter(t => t.length > 0);\n const filters = pipeline ?? createDefaultTokenizePipeline();\n\n const result: string[] = [];\n for (const token of tokens) {\n let processed: string | null = token;\n for (const filter of filters) {\n if (processed === null) break;\n processed = filter(processed);\n }\n if (processed !== null) {\n result.push(processed);\n }\n }\n return result;\n}\n\n// Search cache entry for LRU\ninterface CacheEntry {\n resultIds: string[];\n timestamp: number;\n}\n\n// Per-field document frequency tracking for BM25F\ninterface FieldDocFreq {\n headingText: Map;\n bodyText: Map;\n label: Map;\n breadcrumb: Map;\n}\n\n// Query popularity tracking entry\ninterface PopularityEntry {\n query: string;\n recordId: string;\n count: number;\n}\n\n// Trie node for autocomplete\ninterface TrieNode {\n children: Map;\n records: IndexRecord[]; // Records that have this prefix\n}\n\nexport interface SearchIndex {\n headingIndex: Map;\n headingIds: Map;\n bodyIndex: Map;\n allSections: IndexRecord[];\n queryCache: Map;\n popularQueries: string[];\n docFrequency: Map;\n fieldDocFreq: FieldDocFreq;\n totalDocs: number;\n queryPopularity: Map;\n version: number;\n headingTrie: TrieNode; // Trie for efficient prefix lookups\n}\n\n// Create a new trie node\nfunction createTrieNode(): TrieNode {\n return {\n children: new Map(),\n records: [],\n };\n}\n\n// Insert a string into the trie\nfunction trieInsert(root: TrieNode, text: string, record: IndexRecord): void {\n let current = root;\n \n for (let i = 0; i < text.length; i++) {\n const char = text[i];\n if (!current.children.has(char)) {\n current.children.set(char, createTrieNode());\n }\n current = current.children.get(char)!;\n \n // Add record to this prefix node\n if (!current.records.some(r => r.id === record.id)) {\n current.records.push(record);\n }\n }\n}\n\n// Delete a string from the trie\nfunction trieDelete(root: TrieNode, text: string, recordId: string): void {\n let current = root;\n const path: {node: TrieNode, char: string}[] = [];\n \n // Traverse to find the string\n for (let i = 0; i < text.length; i++) {\n const char = text[i];\n if (!current.children.has(char)) {\n return; // String not in trie\n }\n path.push({node: current, char});\n current = current.children.get(char)!;\n }\n \n // Remove the record from all prefix nodes\n const nodesToClean: TrieNode[] = [];\n let tempCurrent = root;\n \n for (let i = 0; i < text.length; i++) {\n const char = text[i];\n if (tempCurrent.children.has(char)) {\n tempCurrent = tempCurrent.children.get(char)!;\n // Remove the record from this node\n tempCurrent.records = tempCurrent.records.filter(r => r.id !== recordId);\n nodesToClean.push(tempCurrent);\n }\n }\n \n // Clean up empty nodes (optional optimization)\n for (const node of nodesToClean) {\n if (node.records.length === 0 && node.children.size === 0) {\n // This node can be removed, but we'd need to track parent references\n // For now, just leave empty nodes as they don't affect functionality\n }\n }\n}\n\n// Query the trie for prefix matches\nfunction triePrefixQuery(root: TrieNode, prefix: string): IndexRecord[] {\n let current = root;\n \n for (let i = 0; i < prefix.length; i++) {\n const char = prefix[i];\n if (!current.children.has(char)) {\n return []; // Prefix not found\n }\n current = current.children.get(char)!;\n }\n \n // Return all records that have this prefix\n return [...current.records];\n}\n\n// Count total nodes in the trie (for memory comparison)\nfunction countTrieNodes(root: TrieNode): number {\n let count = 1; // Count current node\n for (const child of root.children.values()) {\n count += countTrieNodes(child);\n }\n return count;\n}\n\n// Helper function for finer-grained cache invalidation\nfunction invalidateAffectedQueries(index: SearchIndex, recordId: string, headingText: string, bodyText: string): void {\n // Tokenize the record's heading and body text\n const recordTokens = new Set();\n \n // Add heading tokens\n headingText.toLowerCase().split(/\\s+/).forEach(token => {\n if (token.length >= 2) recordTokens.add(token);\n });\n \n // Add body tokens\n bodyText.toLowerCase().split(/\\s+/).forEach(token => {\n if (token.length >= 2) recordTokens.add(token);\n });\n \n // Find queries that need to be invalidated\n const queriesToInvalidate = new Set();\n \n for (const [query, entry] of index.queryCache.entries()) {\n // Check if this query's results include the mutated record\n if (entry.resultIds.includes(recordId)) {\n queriesToInvalidate.add(query);\n continue;\n }\n \n // Check if query terms overlap with record tokens\n const queryLower = query.toLowerCase();\n const queryTokens = queryLower.split(/\\s+/).filter(t => t.length >= 2);\n \n for (const queryToken of queryTokens) {\n if (recordTokens.has(queryToken)) {\n queriesToInvalidate.add(query);\n break;\n }\n }\n }\n \n // Remove invalidated queries from cache\n if (queriesToInvalidate.size > 0) {\n for (const query of queriesToInvalidate) {\n index.queryCache.delete(query);\n }\n } else {\n // If we can't determine affected queries (e.g., empty cache or no matches),\n // fall back to clearing the entire cache\n index.queryCache.clear();\n }\n}\n\n// Calculate Jaccard similarity between two sets of tokens\nfunction jaccardSimilarity(tokens1: Set, tokens2: Set): number {\n if (tokens1.size === 0 || tokens2.size === 0) return 0;\n \n let intersection = 0;\n for (const token of tokens1) {\n if (tokens2.has(token)) intersection++;\n }\n \n const union = tokens1.size + tokens2.size - intersection;\n return intersection / union;\n}\n\n// Extract tokens from text for MMR similarity comparison\nfunction extractTokensForMMR(text: string): Set {\n const tokens = text.toLowerCase().split(/\\s+/);\n return new Set(tokens.filter(t => t.length > 2)); // Filter out very short tokens\n}\n\n// Maximal Marginal Relevance (MMR) re-ranking\n// Diversifies results by penalizing documents similar to already-selected ones\nfunction applyMMR(\n scoredEntries: Array<[IndexRecord, number]>, \n lambda: number = 0.5\n): Array<[IndexRecord, number]> {\n if (scoredEntries.length <= 1) return scoredEntries;\n \n const results: Array<[IndexRecord, number]> = [];\n const selectedTokens: Set[] = [];\n \n // Sort by score first (descending)\n const sortedByScore = [...scoredEntries].sort((a, b) => b[1] - a[1]);\n \n // Select first result (highest score)\n results.push(sortedByScore[0]);\n selectedTokens.push(extractTokensForMMR(sortedByScore[0][0].headingText + ' ' + sortedByScore[0][0].bodyText));\n \n // Select remaining results using MMR\n for (let i = 1; i < sortedByScore.length; i++) {\n const [record, originalScore] = sortedByScore[i];\n const recordText = record.headingText + ' ' + record.bodyText;\n const recordTokens = extractTokensForMMR(recordText);\n \n // Calculate similarity to already-selected results\n let maxSimilarity = 0;\n for (const selectedTokenSet of selectedTokens) {\n const similarity = jaccardSimilarity(recordTokens, selectedTokenSet);\n maxSimilarity = Math.max(maxSimilarity, similarity);\n }\n \n // MMR score: (1 - lambda) * relevance + lambda * (1 - maxSimilarity)\n const mmrScore = (1 - lambda) * originalScore + lambda * (1 - maxSimilarity);\n \n // Insert in the right position\n let inserted = false;\n for (let j = 0; j < results.length; j++) {\n if (mmrScore > results[j][1]) {\n results.splice(j, 0, [record, mmrScore]);\n selectedTokens.splice(j, 0, recordTokens);\n inserted = true;\n break;\n }\n }\n \n if (!inserted) {\n results.push([record, mmrScore]);\n selectedTokens.push(recordTokens);\n }\n }\n \n return results;\n}\n\nexport function createSearchIndex(): SearchIndex {\n return {\n headingIndex: new Map(),\n headingIds: new Map(),\n bodyIndex: new Map(),\n allSections: [],\n queryCache: new Map(),\n popularQueries: [],\n docFrequency: new Map(),\n fieldDocFreq: {\n headingText: new Map(),\n bodyText: new Map(),\n label: new Map(),\n breadcrumb: new Map(),\n },\n totalDocs: 0,\n queryPopularity: new Map(),\n version: 1,\n headingTrie: createTrieNode(),\n };\n}\n\nexport function addToIndex(index: SearchIndex, records: IndexRecord[], tokenizePipeline?: TokenFilter[]): void {\n index.totalDocs += records.length;\n\n for (const record of records) {\n // Index by exact heading text (backward compatible)\n const headingLower = record.headingText.toLowerCase();\n if (headingLower.length >= 2) {\n const existing = index.headingIds.get(headingLower) ?? [];\n existing.push(record);\n index.headingIds.set(headingLower, existing);\n\n // Also index normalized version for diacritic matching\n const normalizedHeading = headingLower.normalize('NFD').replace(/[\\u0300-\\u036f]/g, '');\n if (normalizedHeading !== headingLower) {\n const normalizedExisting = index.headingIds.get(normalizedHeading) ?? [];\n normalizedExisting.push(record);\n index.headingIds.set(normalizedHeading, normalizedExisting);\n }\n }\n\n // Index by heading prefixes (also normalized) - keep for backward compatibility\n for (let i = 2; i <= headingLower.length; i++) {\n const prefix = headingLower.slice(0, i);\n const existing = index.headingIndex.get(prefix) ?? [];\n existing.push(record);\n index.headingIndex.set(prefix, existing);\n }\n const normalizedHeading = headingLower.normalize('NFD').replace(/[\\u0300-\\u036f]/g, '');\n for (let i = 2; i <= normalizedHeading.length; i++) {\n const prefix = normalizedHeading.slice(0, i);\n if (prefix !== headingLower.slice(0, i)) {\n const existing = index.headingIndex.get(prefix) ?? [];\n existing.push(record);\n index.headingIndex.set(prefix, existing);\n }\n }\n\n // Insert into trie for efficient prefix lookups\n trieInsert(index.headingTrie, headingLower, record);\n if (normalizedHeading !== headingLower) {\n trieInsert(index.headingTrie, normalizedHeading, record);\n }\n\n // Index body words (backward compatible, simple split)\n const bodyLower = record.bodyText.toLowerCase();\n const bodyWords = bodyLower.split(/\\s+/);\n for (const word of bodyWords) {\n if (word.length >= 3) {\n const existing = index.bodyIndex.get(word) ?? [];\n existing.push(record);\n index.bodyIndex.set(word, existing);\n\n // Also index normalized version\n const normalizedWord = word.normalize('NFD').replace(/[\\u0300-\\u036f]/g, '');\n if (normalizedWord !== word) {\n const normalizedExisting = index.bodyIndex.get(normalizedWord) ?? [];\n normalizedExisting.push(record);\n index.bodyIndex.set(normalizedWord, normalizedExisting);\n }\n }\n }\n\n // Track doc frequency for BM25 (use body words for now)\n for (const word of bodyWords) {\n if (word.length >= 2) {\n const currentFreq = index.docFrequency.get(word) ?? 0;\n index.docFrequency.set(word, currentFreq + 1);\n \n // Track per-field doc frequency for BM25F\n const currentBodyFreq = index.fieldDocFreq.bodyText.get(word) ?? 0;\n index.fieldDocFreq.bodyText.set(word, currentBodyFreq + 1);\n }\n }\n\n // Track per-field doc frequency for heading\n const headingWords = headingLower.split(/\\s+/);\n for (const word of headingWords) {\n if (word.length >= 2) {\n const currentHeadingFreq = index.fieldDocFreq.headingText.get(word) ?? 0;\n index.fieldDocFreq.headingText.set(word, currentHeadingFreq + 1);\n }\n }\n\n // Track per-field doc frequency for label if present\n if (record.label) {\n const labelLower = record.label.toLowerCase();\n const labelWords = labelLower.split(/\\s+/);\n for (const word of labelWords) {\n if (word.length >= 2) {\n const currentLabelFreq = index.fieldDocFreq.label.get(word) ?? 0;\n index.fieldDocFreq.label.set(word, currentLabelFreq + 1);\n }\n }\n }\n\n // Track per-field doc frequency for breadcrumb if present\n if (record.breadcrumb) {\n const breadcrumbLower = record.breadcrumb.toLowerCase();\n const breadcrumbWords = breadcrumbLower.split(/\\s+/);\n for (const word of breadcrumbWords) {\n if (word.length >= 2) {\n const currentBreadcrumbFreq = index.fieldDocFreq.breadcrumb.get(word) ?? 0;\n index.fieldDocFreq.breadcrumb.set(word, currentBreadcrumbFreq + 1);\n }\n }\n }\n\n // Also index the label if present\n if (record.label) {\n const labelLower = record.label.toLowerCase();\n if (labelLower.length >= 2) {\n const existing = index.bodyIndex.get(labelLower) ?? [];\n existing.push(record);\n index.bodyIndex.set(labelLower, existing);\n }\n }\n\n if (record.type === 'structured' && record.structuredData) {\n if (record.structuredData.question) {\n const questionWords = record.structuredData.question.toLowerCase().split(/\\s+/);\n for (const word of questionWords) {\n if (word.length >= 3) {\n const existing = index.bodyIndex.get(word) ?? [];\n existing.push(record);\n index.bodyIndex.set(word, existing);\n }\n }\n }\n if (record.structuredData.answer) {\n const answerWords = record.structuredData.answer.toLowerCase().split(/\\s+/);\n for (const word of answerWords) {\n if (word.length >= 3) {\n const existing = index.bodyIndex.get(word) ?? [];\n existing.push(record);\n index.bodyIndex.set(word, existing);\n }\n }\n }\n }\n }\n\n // Also add records to allSections\n index.allSections.push(...records);\n}\n\nexport const addSectionsToIndex = addToIndex;\n\nexport function getAllSections(index: SearchIndex): IndexRecord[] {\n return index.allSections;\n}\n\n/** Return a stable cursor page over the current ranking. */\nexport function searchWithPagination(\n query: string,\n index: SearchIndex,\n options: SearchOptions & { cursor?: string; pageSize?: number } = {}\n): SearchPage {\n const pageSize = Math.max(1, options.pageSize ?? options.limit ?? 20);\n const offset = options.cursor ? Math.max(0, Number.parseInt(options.cursor, 10) || 0) : 0;\n const ranked = searchSections(query, index, { ...options, limit: Number.MAX_SAFE_INTEGER }) as ScoredRecord[] | IndexRecord[];\n const results = ranked.slice(offset, offset + pageSize).map((record) =>\n 'score' in record ? record : { record, score: 0 }\n ) as ScoredRecord[];\n const nextOffset = offset + results.length;\n return {\n results,\n total: ranked.length,\n hasMore: nextOffset < ranked.length,\n ...(nextOffset < ranked.length ? { nextCursor: String(nextOffset) } : {}),\n };\n}\n\nexport function getTotalResultCount(query: string, index: SearchIndex, options: SearchOptions = {}): number {\n return (searchSections(query, index, { ...options, limit: Number.MAX_SAFE_INTEGER }) as unknown[]).length;\n}\n\nexport function levenshteinDistance(a: string, b: string): number {\n const m = a.length;\n const n = b.length;\n if (m === 0) return n;\n if (n === 0) return m;\n\n const dp: number[] = new Array(n + 1);\n for (let j = 0; j <= n; j++) dp[j] = j;\n\n for (let i = 1; i <= m; i++) {\n let prev = dp[0];\n dp[0] = i;\n for (let j = 1; j <= n; j++) {\n const temp = dp[j];\n dp[j] = Math.min(\n dp[j] + 1,\n dp[j - 1] + 1,\n prev + (a[i - 1] !== b[j - 1] ? 1 : 0)\n );\n prev = temp;\n }\n }\n return dp[n];\n}\n\nexport function findClosestWord(query: string, index: SearchIndex, maxDistance = 2): string | null {\n const term = query.trim().toLowerCase();\n if (!term || term.length < 2) return null;\n\n let closest: { word: string; distance: number } | null = null;\n\n const checkWord = (word: string) => {\n if (!word || Math.abs(word.length - term.length) > maxDistance) return;\n const distance = levenshteinDistance(word, term);\n const current = closest as { word: string; distance: number } | null;\n if (distance <= maxDistance && (!current || distance < current.distance)) {\n closest = { word, distance };\n }\n };\n\n for (const key of index.headingIds.keys()) {\n checkWord(key);\n }\n for (const key of index.bodyIndex.keys()) {\n checkWord(key);\n }\n\n return (closest as { word: string; distance: number } | null)?.word ?? null;\n}\n\n// Default field weights (matching Fuse.js convention where higher = more important)\nconst DEFAULT_WEIGHTS: Record = {\n headingText: 2,\n bodyText: 1,\n label: 1.5,\n breadcrumb: 0.5,\n};\n\n// Calculate field length normalization factor (shorter matches = better)\nfunction getLengthNorm(textLength: number, avgLength = 50): number {\n const norm = 1 - Math.exp(-textLength / avgLength);\n return Math.max(0, Math.min(1, norm));\n}\n\n// Find all match positions in a string for a query\nfunction findMatchPositions(text: string, query: string): Array<{ start: number; end: number }> {\n const positions: Array<{ start: number; end: number }> = [];\n if (!query.trim() || !text) return positions;\n\n const lowerText = text.toLowerCase();\n const lowerQuery = query.toLowerCase();\n let idx = 0;\n\n while ((idx = lowerText.indexOf(lowerQuery, idx)) !== -1) {\n positions.push({ start: idx, end: idx + query.length });\n idx += query.length;\n }\n\n return positions;\n}\n\n// Parse extended search query syntax\nexport type QueryNode =\n | { type: 'term'; value: string; field?: string }\n | { type: 'exact'; value: string }\n | { type: 'exclude'; value: string }\n | { type: 'prefix'; value: string }\n | { type: 'suffix'; value: string }\n | { type: 'or'; children: QueryNode[] }\n | { type: 'and'; children: QueryNode[] };\n\nexport function parseExtendedQuery(query: string): QueryNode {\n const tokens = tokenizeExtendedQuery(query);\n return buildQueryTree(tokens);\n}\n\nfunction tokenizeExtendedQuery(query: string): Array<{ type: string; value: string }> {\n const tokens: Array<{ type: string; value: string }> = [];\n\n let rest = query;\n const exactRegex = /'([^']+)'/g;\n const excludeRegex = /!(\\S+)/g;\n const prefixRegex = /\\^(\\S+)/g;\n const suffixRegex = /(\\S+)\\$/g;\n\n while (rest.length > 0) {\n let matched = false;\n\n const exactMatch = exactRegex.exec(rest);\n if (exactMatch && exactMatch.index === 0) {\n tokens.push({ type: 'exact', value: exactMatch[1] });\n rest = rest.slice(exactMatch[0].length);\n matched = true;\n continue;\n }\n\n const excludeMatch = excludeRegex.exec(rest);\n if (excludeMatch && excludeMatch.index === 0) {\n tokens.push({ type: 'exclude', value: excludeMatch[1] });\n rest = rest.slice(excludeMatch[0].length);\n matched = true;\n continue;\n }\n\n const prefixMatch = prefixRegex.exec(rest);\n if (prefixMatch && prefixMatch.index === 0) {\n tokens.push({ type: 'prefix', value: prefixMatch[1] });\n rest = rest.slice(prefixMatch[0].length);\n matched = true;\n continue;\n }\n\n const suffixMatch = suffixRegex.exec(rest);\n if (suffixMatch && suffixMatch.index === 0) {\n tokens.push({ type: 'suffix', value: suffixMatch[1] });\n rest = rest.slice(suffixMatch[0].length);\n matched = true;\n continue;\n }\n\n const pipeMatch = rest.match(/^\\|/);\n if (pipeMatch) {\n tokens.push({ type: 'or', value: '|' });\n rest = rest.slice(1);\n matched = true;\n continue;\n }\n\n const spaceMatch = rest.match(/^\\s+/);\n if (spaceMatch && !matched) {\n rest = rest.slice(spaceMatch[0].length);\n continue;\n }\n\n const wordMatch = rest.match(/^(\\S+)/);\n if (wordMatch) {\n const fieldMatch = wordMatch[1].match(/^([\\w-]+):(.+)$/);\n tokens.push({ type: 'term', value: fieldMatch ? `${fieldMatch[1]}:${fieldMatch[2]}` : wordMatch[1] });\n rest = rest.slice(wordMatch[0].length);\n matched = true;\n }\n }\n\n return tokens;\n}\n\nfunction buildQueryTree(tokens: Array<{ type: string; value: string }>): QueryNode {\n const result: QueryNode = { type: 'and', children: [] };\n let currentOr: QueryNode | null = null;\n\n for (const token of tokens) {\n if (token.type === 'or') {\n if (!currentOr) {\n currentOr = { type: 'or', children: [] };\n result.children.push(currentOr);\n }\n } else {\n if (currentOr) {\n result.children.push(currentOr);\n currentOr = null;\n }\n result.children.push({ type: token.type as any, value: token.value });\n }\n }\n\n if (currentOr) {\n result.children.push(currentOr);\n }\n\n return result;\n}\n\n// Check if a query matches a record via indexed terms with fuzzy tolerance\nfunction getFuzzyCandidates(query: string, index: SearchIndex, distance: number): Set {\n const candidates = new Set();\n const queryLen = query.length;\n\n for (const [key, records] of index.headingIds) {\n if (Math.abs(key.length - queryLen) <= distance) {\n for (const record of records) {\n candidates.add(record);\n }\n }\n }\n\n for (const [key, records] of index.bodyIndex) {\n if (Math.abs(key.length - queryLen) <= distance) {\n for (const record of records) {\n candidates.add(record);\n }\n }\n }\n\n return candidates;\n}\n\n// Extended search matching for a single query node\nfunction matchExtendedNode(node: QueryNode, record: IndexRecord): boolean {\n switch (node.type) {\n case 'term':\n const fieldMatch = node.value.match(/^([\\w-]+):(.+)$/);\n const searchTerm = (fieldMatch ? fieldMatch[2] : node.value).toLowerCase();\n const field = fieldMatch?.[1].toLowerCase();\n const values = field === 'title' || field === 'heading' ? [record.headingText]\n : field === 'body' ? [record.bodyText]\n : field === 'label' ? [record.label ?? '']\n : field === 'breadcrumb' ? [record.breadcrumb]\n : field === 'type' ? [record.type]\n : [record.headingText, record.bodyText, record.label ?? '', record.breadcrumb];\n return values.some(value => value.toLowerCase().includes(searchTerm));\n\n case 'exact':\n const exactTerm = node.value.toLowerCase();\n // Use proper phrase matching instead of string includes\n const phraseTokens = tokenizeForPhraseMatching(exactTerm);\n const headingTokens = tokenizeForPhraseMatching(record.headingText);\n const bodyTokens = tokenizeForPhraseMatching(record.bodyText);\n return hasPhraseMatch(headingTokens, phraseTokens) || hasPhraseMatch(bodyTokens, phraseTokens);\n\n case 'exclude':\n const excludeTerm = node.value.toLowerCase();\n return !record.headingText.toLowerCase().includes(excludeTerm) && !record.bodyText.toLowerCase().includes(excludeTerm);\n\n case 'prefix':\n const prefixTerm = node.value.toLowerCase();\n return record.headingText.toLowerCase().startsWith(prefixTerm) || record.bodyText.toLowerCase().startsWith(prefixTerm);\n\n case 'suffix':\n const suffixTerm = node.value.toLowerCase();\n return record.headingText.toLowerCase().endsWith(suffixTerm) || record.bodyText.toLowerCase().endsWith(suffixTerm);\n\n case 'and':\n return node.children.every(child => matchExtendedNode(child, record));\n\n case 'or':\n return node.children.some(child => matchExtendedNode(child, record));\n }\n}\n\n// Calculate BM25 score for a term\nfunction bm25Score(\n termFreq: number,\n docFreq: number,\n totalDocs: number,\n docLength: number,\n avgDocLength: number,\n k1 = 1.5,\n b = 0.75\n): number {\n const idf = Math.log((totalDocs - docFreq + 0.5) / (docFreq + 0.5) + 1);\n const norm = 1 - b + b * (docLength / avgDocLength);\n return idf * ((termFreq * (k1 + 1)) / (termFreq + k1 * norm));\n}\n\n// Calculate BM25F score - field-weighted BM25\n// BM25F combines per-field weighted term frequency into a single pseudo-frequency\n// before applying the BM25 saturation function\nfunction bm25fScore(\n record: IndexRecord,\n queryTerms: string[],\n index: SearchIndex,\n weights: Record,\n k1 = 1.5,\n b = 0.75\n): number {\n const avgDocLength = getAvgDocLength(index);\n const totalDocs = index.totalDocs;\n \n // Calculate document length (combined length of all searchable fields)\n const docLength = (\n record.headingText.length * (weights.headingText || 1) +\n record.bodyText.length * (weights.bodyText || 1) +\n (record.label ? record.label.length * (weights.label || 1) : 0) +\n (record.breadcrumb ? record.breadcrumb.length * (weights.breadcrumb || 1) : 0)\n );\n \n let totalScore = 0;\n \n // For each query term, calculate BM25F score across all fields\n for (const term of queryTerms) {\n const termLower = term.toLowerCase();\n \n // Calculate per-field term frequencies and doc frequencies\n let weightedTermFreq = 0;\n let combinedDocFreq = 0;\n \n // Check heading field\n if (weights.headingText) {\n const headingText = record.headingText.toLowerCase();\n const headingTermFreq = countTermFrequency(headingText, termLower);\n const headingDocFreq = index.fieldDocFreq.headingText.get(termLower) || 1;\n \n weightedTermFreq += headingTermFreq * (weights.headingText || 1);\n combinedDocFreq = Math.max(combinedDocFreq, headingDocFreq);\n }\n \n // Check body field\n if (weights.bodyText) {\n const bodyText = record.bodyText.toLowerCase();\n const bodyTermFreq = countTermFrequency(bodyText, termLower);\n const bodyDocFreq = index.fieldDocFreq.bodyText.get(termLower) || 1;\n \n weightedTermFreq += bodyTermFreq * (weights.bodyText || 1);\n combinedDocFreq = Math.max(combinedDocFreq, bodyDocFreq);\n }\n \n // Check label field\n if (record.label && weights.label) {\n const labelText = record.label.toLowerCase();\n const labelTermFreq = countTermFrequency(labelText, termLower);\n const labelDocFreq = index.fieldDocFreq.label.get(termLower) || 1;\n \n weightedTermFreq += labelTermFreq * (weights.label || 1);\n combinedDocFreq = Math.max(combinedDocFreq, labelDocFreq);\n }\n \n // Check breadcrumb field\n if (record.breadcrumb && weights.breadcrumb) {\n const breadcrumbText = record.breadcrumb.toLowerCase();\n const breadcrumbTermFreq = countTermFrequency(breadcrumbText, termLower);\n const breadcrumbDocFreq = index.fieldDocFreq.breadcrumb.get(termLower) || 1;\n \n weightedTermFreq += breadcrumbTermFreq * (weights.breadcrumb || 1);\n combinedDocFreq = Math.max(combinedDocFreq, breadcrumbDocFreq);\n }\n \n // Apply BM25 formula to the weighted term frequency\n if (weightedTermFreq > 0 && combinedDocFreq > 0) {\n const idf = Math.log((totalDocs - combinedDocFreq + 0.5) / (combinedDocFreq + 0.5) + 1);\n const norm = 1 - b + b * (docLength / avgDocLength);\n const bm25f = idf * ((weightedTermFreq * (k1 + 1)) / (weightedTermFreq + k1 * norm));\n totalScore += bm25f;\n }\n }\n \n return totalScore;\n}\n\n// Helper function to count term frequency in text\nfunction countTermFrequency(text: string, term: string): number {\n if (!text || !term) return 0;\n const words = text.split(/\\s+/);\n let count = 0;\n for (const word of words) {\n if (word === term) count++;\n }\n return count;\n}\n\n// Tokenize text using the same pipeline as the index\nfunction tokenizeForPhraseMatching(text: string): string[] {\n if (!text) return [];\n // Use simple split for phrase matching (no stop word removal or stemming)\n return text.toLowerCase().split(/\\s+/).filter(t => t.length > 0);\n}\n\n// Check if a phrase (array of tokens) exists as contiguous sequence in text\nfunction hasPhraseMatch(textTokens: string[], phraseTokens: string[]): boolean {\n if (phraseTokens.length === 0) return true;\n if (phraseTokens.length > textTokens.length) return false;\n \n for (let i = 0; i <= textTokens.length - phraseTokens.length; i++) {\n let match = true;\n for (let j = 0; j < phraseTokens.length; j++) {\n if (textTokens[i + j] !== phraseTokens[j]) {\n match = false;\n break;\n }\n }\n if (match) return true;\n }\n return false;\n}\n\n// Snippet/highlight generation helper\n/**\n * Generate a text snippet around match positions with highlights\n * @param record The index record containing the text\n * @param matches Array of MatchSpan objects with start/end positions\n * @param contextChars Number of characters to include around each match (default 60)\n * @returns Truncated excerpt with match boundaries marked using tags\n */\nexport function getSnippet(record: IndexRecord, matches: MatchSpan[], contextChars = 60): string {\n if (!record || !matches || matches.length === 0) return '';\n \n const text = record.bodyText || record.headingText || '';\n if (!text) return '';\n \n // If no specific matches, return first part of text\n if (matches.length === 0) {\n return text.slice(0, contextChars * 2) + (text.length > contextChars * 2 ? '...' : '');\n }\n \n // Sort matches by start position\n const sortedMatches = [...matches].sort((a, b) => a.start - b.start);\n \n // Find the best match (longest match or first one)\n let bestMatch = sortedMatches[0];\n for (const match of sortedMatches) {\n if (match.end - match.start > bestMatch.end - bestMatch.start) {\n bestMatch = match;\n }\n }\n \n const matchStart = bestMatch.start;\n const matchEnd = bestMatch.end;\n \n // Calculate snippet boundaries\n let start = Math.max(0, matchStart - contextChars);\n let end = Math.min(text.length, matchEnd + contextChars);\n \n // Adjust start if we're at the beginning\n if (start === 0) {\n end = Math.min(text.length, matchEnd + contextChars * 2);\n }\n \n // Adjust end if we're at the end\n if (end === text.length) {\n start = Math.max(0, matchStart - contextChars * 2);\n }\n \n const snippetText = text.slice(start, end);\n \n // Add ellipsis if we're not at the beginning or end\n const prefix = start > 0 ? '...' : '';\n const suffix = end < text.length ? '...' : '';\n \n // Find the match position within the snippet\n const snippetMatchStart = matchStart - start;\n const snippetMatchEnd = matchEnd - start;\n \n // Create the highlighted snippet\n const beforeMatch = snippetText.slice(0, snippetMatchStart);\n const matchText = snippetText.slice(snippetMatchStart, snippetMatchEnd);\n const afterMatch = snippetText.slice(snippetMatchEnd);\n \n return prefix + beforeMatch + '' + matchText + '' + afterMatch + suffix;\n}\n\n// Calculate proximity score for multiple terms in text\n// Returns a bonus multiplier based on how close the terms are to each other\nfunction calculateProximityBonus(textTokens: string[], queryTokens: string[]): number {\n if (queryTokens.length <= 1) return 1.0; // No bonus for single term\n \n // Find all positions where each query term appears\n const termPositions: Map = new Map();\n for (let i = 0; i < textTokens.length; i++) {\n const token = textTokens[i];\n if (queryTokens.includes(token)) {\n if (!termPositions.has(token)) {\n termPositions.set(token, []);\n }\n termPositions.get(token)!.push(i);\n }\n }\n \n // For each query term, find its closest neighbor\n let totalProximity = 0;\n let pairCount = 0;\n \n for (let i = 0; i < queryTokens.length; i++) {\n for (let j = i + 1; j < queryTokens.length; j++) {\n const term1 = queryTokens[i];\n const term2 = queryTokens[j];\n \n const positions1 = termPositions.get(term1) || [];\n const positions2 = termPositions.get(term2) || [];\n \n if (positions1.length === 0 || positions2.length === 0) continue;\n \n // Find minimum distance between any occurrence of term1 and term2\n let minDistance = Infinity;\n for (const pos1 of positions1) {\n for (const pos2 of positions2) {\n const distance = Math.abs(pos1 - pos2);\n if (distance < minDistance) {\n minDistance = distance;\n }\n }\n }\n \n if (minDistance !== Infinity) {\n // Convert distance to bonus: closer terms get higher bonus\n // Max bonus of 2.0 for adjacent terms (distance = 1)\n // Linear falloff: bonus = 2.0 - (distance * 0.1)\n const bonus = Math.max(1.0, 2.0 - (minDistance * 0.1));\n totalProximity += bonus;\n pairCount++;\n }\n }\n }\n \n // Return average bonus across all term pairs\n return pairCount > 0 ? 1.0 + (totalProximity - pairCount) / pairCount : 1.0;\n}\n\n// Calculate average document length from index\nfunction getAvgDocLength(index: SearchIndex): number {\n if (index.allSections.length === 0) return 50;\n const total = index.allSections.reduce((sum, r) => sum + r.bodyText.length + r.headingText.length, 0);\n return total / index.allSections.length;\n}\n\n// Get cached shortlist if available\nfunction getCachedShortlist(query: string, index: SearchIndex): Set | null {\n const cached = index.queryCache.get(query);\n if (cached) {\n const candidates = new Set();\n for (const id of cached.resultIds) {\n const record = index.allSections.find(r => r.id === id);\n if (record) candidates.add(record);\n }\n return candidates;\n }\n return null;\n}\n\n// Cache shortlist for future queries\nfunction cacheShortlist(query: string, records: IndexRecord[], index: SearchIndex): void {\n const entry: CacheEntry = {\n resultIds: records.map(r => r.id),\n timestamp: Date.now()\n };\n index.queryCache.set(query, entry);\n\n // Limit cache size\n if (index.queryCache.size > 100) {\n const firstKey = index.queryCache.keys().next().value;\n if (firstKey !== undefined) {\n index.queryCache.delete(firstKey);\n }\n }\n}\n\n// Backward-compatible searchSections - old signature preserved\nexport function searchSections(\n query: string,\n index: SearchIndex,\n limitOrOptions?: number | SearchOptions\n): IndexRecord[] | ScoredRecord[] {\n let limit = 8;\n let options: SearchOptions | undefined;\n\n if (typeof limitOrOptions === 'number') {\n limit = limitOrOptions;\n } else if (limitOrOptions) {\n options = limitOrOptions;\n limit = limitOrOptions.limit ?? 8;\n }\n\n const q = query.trim();\n\n if (!q) {\n return index.allSections.slice(0, limit);\n }\n\n // Normalize diacritics in query for matching\n const normalizedQ = q.normalize('NFD').replace(/[\\u0300-\\u036f]/g, '');\n\n const scores = new Map();\n const matches = new Map();\n const weights = { ...DEFAULT_WEIGHTS, ...(options?.weights ?? {}) };\n const avgLength = getAvgDocLength(index);\n\n const addScore = (record: IndexRecord, score: number, key: string, start: number, end: number) => {\n scores.set(record, (scores.get(record) ?? 0) + score);\n const recordMatches = matches.get(record) ?? [];\n recordMatches.push({ key, start, end });\n matches.set(record, recordMatches);\n };\n\n const useBM25 = options?.scoringAlgorithm === 'bm25';\n const useBM25F = options?.scoringAlgorithm === 'bm25f';\n \n // Get BM25F config options or use defaults\n const bm25fConfig = options?.bm25fOptions || { k1: 1.5, b: 0.75 };\n\n // Check query cache first\n const cached = getCachedShortlist(normalizedQ.toLowerCase(), index);\n const searchPool: IndexRecord[] = cached\n ? [...cached]\n : index.allSections;\n\n // Exact matches on headingText (try both original and normalized)\n const exact = index.headingIds.get(normalizedQ.toLowerCase()) ?? index.headingIds.get(q.toLowerCase());\n if (exact) {\n for (const record of exact) {\n const fieldWeight = weights.headingText ?? 1;\n const lengthNorm = getLengthNorm(record.headingText.length);\n let score: number;\n if (useBM25) {\n const termFreq = 1;\n const docFreq = index.docFrequency.get(normalizedQ.toLowerCase()) ?? 1;\n score = bm25Score(termFreq, docFreq, index.totalDocs, record.headingText.length + record.bodyText.length, avgLength) * 100;\n } else {\n score = 100 * fieldWeight * (1 + lengthNorm);\n }\n addScore(record, score, 'headingText', 0, record.headingText.length);\n }\n }\n\n// Prefix matches on headingText (try both)\n const prefix = index.headingIndex.get(normalizedQ.toLowerCase()) ?? index.headingIndex.get(q.toLowerCase());\n if (prefix) {\n for (const record of prefix) {\n if (scores.has(record)) continue;\n const fieldWeight = weights.headingText ?? 1;\n const lengthNorm = getLengthNorm(record.headingText.length);\n let score: number;\n if (useBM25) {\n const termFreq = 1;\n const docFreq = index.docFrequency.get(normalizedQ.toLowerCase()) ?? 1;\n score = bm25Score(termFreq, docFreq, index.totalDocs, record.headingText.length + record.bodyText.length, avgLength) * 50;\n } else {\n score = 50 * fieldWeight * (1 + lengthNorm);\n }\n const positions = findMatchPositions(record.headingText, q);\n if (positions.length > 0) {\n const pos = positions[0];\n addScore(record, score, 'headingText', pos.start, pos.end);\n } else {\n addScore(record, score, 'headingText', 0, Math.min(q.length, record.headingText.length));\n }\n }\n }\n\n // Word matches in bodyText (try both)\n const words = normalizedQ.toLowerCase().split(/\\s+/);\n for (const word of words) {\n const bodyMatches = index.bodyIndex.get(word);\n if (bodyMatches) {\n for (const record of bodyMatches) {\n if (scores.has(record)) continue;\n const fieldWeight = weights.bodyText ?? 1;\n const lengthNorm = getLengthNorm(record.bodyText.length);\n let score: number;\n if (useBM25) {\n const termFreq = 1;\n const docFreq = index.docFrequency.get(word) ?? 1;\n score = bm25Score(termFreq, docFreq, index.totalDocs, record.headingText.length + record.bodyText.length, avgLength) * 20;\n } else {\n score = 20 * fieldWeight * (1 + lengthNorm);\n }\n const positions = findMatchPositions(record.bodyText, q);\n if (positions.length > 0) {\n const pos = positions[0];\n addScore(record, score, 'bodyText', pos.start, pos.end);\n} else {\n addScore(record, score, 'bodyText', 0, Math.min(word.length, record.bodyText.length));\n }\n }\n }\n }\n\n // BM25F scoring - score all records using field-weighted BM25\n if (useBM25F && scores.size === 0) {\n const queryTerms = normalizedQ.toLowerCase().split(/\\s+/);\n for (const record of index.allSections) {\n const score = bm25fScore(record, queryTerms, index, weights, bm25fConfig.k1, bm25fConfig.b);\n if (score > 0) {\n addScore(record, score * 100, 'bm25f', 0, Math.min(normalizedQ.length, record.headingText.length + record.bodyText.length));\n }\n }\n }\n\n // Apply proximity bonus for multi-term queries\n if (scores.size > 0) {\n const queryTerms = normalizedQ.toLowerCase().split(/\\s+/);\n if (queryTerms.length > 1) {\n for (const [record, score] of scores.entries()) {\n const headingTokens = tokenizeForPhraseMatching(record.headingText);\n const bodyTokens = tokenizeForPhraseMatching(record.bodyText);\n \n // Check if all query terms appear in either heading or body\n const allTermsInHeading = queryTerms.every(term => headingTokens.includes(term));\n const allTermsInBody = queryTerms.every(term => bodyTokens.includes(term));\n \n if (allTermsInHeading || allTermsInBody) {\n // Calculate proximity bonus\n const combinedTokens = [...headingTokens, ...bodyTokens];\n const proximityBonus = calculateProximityBonus(combinedTokens, queryTerms);\n \n // Apply bonus to existing score\n scores.set(record, score * proximityBonus);\n }\n \n // Check for exact phrase match and give additional bonus\n if (hasPhraseMatch(headingTokens, queryTerms) || hasPhraseMatch(bodyTokens, queryTerms)) {\n scores.set(record, score * 1.5); // 50% bonus for exact phrase match\n }\n }\n }\n }\n\n // Apply popularity boosts if enabled\n if (options?.trackPopularity) {\n const boostFactor = options.popularityBoost || 1.2; // Default 20% boost\n for (const [record, score] of scores.entries()) {\n const key = `${normalizedQ.toLowerCase()}||${record.id}`;\n const popularity = index.queryPopularity.get(key);\n if (popularity) {\n // Apply multiplicative boost based on popularity count\n const popularityBoost = 1 + ((popularity.count - 1) * 0.1); // 10% per additional click, max at 2x\n scores.set(record, score * popularityBoost * boostFactor);\n }\n }\n }\n\n // Staged fuzzy search - exact \u2192 1-typo \u2192 2-typo, short-circuit on first hit\n if (options?.fuzzy) {\n const fuzzyDistance = options.fuzzyDistance ?? 2;\n\n // Stage 1: Exact matches (already done above)\n // Stage 2: 1-typo matches on indexed candidates\n if (scores.size === 0) {\n const candidates = getFuzzyCandidates(q.toLowerCase(), index, 1);\n for (const record of candidates) {\n if (scores.has(record)) continue;\n\n const headingLower = record.headingText.toLowerCase();\n const headingMatch = findMatchPositions(headingLower, q);\n if (headingMatch.length === 0) {\n const dist = levenshteinDistance(headingLower, q);\n if (dist <= 1) {\n const normalizedScore = (1 - dist / Math.max(q.length, headingLower.length)) * 60;\n const fieldWeight = weights.headingText ?? 1;\n const lengthNorm = getLengthNorm(record.headingText.length);\n addScore(record, normalizedScore * fieldWeight * (1 + lengthNorm), 'headingText', 0, Math.min(q.length, headingLower.length));\n }\n }\n\n if (scores.has(record)) continue;\n\n const bodyLower = record.bodyText.toLowerCase();\n let bestDist = Infinity;\n let bestStart = 0;\n for (let i = 0; i <= bodyLower.length - q.length; i++) {\n const substr = bodyLower.slice(i, i + q.length);\n const dist = levenshteinDistance(q, substr);\n if (dist < bestDist) {\n bestDist = dist;\n bestStart = i;\n }\n }\n if (bestDist <= 1) {\n const normalizedScore = (1 - bestDist / Math.max(q.length, bodyLower.length)) * 40;\n const fieldWeight = weights.bodyText ?? 1;\n const lengthNorm = getLengthNorm(record.bodyText.length);\n addScore(record, normalizedScore * fieldWeight * (1 + lengthNorm), 'bodyText', bestStart, bestStart + q.length);\n }\n }\n }\n\n // Stage 3: 2-typo matches\n if (scores.size === 0) {\n const candidates = getFuzzyCandidates(q.toLowerCase(), index, 2);\n for (const record of candidates) {\n if (scores.has(record)) continue;\n\n const headingLower = record.headingText.toLowerCase();\n const dist = levenshteinDistance(headingLower, q);\n if (dist <= 2) {\n const normalizedScore = (1 - dist / Math.max(q.length, headingLower.length)) * 30;\n const fieldWeight = weights.headingText ?? 1;\n const lengthNorm = getLengthNorm(record.headingText.length);\n addScore(record, normalizedScore * fieldWeight * (1 + lengthNorm), 'headingText', 0, Math.min(q.length, headingLower.length));\n }\n\n if (scores.has(record)) continue;\n\n const bodyLower = record.bodyText.toLowerCase();\n let bestDist = Infinity;\n let bestStart = 0;\n for (let i = 0; i <= bodyLower.length - q.length; i++) {\n const substr = bodyLower.slice(i, i + q.length);\n const d = levenshteinDistance(q, substr);\n if (d < bestDist) {\n bestDist = d;\n bestStart = i;\n }\n }\n if (bestDist <= 2) {\n const normalizedScore = (1 - bestDist / Math.max(q.length, bodyLower.length)) * 20;\n const fieldWeight = weights.bodyText ?? 1;\n const lengthNorm = getLengthNorm(record.bodyText.length);\n addScore(record, normalizedScore * fieldWeight * (1 + lengthNorm), 'bodyText', bestStart, bestStart + q.length);\n }\n }\n }\n }\n\n // Extended query syntax\n if ((options?.extended || /\\b(?:title|heading|body|label|breadcrumb|type):\\S+/i.test(q)) && scores.size === 0) {\n const parsed = parseExtendedQuery(q);\n for (const record of index.allSections) {\n if (matchExtendedNode(parsed, record)) {\n const fieldWeight = weights.headingText ?? 1;\n const lengthNorm = getLengthNorm(record.headingText.length);\n const score = 50 * fieldWeight * (1 + lengthNorm);\n addScore(record, score, 'headingText', 0, Math.min(q.length, record.headingText.length));\n }\n }\n }\n\n // Filtering\n let filteredScores = [...scores.entries()];\n if (options?.filter) {\n filteredScores = filteredScores.filter(([record]) => options!.filter!(record));\n }\n\n // Sort results\n let sortedEntries = filteredScores.sort((a, b) => b[1] - a[1]);\n if (options?.sortFn) {\n sortedEntries = sortedEntries.sort((a, b) => {\n const aMatches = matches.get(a[0]) ?? [];\n const bMatches = matches.get(b[0]) ?? [];\n return options!.sortFn!({ record: a[0], score: a[1], matches: aMatches }, { record: b[0], score: b[1], matches: bMatches });\n });\n }\n\n // Apply MMR (Maximal Marginal Relevance) for result diversity if enabled\n if (options?.diversify) {\n const lambda = options.mmrLambda ?? 0.5; // Default 50% diversity\n sortedEntries = applyMMR(sortedEntries, lambda);\n }\n\n const topRecords = sortedEntries.slice(0, limit).map(([record]) => record);\n\n // Apply type-based ranking boost\n if (options?.typeWeights) {\n const typeBoost = options.typeWeights;\n for (const record of topRecords) {\n const boost = typeBoost[record.type] ?? 1;\n const currentScore = scores.get(record);\n if (currentScore !== undefined && boost !== 1) {\n scores.set(record, currentScore * boost);\n }\n }\n sortedEntries = [...scores.entries()].sort((a, b) => b[1] - a[1]);\n const boostedRecords = sortedEntries.slice(0, limit).map(([record]) => record);\n \n // Cache the boosted shortlist\n cacheShortlist(q.toLowerCase(), boostedRecords, index);\n \n if (options?.includeScore || options?.includeMatches) {\n return boostedRecords.map(record => {\n const recordMatches = matches.get(record) ?? [];\n const normalizedScore = scores.get(record) ?? 0;\n const maxScore = useBM25 ? 100 : 200;\n const score = Math.max(0, Math.min(1, 1 - normalizedScore / maxScore));\n return {\n record,\n score,\n matches: options.includeMatches ? recordMatches : undefined,\n };\n });\n }\n return boostedRecords;\n }\n\n // Cache the shortlist\n cacheShortlist(q.toLowerCase(), topRecords, index);\n\n // Return format based on options\n if (options?.includeScore || options?.includeMatches) {\n return topRecords.map(record => {\n const recordMatches = matches.get(record) ?? [];\n const normalizedScore = scores.get(record) ?? 0;\n const maxScore = useBM25 ? 100 : 200;\n const score = Math.max(0, Math.min(1, 1 - normalizedScore / maxScore));\n return {\n record,\n score,\n matches: options.includeMatches ? recordMatches : undefined,\n };\n });\n }\n\n return topRecords;\n}\n\n// remove a record from the index by its id\nexport function removeFromIndex(index: SearchIndex, id: string): void {\n const recordIndex = index.allSections.findIndex(r => r.id === id);\n if (recordIndex === -1) return;\n\n const record = index.allSections[recordIndex];\n index.allSections.splice(recordIndex, 1);\n index.totalDocs = Math.max(0, index.totalDocs - 1);\n\n // Invalidate only queries affected by this mutation\n invalidateAffectedQueries(index, record.id, record.headingText, record.bodyText);\n\n const headingLower = record.headingText.toLowerCase();\n const headingIds = index.headingIds.get(headingLower);\n if (headingIds) {\n const idx = headingIds.findIndex(r => r.id === id);\n if (idx !== -1) headingIds.splice(idx, 1);\n if (headingIds.length === 0) index.headingIds.delete(headingLower);\n }\n\n for (let i = 2; i <= headingLower.length; i++) {\n const prefix = headingLower.slice(0, i);\n const prefixRecords = index.headingIndex.get(prefix);\n if (prefixRecords) {\n const idx = prefixRecords.findIndex(r => r.id === id);\n if (idx !== -1) prefixRecords.splice(idx, 1);\n if (prefixRecords.length === 0) index.headingIndex.delete(prefix);\n }\n }\n\n // Remove from trie\n trieDelete(index.headingTrie, headingLower, id);\n const normalizedHeading = record.headingText.toLowerCase().normalize('NFD').replace(/[\\u0300-\\u036f]/g, '');\n if (normalizedHeading !== headingLower) {\n trieDelete(index.headingTrie, normalizedHeading, id);\n }\n\n const bodyWords = record.bodyText.toLowerCase().split(/\\s+/);\n for (const word of bodyWords) {\n if (word.length < 3) continue;\n const bodyRecords = index.bodyIndex.get(word);\n if (bodyRecords) {\n const idx = bodyRecords.findIndex(r => r.id === id);\n if (idx !== -1) bodyRecords.splice(idx, 1);\n if (bodyRecords.length === 0) index.bodyIndex.delete(word);\n }\n }\n}\n\n// Update an existing record in the index\nexport function updateRecord(index: SearchIndex, record: IndexRecord): void {\n // First, remove the old record and invalidate affected queries\n removeFromIndex(index, record.id);\n \n // Then add the updated record\n addToIndex(index, [record]);\n \n // Since we've replaced the record, we need to invalidate queries that might now match\n // the updated content (the remove already handled invalidation, but add might introduce new matches)\n // For simplicity, we'll invalidate queries that match the new record's tokens\n invalidateAffectedQueries(index, record.id, record.headingText, record.bodyText);\n}\n\n// Serialize index to JSON string\n// Note: trie is not serialized as it can be rebuilt from headingIndex on load\nexport function serializeIndex(index: SearchIndex): string {\n const serializable = {\n allSections: index.allSections,\n headingIndex: Array.from(index.headingIndex.entries()),\n headingIds: Array.from(index.headingIds.entries()),\n bodyIndex: Array.from(index.bodyIndex.entries()),\n docFrequency: Array.from(index.docFrequency.entries()),\n fieldDocFreq: {\n headingText: Array.from(index.fieldDocFreq.headingText.entries()),\n bodyText: Array.from(index.fieldDocFreq.bodyText.entries()),\n label: Array.from(index.fieldDocFreq.label.entries()),\n breadcrumb: Array.from(index.fieldDocFreq.breadcrumb.entries()),\n },\n totalDocs: index.totalDocs,\n popularQueries: index.popularQueries,\n queryPopularity: Array.from(index.queryPopularity.entries()),\n version: index.version,\n };\n return JSON.stringify(serializable);\n}\n\n// Deserialize index from JSON string with migration support\nexport function deserializeIndex(json: string): SearchIndex {\n const parsed = JSON.parse(json);\n \n // Migrate old index format to new format with fieldDocFreq\n const fieldDocFreq: FieldDocFreq = {\n headingText: new Map(),\n bodyText: new Map(),\n label: new Map(),\n breadcrumb: new Map(),\n };\n \n // Load query popularity data if present\n const queryPopularity = new Map();\n if (parsed.queryPopularity && Array.isArray(parsed.queryPopularity)) {\n for (const [key, value] of parsed.queryPopularity) {\n if (typeof value === 'object' && value !== null) {\n queryPopularity.set(key, value as PopularityEntry);\n }\n }\n }\n \n // If old format (no fieldDocFreq), we need to rebuild it from docFrequency\n // This is a one-time migration that will happen on first load\n if (!parsed.fieldDocFreq) {\n // We can't perfectly reconstruct fieldDocFreq from the old format,\n // but we can approximate by using docFrequency for all fields\n if (parsed.docFrequency) {\n for (const [term, freq] of parsed.docFrequency) {\n // Distribute the frequency across fields (this is an approximation)\n fieldDocFreq.bodyText.set(term, freq);\n fieldDocFreq.headingText.set(term, Math.floor(freq * 0.3));\n fieldDocFreq.label.set(term, Math.floor(freq * 0.1));\n fieldDocFreq.breadcrumb.set(term, Math.floor(freq * 0.1));\n }\n }\n } else {\n // New format - load fieldDocFreq from parsed data\n fieldDocFreq.headingText = new Map(parsed.fieldDocFreq.headingText ?? []);\n fieldDocFreq.bodyText = new Map(parsed.fieldDocFreq.bodyText ?? []);\n fieldDocFreq.label = new Map(parsed.fieldDocFreq.label ?? []);\n fieldDocFreq.breadcrumb = new Map(parsed.fieldDocFreq.breadcrumb ?? []);\n }\n \n // Rebuild trie from headingIndex data for backward compatibility\n const headingTrie = createTrieNode();\n \n // If we have headingIndex data, rebuild the trie\n if (parsed.headingIndex && Array.isArray(parsed.headingIndex)) {\n const headingIndexMap = new Map(parsed.headingIndex as [string, IndexRecord[]][]);\n const seenRecords = new Set();\n for (const [prefix, records] of headingIndexMap.entries()) {\n if (prefix.length >= 2 && Array.isArray(records)) { // Only prefixes of length 2+ as per original implementation\n for (const record of records) {\n if (seenRecords.has(record.id)) continue;\n seenRecords.add(record.id);\n // Insert each record's full heading into the trie\n const headingLower = record.headingText.toLowerCase();\n trieInsert(headingTrie, headingLower, record);\n \n // Also insert normalized version\n const normalizedHeading = headingLower.normalize('NFD').replace(/[\\u0300-\\u036f]/g, '');\n if (normalizedHeading !== headingLower) {\n trieInsert(headingTrie, normalizedHeading, record);\n }\n }\n }\n }\n } else {\n // If no headingIndex, rebuild from allSections\n const allSections = parsed.allSections ?? [];\n for (const record of allSections) {\n const headingLower = record.headingText.toLowerCase();\n trieInsert(headingTrie, headingLower, record);\n \n const normalizedHeading = headingLower.normalize('NFD').replace(/[\\u0300-\\u036f]/g, '');\n if (normalizedHeading !== headingLower) {\n trieInsert(headingTrie, normalizedHeading, record);\n }\n }\n }\n \n return {\n allSections: parsed.allSections ?? [],\n headingIndex: new Map(parsed.headingIndex ?? []),\n headingIds: new Map(parsed.headingIds ?? []),\n bodyIndex: new Map(parsed.bodyIndex ?? []),\n queryCache: new Map(),\n popularQueries: parsed.popularQueries ?? [],\n docFrequency: new Map(parsed.docFrequency ?? []),\n fieldDocFreq,\n totalDocs: parsed.totalDocs ?? 0,\n queryPopularity,\n version: parsed.version ?? 1,\n headingTrie,\n };\n}\n\n// Suggest function for autocomplete - returns unique headings matching query prefix\nexport function suggest(query: string, index: SearchIndex, limit: number = 10): string[] {\n const q = query.trim().toLowerCase();\n if (!q || q.length < 2) return [];\n\n const suggestions: string[] = [];\n const seen = new Set();\n\n // Get candidates from trie (new efficient method)\n const trieCandidates = triePrefixQuery(index.headingTrie, q);\n for (const record of trieCandidates) {\n if (suggestions.length >= limit) break;\n const heading = record.headingText;\n if (!seen.has(heading)) {\n seen.add(heading);\n suggestions.push(heading);\n }\n }\n\n // Fallback to old method for backward compatibility if trie doesn't have results\n if (suggestions.length < limit) {\n const candidates = index.headingIndex.get(q) ?? [];\n const prefixCandidates = index.headingIds.get(q) ?? [];\n\n const allCandidates = [...candidates, ...prefixCandidates];\n for (const record of allCandidates) {\n if (suggestions.length >= limit) break;\n const heading = record.headingText;\n if (!seen.has(heading)) {\n seen.add(heading);\n suggestions.push(heading);\n }\n }\n }\n\n // If not enough from prefix, try fuzzy match on indexed keys\n if (suggestions.length < limit) {\n for (const [key, records] of index.headingIds) {\n if (suggestions.length >= limit) break;\n if (key.includes(q) && !seen.has(records[0].headingText)) {\n seen.add(records[0].headingText);\n suggestions.push(records[0].headingText);\n }\n }\n }\n\n return suggestions.slice(0, limit);\n}\n\n// Facets function - returns counts per record type\nexport function facets(index: SearchIndex): Record {\n const result: Record = {\n section: 0,\n action: 0,\n field: 0,\n link: 0,\n file: 0,\n media: 0,\n structured: 0,\n };\n\n for (const record of index.allSections) {\n result[record.type] = (result[record.type] ?? 0) + 1;\n }\n\n return result;\n}\n\n// Track query for popularity analytics\nexport function trackQuery(index: SearchIndex, query: string, selectedId?: string): void {\n const q = query.trim();\n if (!q) return;\n\n // Add to ring buffer (keep last 100)\n if (index.popularQueries.length >= 100) {\n index.popularQueries.shift();\n }\n index.popularQueries.push(q);\n\n // If a selected record ID is provided and popularity tracking is enabled,\n // store the (query, recordId) pair for future ranking boosts\n if (selectedId) {\n const key = `${q}||${selectedId}`; // Use double pipe as separator\n const existing = index.queryPopularity.get(key);\n if (existing) {\n existing.count++;\n } else {\n index.queryPopularity.set(key, {\n query: q,\n recordId: selectedId,\n count: 1,\n });\n }\n }\n}\n\n// Get popular queries - returns most frequently searched terms\nexport function getPopularQueries(index: SearchIndex, n: number = 5): string[] {\n const counts = new Map();\n for (const q of index.popularQueries) {\n counts.set(q, (counts.get(q) ?? 0) + 1);\n }\n\n return [...counts.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, n)\n .map(([q]) => q);\n}\n\nexport type { IndexRecord, TokenFilter };\n", "/**\n * @file Spotlight overlay for Reef for Browsers.\n *\n * A Spotlight-style (macOS) cross-tab search overlay injected into every page\n * by the content script. Opens on Ctrl+Shift+K (or Cmd+Shift+K on macOS) via\n * the `open-spotlight` browser command.\n *\n * - Shadow DOM (no page-style leakage; no extension CSS conflicts).\n * - Keyboard-first: ArrowUp/Down navigate, Enter opens, Esc closes,\n * Ctrl+1..9 jumps, Tab cycles matches within a tab.\n * - Mouse is secondary: click a row to open, click backdrop to dismiss.\n * - Ranking is computed in the background service worker (see\n * searchOpenTabs in background.ts); the overlay just renders.\n */\n\nexport interface SpotlightSearchHit {\n tabId: number;\n title: string;\n url: string;\n favIconUrl?: string;\n windowId: number;\n score: number;\n matchedRecords: Array<{\n headingText?: string;\n bodyText?: string;\n selector?: string;\n type?: string;\n }>;\n}\n\nexport interface SpotlightRecent {\n url: string;\n title: string;\n favicon?: string;\n visitedAt: number;\n}\n\nexport interface SpotlightSiteResult {\n url: string;\n headingText: string;\n bodyText: string;\n selector?: string;\n type: string;\n score: number;\n sourceOrigin: string;\n}\n\nexport interface SpotlightAction {\n type: 'open-url' | 'search-web';\n title: string;\n url: string;\n}\n\nexport interface SpotlightHandle {\n show(): Promise;\n hide(): void;\n toggle(): Promise;\n isOpen(): boolean;\n destroy(): void;\n}\n\nexport interface SpotlightOptions {\n /** When provided, skip reading chrome.storage for theme. */\n theme?: 'light' | 'dark' | 'system';\n /** Override the chrome.runtime sender (used in tests). */\n sendMessage?: (msg: any) => Promise;\n /** Maximum rows rendered; results beyond this still count toward the footer. */\n maxRows?: number;\n /** Debounce for keystroke-driven searches (ms). */\n debounceMs?: number;\n /** Maximum recents to show when the input is empty. */\n maxRecents?: number;\n}\n\nconst HOST_ID = 'reef-spotlight-host';\nconst MAX_ROWS_DEFAULT = 50;\nconst DEBOUNCE_DEFAULT = 80;\nconst MAX_RECENTS_DEFAULT = 8;\n\nconst STYLES = `\n:host { all: initial; }\n*, *::before, *::after { box-sizing: border-box; }\n\n.backdrop {\n position: fixed;\n inset: 0;\n background: rgba(15, 17, 21, 0.42);\n -webkit-backdrop-filter: blur(8px);\n backdrop-filter: blur(8px);\n z-index: 2147483647;\n display: flex;\n align-items: flex-start;\n justify-content: center;\n padding-top: 14vh;\n animation: reef-spotlight-fade 120ms ease-out;\n}\n.card {\n width: min(640px, 92vw);\n max-height: min(560px, 70vh);\n background: #ffffff;\n color: #111111;\n border-radius: 14px;\n box-shadow: 0 24px 64px rgba(0, 0, 0, 0.32), 0 4px 12px rgba(0, 0, 0, 0.18);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n animation: reef-spotlight-slide 140ms cubic-bezier(0.2, 0.8, 0.2, 1);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", system-ui, sans-serif;\n}\n.input-row {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 14px 18px;\n border-bottom: 1px solid #ececec;\n}\n.input-row .glyph {\n width: 18px;\n height: 18px;\n flex: 0 0 18px;\n color: #6b7280;\n}\n.input-row input {\n flex: 1 1 auto;\n font-size: 18px;\n line-height: 24px;\n border: none;\n outline: none;\n background: transparent;\n color: inherit;\n font-family: inherit;\n min-width: 0;\n}\n.input-row input::placeholder { color: #9ca3af; }\n.input-row .esc {\n font-size: 11px;\n color: #6b7280;\n border: 1px solid #e5e7eb;\n border-radius: 4px;\n padding: 1px 6px;\n background: #fafafa;\n user-select: none;\n}\n.results {\n flex: 1 1 auto;\n overflow-y: auto;\n padding: 6px 0;\n scrollbar-width: thin;\n}\n.empty {\n padding: 28px 20px;\n text-align: center;\n color: #6b7280;\n font-size: 14px;\n line-height: 1.5;\n}\n.empty .suggestion {\n color: #2563eb;\n font-weight: 600;\n text-decoration: underline;\n cursor: pointer;\n}\n.empty .suggestion:hover {\n color: #1d4ed8;\n}\n.row {\n display: grid;\n grid-template-columns: 18px 1fr auto;\n align-items: center;\n gap: 10px;\n padding: 8px 16px;\n cursor: pointer;\n border-left: 2px solid transparent;\n user-select: none;\n}\n.row[aria-selected=\"true\"] {\n background: #f3f4f6;\n border-left-color: #111111;\n}\n.row .favicon {\n width: 16px;\n height: 16px;\n border-radius: 2px;\n background: #f3f4f6;\n object-fit: contain;\n}\n.row .main { min-width: 0; }\n.row .title {\n font-size: 14px;\n font-weight: 600;\n line-height: 1.3;\n color: #111111;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.row .title mark {\n background: #fde68a;\n color: inherit;\n padding: 0 1px;\n border-radius: 2px;\n}\n.row .url {\n font-size: 12px;\n color: #6b7280;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n margin-top: 1px;\n}\n.row .match {\n font-size: 11px;\n color: #6b7280;\n max-width: 180px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding: 8px 14px;\n border-top: 1px solid #ececec;\n font-size: 11px;\n color: #6b7280;\n background: #fafafa;\n}\n.footer .hints { display: flex; gap: 10px; flex-wrap: wrap; }\n.footer .hint kbd {\n font-family: inherit;\n font-size: 10px;\n border: 1px solid #e5e7eb;\n background: #ffffff;\n border-radius: 3px;\n padding: 0 4px;\n margin-right: 2px;\n}\n.footer .brand { font-weight: 600; color: #111111; }\n\n/* Section headers */\n.section-header {\n font-size: 10px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.8px;\n color: #9ca3af;\n padding: 8px 16px 4px;\n user-select: none;\n}\n\n/* Autocorrect banner */\n.autocorrect-banner {\n padding: 8px 16px;\n font-size: 12px;\n color: #6b7280;\n background: #fefce8;\n border-bottom: 1px solid #fde68a;\n}\n.autocorrect-banner strong { color: #111111; }\n.autocorrect-banner .autocorrect-orig {\n color: #2563eb;\n text-decoration: none;\n cursor: pointer;\n}\n.autocorrect-banner .autocorrect-orig:hover { text-decoration: underline; }\n\n/* Site result rows */\n.row-site .site-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n color: #6b7280;\n background: #f3f4f6;\n border-radius: 2px;\n}\n.row-site .site-icon svg { stroke: #6366f1; }\n\n/* Action rows */\n.row-action { opacity: 0.85; }\n.row-action:hover { opacity: 1; }\n.row-action .action-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n background: #f0fdf4;\n border-radius: 2px;\n}\n.row-action .action-icon svg { stroke: #16a34a; }\n.row-action .action-title { font-style: italic; }\n.row-action .action-badge {\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: #16a34a;\n font-weight: 600;\n}\n\n/* Dark theme */\n:host([data-theme=\"dark\"]) .card { background: #1f2024; color: #e7e7ea; box-shadow: 0 24px 64px rgba(0, 0, 0, 0.55), 0 4px 12px rgba(0, 0, 0, 0.35); }\n:host([data-theme=\"dark\"]) .input-row { border-bottom-color: #2c2d31; }\n:host([data-theme=\"dark\"]) .input-row .glyph,\n:host([data-theme=\"dark\"]) .input-row input::placeholder,\n:host([data-theme=\"dark\"]) .row .url,\n:host([data-theme=\"dark\"]) .row .match,\n:host([data-theme=\"dark\"]) .footer { color: #a1a1aa; }\n:host([data-theme=\"dark\"]) .input-row .esc { background: #2c2d31; border-color: #3a3b40; color: #a1a1aa; }\n:host([data-theme=\"dark\"]) .row[aria-selected=\"true\"] { background: #2c2d31; border-left-color: #ffffff; }\n:host([data-theme=\"dark\"]) .row .title { color: #e7e7ea; }\n:host([data-theme=\"dark\"]) .row .favicon { background: #2c2d31; }\n:host([data-theme=\"dark\"]) .row .title mark { background: #facc15; color: #111111; }\n:host([data-theme=\"dark\"]) .footer { background: #161719; border-top-color: #2c2d31; }\n:host([data-theme=\"dark\"]) .footer .brand { color: #e7e7ea; }\n:host([data-theme=\"dark\"]) .footer .hint kbd { background: #2c2d31; border-color: #3a3b40; color: #e7e7ea; }\n:host([data-theme=\"dark\"]) .empty .suggestion { color: #60a5fa; }\n:host([data-theme=\"dark\"]) .empty .suggestion:hover { color: #93bbfd; }\n:host([data-theme=\"dark\"]) .section-header { color: #71717a; }\n:host([data-theme=\"dark\"]) .autocorrect-banner { background: #422006; border-bottom-color: #854d0e; color: #a1a1aa; }\n:host([data-theme=\"dark\"]) .autocorrect-banner strong { color: #e7e7ea; }\n:host([data-theme=\"dark\"]) .autocorrect-banner .autocorrect-orig { color: #60a5fa; }\n:host([data-theme=\"dark\"]) .row-site .site-icon { background: #2c2d31; }\n:host([data-theme=\"dark\"]) .row-action .action-icon { background: #1a2e1a; }\n:host([data-theme=\"dark\"]) .row-action .action-badge { color: #4ade80; }\n\n@keyframes reef-spotlight-fade { from { opacity: 0; } to { opacity: 1; } }\n@keyframes reef-spotlight-slide { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: translateY(0); } }\n@media (prefers-reduced-motion: reduce) {\n .backdrop, .card { animation: none; }\n}\n`;\n\nfunction defaultSendMessage(msg: any): Promise {\n return new Promise((resolve) => {\n try {\n const result = (globalThis as any).chrome?.runtime?.sendMessage(msg, (res: any) => resolve(res));\n // Some chrome.runtime.sendMessage variants return a promise directly\n if (result && typeof result.then === 'function') {\n result.then(resolve, () => resolve(undefined));\n }\n } catch {\n resolve(undefined);\n }\n });\n}\n\nasync function getStoredTheme(): Promise<'light' | 'dark' | 'system'> {\n try {\n const chrome = (globalThis as any).chrome;\n if (chrome?.storage?.local?.get) {\n const data = await new Promise((resolve) => {\n chrome.storage.local.get(['theme'], (d: any) => resolve(d));\n });\n const t = data?.theme;\n if (t === 'light' || t === 'dark' || t === 'system') return t;\n }\n } catch {\n /* ignore */\n }\n return 'system';\n}\n\nfunction resolveTheme(t: 'light' | 'dark' | 'system'): 'light' | 'dark' {\n if (t === 'system') {\n try {\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n } catch {\n return 'light';\n }\n }\n return t;\n}\n\nfunction hostFromUrl(url: string): string {\n try {\n return new URL(url).host;\n } catch {\n return url;\n }\n}\n\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, '&')\n .replace(//g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nfunction highlight(text: string, query: string): string {\n if (!query) return escapeHtml(text);\n const safe = escapeHtml(text);\n const q = escapeHtml(query);\n // Case-insensitive replace\n try {\n const re = new RegExp(`(${q.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')})`, 'ig');\n return safe.replace(re, '$1');\n } catch {\n return safe;\n }\n}\n\nfunction truncate(s: string, n: number): string {\n if (s.length <= n) return s;\n return s.slice(0, n - 1) + '\\u2026';\n}\n\nconst FALLBACK_FAVICON =\n \"data:image/svg+xml;utf8,\";\n\nexport function createSpotlight(opts: SpotlightOptions = {}): SpotlightHandle {\n const maxRows = opts.maxRows ?? MAX_ROWS_DEFAULT;\n const debounceMs = opts.debounceMs ?? DEBOUNCE_DEFAULT;\n const maxRecents = opts.maxRecents ?? MAX_RECENTS_DEFAULT;\n const send = opts.sendMessage ?? defaultSendMessage;\n\n let host: HTMLElement | null = null;\n let shadow: ShadowRoot | null = null;\n let card: HTMLElement | null = null;\n let input: HTMLInputElement | null = null;\n let results: HTMLElement | null = null;\n let footer: HTMLElement | null = null;\n let mounted = false;\n let open = false;\n\n // Internal unified result for keyboard navigation\n type UnifiedResult =\n | { kind: 'tab'; data: SpotlightSearchHit }\n | { kind: 'site'; data: SpotlightSiteResult }\n | { kind: 'action'; data: SpotlightAction };\n\n // State\n let currentResults: SpotlightSearchHit[] = [];\n let unifiedResults: UnifiedResult[] = [];\n let currentQuery = '';\n let currentSuggestion: string | undefined;\n let currentSiteResults: SpotlightSiteResult[] = [];\n let currentActions: SpotlightAction[] = [];\n let currentAutocorrected = false;\n let selectedIndex = 0;\n let perTabMatchIndex = new Map(); // tabId -> matchedRecord index\n let debounceHandle: any = null;\n // Sentinel that no real query can match, so the first runQuery() always runs.\n let lastQueryKey = '\\u0000never-queried\\u0000';\n let inFlight = 0;\n\n function mount(): void {\n if (mounted) return;\n host = document.createElement('div');\n host.id = HOST_ID;\n host.style.position = 'fixed';\n host.style.inset = '0';\n host.style.zIndex = '2147483647';\n host.style.display = 'none';\n shadow = host.attachShadow({ mode: 'open' });\n\n const style = document.createElement('style');\n style.textContent = STYLES;\n shadow.appendChild(style);\n\n const backdrop = document.createElement('div');\n backdrop.className = 'backdrop';\n backdrop.addEventListener('mousedown', (e) => {\n if (e.target === backdrop) hide();\n });\n\n card = document.createElement('div');\n card.className = 'card';\n card.setAttribute('role', 'dialog');\n card.setAttribute('aria-label', 'Reef Spotlight');\n card.addEventListener('mousedown', (e) => e.stopPropagation());\n\n // Input row\n const inputRow = document.createElement('div');\n inputRow.className = 'input-row';\n const glyph = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n glyph.setAttribute('class', 'glyph');\n glyph.setAttribute('viewBox', '0 0 20 20');\n glyph.setAttribute('fill', 'none');\n glyph.setAttribute('stroke', 'currentColor');\n glyph.setAttribute('stroke-width', '2');\n const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');\n circle.setAttribute('cx', '9');\n circle.setAttribute('cy', '9');\n circle.setAttribute('r', '6');\n const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');\n line.setAttribute('x1', '13.5');\n line.setAttribute('y1', '13.5');\n line.setAttribute('x2', '18');\n line.setAttribute('y2', '18');\n line.setAttribute('stroke-linecap', 'round');\n glyph.appendChild(circle);\n glyph.appendChild(line);\n inputRow.appendChild(glyph);\n\n input = document.createElement('input');\n input.type = 'text';\n input.placeholder = 'Search tabs & page content\\u2026';\n input.autocomplete = 'off';\n input.spellcheck = false;\n input.setAttribute('aria-label', 'Search tabs and page content');\n input.setAttribute('aria-autocomplete', 'list');\n input.setAttribute('aria-controls', 'reef-spotlight-results');\n input.addEventListener('input', onInput);\n input.addEventListener('keydown', onKeyDown);\n inputRow.appendChild(input);\n\n const esc = document.createElement('span');\n esc.className = 'esc';\n esc.textContent = 'esc';\n inputRow.appendChild(esc);\n\n // Results container\n results = document.createElement('div');\n results.className = 'results';\n results.id = 'reef-spotlight-results';\n results.setAttribute('role', 'listbox');\n results.setAttribute('aria-label', 'Tabs');\n results.addEventListener('mousedown', (e) => {\n const target = e.target as HTMLElement;\n const row = target.closest('.row') as HTMLElement | null;\n if (!row) return;\n e.preventDefault();\n const idx = Number(row.dataset.index);\n if (Number.isFinite(idx)) {\n selectedIndex = idx;\n applySelection();\n openSelected();\n }\n });\n\n // Footer\n footer = document.createElement('div');\n footer.className = 'footer';\n const hints = document.createElement('span');\n hints.className = 'hints';\n hints.innerHTML =\n '\\u2191\\u2193navigate' +\n '\\u21B5open' +\n 'escclose' +\n 'tabcycle matches' +\n 'Ctrl+1\\u20139jump';\n footer.appendChild(hints);\n const brand = document.createElement('span');\n brand.className = 'brand';\n brand.textContent = 'Reef Spotlight';\n footer.appendChild(brand);\n\n card.appendChild(inputRow);\n card.appendChild(results);\n card.appendChild(footer);\n\n backdrop.appendChild(card);\n shadow.appendChild(backdrop);\n\n // Capture-phase Esc handler at the host level so it works even if focus drifts\n host.addEventListener('keydown', (e) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n e.stopPropagation();\n hide();\n }\n }, true);\n\n document.documentElement.appendChild(host);\n mounted = true;\n }\n\n function setSelected(idx: number, scroll = false): void {\n if (!results) return;\n if (unifiedResults.length === 0) {\n selectedIndex = 0;\n return;\n }\n if (idx < 0) idx = unifiedResults.length - 1;\n if (idx >= unifiedResults.length) idx = 0;\n selectedIndex = idx;\n applySelection(scroll);\n }\n\n function applySelection(scroll = false): void {\n if (!results) return;\n const rows = results.querySelectorAll('.row');\n rows.forEach((r) => r.setAttribute('aria-selected', 'false'));\n const sel = rows[selectedIndex];\n if (sel) {\n sel.setAttribute('aria-selected', 'true');\n if (scroll && typeof (sel as any).scrollIntoView === 'function') {\n try {\n sel.scrollIntoView({ block: 'nearest' });\n } catch {\n // ignore \u2014 scrollIntoView is not implemented in jsdom and some test envs\n }\n }\n }\n }\n\n function renderResults(\n items: SpotlightSearchHit[],\n query: string,\n suggestion?: string,\n autocorrected?: boolean,\n siteResults?: SpotlightSiteResult[],\n actions?: SpotlightAction[],\n ): void {\n if (!results) return;\n currentResults = items;\n unifiedResults = [];\n selectedIndex = 0;\n results.replaceChildren();\n\n const hasAny = items.length > 0 || (siteResults && siteResults.length > 0) || (actions && actions.length > 0);\n\n if (!hasAny) {\n const empty = document.createElement('div');\n empty.className = 'empty';\n if (suggestion && suggestion !== query.toLowerCase() && !autocorrected) {\n empty.innerHTML = `No matching tabs found. Did you mean ${escapeHtml(suggestion)}?`;\n const link = empty.querySelector('.suggestion');\n if (link) {\n link.addEventListener('click', (e) => {\n e.preventDefault();\n if (input) {\n input.value = suggestion;\n currentQuery = suggestion;\n onInput();\n }\n });\n }\n } else {\n empty.textContent = query\n ? 'No matching tabs found.'\n : 'Start typing to search every open tab.';\n }\n // Still show actions even when no results\n if (actions && actions.length > 0) {\n const frag = document.createDocumentFragment();\n renderSectionHeader(frag, 'Actions');\n for (const action of actions) {\n const idx = unifiedResults.length;\n unifiedResults.push({ kind: 'action', data: action });\n frag.appendChild(createActionRow(action, idx));\n }\n results.appendChild(frag);\n }\n results.appendChild(empty);\n return;\n }\n\n const fragment = document.createDocumentFragment();\n\n // Autocorrect banner\n if (autocorrected && suggestion) {\n const banner = document.createElement('div');\n banner.className = 'autocorrect-banner';\n banner.innerHTML = `Showing results for ${escapeHtml(suggestion)}search for \"${escapeHtml(query)}\" instead`;\n const origLink = banner.querySelector('.autocorrect-orig');\n if (origLink) {\n origLink.addEventListener('click', (e) => {\n e.preventDefault();\n // Force re-search with original query by resetting the sentinel\n lastQueryKey = '\\u0000force-requery\\u0000';\n if (input) {\n input.value = query;\n currentQuery = query;\n onInput();\n }\n });\n }\n fragment.appendChild(banner);\n }\n\n // Tab results\n if (items.length > 0) {\n if (siteResults?.length || autocorrected) {\n renderSectionHeader(fragment, 'Tabs');\n }\n const renderCount = Math.min(items.length, maxRows);\n for (let i = 0; i < renderCount; i++) {\n const item = items[i];\n const idx = unifiedResults.length;\n unifiedResults.push({ kind: 'tab', data: item });\n fragment.appendChild(createTabRow(item, query, idx));\n }\n }\n\n // Site content results\n if (siteResults && siteResults.length > 0) {\n renderSectionHeader(fragment, 'Site Content');\n for (const sr of siteResults) {\n const idx = unifiedResults.length;\n unifiedResults.push({ kind: 'site', data: sr });\n fragment.appendChild(createSiteRow(sr, query, idx));\n }\n }\n\n // Action items\n if (actions && actions.length > 0) {\n renderSectionHeader(fragment, 'New Tab');\n for (const action of actions) {\n const idx = unifiedResults.length;\n unifiedResults.push({ kind: 'action', data: action });\n fragment.appendChild(createActionRow(action, idx));\n }\n }\n\n results.appendChild(fragment);\n applySelection();\n }\n\n function renderSectionHeader(parent: DocumentFragment | HTMLElement, label: string): void {\n const header = document.createElement('div');\n header.className = 'section-header';\n header.textContent = label;\n parent.appendChild(header);\n }\n\n function createTabRow(item: SpotlightSearchHit, query: string, idx: number): HTMLElement {\n const row = document.createElement('div');\n row.className = 'row';\n row.setAttribute('role', 'option');\n row.setAttribute('aria-selected', idx === selectedIndex ? 'true' : 'false');\n row.dataset.index = String(idx);\n row.dataset.tabId = String(item.tabId);\n row.dataset.windowId = String(item.windowId);\n row.title = item.title;\n\n const fav = document.createElement('img');\n fav.className = 'favicon';\n fav.alt = '';\n fav.width = 16;\n fav.height = 16;\n fav.src = item.favIconUrl || FALLBACK_FAVICON;\n fav.addEventListener('error', () => { fav.src = FALLBACK_FAVICON; }, { once: true });\n row.appendChild(fav);\n\n const main = document.createElement('div');\n main.className = 'main';\n const title = document.createElement('div');\n title.className = 'title';\n title.innerHTML = highlight(truncate(item.title, 60), query);\n const url = document.createElement('div');\n url.className = 'url';\n url.textContent = hostFromUrl(item.url);\n main.appendChild(title);\n main.appendChild(url);\n row.appendChild(main);\n\n const match = item.matchedRecords?.[perTabMatchIndex.get(item.tabId) ?? 0];\n if (match && (match.headingText || match.bodyText)) {\n const snippet = document.createElement('div');\n snippet.className = 'match';\n const text = (match.headingText ? match.headingText + ' \\u2014 ' : '') + (match.bodyText || '');\n snippet.textContent = truncate(text, 90);\n row.appendChild(snippet);\n } else {\n const placeholder = document.createElement('div');\n row.appendChild(placeholder);\n }\n return row;\n }\n\n function createSiteRow(sr: SpotlightSiteResult, query: string, idx: number): HTMLElement {\n const row = document.createElement('div');\n row.className = 'row row-site';\n row.setAttribute('role', 'option');\n row.setAttribute('aria-selected', idx === selectedIndex ? 'true' : 'false');\n row.dataset.index = String(idx);\n row.title = sr.headingText;\n\n const icon = document.createElement('div');\n icon.className = 'favicon site-icon';\n icon.innerHTML = '';\n row.appendChild(icon);\n\n const main = document.createElement('div');\n main.className = 'main';\n const title = document.createElement('div');\n title.className = 'title';\n title.innerHTML = highlight(truncate(sr.headingText, 60), query);\n const url = document.createElement('div');\n url.className = 'url';\n url.textContent = sr.sourceOrigin;\n main.appendChild(title);\n main.appendChild(url);\n row.appendChild(main);\n\n const snippet = document.createElement('div');\n snippet.className = 'match';\n snippet.textContent = truncate(sr.bodyText, 90);\n row.appendChild(snippet);\n\n return row;\n }\n\n function createActionRow(action: SpotlightAction, idx: number): HTMLElement {\n const row = document.createElement('div');\n row.className = 'row row-action';\n row.setAttribute('role', 'option');\n row.setAttribute('aria-selected', idx === selectedIndex ? 'true' : 'false');\n row.dataset.index = String(idx);\n row.title = action.title;\n\n const icon = document.createElement('div');\n icon.className = 'favicon action-icon';\n if (action.type === 'search-web') {\n icon.innerHTML = '';\n } else {\n icon.innerHTML = '';\n }\n row.appendChild(icon);\n\n const main = document.createElement('div');\n main.className = 'main';\n const title = document.createElement('div');\n title.className = 'title action-title';\n title.textContent = action.title;\n main.appendChild(title);\n row.appendChild(main);\n\n const badge = document.createElement('div');\n badge.className = 'match action-badge';\n badge.textContent = 'new tab';\n row.appendChild(badge);\n\n return row;\n }\n\n function renderRecents(items: SpotlightRecent[]): void {\n if (!results) return;\n currentResults = items.map((r) => ({\n tabId: -1,\n windowId: -1,\n title: r.title,\n url: r.url,\n favIconUrl: r.favicon,\n score: 0,\n matchedRecords: [],\n _isRecent: true as any,\n }));\n selectedIndex = 0;\n results.replaceChildren();\n if (items.length === 0) {\n const empty = document.createElement('div');\n empty.className = 'empty';\n empty.textContent = 'Start typing to search every open tab.';\n results.appendChild(empty);\n return;\n }\n const fragment = document.createDocumentFragment();\n items.forEach((r, i) => {\n const row = document.createElement('div');\n row.className = 'row';\n row.setAttribute('role', 'option');\n row.setAttribute('aria-selected', i === 0 ? 'true' : 'false');\n row.dataset.index = String(i);\n row.dataset.recent = '1';\n row.title = r.title;\n\n const fav = document.createElement('img');\n fav.className = 'favicon';\n fav.alt = '';\n fav.width = 16;\n fav.height = 16;\n fav.src = r.favicon || FALLBACK_FAVICON;\n fav.addEventListener('error', () => { fav.src = FALLBACK_FAVICON; }, { once: true });\n row.appendChild(fav);\n\n const main = document.createElement('div');\n main.className = 'main';\n const title = document.createElement('div');\n title.className = 'title';\n title.textContent = truncate(r.title, 60);\n const url = document.createElement('div');\n url.className = 'url';\n url.textContent = hostFromUrl(r.url);\n main.appendChild(title);\n main.appendChild(url);\n row.appendChild(main);\n\n const tag = document.createElement('div');\n tag.className = 'match';\n tag.textContent = 'recent';\n row.appendChild(tag);\n\n fragment.appendChild(row);\n });\n results.appendChild(fragment);\n }\n\n async function runQuery(query: string): Promise {\n const key = query.trim();\n if (key === lastQueryKey) return;\n lastQueryKey = key;\n perTabMatchIndex = new Map();\n if (!key) {\n currentSuggestion = undefined;\n currentSiteResults = [];\n currentActions = [];\n currentAutocorrected = false;\n const recents = await fetchRecents();\n renderRecents(recents);\n return;\n }\n const reqId = ++inFlight;\n const res = await send({ type: 'SPOTLIGHT_SEARCH', query: key, limit: maxRows });\n if (reqId !== inFlight) return;\n const items: SpotlightSearchHit[] = (res && res.success && Array.isArray(res.items)) ? res.items : [];\n currentSuggestion = (res && res.suggestion) || undefined;\n currentAutocorrected = !!(res && res.autocorrected);\n currentSiteResults = (res && Array.isArray(res.siteResults)) ? res.siteResults : [];\n currentActions = (res && Array.isArray(res.actions)) ? res.actions : [];\n renderResults(items, key, currentSuggestion, currentAutocorrected, currentSiteResults, currentActions);\n }\n\n async function fetchRecents(): Promise {\n try {\n const res = await send({ type: 'LIBRARY_RECENTS_LIST' });\n const list = (res && res.success && Array.isArray(res.items)) ? res.items : [];\n return (list as any[]).slice(0, maxRecents).map((r) => ({\n url: r.url,\n title: r.title,\n favicon: r.favicon,\n visitedAt: r.visitedAt ?? 0,\n }));\n } catch {\n return [];\n }\n }\n\n function onInput(): void {\n if (!input) return;\n currentQuery = input.value;\n if (debounceHandle) clearTimeout(debounceHandle);\n debounceHandle = setTimeout(() => {\n debounceHandle = null;\n void runQuery(currentQuery);\n }, debounceMs);\n }\n\n function onKeyDown(e: KeyboardEvent): void {\n if (!input) return;\n const isMac = /Mac|iPod|iPhone|iPad/.test(navigator.platform);\n const mod = isMac ? e.metaKey : e.ctrlKey;\n\n if (e.key === 'ArrowDown' || (mod && (e.key === 'n' || e.key === 'N'))) {\n e.preventDefault();\n setSelected(selectedIndex + 1, true);\n return;\n }\n if (e.key === 'ArrowUp' || (mod && (e.key === 'p' || e.key === 'P'))) {\n e.preventDefault();\n setSelected(selectedIndex - 1, true);\n return;\n }\n if (e.key === 'Home') {\n e.preventDefault();\n setSelected(0, true);\n return;\n }\n if (e.key === 'End') {\n e.preventDefault();\n setSelected(unifiedResults.length - 1, true);\n return;\n }\n if (e.key === 'Enter') {\n e.preventDefault();\n openSelected();\n return;\n }\n if (e.key === 'Tab') {\n const sel = unifiedResults[selectedIndex];\n if (sel && sel.kind === 'tab' && sel.data.matchedRecords && sel.data.matchedRecords.length > 1) {\n e.preventDefault();\n const next = ((perTabMatchIndex.get(sel.data.tabId) ?? 0) + 1) % sel.data.matchedRecords.length;\n perTabMatchIndex.set(sel.data.tabId, next);\n applySelection();\n if (results) {\n const row = results.querySelectorAll('.row')[selectedIndex];\n if (row) {\n const match = sel.data.matchedRecords[next];\n const snippetEl = row.querySelector('.match');\n if (snippetEl) {\n const text = (match.headingText ? match.headingText + ' \\u2014 ' : '') + (match.bodyText || '');\n snippetEl.textContent = truncate(text, 90);\n }\n }\n }\n }\n return;\n }\n if (e.key === 'Backspace' && e.shiftKey && (mod)) {\n e.preventDefault();\n if (input) {\n input.value = '';\n currentQuery = '';\n onInput();\n }\n return;\n }\n if (mod && e.key >= '1' && e.key <= '9') {\n e.preventDefault();\n const idx = Number(e.key) - 1;\n if (idx < unifiedResults.length) {\n setSelected(idx, true);\n openSelected();\n }\n return;\n }\n // Esc handled in capture listener\n }\n\n async function openSelected(): Promise {\n const sel = unifiedResults[selectedIndex];\n if (!sel) return;\n\n if (sel.kind === 'action') {\n try {\n await send({ type: 'SPOTLIGHT_OPEN_NEW_TAB', url: sel.data.url });\n } catch { /* ignore */ }\n hide();\n return;\n }\n\n if (sel.kind === 'site') {\n try {\n await send({ type: 'SPOTLIGHT_OPEN_NEW_TAB', url: sel.data.url });\n } catch { /* ignore */ }\n hide();\n return;\n }\n\n // kind === 'tab'\n const tabHit = sel.data;\n if ((tabHit as any)._isRecent) {\n try {\n await send({ type: 'LIBRARY_OPEN_RECENT', url: tabHit.url });\n } catch { /* ignore */ }\n hide();\n return;\n }\n const matchIdx = perTabMatchIndex.get(tabHit.tabId) ?? 0;\n const record = tabHit.matchedRecords?.[matchIdx];\n try {\n await send({ type: 'TAB_SWITCH', tabId: tabHit.tabId, windowId: tabHit.windowId });\n if (record) {\n await send({ type: 'SPOTLIGHT_OPEN_RECORD', tabId: tabHit.tabId, record });\n }\n } catch { /* ignore */ }\n hide();\n }\n\n async function applyTheme(theme: 'light' | 'dark' | 'system'): Promise {\n if (!host) return;\n const resolved = resolveTheme(theme);\n host.setAttribute('data-theme', resolved);\n }\n\n async function show(): Promise {\n mount();\n if (!host || !input) return;\n open = true;\n host.style.display = 'block';\n const t = opts.theme ?? (await getStoredTheme());\n await applyTheme(t);\n lastQueryKey = '\\u0000never-queried\\u0000';\n perTabMatchIndex = new Map();\n currentSuggestion = undefined;\n currentSiteResults = [];\n currentActions = [];\n currentAutocorrected = false;\n unifiedResults = [];\n input.value = '';\n currentQuery = '';\n await runQuery('');\n // Focus the input after the host is visible\n requestAnimationFrame(() => {\n try { input?.focus(); } catch { /* ignore */ }\n });\n }\n\n function hide(): void {\n if (!host) return;\n open = false;\n host.style.display = 'none';\n if (debounceHandle) {\n clearTimeout(debounceHandle);\n debounceHandle = null;\n }\n }\n\n async function toggle(): Promise {\n if (open) hide();\n else await show();\n }\n\n function isOpen(): boolean {\n return open;\n }\n\n function destroy(): void {\n if (debounceHandle) clearTimeout(debounceHandle);\n if (host && host.parentNode) host.parentNode.removeChild(host);\n host = null;\n shadow = null;\n card = null;\n input = null;\n results = null;\n footer = null;\n mounted = false;\n open = false;\n }\n\n return { show, hide, toggle, isOpen, destroy };\n}\n", "/**\n * @file Content script for Reef for Browsers.\n * Inspects live DOM for window.__reefAgentManifest or extracts on-the-fly,\n * and hosts the Agent instance for executing in-page actions.\n */\n\nimport {\n extractSections,\n extractActions,\n extractFields,\n extractLinks,\n extractFiles,\n extractMedia,\n extractStructuredData,\n extractAccessibilityTree,\n} from '../../src/extraction.js';\nimport { Agent } from '../../src/agent.ts';\nimport { createSearchIndex, addToIndex } from '../../src/search-index.js';\nimport type { IndexRecord } from '../../src/types.js';\nimport type { AgentManifest } from '../../src/agent-ready.js';\nimport { createSpotlight, type SpotlightHandle } from './spotlight.js';\n\nexport interface ExtensionMessage {\n type:\n | 'PING' | 'GET_MANIFEST' | 'RESCAN' | 'EXECUTE_ACTION' | 'HIGHLIGHT_RECORD'\n | 'REEF_BOOKMARK_SELECTION' | 'REEF_SNIPPET_SELECTION' | 'REEF_BOOKMARK_PAGE'\n | 'REEF_OPEN_POPUP_QUERY' | 'REEF_OPEN_NOTE_FOR_PAGE' | 'REEF_SHOW_TOAST'\n | 'SHOW_SPOTLIGHT' | 'HIDE_SPOTLIGHT';\n record?: IndexRecord;\n actionType?: 'click' | 'type' | 'navigate';\n value?: string;\n text?: string;\n message?: string;\n toastType?: 'info' | 'success' | 'error';\n query?: string;\n options?: {\n actionsMode?: 'execute' | 'navigate-only';\n exclusionSelectors?: string[];\n };\n}\n\nexport interface ExtensionResponse {\n success: boolean;\n manifest?: AgentManifest;\n error?: string;\n url?: string;\n}\n\nconst HARD_EXCLUSION_SELECTOR =\n 'input[type=\"password\"], input[name*=\"card\" i], input[autocomplete*=\"cc-\" i], input[name*=\"ssn\" i], input[name*=\"social-security\" i], [data-reef-agent=\"off\"], [data-sensitive]';\n\nfunction isSensitiveElement(element: Element, customExclusions: string[] = []): boolean {\n if (element.matches(HARD_EXCLUSION_SELECTOR) || element.closest('[data-reef-agent=\"off\"], [data-sensitive]')) {\n return true;\n }\n return customExclusions.some(selector => {\n try {\n return element.matches(selector) || !!element.closest(selector);\n } catch {\n return false;\n }\n });\n}\n\nfunction getAuthoritativeManifest(): AgentManifest | null {\n if (typeof window !== 'undefined' && window.__reefAgentManifest) {\n return window.__reefAgentManifest;\n }\n\n const scriptTag = document.querySelector('script[type=\"application/agent-manifest+json\"]');\n if (scriptTag?.textContent) {\n try {\n return JSON.parse(scriptTag.textContent) as AgentManifest;\n } catch {\n // Invalid JSON tag\n }\n }\n return null;\n}\n\nexport function extractPageManifest(customExclusions: string[] = []): AgentManifest {\n const authoritative = getAuthoritativeManifest();\n if (authoritative) {\n // Filter authoritative manifest against per-site custom exclusions & sensitive guardrails\n const filteredRecords = authoritative.records.filter(record => {\n if (!record.selector) return true;\n try {\n const el = document.querySelector(record.selector);\n return el ? !isSensitiveElement(el, customExclusions) : true;\n } catch {\n return true;\n }\n });\n return {\n ...authoritative,\n records: filteredRecords,\n };\n }\n\n const url = location.href;\n const html = document.documentElement.outerHTML;\n\n const rawRecords: IndexRecord[] = [\n ...extractSections(html, url),\n ...extractActions(html, url),\n ...extractFields(html, url),\n ...extractLinks(html, url),\n ...extractFiles(html, url),\n ...extractMedia(html, url),\n ...extractStructuredData(html, url),\n ...extractAccessibilityTree(document),\n ];\n\n const filtered = rawRecords.filter(record => {\n if (record.selector) {\n try {\n const element = document.querySelector(record.selector);\n if (element && isSensitiveElement(element, customExclusions)) return false;\n } catch {\n // Invalid selector string\n }\n }\n return true;\n });\n\n const deduped = [\n ...new Map(\n filtered.map(record => [`${record.type}:${record.headingText}:${record.selector || record.url}`, record])\n ).values(),\n ];\n\n return {\n version: 1,\n url,\n generatedAt: Date.now(),\n records: deduped,\n excludedCount: rawRecords.length - deduped.length,\n };\n}\n\n// Dummy inspector interface for Agent instantiation\nconst dummyInspector = {\n activate: () => {},\n deactivate: () => {},\n isActive: () => false,\n setRecords: () => {},\n};\n\nlet currentAgent: Agent | null = null;\nfunction getOrCreateAgent(actionsMode: 'execute' | 'navigate-only' = 'execute'): Agent {\n const index = createSearchIndex();\n currentAgent = new Agent(index, dummyInspector, { actionsMode });\n return currentAgent;\n}\n\n// \u2500\u2500\u2500 SPOTLIGHT HOTKEY (Cmd+Shift+L / Meta+Shift+L) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Chrome manifest commands cannot bind to the Meta (Cmd) key,\n// so we listen at the content-script level for Cmd+Shift+L.\nif (typeof document !== 'undefined') {\n document.addEventListener('keydown', (e: KeyboardEvent) => {\n const isMac = /Mac|iPod|iPhone|iPad/.test(navigator.platform);\n if (!isMac) return;\n if (e.metaKey && e.shiftKey && (e.key === 'l' || e.key === 'L') && !e.ctrlKey && !e.altKey) {\n e.preventDefault();\n e.stopPropagation();\n ensureSpotlight().show();\n }\n }, true);\n}\n\n// \u2500\u2500\u2500 SPOTLIGHT (lazy) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nlet spotlightHandle: SpotlightHandle | null = null;\nfunction ensureSpotlight(): SpotlightHandle {\n if (!spotlightHandle) spotlightHandle = createSpotlight();\n return spotlightHandle;\n}\n\n// Global message listener for Chrome extension runtime\nif (typeof chrome !== 'undefined' && chrome.runtime?.onMessage) {\n chrome.runtime.onMessage.addListener((message: ExtensionMessage, sender, sendResponse) => {\n (async () => {\n try {\n if (message.type === 'PING') {\n sendResponse({ success: true, url: location.href });\n return;\n }\n\n if (message.type === 'GET_MANIFEST' || message.type === 'RESCAN') {\n const manifest = extractPageManifest(message.options?.exclusionSelectors || []);\n sendResponse({ success: true, manifest });\n return;\n }\n\n if (message.type === 'EXECUTE_ACTION' && message.record) {\n const actionsMode = message.options?.actionsMode || 'execute';\n const agent = getOrCreateAgent(actionsMode);\n\n if (message.record.destructive && actionsMode === 'navigate-only') {\n sendResponse({ success: false, error: 'destructive-action-blocked-by-mode' });\n return;\n }\n\n if (message.actionType === 'click' || message.record.type === 'action' || message.record.type === 'link') {\n if (message.record.selector) {\n await agent.click(message.record);\n } else if (message.record.url) {\n location.href = message.record.url;\n }\n sendResponse({ success: true, url: location.href });\n return;\n }\n\n if (message.actionType === 'type' || message.record.type === 'field') {\n const valueToType = message.value ?? message.record.value ?? '';\n await agent.type(message.record, valueToType);\n sendResponse({ success: true });\n return;\n }\n\n sendResponse({ success: false, error: 'unknown-action-type' });\n return;\n }\n\n if (message.type === 'HIGHLIGHT_RECORD' && message.record?.selector) {\n const el = document.querySelector(message.record.selector);\n if (el) {\n el.scrollIntoView({ behavior: 'smooth', block: 'center' });\n const origOutline = (el as HTMLElement).style.outline;\n (el as HTMLElement).style.outline = '3px solid #00a8b5';\n setTimeout(() => {\n (el as HTMLElement).style.outline = origOutline;\n }, 2000);\n }\n sendResponse({ success: true });\n return;\n }\n\n // Selection toolbar requests from background context-menu\n if (message.type === 'REEF_BOOKMARK_SELECTION' && message.text) {\n const created = await createBookmarkFromSelection(message.text);\n showReefToast(created ? 'Bookmarked in Reef' : 'Bookmark failed', created ? 'success' : 'error');\n sendResponse({ success: !!created });\n return;\n }\n if (message.type === 'REEF_SNIPPET_SELECTION' && message.text) {\n const created = await createSnippetFromSelection(message.text);\n showReefToast(created ? 'Saved as snippet' : 'Snippet failed', created ? 'success' : 'error');\n sendResponse({ success: !!created });\n return;\n }\n if (message.type === 'REEF_BOOKMARK_PAGE') {\n const created = await createBookmarkFromPage();\n showReefToast(created ? 'Page bookmarked' : 'Bookmark failed', created ? 'success' : 'error');\n sendResponse({ success: !!created });\n return;\n }\n if (message.type === 'REEF_SHOW_TOAST' && message.message) {\n showReefToast(message.message, message.toastType || 'info');\n sendResponse({ success: true });\n return;\n }\n if (message.type === 'REEF_OPEN_POPUP_QUERY' || message.type === 'REEF_OPEN_NOTE_FOR_PAGE') {\n // Background opens popup; nothing extra to do here\n sendResponse({ success: true });\n return;\n }\n\n // Spotlight overlay\n if (message.type === 'SHOW_SPOTLIGHT') {\n try {\n await ensureSpotlight().show();\n sendResponse({ success: true });\n } catch (err: any) {\n sendResponse({ success: false, error: err?.message || String(err) });\n }\n return;\n }\n if (message.type === 'HIDE_SPOTLIGHT') {\n spotlightHandle?.hide();\n sendResponse({ success: true });\n return;\n }\n\n sendResponse({ success: false, error: 'unsupported-message-type' });\n } catch (err: any) {\n sendResponse({ success: false, error: err?.message || String(err) });\n }\n })();\n return true; // Keep response channel open for async execution\n });\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Reef Selection Toolbar \u2014 a lightweight floating mini-toolbar\n// that surfaces quick actions whenever the user selects text.\n// It does not modify the page, only attaches a Shadow DOM host.\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst TOOLBAR_HOST_ID = 'reef-selection-toolbar-host';\n\nfunction isInsideEditable(target: Node | null): boolean {\n if (!target) return false;\n const el = target as Element;\n if (!el || !el.closest) return false;\n return !!el.closest('input, textarea, [contenteditable=\"true\"], [contenteditable=\"\"], [data-reef-agent=\"off\"], [data-sensitive]');\n}\n\nfunction getSelectionContext(): { text: string; rect: DOMRect | null; range: Range | null } {\n const sel = typeof window !== 'undefined' ? window.getSelection() : null;\n if (!sel || sel.rangeCount === 0 || sel.isCollapsed) {\n return { text: '', rect: null, range: null };\n }\n const text = sel.toString().trim();\n if (text.length < 1) return { text: '', rect: null, range: null };\n const range = sel.getRangeAt(0);\n const rect = range.getBoundingClientRect();\n return { text, rect, range };\n}\n\nfunction ensureToolbarHost(): HTMLElement | null {\n if (typeof document === 'undefined') return null;\n let host = document.getElementById(TOOLBAR_HOST_ID) as HTMLElement | null;\n if (host && host.shadowRoot) return host;\n if (!host) {\n host = document.createElement('div');\n host.id = TOOLBAR_HOST_ID;\n host.setAttribute('data-reef-agent', 'on');\n host.style.cssText = 'position: fixed; z-index: 2147483646; top: 0; left: 0; display: none; pointer-events: auto;';\n (document.body || document.documentElement).appendChild(host);\n }\n if (!host.shadowRoot) {\n const root = host.attachShadow({ mode: 'open' });\n const style = document.createElement('style');\n style.textContent = REEF_TOOLBAR_CSS;\n const bar = document.createElement('div');\n bar.className = 'bar';\n // Static SVG icons only \u2014 no user data, network input, or storage values flow into this markup.\n bar.innerHTML = `\n \n \n \n \n `;\n root.appendChild(style);\n root.appendChild(bar);\n\n bar.addEventListener('mousedown', (e) => {\n e.preventDefault();\n e.stopPropagation();\n });\n bar.addEventListener('click', async (e) => {\n const target = e.target as HTMLElement;\n const btn = target.closest('.btn') as HTMLElement | null;\n if (!btn) return;\n const action = btn.dataset.action;\n const text = (host as any)._reefText as string || '';\n hideToolbar();\n if (action === 'bookmark') {\n const ok = await createBookmarkFromSelection(text);\n showReefToast(ok ? 'Bookmarked in Reef' : 'Bookmark failed', ok ? 'success' : 'error');\n } else if (action === 'snippet') {\n const ok = await createSnippetFromSelection(text);\n showReefToast(ok ? 'Saved as snippet' : 'Snippet failed', ok ? 'success' : 'error');\n } else if (action === 'search') {\n if (typeof chrome !== 'undefined' && chrome.runtime?.sendMessage) {\n chrome.runtime.sendMessage({ type: 'TAB_SEARCH_PROMPT', query: text });\n }\n try {\n (chrome as any)?.action?.openPopup?.();\n } catch {\n // ignore\n }\n } else if (action === 'copy') {\n try {\n await navigator.clipboard.writeText(text);\n showReefToast('Copied to clipboard', 'success');\n } catch {\n showReefToast('Copy failed', 'error');\n }\n }\n });\n }\n return host;\n}\n\nconst REEF_TOOLBAR_CSS = `\n:host { all: initial; }\n.bar {\n display: inline-flex;\n align-items: center;\n gap: 2px;\n background: #111;\n color: #fff;\n border-radius: 8px;\n padding: 4px;\n box-shadow: 0 6px 20px rgba(0,0,0,.25);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", system-ui, sans-serif;\n font-size: 12px;\n line-height: 1;\n user-select: none;\n}\n.btn {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 6px 8px;\n background: transparent;\n color: #fff;\n border: 0;\n border-radius: 6px;\n cursor: pointer;\n font: inherit;\n white-space: nowrap;\n transition: background .12s;\n}\n.btn:hover { background: rgba(255,255,255,.12); }\n.btn:active { background: rgba(255,255,255,.2); }\n.btn svg { display: block; }\n`;\n\nfunction showToolbarAt(rect: DOMRect, text: string) {\n const host = ensureToolbarHost();\n if (!host) return;\n (host as any)._reefText = text;\n host.style.display = 'block';\n\n // Position above the selection if there's room, otherwise below\n const bar = host.shadowRoot!.querySelector('.bar') as HTMLElement;\n const margin = 8;\n const barHeight = bar ? bar.offsetHeight : 36;\n const barWidth = bar ? bar.offsetWidth : 280;\n\n let top = window.scrollY + rect.top - barHeight - margin;\n let left = window.scrollX + rect.left + rect.width / 2 - barWidth / 2;\n\n if (rect.top - barHeight - margin < 0) {\n top = window.scrollY + rect.bottom + margin;\n }\n const maxLeft = window.scrollX + window.innerWidth - barWidth - 8;\n if (left > maxLeft) left = maxLeft;\n if (left < window.scrollX + 8) left = window.scrollX + 8;\n\n host.style.transform = `translate(${left}px, ${top}px)`;\n}\n\nfunction hideToolbar() {\n const host = document.getElementById(TOOLBAR_HOST_ID) as HTMLElement | null;\n if (host) {\n host.style.display = 'none';\n (host as any)._reefText = '';\n }\n}\n\nlet toolbarTimer: number | null = null;\n\nfunction onSelectionChange() {\n if (typeof document === 'undefined') return;\n const active = document.activeElement;\n if (active && isInsideEditable(active)) {\n hideToolbar();\n return;\n }\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0 || sel.isCollapsed) {\n hideToolbar();\n return;\n }\n if (isInsideEditable(sel.anchorNode)) {\n hideToolbar();\n return;\n }\n const { text, rect } = getSelectionContext();\n if (!text || text.length < 1 || !rect || rect.width === 0) {\n hideToolbar();\n return;\n }\n if (toolbarTimer) window.clearTimeout(toolbarTimer);\n toolbarTimer = window.setTimeout(() => {\n const ctx = getSelectionContext();\n if (ctx.text && ctx.rect) showToolbarAt(ctx.rect, ctx.text);\n }, 220) as unknown as number;\n}\n\nfunction dismissOnOutside() {\n document.addEventListener('mousedown', (e) => {\n const host = document.getElementById(TOOLBAR_HOST_ID) as HTMLElement | null;\n if (!host || host.style.display === 'none') return;\n if (host.contains(e.target as Node)) return;\n const sel = window.getSelection();\n if (sel && !sel.isCollapsed) return;\n hideToolbar();\n }, true);\n document.addEventListener('keydown', (e) => {\n if (e.key === 'Escape') hideToolbar();\n });\n window.addEventListener('scroll', () => hideToolbar(), { passive: true });\n window.addEventListener('resize', () => hideToolbar());\n}\n\nif (typeof document !== 'undefined') {\n document.addEventListener('selectionchange', onSelectionChange);\n if (document.readyState === 'complete' || document.readyState === 'interactive') {\n dismissOnOutside();\n } else {\n document.addEventListener('DOMContentLoaded', dismissOnOutside);\n }\n}\n\n// \u2500\u2500\u2500 TOAST \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst TOAST_HOST_ID = 'reef-toast-host';\nfunction showReefToast(message: string, type: 'info' | 'success' | 'error' = 'info') {\n if (typeof document === 'undefined') return;\n let host = document.getElementById(TOAST_HOST_ID) as HTMLElement | null;\n if (!host) {\n host = document.createElement('div');\n host.id = TOAST_HOST_ID;\n host.setAttribute('data-reef-agent', 'on');\n host.style.cssText = 'position: fixed; z-index: 2147483647; top: 16px; right: 16px; display: flex; flex-direction: column; gap: 6px; pointer-events: none;';\n (document.body || document.documentElement).appendChild(host);\n }\n const shadow = host.shadowRoot || host.attachShadow({ mode: 'open' });\n if (!shadow.querySelector('style')) {\n const style = document.createElement('style');\n style.textContent = `\n :host { all: initial; }\n .toast {\n background: ${type === 'error' ? '#b91c1c' : type === 'success' ? '#047857' : '#111'};\n color: #fff;\n padding: 8px 12px;\n border-radius: 8px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", system-ui, sans-serif;\n font-size: 12px;\n line-height: 1.3;\n max-width: 320px;\n box-shadow: 0 8px 24px rgba(0,0,0,.25);\n animation: reef-in .18s ease-out;\n pointer-events: auto;\n }\n @keyframes reef-in {\n from { opacity: 0; transform: translateY(-4px); }\n to { opacity: 1; transform: translateY(0); }\n }\n `;\n shadow.appendChild(style);\n }\n const el = document.createElement('div');\n el.className = 'toast';\n el.textContent = message;\n shadow.appendChild(el);\n window.setTimeout(() => {\n el.style.transition = 'opacity .2s';\n el.style.opacity = '0';\n window.setTimeout(() => el.remove(), 220);\n }, 2200);\n}\n\n// \u2500\u2500\u2500 LIBRARY HELPERS (via background) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nasync function createBookmarkFromSelection(text: string): Promise {\n if (typeof chrome === 'undefined' || !chrome.runtime?.sendMessage) return false;\n const { selectionContext } = captureSelectionContext();\n return new Promise(resolve => {\n chrome.runtime.sendMessage({\n type: 'LIBRARY_BOOKMARK_CREATE',\n data: {\n url: location.href,\n title: document.title,\n selectedText: text.slice(0, 4000),\n note: '',\n tags: [],\n contextBefore: selectionContext.before,\n contextAfter: selectionContext.after,\n favicon: getFavicon(),\n },\n }, (res: any) => resolve(!!(res && res.success)));\n });\n}\n\nasync function createSnippetFromSelection(text: string): Promise {\n if (typeof chrome === 'undefined' || !chrome.runtime?.sendMessage) return false;\n return new Promise(resolve => {\n chrome.runtime.sendMessage({\n type: 'LIBRARY_SNIPPET_CREATE',\n data: {\n text: text.slice(0, 4000),\n title: text.slice(0, 80),\n tags: [],\n source: { url: location.href, title: document.title },\n },\n }, (res: any) => resolve(!!(res && res.success)));\n });\n}\n\nasync function createBookmarkFromPage(): Promise {\n if (typeof chrome === 'undefined' || !chrome.runtime?.sendMessage) return false;\n return new Promise(resolve => {\n chrome.runtime.sendMessage({\n type: 'LIBRARY_BOOKMARK_CREATE',\n data: {\n url: location.href,\n title: document.title,\n note: '',\n tags: [],\n favicon: getFavicon(),\n },\n }, (res: any) => resolve(!!(res && res.success)));\n });\n}\n\nfunction captureSelectionContext(): { before: string; after: string } {\n try {\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return { before: '', after: '' };\n const range = sel.getRangeAt(0);\n const beforeRange = document.createRange();\n beforeRange.setStart(document.body, 0);\n beforeRange.setEnd(range.startContainer, range.startOffset);\n const afterRange = document.createRange();\n afterRange.setStart(range.endContainer, range.endOffset);\n afterRange.setEndAfter(document.body.lastChild || document.body);\n return {\n before: (beforeRange.toString() || '').slice(-160).trim(),\n after: (afterRange.toString() || '').slice(0, 160).trim(),\n };\n } catch {\n return { before: '', after: '' };\n }\n}\n\nfunction getFavicon(): string | undefined {\n if (typeof document === 'undefined') return undefined;\n const link = document.querySelector('link[rel*=\"icon\"]') as HTMLLinkElement | null;\n return link?.href || undefined;\n}\n"], - "mappings": "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ACpQA,MAAM,UAAU;AAChB,MAAM,mBAAmB;AACzB,MAAM,mBAAmB;AACzB,MAAM,sBAAsB;AAE5B,MAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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": ["selectors", "inputElement", "chrome"] -} diff --git a/reef-extension-chrome/icons/icon-128.png b/reef-extension-chrome/icons/icon-128.png deleted file mode 100644 index 312dc82..0000000 Binary files a/reef-extension-chrome/icons/icon-128.png and /dev/null differ diff --git a/reef-extension-chrome/icons/icon-16.png b/reef-extension-chrome/icons/icon-16.png deleted file mode 100644 index e8ed29b..0000000 Binary files a/reef-extension-chrome/icons/icon-16.png and /dev/null differ diff --git a/reef-extension-chrome/icons/icon-32.png b/reef-extension-chrome/icons/icon-32.png deleted file mode 100644 index b5921b4..0000000 Binary files a/reef-extension-chrome/icons/icon-32.png and /dev/null differ diff --git a/reef-extension-chrome/icons/icon-48.png b/reef-extension-chrome/icons/icon-48.png deleted file mode 100644 index 591ce12..0000000 Binary files a/reef-extension-chrome/icons/icon-48.png and /dev/null differ diff --git a/reef-extension-chrome/icons/plugin_logo.png b/reef-extension-chrome/icons/plugin_logo.png deleted file mode 100644 index a6c7bfa..0000000 Binary files a/reef-extension-chrome/icons/plugin_logo.png and /dev/null differ diff --git a/reef-extension-chrome/icons/reef-logo.png b/reef-extension-chrome/icons/reef-logo.png deleted file mode 100644 index e3ab4f6..0000000 Binary files a/reef-extension-chrome/icons/reef-logo.png and /dev/null differ diff --git a/reef-extension-chrome/lib/reef.min.js b/reef-extension-chrome/lib/reef.min.js deleted file mode 100644 index 55d68a4..0000000 --- a/reef-extension-chrome/lib/reef.min.js +++ /dev/null @@ -1,226 +0,0 @@ -"use strict";var ReefSearch=(()=>{function q(){return{headingIndex:new Map,headingIds:new Map,bodyIndex:new Map,allSections:[],queryCache:new Map,popularQueries:[],docFrequency:new Map,totalDocs:0}}function H(c,e,t){c.totalDocs+=e.length;for(let n of e){let r=n.headingText.toLowerCase();if(r.length>=2){let a=c.headingIds.get(r)??[];a.push(n),c.headingIds.set(r,a);let l=r.normalize("NFD").replace(/[\u0300-\u036f]/g,"");if(l!==r){let d=c.headingIds.get(l)??[];d.push(n),c.headingIds.set(l,d)}}for(let a=2;a<=r.length;a++){let l=r.slice(0,a),d=c.headingIndex.get(l)??[];d.push(n),c.headingIndex.set(l,d)}let o=r.normalize("NFD").replace(/[\u0300-\u036f]/g,"");for(let a=2;a<=o.length;a++){let l=o.slice(0,a);if(l!==r.slice(0,a)){let d=c.headingIndex.get(l)??[];d.push(n),c.headingIndex.set(l,d)}}let i=n.bodyText.toLowerCase().split(/\s+/);for(let a of i)if(a.length>=3){let l=c.bodyIndex.get(a)??[];l.push(n),c.bodyIndex.set(a,l);let d=a.normalize("NFD").replace(/[\u0300-\u036f]/g,"");if(d!==a){let g=c.bodyIndex.get(d)??[];g.push(n),c.bodyIndex.set(d,g)}}for(let a of i)if(a.length>=2){let l=c.docFrequency.get(a)??0;c.docFrequency.set(a,l+1)}if(n.label){let a=n.label.toLowerCase();if(a.length>=2&&a.length<3){let l=c.bodyIndex.get(a)??[];l.push(n),c.bodyIndex.set(a,l)}}if(n.type==="structured"&&n.structuredData){if(n.structuredData.question){let a=n.structuredData.question.toLowerCase().split(/\s+/);for(let l of a)if(l.length>=3){let d=c.bodyIndex.get(l)??[];d.push(n),c.bodyIndex.set(l,d)}}if(n.structuredData.answer){let a=n.structuredData.answer.toLowerCase().split(/\s+/);for(let l of a)if(l.length>=3){let d=c.bodyIndex.get(l)??[];d.push(n),c.bodyIndex.set(l,d)}}}}c.allSections.push(...e)}function j(c){return c.allSections}function F(c,e){let t=c.length,n=e.length;if(t===0)return n;if(n===0)return t;let r=new Array(n+1);for(let o=0;o<=n;o++)r[o]=o;for(let o=1;o<=t;o++){let s=r[0];r[0]=o;for(let i=1;i<=n;i++){let a=r[i];r[i]=Math.min(r[i]+1,r[i-1]+1,s+(c[o-1]!==e[i-1]?1:0)),s=a}}return r[n]}function B(c,e,t=2){let n=c.trim().toLowerCase();if(!n||n.length<2)return null;let r=null,o=s=>{if(!s||Math.abs(s.length-n.length)>t)return;let i=F(s,n),a=r;i<=t&&(!a||i0;){let i=!1,a=n.exec(t);if(a&&a.index===0){e.push({type:"exact",value:a[1]}),t=t.slice(a[0].length),i=!0;continue}let l=r.exec(t);if(l&&l.index===0){e.push({type:"exclude",value:l[1]}),t=t.slice(l[0].length),i=!0;continue}let d=o.exec(t);if(d&&d.index===0){e.push({type:"prefix",value:d[1]}),t=t.slice(d[0].length),i=!0;continue}let g=s.exec(t);if(g&&g.index===0){e.push({type:"suffix",value:g[1]}),t=t.slice(g[0].length),i=!0;continue}if(t.match(/^\|/)){e.push({type:"or",value:"|"}),t=t.slice(1),i=!0;continue}let x=t.match(/^\s+/);if(x&&!i){t=t.slice(x[0].length);continue}let f=t.match(/^(\S+)/);f&&(e.push({type:"term",value:f[1]}),t=t.slice(f[0].length),i=!0)}return e}function ye(c){let e={type:"and",children:[]},t=null;for(let n of c)n.type==="or"?t||(t={type:"or",children:[]},e.children.push(t)):(t&&(e.children.push(t),t=null),e.children.push({type:n.type,value:n.value}));return t&&e.children.push(t),e}function ie(c,e,t){let n=new Set,r=c.length;for(let[o,s]of e.headingIds)if(Math.abs(o.length-r)<=t)for(let i of s)n.add(i);for(let[o,s]of e.bodyIndex)if(Math.abs(o.length-r)<=t)for(let i of s)n.add(i);return n}function U(c,e){switch(c.type){case"term":let t=c.value.toLowerCase(),n=e.headingText.toLowerCase(),r=e.bodyText.toLowerCase();return n.includes(t)||r.includes(t);case"exact":let o=c.value.toLowerCase();return e.headingText.toLowerCase().includes(o)||e.bodyText.toLowerCase().includes(o);case"exclude":let s=c.value.toLowerCase();return!e.headingText.toLowerCase().includes(s)&&!e.bodyText.toLowerCase().includes(s);case"prefix":let i=c.value.toLowerCase();return e.headingText.toLowerCase().startsWith(i)||e.bodyText.toLowerCase().startsWith(i);case"suffix":let a=c.value.toLowerCase();return e.headingText.toLowerCase().endsWith(a)||e.bodyText.toLowerCase().endsWith(a);case"and":return c.children.every(l=>U(l,e));case"or":return c.children.some(l=>U(l,e))}}function W(c,e,t,n,r,o=1.5,s=.75){let i=Math.log((t-e+.5)/(e+.5)+1),a=1-s+s*(n/r);return i*(c*(o+1)/(c+o*a))}function be(c){return c.allSections.length===0?50:c.allSections.reduce((t,n)=>t+n.bodyText.length+n.headingText.length,0)/c.allSections.length}function we(c,e){let t=e.queryCache.get(c);if(t){let n=new Set;for(let r of t.resultIds){let o=e.allSections.find(s=>s.id===r);o&&n.add(o)}return n}return null}function ve(c,e,t){let n={resultIds:e.map(r=>r.id),timestamp:Date.now()};if(t.queryCache.set(c,n),t.queryCache.size>100){let r=t.queryCache.keys().next().value;r!==void 0&&t.queryCache.delete(r)}}function z(c,e,t){let n=8,r;typeof t=="number"?n=t:t&&(r=t,n=t.limit??8);let o=c.trim();if(!o)return e.allSections.slice(0,n);let s=o.normalize("NFD").replace(/[\u0300-\u036f]/g,""),i=new Map,a=new Map,l={...me,...r?.weights??{}},d=be(e),g=(h,m,u,p,v)=>{i.set(h,(i.get(h)??0)+m);let w=a.get(h)??[];w.push({key:u,start:p,end:v}),a.set(h,w)},y=r?.scoringAlgorithm==="bm25",x=we(s.toLowerCase(),e),f=x?[...x]:e.allSections,S=e.headingIds.get(s.toLowerCase())??e.headingIds.get(o.toLowerCase());if(S)for(let h of S){let m=l.headingText??1,u=M(h.headingText.length),p;if(y){let w=e.docFrequency.get(s.toLowerCase())??1;p=W(1,w,e.totalDocs,h.headingText.length+h.bodyText.length,d)*100}else p=100*m*(1+u);g(h,p,"headingText",0,h.headingText.length)}let A=e.headingIndex.get(s.toLowerCase())??e.headingIndex.get(o.toLowerCase());if(A)for(let h of A){if(i.has(h))continue;let m=l.headingText??1,u=M(h.headingText.length),p;if(y){let I=e.docFrequency.get(s.toLowerCase())??1;p=W(1,I,e.totalDocs,h.headingText.length+h.bodyText.length,d)*50}else p=50*m*(1+u);let v=Q(h.headingText,o);if(v.length>0){let w=v[0];g(h,p,"headingText",w.start,w.end)}else g(h,p,"headingText",0,Math.min(o.length,h.headingText.length))}let $=s.toLowerCase().split(/\s+/);for(let h of $){let m=e.bodyIndex.get(h)??e.bodyIndex.get(o.toLowerCase().split(/\s+/).find(u=>u.includes(h))??"");if(m)for(let u of m){if(i.has(u))continue;let p=l.bodyText??1,v=M(u.bodyText.length),w;if(y){let b=e.docFrequency.get(h)??1;w=W(1,b,e.totalDocs,u.headingText.length+u.bodyText.length,d)*20}else w=20*p*(1+v);let I=Q(u.bodyText,o);if(I.length>0){let k=I[0];g(u,w,"bodyText",k.start,k.end)}else g(u,w,"bodyText",0,Math.min(h.length,u.bodyText.length))}}if(r?.fuzzy){let h=r.fuzzyDistance??2;if(i.size===0){let m=ie(o.toLowerCase(),e,1);for(let u of m){if(i.has(u))continue;let p=u.headingText.toLowerCase();if(Q(p,o).length===0){let b=F(p,o);if(b<=1){let L=(1-b/Math.max(o.length,p.length))*60,T=l.headingText??1,pe=M(u.headingText.length);g(u,L*T*(1+pe),"headingText",0,Math.min(o.length,p.length))}}if(i.has(u))continue;let w=u.bodyText.toLowerCase(),I=1/0,k=0;for(let b=0;b<=w.length-o.length;b++){let L=w.slice(b,b+o.length),T=F(o,L);Tr.filter(h)));let N=R.sort((h,m)=>m[1]-h[1]);r?.sortFn&&(N=N.sort((h,m)=>{let u=a.get(h[0])??[],p=a.get(m[0])??[];return r.sortFn({record:h[0],score:h[1],matches:u},{record:m[0],score:m[1],matches:p})}));let O=N.slice(0,n).map(([h])=>h);return ve(o.toLowerCase(),O,e),r?.includeScore||r?.includeMatches?O.map(h=>{let m=a.get(h)??[],u=i.get(h)??0,v=Math.max(0,Math.min(1,1-u/(y?100:200)));return{record:h,score:v,matches:r.includeMatches?m:void 0}}):O}function K(c){let e=JSON.parse(c);return{allSections:e.allSections??[],headingIndex:new Map(e.headingIndex??[]),headingIds:new Map(e.headingIds??[]),bodyIndex:new Map(e.bodyIndex??[]),queryCache:new Map,popularQueries:e.popularQueries??[],docFrequency:new Map(e.docFrequency??[]),totalDocs:e.totalDocs??0}}function V(c,e,t=10){let n=c.trim().toLowerCase();if(!n||n.length<2)return[];let r=[],o=new Set,s=e.headingIndex.get(n)??[],i=e.headingIds.get(n)??[],a=[...s,...i];for(let l of a){if(r.length>=t)break;let d=l.headingText;o.has(d)||(o.add(d),r.push(d))}if(r.length=t)break;l.includes(n)&&!o.has(d[0].headingText)&&(o.add(d[0].headingText),r.push(d[0].headingText))}return r.slice(0,t)}function J(c){let e={section:0,action:0,field:0,link:0,file:0,media:0,structured:0};for(let t of c.allSections)e[t.type]=(e[t.type]??0)+1;return e}function _(c,e){let t=e.trim();t&&(c.popularQueries.length>=100&&c.popularQueries.shift(),c.popularQueries.push(t))}function G(c,e=5){let t=new Map;for(let n of c.popularQueries)t.set(n,(t.get(n)??0)+1);return[...t.entries()].sort((n,r)=>r[1]-n[1]).slice(0,e).map(([n])=>n)}function X(c){let e="",t=!1;for(let n=0;n"&&t===!0?t=!1:t===!1&&(e+=r)}return e.replace(/\s+/g," ").trim()}function D(c){let e=[],t=c;for(;t&&t.nodeType===Node.ELEMENT_NODE;){let n=t.tagName.toLowerCase();if(t.id){n+=`#${t.id}`,e.unshift(n);break}else if(t.className){let s=t.className.trim().split(/\s+/);s.length&&(n+=`.${s.join(".")}`)}let r=1,o=t.previousElementSibling;for(;o;)o.tagName===t.tagName&&r++,o=o.previousElementSibling;r>1&&(n+=`:nth-child(${r})`),e.unshift(n),t=t.parentElement}return e.length>0?e.join(" > "):c.tagName.toLowerCase()}function ae(c,e){let t=c.match(/\bid=["']([^"']+)['"]/i);return t?.[1]?t[1]:e.toLowerCase().replace(/[^a-z0-9]+/g,"-")||Math.random().toString(36).slice(2)}function le(c){return/\bid=["'][^"']+["']/i.test(c)}function de(c,e){let t=c.slice(e,e+500),n=t.match(/]*id="([^"]+)"/i);if(n?.[1])return n[1];let r=t.match(/]*id="([^"]+)"/i);return r?.[1]?r[1]:null}var Y=new Map;function Z(c,e){if(Y.has(e))return Y.get(e);let t=c.replace(//gi," ").replace(//gi," ").replace(//gi," ").replace(//g," "),n=[],r=/<(h[1-6])[^>]*>([\s\S]*?)<\/h[1-6]>/gi,o;for(r.lastIndex=0;(o=r.exec(t))!==null;){let[,a,l]=o,d=X(l),g=parseInt(a[1],10);n.push({level:g,index:o.index,text:d,id:ae(o[0],d),hasRealId:le(o[0])})}let s=n.length,i=new Array(s);for(let a=0;a0&&(S+=" \u203A "),S+=n[R].text;let A=l.hasRealId?null:de(t,l.index+l.text.length),$=l.hasRealId?"#"+l.id:A?"#"+A:void 0;i[a]={id:`${e}#${l.id}`,url:`${e}#${l.id}`,headingText:l.text,headingId:l.id,breadcrumb:S,bodyText:f,type:"section",selector:$}}return Y.set(e,i),i}function he(c){let e=c.getAttribute("aria-label");if(e?.trim())return e.trim();let t=c.getAttribute("aria-labelledby");if(t){let o=document.getElementById(t);if(o?.textContent?.trim())return o.textContent.trim()}let n=c.textContent?.trim();if(n)return n;let r=c.getAttribute("title");return r?.trim()?r.trim():null}function ue(c){let e=["delete","remove","cancel subscription","unsubscribe","pay","checkout","submit order","confirm"],t=c.toLowerCase();return e.some(n=>t.includes(n))}function ee(c,e){let t=[],n=new DOMParser().parseFromString(c,"text/html"),r=["button",'[role="button"]','input[type="button"]','input[type="submit"]',"summary","[data-reef-action]"],o=Array.from(n.querySelectorAll(r.join(",")));for(let s of o){let i=he(s);if(!i)continue;let a=D(s);t.push({id:`${e}#action-${t.length}`,url:e,headingText:i,headingId:`action-${t.length}`,breadcrumb:"",bodyText:i,type:"action",selector:a,destructive:ue(i),label:i})}return t}function te(c,e){let t=[],n=new DOMParser().parseFromString(c,"text/html"),r=Array.from(n.querySelectorAll("form"));for(let o of r){let s="",i=o.parentElement;for(;i&&i!==n.body;){if(i.matches('h1, h2, h3, h4, h5, h6, article, section, [role="main"], main')){let l=i.textContent?.trim()||"";l&&(s=l);break}i=i.parentElement}let a=Array.from(o.querySelectorAll("input, textarea, select"));for(let l of a){if(l.matches('input[type="hidden"], input[type="button"], input[type="submit"], input[type="reset"]'))continue;let d="",g=l.id;if(g){let f=n.querySelector(`label[for="${g}"]`);f&&(d=f.textContent?.trim()||"")}if(!d){let f=l.closest("label");if(f){d=f.textContent?.trim()||"";let S=l;d&&S.value&&d.includes(S.value)&&(d=d.replace(S.value,"").trim())}}if(!d){let f=l;d=("placeholder"in f?f.placeholder:"")||l.getAttribute("aria-label")||""}if(!d)continue;let y=D(l),x=l;t.push({id:`${e}#field-${t.length}`,url:e,headingText:d,headingId:`field-${t.length}`,breadcrumb:s,bodyText:d,type:"field",selector:y,label:d,value:x.value})}}return t}function ne(c,e){let t=[],n=new DOMParser().parseFromString(c,"text/html"),r=Array.from(n.querySelectorAll("a[href]"));for(let o of r){let s=o.getAttribute("href");if(!s||s==="#"||s.startsWith("javascript:"))continue;let i=o.textContent?.trim()||"";if(!i)continue;let a=E(s,e),l=!a.startsWith(window.location.origin),d=D(o);t.push({id:`${e}#link-${t.length}`,url:a,headingText:i,headingId:`link-${t.length}`,breadcrumb:"",bodyText:i,type:l?"link":"section",selector:d})}return t}function re(c,e){let t=[],n=new DOMParser().parseFromString(c,"text/html"),r=["pdf","doc","docx","xls","xlsx","ppt","pptx","zip","csv"],o=Array.from(n.querySelectorAll("a[href]"));for(let s of o){let i=s.getAttribute("href");if(!i||!r.some(y=>i.toLowerCase().endsWith(`.${y}`)||i.toLowerCase().endsWith(`.${y}?`)||i.toLowerCase().endsWith(`.${y}#`)))continue;let l=s.textContent?.trim()||i.split("/").pop()||"";if(!l)continue;let d=E(i,e),g=D(s);t.push({id:`${e}#file-${t.length}`,url:d,headingText:l,headingId:`file-${t.length}`,breadcrumb:"",bodyText:l,type:"file",selector:g})}return t}function oe(c,e){let t=[],n=new DOMParser().parseFromString(c,"text/html"),r=Array.from(n.querySelectorAll("img"));for(let s of r){let i=s.alt.trim();if(!i)continue;let a="",l=s.closest("figure");if(l){let y=l.querySelector("figcaption");y&&(a=y.textContent?.trim()||"")}let d=a?`${i} ${a}`:i;if(!d.trim())continue;let g=D(s);t.push({id:`${e}#media-image-${t.length}`,url:e,headingText:i,headingId:`media-image-${t.length}`,breadcrumb:"",bodyText:d,type:"media",selector:g})}let o=Array.from(n.querySelectorAll("video, audio"));for(let s of o){let i=s.getAttribute("title")||s.getAttribute("aria-label")||"";if(!i)continue;let a="",l=Array.from(s.querySelectorAll('track[kind="captions"], track[kind="subtitles"]'));for(let y of l){let x=y.getAttribute("src");x&&(a+=`[Transcript available: ${x}] `)}let d=a?`${i} ${a}`:i;if(!d.trim())continue;let g=D(s);t.push({id:`${e}#media-${t.length}`,url:e,headingText:i,headingId:`media-${t.length}`,breadcrumb:"",bodyText:d,type:"media",selector:g,transcript:a.trim()})}return t}function se(c,e){let t=[],n=new DOMParser().parseFromString(c,"text/html"),r=Array.from(n.querySelectorAll('script[type="application/ld+json"]'));for(let o of r)try{let s=JSON.parse(o.textContent||"{}");if(Array.isArray(s)?s.some(i=>i["@type"]==="FAQPage"):s["@type"]==="FAQPage"){let i=Array.isArray(s)?s.flatMap(a=>a.mainEntity||[]):s.mainEntity||[];for(let[a,l]of i.entries()){if(!l||!l.name)continue;let d=l.acceptedAnswer?.text||l.suggestedAnswer?.text||"";if(!d)continue;let g=`${l.name} ${d}`;t.push({id:`${e}#structured-faq-${a}`,url:e,headingText:l.name,headingId:`structured-faq-${a}`,breadcrumb:"",bodyText:g,type:"structured",structuredData:{question:l.name,answer:d}})}}else if(s["@type"]){let i=s["@type"],a=s.name||s.headline||"",l=s.description||"";if(!a&&!l)continue;let d=`${a} ${l}`.trim();if(!d)continue;t.push({id:`${e}#structured-${i.toLowerCase()}-${t.length}`,url:e,headingText:a||"Structured Data",headingId:`structured-${i.toLowerCase()}-${t.length}`,breadcrumb:"",bodyText:d,type:"structured",structuredData:s})}}catch{continue}return t}function E(c,e){if(!c)return e;try{return new URL(c,e).toString()}catch{return c}}function C(c){let e={"&":"&","<":"<",">":">",'"':""","'":"'"},t="";for(let n=0;n${s}${i}`}function Ie(c,e){if(!e.trim())return c.slice(0,90)+"\u2026";let t=c.toLowerCase(),n=e.toLowerCase(),r=t.indexOf(n);if(r===-1)return c.slice(0,90)+"\u2026";let o=Math.max(0,r-30),s=Math.min(c.length,r+e.length+40);return(o>0?"\u2026":"")+c.slice(o,s)+(s';case"action":return'';case"field":return'';case"link":return'';case"file":return'';case"media":return'';case"structured":return'';default:return''}}function fe(c){switch(c){case"section":return"Section";case"action":return"Action";case"field":return"Field";case"link":return"Link";case"file":return"File";case"media":return"Media";case"structured":return"Answer";default:return"Section"}}var P=class{constructor(){this.config={};this.index=q();this.root=null;this.host=null;this.input=null;this.resultsList=null;this.isOpen=!1;this.selectedIndex=0;this.currentQuery="";this.searchDebounce=0;this.deferredActions=[];this.selectCallback=null;this.hotkeyHandler=null;this.config=this.readConfig(),this.config.headless||(this.applyConfigToUI(),this.registerHotkey()),this.handleDeferredScroll(),this.boot()}readConfig(){let t=document.currentScript?.dataset??{},n;if(t.stemming==="true"||t.stopwords==="true"||t.diacritics==="true"){let o=[];if(t.stopwords==="true"){let s=new Set(["a","an","the","and","or","but","in","on","at","to","for","of","with","by","from","up","about","into","through","during","before","after","above","below"]);o.push(i=>s.has(i)?null:i)}t.diacritics==="true"&&o.push(s=>s.normalize("NFD").replace(/[\u0300-\u036f]/g,"")),t.stemming==="true"&&o.push(s=>s.length<=2?s:/ing$/.test(s)?s.slice(0,-3):/ed$/.test(s)||/es$/.test(s)&&s.length>3?s.slice(0,-2):/s$/.test(s)&&s.length>3?s.slice(0,-1):s),n=o.length>0?o:void 0}let r;if(t.synonyms)try{r=JSON.parse(t.synonyms)}catch{console.warn("[reef] Invalid synonyms JSON in data-synonyms attribute")}return{sitemap:t.sitemap??"/sitemap.xml",maxPages:Number(t.maxPages??500),scope:t.scope,indexActions:t.indexActions!=="false",indexMedia:t.indexMedia!=="false",indexStructuredData:t.indexStructuredData!=="false",indexHidden:t.indexHidden!=="false",fileExtensions:t.fileExtensions,excludeAction:t.excludeAction,actionsMode:t.actionsMode||"execute",primaryColor:t.primaryColor,secondaryColor:t.secondaryColor,backgroundColor:t.backgroundColor,textColor:t.textColor,borderColor:t.borderColor,radius:t.radius?Number(t.radius):16,theme:t.theme,fontFamily:t.fontFamily,mode:t.mode,hotkey:t.hotkey,placeholder:t.placeholder,headless:t.headless==="true",onReady:void 0,tokenizePipeline:n,synonyms:r,prebuiltIndexUrl:t.prebuiltIndexUrl,useWorkerIndexing:t.useWorkerIndexing==="true"}}applyConfigToUI(){if(!this.host)return;let e=this.config;switch(this.host.style.setProperty("--primary-color",e.primaryColor??"#43d9c8"),this.host.style.setProperty("--secondary-color",e.secondaryColor??"#ff8562"),this.host.style.setProperty("--background-color",e.backgroundColor??"rgba(20,30,28,0.65)"),this.host.style.setProperty("--text-color",e.textColor??"#edebe6"),this.host.style.setProperty("--border-color",e.borderColor??"rgba(67,217,200,0.25)"),this.host.style.setProperty("--radius",e.radius?.toString()??"16"),this.host.style.setProperty("--font-family",e.fontFamily??"Inter, system-ui, sans-serif"),this.host.classList.remove("mode-regular","mode-opaque","mode-high-contrast"),e.mode){case"opaque":this.host.classList.add("mode-opaque");break;case"high-contrast":this.host.classList.add("mode-high-contrast");break;default:this.host.classList.add("mode-regular")}}scrollSelectedIntoView(){if(!this.resultsList)return;this.resultsList.querySelector(`.result[data-index="${this.selectedIndex}"]`)?.scrollIntoView({block:"nearest",behavior:"smooth"})}registerHotkey(){this.unregisterHotkey(),this.hotkeyHandler=e=>{let t=this.config.hotkey||"ctrlk,cmdk",n={ctrlk:e.ctrlKey&&e.key==="k"&&!e.shiftKey&&!e.altKey&&!e.metaKey,cmdk:e.metaKey&&e.key==="k"&&!e.shiftKey&&!e.altKey&&!e.ctrlKey,ctrlshiftk:e.ctrlKey&&e.shiftKey&&e.key.toLowerCase()==="k",altk:e.altKey&&e.key==="k",f:e.ctrlKey&&e.key==="f"};t.split(",").some(o=>n[o.trim()])&&(e.preventDefault(),this.open())},document.addEventListener("keydown",this.hotkeyHandler)}unregisterHotkey(){this.hotkeyHandler&&document.removeEventListener("keydown",this.hotkeyHandler)}getSitemapCandidates(){let e=window.location.href,t=this.config.sitemap??"sitemap.xml",n=[E(t,e)];return t.startsWith("/")?t==="/sitemap.xml"&&n.push(E("sitemap.xml",e)):n.push(E("./sitemap.xml",e)),[...new Set(n)]}async boot(){if(this.config.prebuiltIndexUrl)try{let t=await fetch(this.config.prebuiltIndexUrl);if(t.ok){let n=await t.text();this.index=K(n),console.info(`[reef] loaded prebuilt index with ${this.index.allSections.length} sections`),this.callOnReady();return}}catch(t){console.warn("[reef] prebuilt index fetch failed, falling back to crawling",t)}let e=this.getSitemapCandidates();for(let t of e)try{let n=await fetch(t);if(!n.ok)continue;let r=await n.text(),o=[],s=/(.*?)<\/loc>/g,i;for(;(i=s.exec(r))!==null;)o.push(i[1].trim());let a=this.config.maxPages??500,l=await this.fetchPagesParallel(o.slice(0,a),t);if(l.length){H(this.index,l,this.config.tokenizePipeline),console.info(`[reef] indexed ${l.length} sections`),this.callOnReady();return}}catch(n){console.warn("[reef] sitemap fetch failed",t,n)}this.indexCurrentPage()}callOnReady(){if(this.config.onReady)try{this.config.onReady({index:this.getIndex()})}catch(e){console.error("[reef] onReady callback error:",e)}}async fetchPagesParallel(e,t){let r=[],o=new Array(e.length),s=0,i=async()=>{for(;si()));for(let a=0;a - :host { - position: fixed; - inset: 0; - z-index: 2147483647; - display: flex; - align-items: flex-start; - justify-content: center; - padding: 12vh 1.25rem 0; - background: var(--background-color, rgba(5,5,6,0.45)); - backdrop-filter: blur(10px); - opacity: 0; - pointer-events: none; - transition: opacity 0.14s ease; - } - :host(.is-hidden) { display: none; } - :host(.open) { opacity: 1; pointer-events: auto; } - - :host(.mode-opaque) .panel { - background: rgba(20,30,28,0.85) !important; - } - :host(.mode-high-contrast) .panel { - background: rgba(255,255,255,0.95); - --primary-color: #0066cc; - --text-color: #111111; - --border-color: #000000; - } - :host(.mode-high-contrast) .input { - color: #111111; - } - :host(.mode-high-contrast) .result-type-label { - color: #0066cc; - } - :host(.mode-high-contrast) .result .heading { - color: #111111; - } - :host(.mode-high-contrast) .result .snippet { - color: #333333; - } - - .panel { - width: 100%; - max-width: 560px; - background: var(--background-color, rgba(20,30,28,0.65)); - color: var(--text-color, #edebe6); - border: 1px solid var(--border-color, rgba(67,217,200,0.25)); - border-radius: var(--radius, 16px); - box-shadow: 0 8px 32px rgba(0,0,0,0.3), inset 0 0 0 1px rgba(255,255,255,0.05); - overflow: hidden; - transform: translateY(-8px) scale(0.98); - transition: transform 0.14s ease; - } - :host(.open) .panel { transform: translateY(0) scale(1); } - - .input-row { - display: flex; - align-items: center; - gap: 0.75rem; - padding: 0.95rem 1rem; - border-bottom: 1px solid var(--border-color, rgba(67,217,200,0.15)); - } - - .icon { - opacity: 0.6; - flex-shrink: 0; - stroke: var(--primary-color, #43d9c8); - } - - .input { - flex: 1; - background: transparent; - border: 0; - outline: none; - color: var(--text-color, #edebe6); - font-size: 1rem; - font-family: var(--font-family, Inter, system-ui, sans-serif); - } - .input::placeholder { - color: #55555a; - } - - .hint { - font-family: ui-monospace, monospace; - font-size: 0.72rem; - color: #8a8a8f; - border: 1px solid var(--border-color, rgba(67,217,200,0.2)); - border-radius: 6px; - padding: 0.15rem 0.5rem; - } - - .results { - max-height: 340px; - overflow-y: auto; - padding: 0.5rem; - } - - .result { - display: flex; - flex-direction: column; - gap: 0.25rem; - width: 100%; - text-align: left; - padding: 0.8rem 0.75rem; - border-radius: var(--radius, 10px); - margin-top: 0.25rem; - cursor: pointer; - border: 0; - background: transparent; - color: inherit; - } - .result:hover, .result.is-selected { - background: rgba(67,217,200,0.12); - } - .result-type { - display: flex; - align-items: center; - gap: 0.25rem; - font-size: 0.7rem; - margin-bottom: 0.25rem; - } - .result-type-icon { - display: flex; - align-items: center; - justify-content: center; - width: 16px; - height: 16px; - flex-shrink: 0; - } - .result-type-icon svg { - width: 14px; - height: 14px; - stroke: var(--primary-color, #43d9c8); - } - .result-type-label { - font-family: ui-monospace, monospace; - font-size: 0.7rem; - text-transform: uppercase; - letter-spacing: 0.5px; - color: var(--primary-color, #43d9c8); - } - .result .breadcrumb { - font-family: ui-monospace, monospace; - font-size: 0.75rem; - color: #8a8a8f; - } - .result .heading { - font-size: 0.95rem; - font-weight: 500; - color: var(--text-color, #edebe6); - } - .result .snippet { - font-size: 0.85rem; - color: var(--text-color, #8a8a8f); - line-height: 1.5; - } - .result mark { - background: rgba(67,217,200,0.22); - color: var(--primary-color, #43d9c8); - border-radius: 2px; - padding: 0 1px; - } - .result-action-hint { - font-size: 0.75rem; - font-family: ui-monospace, monospace; - color: #55555a; - margin-top: 0.25rem; - } - .result-action-hint.run-here { color: var(--primary-color, #43d9c8); } - .result-action-hint.go-there { color: #8a8a8f; } - .empty { - padding: 2rem; - color: #55555a; - text-align: center; - font-size: 0.9rem; - } - .footer { - display: flex; - justify-content: space-between; - align-items: center; - padding: 0.75rem 1rem; - border-top: 1px solid var(--border-color, rgba(67,217,200,0.15)); - color: #55555a; - font-size: 0.75rem; - font-family: ui-monospace, monospace; - } - .k { - border: 1px solid var(--border-color, rgba(67,217,200,0.2)); - border-radius: 4px; - padding: 0.1rem 0.4rem; - margin: 0 0.2rem; - } - @media (prefers-reduced-motion: reduce) { - :host, .panel { transition: none; } - } - - - `,this.input=t.querySelector("input"),this.resultsList=t.querySelector(".results"),t.querySelector("#modeSelect")?.addEventListener("change",s=>{let i=s.target.value;this.setMode(i)}),this.input?.addEventListener("input",()=>{this.currentQuery=this.input?.value??"",this.selectedIndex=0,this.searchDebounce&&cancelAnimationFrame(this.searchDebounce),this.searchDebounce=requestAnimationFrame(()=>this.renderResults())}),this.input?.addEventListener("keydown",s=>{let i=this.getVisibleResults();if(s.key==="ArrowDown")s.preventDefault(),i.length&&(this.selectedIndex=(this.selectedIndex+1)%i.length),this.renderResults();else if(s.key==="ArrowUp")s.preventDefault(),i.length&&(this.selectedIndex=(this.selectedIndex-1+i.length)%i.length),this.renderResults();else if(s.key==="Enter"){s.preventDefault();let a=this.getVisibleResults()[this.selectedIndex];a&&(this.runSelectCallback(a),this.executeAction(a),this.close())}else s.key==="Escape"&&(s.preventDefault(),this.close())}),document.addEventListener("keydown",s=>{this.isOpen&&s.key==="Escape"&&(s.preventDefault(),this.close())}),e.addEventListener("click",s=>{s.composedPath().some(l=>l instanceof Element&&l.classList.contains("panel"))||this.close()}),this.handleDeferredActions()}getVisibleResults(){let e=this.currentQuery;return z(e,this.index,8)}renderResults(){let e=this.currentQuery,t=this.getVisibleResults(),n=this.root?.querySelector("#count");if(this.resultsList){if(!t.length){let r=B(e,this.index);r?this.resultsList.innerHTML=`
No sections match "${C(e)}". Did you mean ${C(r)}?
`:this.resultsList.innerHTML=`
No sections match "${C(e)}"
`,n&&(n.textContent="0 results");return}if(n){let r={};for(let s of t){let i=fe(s.type);r[i]=(r[i]||0)+1}let o=Object.entries(r).map(([s,i])=>`${i} ${s.toLowerCase()}${i!==1?"s":""}`).join(", ");n.textContent=o}this.resultsList&&(this.resultsList.innerHTML=t.map((r,o)=>{let s=o===this.selectedIndex,i=Ie(r.bodyText,e),a=Se(r.type),l=fe(r.type),d=r.type==="action",g=r.url===window.location.href.split("#")[0],x=d&&g&&!r.destructive?'\u21B5 to run here':'\u21B5 to go there',f="";return r.type==="structured"&&r.structuredData&&(r.structuredData.answer?f=`
${C(r.structuredData.answer.substring(0,100))}${r.structuredData.answer.length>100?"\u2026":""}
`:r.structuredData.question&&r.structuredData.answer&&(f=`
${C(r.structuredData.question)}: ${C(r.structuredData.answer.substring(0,100))}${r.structuredData.answer.length>100?"\u2026":""}
`)),` - - `}).join(""),this.resultsList.querySelectorAll("button").forEach(r=>{r.addEventListener("mouseenter",()=>{this.selectedIndex=Number(r.getAttribute("data-index"))??0,this.renderResults()}),r.addEventListener("click",o=>{o.preventDefault(),o.stopPropagation();let s=t[Number(r.getAttribute("data-index"))??0];s&&(this.runSelectCallback(s),this.executeAction(s),["section","link","file","media","structured"].includes(s.type)||this.close())}),r.addEventListener("pointerup",o=>{o.preventDefault(),o.stopPropagation();let s=t[Number(r.getAttribute("data-index"))??0];s&&(this.runSelectCallback(s),this.executeAction(s),["section","link","file","media","structured"].includes(s.type)||this.close())})}))}}executeAction(e){switch(e.type){case"action":this.executeActionResult(e);break;case"field":this.focusField(e);break;case"link":case"file":case"media":case"structured":window.location.href=e.url;break;case"section":this.navigateToSection(e);break}}findHeadingElementByText(e){let t=e.trim().toLowerCase();if(!t)return null;let n=document.querySelectorAll("h1, h2, h3, h4, h5, h6");for(let r of Array.from(n))if(r.textContent?.trim().toLowerCase()===t)return r;return null}highlightAndScrollTo(e){this.close(),e.scrollIntoView({behavior:"smooth",block:"center"}),e.style.boxShadow="0 0 0 3px rgba(67, 217, 200, 0.5)",e.style.borderRadius="4px",setTimeout(()=>{e.style.boxShadow=""},2e3)}navigateToSection(e){let t=window.location.href.split("#")[0],n=e.url.split("#")[0],r=t===n;if(e.selector){let o=document.querySelector(e.selector);if(o){this.highlightAndScrollTo(o);return}}if(r){let o=this.findHeadingElementByText(e.headingText);if(o){this.highlightAndScrollTo(o);return}}this.setupDeferredScroll(e),this.close(),window.location.href=e.url}setupDeferredScroll(e){let t={headingText:e.headingText,selector:e.selector};try{sessionStorage.setItem("reef-deferred-scroll",JSON.stringify(t))}catch(n){console.error("[reef] Failed to store deferred scroll target:",n)}}handleDeferredScroll(){let e=sessionStorage.getItem("reef-deferred-scroll");if(!e)return;sessionStorage.removeItem("reef-deferred-scroll");let t=null;try{t=JSON.parse(e)}catch(i){console.error("[reef] Failed to parse deferred scroll target:",i);return}if(!t||!t.headingText)return;let n=t,r=()=>{if(n.selector){let i=document.querySelector(n.selector);if(i)return i}return this.findHeadingElementByText(n.headingText)},o=()=>{let i=r();return i?(i.scrollIntoView({behavior:"smooth",block:"center"}),i.style.boxShadow="0 0 0 3px rgba(67, 217, 200, 0.5)",i.style.borderRadius="4px",setTimeout(()=>{i.style.boxShadow=""},2e3),!0):!1},s=()=>{if(o())return;let i=new MutationObserver(()=>{o()&&i.disconnect()});i.observe(document.body,{childList:!0,subtree:!0}),setTimeout(()=>i.disconnect(),5e3)};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",s,{once:!0}):s()}executeActionResult(e){if(e.destructive&&this.config.actionsMode!=="execute"){this.highlightAndNavigate(e);return}let t=window.location.href.split("#")[0],n=e.url.split("#")[0];t===n?this.executeActionOnCurrentPage(e):this.setupDeferredAction(e)}executeActionOnCurrentPage(e){if(!e.selector){e.type==="field"&&this.focusField(e);return}try{let t=document.querySelector(e.selector);if(!t){this.showToast("Could not find that element on the page. It may have changed.");return}if(e.type==="action"){let n=new MouseEvent("click",{bubbles:!0,cancelable:!0,view:window});t.dispatchEvent(n)}else e.type==="field"&&t.focus()}catch(t){console.error("Failed to execute action:",t),this.showToast("Could not interact with that element. It may have changed or be unavailable.")}}focusField(e){if(e.selector)try{let t=document.querySelector(e.selector);t&&(t.focus(),(t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement)&&t.select())}catch(t){console.error("Failed to focus field:",t)}}showToast(e){let t=document.createElement("div");t.textContent=e,t.style.position="fixed",t.style.bottom="20px",t.style.right="20px",t.style.background="#333",t.style.color="#fff",t.style.padding="10px 20px",t.style.borderRadius="4px",t.style.zIndex="9999",t.style.opacity="0",t.style.transition="opacity 0.3s",document.body.appendChild(t),t.offsetWidth,t.style.opacity="1",setTimeout(()=>{t.style.opacity="0",setTimeout(()=>{document.body.removeChild(t)},300)},2e3)}handleDeferredActions(){let e=sessionStorage.getItem("reef-deferred-action");if(e)try{let t=JSON.parse(e);if(sessionStorage.removeItem("reef-deferred-action"),t.selector){let n=document.querySelector(t.selector);if(n)if(t.type==="action"&&!t.destructive){let r=new MouseEvent("click",{bubbles:!0,cancelable:!0,view:window});n.dispatchEvent(r)}else t.type==="field"&&n.focus()}}catch(t){console.error("Failed to handle deferred action:",t),sessionStorage.removeItem("reef-deferred-action")}}setupDeferredAction(e){let t={selector:e.selector,type:e.type,label:e.label,destructive:e.destructive};sessionStorage.setItem("reef-deferred-action",JSON.stringify(t)),window.location.href=e.url}highlightAndNavigate(e){if(window.location.href=e.url,e.selector){let t=e.selector,n=new MutationObserver(()=>{let r=document.querySelector(t);r&&(r.scrollIntoView({behavior:"smooth",block:"center"}),r.style.boxShadow="0 0 0 3px rgba(108, 140, 255, 0.5)",r.style.borderRadius="4px",n.disconnect())});n.observe(document.body,{childList:!0,subtree:!0}),setTimeout(()=>n.disconnect(),5e3)}}setConfig(e){Object.assign(this.config,e),this.isOpen&&this.applyConfigToUI()}setHeadless(e){this.config.headless=e,e&&(this.unregisterHotkey(),this.host&&(this.host.remove(),this.host=null,this.root=null))}setColorScheme(e){this.config.primaryColor=e.primary,this.config.secondaryColor=e.secondary,this.config.backgroundColor=e.background,this.config.textColor=e.text,this.config.borderColor=e.border,this.config.radius=e.radius,this.isOpen&&this.applyConfigToUI()}setTheme(e){this.config.theme=e,this.isOpen&&this.applyConfigToUI()}setFontFamily(e){this.config.fontFamily=e,this.isOpen&&this.applyConfigToUI()}setMode(e){this.config.mode=e,this.isOpen&&this.applyConfigToUI()}setHotkey(e){this.config.hotkey=e,this.registerHotkey()}setPlaceholder(e){this.config.placeholder=e,this.input&&(this.input.placeholder=e)}onselect(e){this.selectCallback=e}offselect(){this.selectCallback=null}reindex(){this.index=q(),this.boot()}rebuildIndex(){return new Promise(e=>{this.index=q();let t=this.config.onReady;this.config.onReady=({index:n})=>{t?.({index:n}),e()},this.boot()})}getIndex(){return j(this.index)}addCustomRecords(e){H(this.index,e)}clearCustomRecords(){this.index=q(),this.boot()}openWithQuery(e){if(this.config.headless){console.warn("[reef] openWithQuery not available in headless mode. Use search() instead.");return}this.root||this.renderUI(),this.currentQuery=e,this.isOpen=!0,this.selectedIndex=0,this.host?.classList.remove("is-hidden"),this.host?.classList.add("open"),this.input&&(this.input.value=e),this.input?.focus(),this.renderResults()}getHotkey(){return this.config.hotkey||"ctrlk,cmdk"}isOpenState(){return this.isOpen}getConfig(){return{...this.config}}search(e,t=8){return z(e,this.index,t)}searchSections(e,t){return z(e,this.index,t??8)}suggest(e,t=10){return V(e,this.index,t)}facets(){return J(this.index)}trackQuery(e){_(this.index,e)}getPopularQueries(e=10){return G(this.index,e)}setOnReady(e){if(this.config.onReady=e,this.index.allSections.length>0)try{e({index:this.getIndex()})}catch(t){console.error("[reef] onReady callback error:",t)}}getSitemapUrls(){return this.fetchSitemapUrls(this.getSitemapCandidates())}fetchSitemapUrls(e){return new Promise(async t=>{for(let n of e)try{let r=await fetch(n);if(!r.ok)continue;let o=await r.text(),s=[],i=/(.*?)<\/loc>/g,a;for(;(a=i.exec(o))!==null;)s.push(a[1].trim());if(s.length){t(s);return}}catch{continue}t([])})}runSelectCallback(e){if(this.selectCallback)try{this.selectCallback(e)}catch(t){console.error("[reef] select callback error:",t)}}};var Te=new P;window.Reef=Te;})(); diff --git a/reef-extension-chrome/manifest.json b/reef-extension-chrome/manifest.json deleted file mode 100644 index 95cf8ec..0000000 --- a/reef-extension-chrome/manifest.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "manifest_version": 3, - "name": "Reef for Browsers", - "version": "0.1.0", - "default_locale": "en", - "description": "__MSG_appDescription__", - "icons": { - "16": "icons/plugin_logo.png", - "32": "icons/plugin_logo.png", - "48": "icons/plugin_logo.png", - "128": "icons/plugin_logo.png" - }, - "action": { - "default_popup": "src/popup/popup.html", - "default_title": "__MSG_appName__", - "default_icon": { - "16": "icons/plugin_logo.png", - "32": "icons/plugin_logo.png", - "48": "icons/plugin_logo.png", - "128": "icons/plugin_logo.png" - } - }, - "options_ui": { - "page": "src/options/options.html", - "open_in_tab": true - }, - "background": { - "service_worker": "background.js", - "type": "module" - }, - "content_scripts": [ - { - "matches": [""], - "js": ["lib/reef.min.js", "content.js"], - "run_at": "document_idle" - } - ], - "permissions": [ - "activeTab", - "scripting", - "storage", - "tabs", - "contextMenus", - "unlimitedStorage" - ], - "host_permissions": [ - "" - ], - "omnibox": { - "keyword": "reef" - }, - "commands": { - "_execute_action": { - "suggested_key": { - "default": "Ctrl+Shift+R", - "mac": "MacCtrl+Shift+R" - }, - "description": "Open Reef for Browsers popup" - }, - "open-spotlight": { - "suggested_key": { - "default": "Ctrl+Shift+L", - "mac": "MacCtrl+Shift+L" - }, - "description": "Open Reef Spotlight (search across all tabs)" - } - } -} diff --git a/reef-extension-chrome/src/options/options.css b/reef-extension-chrome/src/options/options.css deleted file mode 100644 index 5fab4a9..0000000 --- a/reef-extension-chrome/src/options/options.css +++ /dev/null @@ -1,512 +0,0 @@ -/* ─── RESET & VARIABLES ─── */ -:root { - --black: #000000; - --white: #ffffff; - --gray-50: #fafafa; - --gray-100: #f5f5f5; - --gray-200: #e5e5e5; - --gray-300: #d4d4d4; - --gray-400: #a3a3a3; - --gray-500: #737373; - --gray-600: #525252; - --gray-700: #404040; - --gray-800: #262626; - --gray-900: #171717; - --sidebar-w: 220px; - - /* Semantic tokens — light (default) */ - --bg: var(--white); - --fg: var(--black); - --bg-subtle: var(--gray-50); - --bg-muted: var(--gray-100); - --border: var(--gray-200); - --border-strong: var(--gray-300); - --text-muted: var(--gray-500); - --sidebar-bg: var(--black); - --sidebar-fg: var(--white); - --sidebar-border: var(--gray-800); - --nav-color: var(--gray-400); - --nav-hover-bg: var(--gray-800); - --nav-active-bg: var(--white); - --nav-active-fg: var(--black); - --input-bg: var(--white); - --input-fg: var(--black); - --input-border: var(--gray-300); - --seg-bg: var(--white); - --seg-fg: var(--black); - --seg-active-bg: var(--black); - --seg-active-fg: var(--white); - --seg-border: var(--black); -} - -/* ─── DARK THEME ─── */ -[data-theme="dark"] { - --bg: var(--gray-900); - --fg: var(--white); - --bg-subtle: var(--gray-800); - --bg-muted: var(--gray-800); - --border: var(--gray-700); - --border-strong: var(--gray-600); - --text-muted: var(--gray-400); - --sidebar-bg: var(--black); - --sidebar-fg: var(--white); - --sidebar-border: var(--gray-800); - --nav-color: var(--gray-400); - --nav-hover-bg: var(--gray-800); - --nav-active-bg: var(--gray-700); - --nav-active-fg: var(--white); - --input-bg: var(--gray-800); - --input-fg: var(--white); - --input-border: var(--gray-600); - --seg-bg: var(--gray-800); - --seg-fg: var(--white); - --seg-active-bg: var(--white); - --seg-active-fg: var(--black); - --seg-border: var(--white); -} - -/* ─── SYSTEM THEME ─── */ -@media (prefers-color-scheme: dark) { - [data-theme="system"] { - --bg: var(--gray-900); - --fg: var(--white); - --bg-subtle: var(--gray-800); - --bg-muted: var(--gray-800); - --border: var(--gray-700); - --border-strong: var(--gray-600); - --text-muted: var(--gray-400); - --nav-active-bg: var(--gray-700); - --nav-active-fg: var(--white); - --input-bg: var(--gray-800); - --input-fg: var(--white); - --input-border: var(--gray-600); - --seg-bg: var(--gray-800); - --seg-fg: var(--white); - --seg-active-bg: var(--white); - --seg-active-fg: var(--black); - --seg-border: var(--white); - } -} - -*, *::before, *::after { - box-sizing: border-box; - margin: 0; - padding: 0; -} - -body { - background: var(--bg); - color: var(--fg); - font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; - font-size: 14px; - line-height: 1.5; - -webkit-font-smoothing: antialiased; -} - -/* ─── LAYOUT ─── */ -.layout { - display: flex; - min-height: 100vh; -} - -/* ─── SIDEBAR ─── */ -.sidebar { - width: var(--sidebar-w); - background: var(--sidebar-bg); - color: var(--sidebar-fg); - padding: 24px 0; - position: fixed; - top: 0; - left: 0; - bottom: 0; - display: flex; - flex-direction: column; - border-right: 1px solid var(--sidebar-border); -} - -.sidebar-brand { - display: flex; - align-items: center; - gap: 10px; - padding: 0 20px 24px 20px; - border-bottom: 1px solid var(--sidebar-border); - margin-bottom: 16px; -} - -.sidebar-logo { - width: 20px; - height: 20px; - object-fit: contain; -} - -.sidebar-title { - font-size: 15px; - font-weight: 600; - letter-spacing: -0.01em; -} - -.sidebar-nav { - display: flex; - flex-direction: column; - gap: 2px; - padding: 0 8px; -} - -.nav-item { - display: block; - padding: 8px 12px; - color: var(--nav-color); - text-decoration: none; - font-size: 13px; - font-weight: 500; - border: 1px solid transparent; - transition: background 0.1s, color 0.1s; -} - -.nav-item:hover { - background: var(--nav-hover-bg); - color: var(--sidebar-fg); -} - -.nav-item.active { - background: var(--nav-active-bg); - color: var(--nav-active-fg); - border-color: var(--nav-active-bg); -} - -/* ─── MAIN ─── */ -.main { - flex: 1; - margin-left: var(--sidebar-w); - padding: 0 48px 64px 48px; - max-width: 800px; -} - -.main-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 24px 0; - border-bottom: 2px solid var(--fg); - margin-bottom: 0; -} - -.page-title { - font-size: 20px; - font-weight: 700; - letter-spacing: -0.02em; -} - -.header-actions { - display: flex; - gap: 8px; -} - -/* ─── SAVE STATUS ─── */ -.save-status { - padding: 10px 16px; - background: var(--fg); - color: var(--bg); - font-size: 13px; - font-weight: 500; - margin-top: 16px; -} - -.save-status.hidden { - display: none; -} - -/* ─── SECTIONS ─── */ -.settings-section { - display: none; -} - -.settings-section.visible { - display: block; -} - -.section-header { - padding: 24px 0 16px 0; - border-bottom: 1px solid var(--border); - margin-bottom: 0; -} - -.section-header h2 { - font-size: 16px; - font-weight: 600; - margin-bottom: 4px; -} - -.section-desc { - font-size: 13px; - color: var(--text-muted); -} - -/* ─── SETTING GROUP ─── */ -.setting-group { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 32px; - padding: 20px 0; - border-bottom: 1px solid var(--bg-muted); -} - -.setting-group:last-child { - border-bottom: none; -} - -.setting-label { - flex: 1; - min-width: 0; -} - -.setting-name { - display: block; - font-size: 14px; - font-weight: 600; - margin-bottom: 4px; -} - -.setting-hint { - display: block; - font-size: 12px; - color: var(--text-muted); - line-height: 1.5; -} - -.setting-control { - flex-shrink: 0; - display: flex; - align-items: center; -} - -/* ─── BUTTONS ─── */ -.btn { - display: inline-flex; - align-items: center; - justify-content: center; - padding: 7px 14px; - font-size: 13px; - font-weight: 500; - border: 1px solid var(--fg); - cursor: pointer; - transition: background 0.1s, color 0.1s; - font-family: inherit; -} - -.btn-primary { - background: var(--fg); - color: var(--bg); -} - -.btn-primary:hover { - opacity: 0.85; -} - -.btn-outline { - background: var(--bg); - color: var(--fg); - border-color: var(--border-strong); -} - -.btn-outline:hover { - background: var(--bg-subtle); - border-color: var(--fg); -} - -.btn-ghost { - background: transparent; - color: var(--text-muted); - border-color: transparent; -} - -.btn-ghost:hover { - background: var(--bg-muted); - color: var(--fg); -} - -.btn-danger { - background: var(--bg); - color: var(--fg); - border-color: var(--fg); -} - -.btn-danger:hover { - background: var(--fg); - color: var(--bg); -} - -/* ─── SEGMENTED CONTROL ─── */ -.segmented-control { - display: inline-flex; - border: 1px solid var(--seg-border); -} - -.seg-btn { - padding: 6px 14px; - font-size: 12px; - font-weight: 500; - font-family: inherit; - background: var(--seg-bg); - color: var(--seg-fg); - border: none; - border-right: 1px solid var(--seg-border); - cursor: pointer; - transition: background 0.1s, color 0.1s; -} - -.seg-btn:last-child { - border-right: none; -} - -.seg-btn:hover { - opacity: 0.8; -} - -.seg-btn.active { - background: var(--seg-active-bg); - color: var(--seg-active-fg); -} - -/* ─── TOGGLE SWITCH ─── */ -.toggle { - display: inline-flex; - align-items: center; - cursor: pointer; -} - -.toggle input { - position: absolute; - opacity: 0; - width: 0; - height: 0; -} - -.toggle-track { - display: block; - width: 36px; - height: 20px; - background: var(--border); - border: 1px solid var(--border-strong); - position: relative; - transition: background 0.15s; -} - -.toggle-thumb { - display: block; - width: 14px; - height: 14px; - background: var(--bg); - border: 1px solid var(--border-strong); - position: absolute; - top: 2px; - left: 2px; - transition: transform 0.15s, background 0.15s; -} - -.toggle input:checked + .toggle-track { - background: var(--fg); - border-color: var(--fg); -} - -.toggle input:checked + .toggle-track .toggle-thumb { - transform: translateX(16px); - background: var(--bg); - border-color: var(--fg); -} - -/* ─── TEXTAREA ─── */ -textarea { - width: 280px; - padding: 8px 10px; - background: var(--input-bg); - border: 1px solid var(--input-border); - color: var(--input-fg); - font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace; - font-size: 12px; - line-height: 1.5; - resize: vertical; - transition: border-color 0.1s; -} - -textarea:focus { - outline: none; - border-color: var(--fg); -} - -textarea::placeholder { - color: var(--text-muted); -} - -/* ─── NUMBER INPUT ─── */ -.number-input input { - width: 80px; - padding: 6px 10px; - border: 1px solid var(--input-border); - background: var(--input-bg); - color: var(--input-fg); - font-family: inherit; - font-size: 13px; - text-align: center; -} - -.number-input input:focus { - outline: none; - border-color: var(--fg); -} - -/* ─── ABOUT GRID ─── */ -.about-grid { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 0; - margin-top: 16px; - border: 1px solid var(--border); -} - -.about-item { - display: flex; - flex-direction: column; - padding: 14px 16px; - border-bottom: 1px solid var(--border); -} - -.about-item:nth-child(odd) { - border-right: 1px solid var(--border); -} - -.about-item:nth-last-child(-n+2) { - border-bottom: none; -} - -.about-key { - font-size: 11px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.05em; - color: var(--text-muted); - margin-bottom: 4px; -} - -.about-value { - font-size: 14px; - font-weight: 500; -} - -/* ─── SCROLLBAR ─── */ -::-webkit-scrollbar { - width: 6px; -} - -::-webkit-scrollbar-track { - background: transparent; -} - -::-webkit-scrollbar-thumb { - background: var(--gray-300); -} - -::-webkit-scrollbar-thumb:hover { - background: var(--gray-500); -} diff --git a/reef-extension-chrome/src/options/options.html b/reef-extension-chrome/src/options/options.html deleted file mode 100644 index 85662c7..0000000 --- a/reef-extension-chrome/src/options/options.html +++ /dev/null @@ -1,307 +0,0 @@ - - - - - - Reef — Settings - - - -
- - -
-
-

Settings

-
- - - -
-
- - - - -
-
-

Execution

-

Control how Reef interacts with page elements.

-
- -
-
- Action mode - Determines whether Reef can execute clicks and form submissions or only navigate to links. -
-
-
- - -
-
-
- -
-
- Destructive action confirmation - Require explicit confirmation before executing destructive actions (delete, remove, checkout, etc). -
-
- -
-
- -
-
- Auto-execute on selection - Automatically execute an action when selected from search results, instead of requiring a second click. -
-
- -
-
-
- - -
-
-

Privacy & Security

-

Control what Reef can access and index.

-
- -
-
- Exclusion selectors - CSS selectors for elements Reef must never index or interact with. Sensitive inputs (passwords, payment, SSN) are always excluded. -
-
- -
-
- -
-
- Blocked domains - Domains where Reef will not activate. One per line. -
-
- -
-
- -
-
- Anonymous telemetry - Send anonymous usage data to help improve Reef. No page content ever leaves your browser. -
-
- -
-
-
- - - - - -
-
-

Appearance

-

Customize the look and feel of the extension UI.

-
- -
-
- Theme - Choose a color theme for the popup and settings pages. System will follow your OS preference. -
-
-
- - - -
-
-
- -
-
- Compact mode - Reduce spacing and font sizes for a denser information layout. -
-
- -
-
-
- - -
-
-

Data Management

-

Manage cached indices and stored data.

-
- -
-
- Cached indices - Clear all cached page indices. Pages will be re-indexed on next visit. -
-
- -
-
- -
-
- Query history - Clear all tracked search queries and popularity data. -
-
- -
-
- -
-
- Reset all settings - Restore all settings to their default values. This cannot be undone. -
-
- -
-
-
- - -
-
-

About

-

Reef for Browsers — client-side search and browser agent layer.

-
- -
-
- Version - 0.1.0 -
-
- Manifest - V3 -
-
- Runtime - Service Worker -
-
- Spotlight shortcut - Ctrl+Shift+L / Cmd+Shift+L -
-
- Popup shortcut - Ctrl+Shift+R / Cmd+Shift+R -
-
-
-
-
- - - - diff --git a/reef-extension-chrome/src/options/options.js b/reef-extension-chrome/src/options/options.js deleted file mode 100644 index a375de6..0000000 --- a/reef-extension-chrome/src/options/options.js +++ /dev/null @@ -1,192 +0,0 @@ -// src/options/options.ts -var DEFAULTS = { - actionsMode: "execute", - confirmDestructive: true, - autoExecute: false, - exclusionSelectors: [], - blockedDomains: [], - telemetryEnabled: false, - enableCrossTabCrawl: false, - pageSize: 20, - scoringAlgorithm: "default", - fuzzyEnabled: true, - diversifyResults: false, - theme: "light", - compactMode: false -}; -document.addEventListener("DOMContentLoaded", async () => { - const navItems = document.querySelectorAll(".nav-item"); - const sections = document.querySelectorAll(".settings-section"); - navItems.forEach((item) => { - item.addEventListener("click", (e) => { - e.preventDefault(); - const target = item.dataset.section; - if (!target) return; - navItems.forEach((n) => n.classList.remove("active")); - item.classList.add("active"); - sections.forEach((s) => s.classList.remove("visible")); - const section = document.getElementById(`section-${target}`); - if (section) section.classList.add("visible"); - }); - }); - const actionsModeControl = document.getElementById("actions-mode-control"); - const confirmDestructive = document.getElementById("confirm-destructive"); - const autoExecute = document.getElementById("auto-execute"); - const exclusionSelectorsInput = document.getElementById("exclusion-selectors"); - const blockedDomainsInput = document.getElementById("blocked-domains"); - const telemetryEnabled = document.getElementById("telemetry-enabled"); - const enableCrossTabCrawl = document.getElementById("enable-cross-tab-crawl"); - const pageSizeInput = document.getElementById("page-size"); - const scoringControl = document.getElementById("scoring-control"); - const themeControl = document.getElementById("theme-control"); - const compactMode = document.getElementById("compact-mode"); - const fuzzyEnabled = document.getElementById("fuzzy-enabled"); - const diversifyResults = document.getElementById("diversify-results"); - const saveBtn = document.getElementById("btn-save"); - const exportBtn = document.getElementById("btn-export"); - const importBtn = document.getElementById("btn-import"); - const clearCacheBtn = document.getElementById("btn-clear-cache"); - const clearHistoryBtn = document.getElementById("btn-clear-history"); - const resetBtn = document.getElementById("btn-reset"); - const saveStatus = document.getElementById("save-status"); - function setupSegmented(control, callback) { - const buttons = control.querySelectorAll(".seg-btn"); - buttons.forEach((btn) => { - btn.addEventListener("click", () => { - buttons.forEach((b) => b.classList.remove("active")); - btn.classList.add("active"); - callback(btn.dataset.value || ""); - }); - }); - } - let selectedActionsMode = DEFAULTS.actionsMode; - let selectedScoring = DEFAULTS.scoringAlgorithm; - let selectedTheme = DEFAULTS.theme; - setupSegmented(actionsModeControl, (value) => { - selectedActionsMode = value; - }); - setupSegmented(scoringControl, (value) => { - selectedScoring = value; - }); - setupSegmented(themeControl, (value) => { - selectedTheme = value; - applyTheme(selectedTheme); - }); - function applyTheme(theme) { - document.body.setAttribute("data-theme", theme); - } - if (typeof chrome !== "undefined" && chrome.storage?.local) { - const data = await chrome.storage.local.get(Object.keys(DEFAULTS)); - selectedActionsMode = data.actionsMode || DEFAULTS.actionsMode; - actionsModeControl.querySelectorAll(".seg-btn").forEach((btn) => { - btn.classList.toggle("active", btn.dataset.value === selectedActionsMode); - }); - confirmDestructive.checked = data.confirmDestructive ?? DEFAULTS.confirmDestructive; - autoExecute.checked = data.autoExecute ?? DEFAULTS.autoExecute; - exclusionSelectorsInput.value = (data.exclusionSelectors || DEFAULTS.exclusionSelectors).join(", "); - blockedDomainsInput.value = (data.blockedDomains || DEFAULTS.blockedDomains).join("\n"); - telemetryEnabled.checked = data.telemetryEnabled ?? DEFAULTS.telemetryEnabled; - enableCrossTabCrawl.checked = data.enableCrossTabCrawl ?? DEFAULTS.enableCrossTabCrawl; - pageSizeInput.value = String(data.pageSize ?? DEFAULTS.pageSize); - selectedScoring = data.scoringAlgorithm || DEFAULTS.scoringAlgorithm; - scoringControl.querySelectorAll(".seg-btn").forEach((btn) => { - btn.classList.toggle("active", btn.dataset.value === selectedScoring); - }); - fuzzyEnabled.checked = data.fuzzyEnabled ?? DEFAULTS.fuzzyEnabled; - diversifyResults.checked = data.diversifyResults ?? DEFAULTS.diversifyResults; - selectedTheme = data.theme || DEFAULTS.theme; - themeControl.querySelectorAll(".seg-btn").forEach((btn) => { - btn.classList.toggle("active", btn.dataset.value === selectedTheme); - }); - applyTheme(selectedTheme); - compactMode.checked = data.compactMode ?? DEFAULTS.compactMode; - } - async function saveSettings() { - const settings = { - actionsMode: selectedActionsMode, - confirmDestructive: confirmDestructive.checked, - autoExecute: autoExecute.checked, - exclusionSelectors: exclusionSelectorsInput.value.split(",").map((s) => s.trim()).filter(Boolean), - blockedDomains: blockedDomainsInput.value.split("\n").map((s) => s.trim()).filter(Boolean), - telemetryEnabled: telemetryEnabled.checked, - enableCrossTabCrawl: enableCrossTabCrawl.checked, - pageSize: parseInt(pageSizeInput.value, 10) || DEFAULTS.pageSize, - scoringAlgorithm: selectedScoring, - fuzzyEnabled: fuzzyEnabled.checked, - diversifyResults: diversifyResults.checked, - theme: selectedTheme, - compactMode: compactMode.checked - }; - if (typeof chrome !== "undefined" && chrome.storage?.local) { - await chrome.storage.local.set(settings); - showSaveStatus("Settings saved"); - } - } - saveBtn.addEventListener("click", saveSettings); - document.addEventListener("keydown", (e) => { - if ((e.ctrlKey || e.metaKey) && e.key === "s") { - e.preventDefault(); - saveSettings(); - } - }); - exportBtn.addEventListener("click", async () => { - if (typeof chrome === "undefined" || !chrome.storage?.local) return; - const data = await chrome.storage.local.get(null); - const blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json" }); - const url = URL.createObjectURL(blob); - const a = document.createElement("a"); - a.href = url; - a.download = "reef-settings.json"; - a.click(); - URL.revokeObjectURL(url); - showSaveStatus("Settings exported"); - }); - importBtn.addEventListener("click", () => { - const input = document.createElement("input"); - input.type = "file"; - input.accept = ".json"; - input.addEventListener("change", async (e) => { - const file = e.target.files?.[0]; - if (!file) return; - try { - const text = await file.text(); - const data = JSON.parse(text); - if (typeof chrome !== "undefined" && chrome.storage?.local) { - await chrome.storage.local.set(data); - showSaveStatus("Settings imported \u2014 reload page to apply"); - setTimeout(() => location.reload(), 1500); - } - } catch { - showSaveStatus("Import failed: invalid file"); - } - }); - input.click(); - }); - clearCacheBtn.addEventListener("click", async () => { - if (typeof chrome !== "undefined" && chrome.storage?.local) { - await chrome.storage.local.remove(["cachedIndices", "siteIndices"]); - showSaveStatus("Cache cleared"); - } - }); - clearHistoryBtn.addEventListener("click", async () => { - if (typeof chrome !== "undefined" && chrome.storage?.local) { - await chrome.storage.local.remove(["queryHistory", "popularQueries"]); - showSaveStatus("History cleared"); - } - }); - resetBtn.addEventListener("click", async () => { - if (!confirm("Reset all settings to defaults? This cannot be undone.")) return; - if (typeof chrome !== "undefined" && chrome.storage?.local) { - await chrome.storage.local.clear(); - await chrome.storage.local.set(DEFAULTS); - showSaveStatus("Settings reset to defaults"); - setTimeout(() => location.reload(), 1e3); - } - }); - function showSaveStatus(msg) { - saveStatus.textContent = msg; - saveStatus.classList.remove("hidden"); - setTimeout(() => saveStatus.classList.add("hidden"), 2500); - } -}); -//# sourceMappingURL=options.js.map diff --git a/reef-extension-chrome/src/options/options.js.map b/reef-extension-chrome/src/options/options.js.map deleted file mode 100644 index 571e5e7..0000000 --- a/reef-extension-chrome/src/options/options.js.map +++ /dev/null @@ -1,7 +0,0 @@ -{ - "version": 3, - "sources": ["../../../src/options/options.ts"], - "sourcesContent": ["/**\n * @file Options page script for Reef for Browsers.\n * Manages user preferences in chrome.storage.local.\n */\n\nconst DEFAULTS = {\n actionsMode: 'execute',\n confirmDestructive: true,\n autoExecute: false,\n exclusionSelectors: [] as string[],\n blockedDomains: [] as string[],\n telemetryEnabled: false,\n enableCrossTabCrawl: false,\n pageSize: 20,\n scoringAlgorithm: 'default',\n fuzzyEnabled: true,\n diversifyResults: false,\n theme: 'light' as 'light' | 'dark' | 'system',\n compactMode: false,\n};\n\ndocument.addEventListener('DOMContentLoaded', async () => {\n // \u2500\u2500\u2500 NAV \u2500\u2500\u2500\n const navItems = document.querySelectorAll('.nav-item');\n const sections = document.querySelectorAll('.settings-section');\n\n navItems.forEach(item => {\n item.addEventListener('click', (e) => {\n e.preventDefault();\n const target = item.dataset.section;\n if (!target) return;\n\n navItems.forEach(n => n.classList.remove('active'));\n item.classList.add('active');\n\n sections.forEach(s => s.classList.remove('visible'));\n const section = document.getElementById(`section-${target}`);\n if (section) section.classList.add('visible');\n });\n });\n\n // \u2500\u2500\u2500 ELEMENTS \u2500\u2500\u2500\n const actionsModeControl = document.getElementById('actions-mode-control');\n const confirmDestructive = document.getElementById('confirm-destructive') as HTMLInputElement;\n const autoExecute = document.getElementById('auto-execute') as HTMLInputElement;\n const exclusionSelectorsInput = document.getElementById('exclusion-selectors') as HTMLTextAreaElement;\n const blockedDomainsInput = document.getElementById('blocked-domains') as HTMLTextAreaElement;\n const telemetryEnabled = document.getElementById('telemetry-enabled') as HTMLInputElement;\n const enableCrossTabCrawl = document.getElementById('enable-cross-tab-crawl') as HTMLInputElement;\n const pageSizeInput = document.getElementById('page-size') as HTMLInputElement;\n const scoringControl = document.getElementById('scoring-control');\n const themeControl = document.getElementById('theme-control');\n const compactMode = document.getElementById('compact-mode') as HTMLInputElement;\n const fuzzyEnabled = document.getElementById('fuzzy-enabled') as HTMLInputElement;\n const diversifyResults = document.getElementById('diversify-results') as HTMLInputElement;\n const saveBtn = document.getElementById('btn-save') as HTMLButtonElement;\n const exportBtn = document.getElementById('btn-export') as HTMLButtonElement;\n const importBtn = document.getElementById('btn-import') as HTMLButtonElement;\n const clearCacheBtn = document.getElementById('btn-clear-cache') as HTMLButtonElement;\n const clearHistoryBtn = document.getElementById('btn-clear-history') as HTMLButtonElement;\n const resetBtn = document.getElementById('btn-reset') as HTMLButtonElement;\n const saveStatus = document.getElementById('save-status') as HTMLDivElement;\n\n // \u2500\u2500\u2500 SEGMENTED CONTROLS \u2500\u2500\u2500\n function setupSegmented(control: HTMLElement, callback: (value: string) => void) {\n const buttons = control.querySelectorAll('.seg-btn');\n buttons.forEach(btn => {\n btn.addEventListener('click', () => {\n buttons.forEach(b => b.classList.remove('active'));\n btn.classList.add('active');\n callback((btn as HTMLElement).dataset.value || '');\n });\n });\n }\n\n let selectedActionsMode = DEFAULTS.actionsMode;\n let selectedScoring = DEFAULTS.scoringAlgorithm;\n let selectedTheme = DEFAULTS.theme;\n\n setupSegmented(actionsModeControl!, (value) => { selectedActionsMode = value; });\n setupSegmented(scoringControl!, (value) => { selectedScoring = value; });\n setupSegmented(themeControl!, (value) => {\n selectedTheme = value as 'light' | 'dark' | 'system';\n applyTheme(selectedTheme);\n });\n\n // \u2500\u2500\u2500 THEME \u2500\u2500\u2500\n function applyTheme(theme: 'light' | 'dark' | 'system') {\n document.body.setAttribute('data-theme', theme);\n }\n\n // \u2500\u2500\u2500 LOAD \u2500\u2500\u2500\n if (typeof chrome !== 'undefined' && chrome.storage?.local) {\n const data = await chrome.storage.local.get(Object.keys(DEFAULTS));\n\n // Actions mode\n selectedActionsMode = data.actionsMode || DEFAULTS.actionsMode;\n actionsModeControl!.querySelectorAll('.seg-btn').forEach(btn => {\n btn.classList.toggle('active', (btn as HTMLElement).dataset.value === selectedActionsMode);\n });\n\n confirmDestructive.checked = data.confirmDestructive ?? DEFAULTS.confirmDestructive;\n autoExecute.checked = data.autoExecute ?? DEFAULTS.autoExecute;\n\n exclusionSelectorsInput.value = ((data.exclusionSelectors as string[]) || DEFAULTS.exclusionSelectors).join(', ');\n blockedDomainsInput.value = ((data.blockedDomains as string[]) || DEFAULTS.blockedDomains).join('\\n');\n\n telemetryEnabled.checked = data.telemetryEnabled ?? DEFAULTS.telemetryEnabled;\n enableCrossTabCrawl.checked = data.enableCrossTabCrawl ?? DEFAULTS.enableCrossTabCrawl;\n\n pageSizeInput.value = String(data.pageSize ?? DEFAULTS.pageSize);\n\n selectedScoring = data.scoringAlgorithm || DEFAULTS.scoringAlgorithm;\n scoringControl!.querySelectorAll('.seg-btn').forEach(btn => {\n btn.classList.toggle('active', (btn as HTMLElement).dataset.value === selectedScoring);\n });\n\n fuzzyEnabled.checked = data.fuzzyEnabled ?? DEFAULTS.fuzzyEnabled;\n diversifyResults.checked = data.diversifyResults ?? DEFAULTS.diversifyResults;\n\n selectedTheme = (data.theme as 'light' | 'dark' | 'system') || DEFAULTS.theme;\n themeControl!.querySelectorAll('.seg-btn').forEach(btn => {\n btn.classList.toggle('active', (btn as HTMLElement).dataset.value === selectedTheme);\n });\n applyTheme(selectedTheme);\n\n compactMode.checked = data.compactMode ?? DEFAULTS.compactMode;\n }\n\n // \u2500\u2500\u2500 SAVE \u2500\u2500\u2500\n async function saveSettings() {\n const settings = {\n actionsMode: selectedActionsMode,\n confirmDestructive: confirmDestructive.checked,\n autoExecute: autoExecute.checked,\n exclusionSelectors: exclusionSelectorsInput.value.split(',').map(s => s.trim()).filter(Boolean),\n blockedDomains: blockedDomainsInput.value.split('\\n').map(s => s.trim()).filter(Boolean),\n telemetryEnabled: telemetryEnabled.checked,\n enableCrossTabCrawl: enableCrossTabCrawl.checked,\n pageSize: parseInt(pageSizeInput.value, 10) || DEFAULTS.pageSize,\n scoringAlgorithm: selectedScoring,\n fuzzyEnabled: fuzzyEnabled.checked,\n diversifyResults: diversifyResults.checked,\n theme: selectedTheme,\n compactMode: compactMode.checked,\n };\n\n if (typeof chrome !== 'undefined' && chrome.storage?.local) {\n await chrome.storage.local.set(settings);\n showSaveStatus('Settings saved');\n }\n }\n\n saveBtn.addEventListener('click', saveSettings);\n\n // Ctrl+S / Cmd+S to save\n document.addEventListener('keydown', (e) => {\n if ((e.ctrlKey || e.metaKey) && e.key === 's') {\n e.preventDefault();\n saveSettings();\n }\n });\n\n // \u2500\u2500\u2500 EXPORT \u2500\u2500\u2500\n exportBtn.addEventListener('click', async () => {\n if (typeof chrome === 'undefined' || !chrome.storage?.local) return;\n const data = await chrome.storage.local.get(null);\n const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = 'reef-settings.json';\n a.click();\n URL.revokeObjectURL(url);\n showSaveStatus('Settings exported');\n });\n\n // \u2500\u2500\u2500 IMPORT \u2500\u2500\u2500\n importBtn.addEventListener('click', () => {\n const input = document.createElement('input');\n input.type = 'file';\n input.accept = '.json';\n input.addEventListener('change', async (e) => {\n const file = (e.target as HTMLInputElement).files?.[0];\n if (!file) return;\n try {\n const text = await file.text();\n const data = JSON.parse(text);\n if (typeof chrome !== 'undefined' && chrome.storage?.local) {\n await chrome.storage.local.set(data);\n showSaveStatus('Settings imported \u2014 reload page to apply');\n setTimeout(() => location.reload(), 1500);\n }\n } catch {\n showSaveStatus('Import failed: invalid file');\n }\n });\n input.click();\n });\n\n // \u2500\u2500\u2500 CLEAR CACHE \u2500\u2500\u2500\n clearCacheBtn.addEventListener('click', async () => {\n if (typeof chrome !== 'undefined' && chrome.storage?.local) {\n await chrome.storage.local.remove(['cachedIndices', 'siteIndices']);\n showSaveStatus('Cache cleared');\n }\n });\n\n // \u2500\u2500\u2500 CLEAR HISTORY \u2500\u2500\u2500\n clearHistoryBtn.addEventListener('click', async () => {\n if (typeof chrome !== 'undefined' && chrome.storage?.local) {\n await chrome.storage.local.remove(['queryHistory', 'popularQueries']);\n showSaveStatus('History cleared');\n }\n });\n\n // \u2500\u2500\u2500 RESET \u2500\u2500\u2500\n resetBtn.addEventListener('click', async () => {\n if (!confirm('Reset all settings to defaults? This cannot be undone.')) return;\n if (typeof chrome !== 'undefined' && chrome.storage?.local) {\n await chrome.storage.local.clear();\n await chrome.storage.local.set(DEFAULTS);\n showSaveStatus('Settings reset to defaults');\n setTimeout(() => location.reload(), 1000);\n }\n });\n\n // \u2500\u2500\u2500 STATUS \u2500\u2500\u2500\n function showSaveStatus(msg: string) {\n saveStatus.textContent = msg;\n saveStatus.classList.remove('hidden');\n setTimeout(() => saveStatus.classList.add('hidden'), 2500);\n }\n});\n"], - "mappings": "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", - "names": [] -} diff --git a/reef-extension-chrome/src/options/reef-logo.png b/reef-extension-chrome/src/options/reef-logo.png deleted file mode 100644 index e3ab4f6..0000000 Binary files a/reef-extension-chrome/src/options/reef-logo.png and /dev/null differ diff --git a/reef-extension-chrome/src/popup/popup.css b/reef-extension-chrome/src/popup/popup.css deleted file mode 100644 index 6655191..0000000 --- a/reef-extension-chrome/src/popup/popup.css +++ /dev/null @@ -1,650 +0,0 @@ -/* ─── RESET & VARIABLES ─── */ -:root { - --black: #000000; - --white: #ffffff; - --gray-50: #fafafa; - --gray-100: #f5f5f5; - --gray-200: #e5e5e5; - --gray-300: #d4d4d4; - --gray-400: #a3a3a3; - --gray-500: #737373; - --gray-600: #525252; - --gray-700: #404040; - --gray-800: #262626; - --gray-900: #171717; - - /* Semantic tokens — light (default) */ - --bg: var(--white); - --fg: var(--black); - --bg-subtle: var(--gray-50); - --bg-muted: var(--gray-100); - --border: var(--gray-200); - --border-strong: var(--gray-300); - --text-muted: var(--gray-500); - --header-bg: var(--black); - --header-fg: var(--white); - --badge-bg: var(--white); - --badge-fg: var(--black); - --badge-border: var(--white); - --card-bg: var(--white); - --card-hover: var(--gray-50); - --card-border: var(--gray-100); - --btn-bg: var(--black); - --btn-fg: var(--white); - --pill-bg: var(--gray-100); - --pill-fg: var(--gray-600); - --pill-border: var(--gray-200); -} - -/* ─── DARK THEME ─── */ -[data-theme="dark"] { - --bg: var(--gray-900); - --fg: var(--white); - --bg-subtle: var(--gray-800); - --bg-muted: var(--gray-800); - --border: var(--gray-700); - --border-strong: var(--gray-600); - --text-muted: var(--gray-400); - --header-bg: var(--black); - --header-fg: var(--white); - --badge-bg: var(--gray-800); - --badge-fg: var(--white); - --badge-border: var(--gray-600); - --card-bg: var(--gray-900); - --card-hover: var(--gray-800); - --card-border: var(--gray-700); - --btn-bg: var(--white); - --btn-fg: var(--black); - --pill-bg: var(--gray-800); - --pill-fg: var(--gray-300); - --pill-border: var(--gray-700); -} - -/* ─── SYSTEM THEME ─── */ -[data-theme="system"] { - --bg: var(--white); - --fg: var(--black); - --bg-subtle: var(--gray-50); - --bg-muted: var(--gray-100); - --border: var(--gray-200); - --border-strong: var(--gray-300); - --text-muted: var(--gray-500); - --header-bg: var(--black); - --header-fg: var(--white); - --badge-bg: var(--white); - --badge-fg: var(--black); - --badge-border: var(--white); - --card-bg: var(--white); - --card-hover: var(--gray-50); - --card-border: var(--gray-100); - --btn-bg: var(--black); - --btn-fg: var(--white); - --pill-bg: var(--gray-100); - --pill-fg: var(--gray-600); - --pill-border: var(--gray-200); -} - -@media (prefers-color-scheme: dark) { - [data-theme="system"] { - --bg: var(--gray-900); - --fg: var(--white); - --bg-subtle: var(--gray-800); - --bg-muted: var(--gray-800); - --border: var(--gray-700); - --border-strong: var(--gray-600); - --text-muted: var(--gray-400); - --badge-bg: var(--gray-800); - --badge-fg: var(--white); - --badge-border: var(--gray-600); - --card-bg: var(--gray-900); - --card-hover: var(--gray-800); - --card-border: var(--gray-700); - --btn-bg: var(--white); - --btn-fg: var(--black); - --pill-bg: var(--gray-800); - --pill-fg: var(--gray-300); - --pill-border: var(--gray-700); - } -} - -* { - box-sizing: border-box; - margin: 0; - padding: 0; - font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; -} - -body { - width: 380px; - height: 520px; - background: var(--bg); - color: var(--fg); - overflow: hidden; - -webkit-font-smoothing: antialiased; -} - -.popup-container { - display: flex; - flex-direction: column; - height: 100%; -} - -/* ─── HEADER ─── */ -.popup-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 14px; - background: var(--header-bg); - color: var(--header-fg); - border-bottom: 1px solid var(--header-bg); -} - -.brand { - display: flex; - align-items: center; - gap: 8px; -} - -.logo { - width: 18px; - height: 18px; - object-fit: contain; -} - -.title { - font-weight: 700; - font-size: 14px; - letter-spacing: -0.01em; -} - -.header-actions { - display: flex; - align-items: center; - gap: 8px; -} - -.badge { - font-size: 10px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.04em; - padding: 3px 8px; - background: var(--badge-bg); - color: var(--badge-fg); - border: 1px solid var(--badge-border); -} - -.badge.authoritative { - background: var(--gray-800); - color: var(--white); - border-color: var(--gray-600); -} - -.icon-btn { - background: none; - border: 1px solid var(--gray-600); - color: var(--white); - cursor: pointer; - font-size: 14px; - width: 26px; - height: 26px; - display: flex; - align-items: center; - justify-content: center; - transition: background 0.1s; -} - -.icon-btn:hover { - background: var(--gray-800); -} - -/* ─── SEARCH ─── */ -.search-box-wrapper { - position: relative; - padding: 10px 14px; - background: var(--bg); - border-bottom: 1px solid var(--border); -} - -#search-input { - width: 100%; - padding: 8px 32px 8px 10px; - border: 1px solid var(--border-strong); - background: var(--bg); - color: var(--fg); - font-size: 13px; - outline: none; - transition: border-color 0.1s; -} - -#search-input:focus { - border-color: var(--fg); -} - -#search-input::placeholder { - color: var(--text-muted); -} - -.clear-btn { - position: absolute; - right: 22px; - top: 50%; - transform: translateY(-50%); - background: none; - border: none; - color: var(--text-muted); - font-size: 16px; - cursor: pointer; - line-height: 1; -} - -.clear-btn:hover { - color: var(--fg); -} - -.clear-btn.hidden { - display: none; -} - -/* ─── FILTER TABS ─── */ -.filter-tabs { - display: flex; - gap: 0; - padding: 0; - border-bottom: 1px solid var(--border); - overflow-x: auto; -} - -.tab-btn { - background: var(--bg); - border: none; - border-bottom: 2px solid transparent; - color: var(--text-muted); - font-size: 11px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.04em; - padding: 8px 12px; - cursor: pointer; - white-space: nowrap; - transition: color 0.1s, border-color 0.1s; -} - -.tab-btn:hover { - color: var(--fg); -} - -.tab-btn.active { - color: var(--fg); - border-bottom-color: var(--fg); -} - -/* ─── RESULTS ─── */ -.results-container { - flex: 1; - overflow-y: auto; - display: flex; - flex-direction: column; -} - -.loading-state, .empty-state { - text-align: center; - color: var(--text-muted); - font-size: 12px; - padding: 40px 0; -} - -.result-card { - background: var(--card-bg); - border-bottom: 1px solid var(--card-border); - padding: 10px 14px; - display: flex; - flex-direction: column; - gap: 4px; - cursor: pointer; - transition: background 0.1s; -} - -.result-card:hover { - background: var(--card-hover); -} - -.result-card:last-child { - border-bottom: none; -} - -.card-top { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; -} - -.card-title { - font-size: 13px; - font-weight: 600; - color: var(--fg); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - flex: 1; -} - -.type-pill { - font-size: 9px; - text-transform: uppercase; - font-weight: 700; - letter-spacing: 0.05em; - padding: 2px 6px; - background: var(--pill-bg); - color: var(--pill-fg); - border: 1px solid var(--pill-border); - flex-shrink: 0; -} - -[data-theme="dark"] .type-pill.action { background: var(--white); color: var(--black); border-color: var(--white); } -[data-theme="dark"] .type-pill.field { background: var(--gray-600); color: var(--white); border-color: var(--gray-600); } -[data-theme="dark"] .type-pill.section { background: var(--gray-700); color: var(--white); border-color: var(--gray-600); } -[data-theme="dark"] .type-pill.link { background: var(--gray-800); color: var(--white); border-color: var(--gray-700); } -[data-theme="dark"] .type-pill.file { background: var(--gray-600); color: var(--white); border-color: var(--gray-600); } - -.type-pill.action { background: var(--black); color: var(--white); border-color: var(--black); } -.type-pill.field { background: var(--gray-700); color: var(--white); border-color: var(--gray-700); } -.type-pill.section { background: var(--gray-200); color: var(--black); border-color: var(--gray-300); } -.type-pill.link { background: var(--gray-100); color: var(--black); border-color: var(--gray-300); } -.type-pill.file { background: var(--gray-800); color: var(--white); border-color: var(--gray-800); } - -.card-snippet { - font-size: 12px; - color: var(--text-muted); - line-height: 1.4; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; -} - -.card-actions { - display: flex; - align-items: center; - justify-content: flex-end; - gap: 6px; - margin-top: 4px; -} - -.run-btn { - font-size: 10px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.04em; - padding: 4px 10px; - border: 1px solid var(--btn-bg); - background: var(--btn-bg); - color: var(--btn-fg); - cursor: pointer; - transition: background 0.1s; -} - -.run-btn:hover { - opacity: 0.85; -} - -.run-btn.destructive { - background: var(--bg); - color: var(--fg); - border-color: var(--fg); -} - -.run-btn.destructive:hover { - background: var(--fg); - color: var(--bg); -} - -/* ─── FOOTER ─── */ -.popup-footer { - padding: 8px 14px; - background: var(--bg-subtle); - border-top: 1px solid var(--border); - display: flex; - align-items: center; - justify-content: space-between; - font-size: 11px; - color: var(--text-muted); -} - -.shortcut-tip { - font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace; - font-size: 10px; - padding: 2px 6px; - background: var(--bg-muted); - border: 1px solid var(--border); - color: var(--text-muted); -} - -/* ─── SCROLLBAR ─── */ -::-webkit-scrollbar { - width: 4px; -} - -::-webkit-scrollbar-track { - background: transparent; -} - -::-webkit-scrollbar-thumb { - background: var(--gray-300); -} - -::-webkit-scrollbar-thumb:hover { - background: var(--gray-500); -} - -/* ─── MODE TABS ─── */ -.mode-tabs { - display: flex; - gap: 0; - background: var(--bg-subtle); - border-bottom: 1px solid var(--border); -} - -.mode-tab { - flex: 1; - background: transparent; - border: none; - border-bottom: 2px solid transparent; - color: var(--text-muted); - font-size: 11px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.04em; - padding: 8px 10px; - cursor: pointer; - transition: color .12s, border-color .12s, background .12s; -} - -.mode-tab:hover { - color: var(--fg); - background: var(--bg-muted); -} - -.mode-tab.active { - color: var(--fg); - border-bottom-color: var(--fg); - background: var(--bg); -} - -.hidden { display: none !important; } - -/* ─── MODE PANELS ─── */ -.mode-panel { - display: flex; - flex-direction: column; - flex: 1; - min-height: 0; -} - -/* ─── MINI BUTTONS ─── */ -.mini-btn { - font-size: 11px; - font-weight: 700; - padding: 2px 7px; - background: var(--bg); - color: var(--fg); - border: 1px solid var(--border-strong); - cursor: pointer; - line-height: 1.4; - transition: background .1s, color .1s; -} -.mini-btn:hover { - background: var(--fg); - color: var(--bg); - border-color: var(--fg); -} -.mini-btn.danger:hover { - background: #b91c1c; - color: #fff; - border-color: #b91c1c; -} - -/* ─── LIBRARY CARDS ─── */ -.library-card { - gap: 6px; -} - -.card-meta { - font-size: 10px; - color: var(--text-muted); - letter-spacing: 0.02em; -} - -.card-url { - font-size: 10px; - color: var(--text-muted); - font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace; - padding: 2px 5px; - background: var(--bg-muted); - border: 1px solid var(--border); - flex-shrink: 0; - max-width: 50%; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.card-pre { - font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace; - font-size: 11px; - line-height: 1.4; - background: var(--bg-muted); - border: 1px solid var(--border); - padding: 6px 8px; - white-space: pre-wrap; - word-break: break-word; - max-height: 120px; - overflow: auto; - color: var(--fg); - margin: 0; -} - -.tag-row { - display: flex; - flex-wrap: wrap; - gap: 3px; - margin-top: 2px; -} - -.tag-chip { - font-size: 10px; - font-weight: 600; - background: var(--bg-muted); - color: var(--text-muted); - border: 1px solid var(--border); - padding: 1px 6px; - letter-spacing: 0.02em; -} - -[data-theme="dark"] .tag-chip { - background: var(--gray-800); - color: var(--gray-300); - border-color: var(--gray-700); -} - -/* ─── STATS FLASH ─── */ -#stats-label.flash { - color: #047857; - font-weight: 700; -} - -[data-theme="dark"] #stats-label.flash { - color: #34d399; -} - -/* ─── AUTOCORRECT BANNER ─── */ -.autocorrect-banner { - padding: 8px 12px; - margin: 0 0 6px; - font-size: 12px; - line-height: 1.4; - color: var(--text-muted); - background: #fefce8; - border: 1px solid #fde68a; - border-radius: 6px; -} -.autocorrect-banner .autocorrect-link { - color: #2563eb; - font-weight: 600; - text-decoration: none; - cursor: pointer; -} -.autocorrect-banner .autocorrect-link:hover { - text-decoration: underline; - color: #1d4ed8; -} - -[data-theme="dark"] .autocorrect-banner { - background: #422006; - border-color: #854d0e; - color: var(--gray-400); -} -[data-theme="dark"] .autocorrect-banner .autocorrect-link { - color: #60a5fa; -} -[data-theme="dark"] .autocorrect-banner .autocorrect-link:hover { - color: #93bbfd; -} - -/* ─── SUGGESTION CHIPS ─── */ -.suggestion-chips { - display: flex; - flex-wrap: wrap; - gap: 4px; - padding: 0 0 8px; -} -.suggestion-chip { - font-size: 11px; - line-height: 1; - padding: 4px 10px; - border-radius: 12px; - border: 1px solid var(--border); - background: var(--bg-subtle); - color: var(--text-muted); - cursor: pointer; - transition: background 0.1s, color 0.1s, border-color 0.1s; - white-space: nowrap; -} -.suggestion-chip:hover { - background: var(--accent, #2563eb); - color: var(--white); - border-color: var(--accent, #2563eb); -} - -[data-theme="dark"] .suggestion-chip { - background: var(--gray-800); - border-color: var(--gray-700); - color: var(--gray-400); -} -[data-theme="dark"] .suggestion-chip:hover { - background: #3b82f6; - color: var(--white); - border-color: #3b82f6; -} diff --git a/reef-extension-chrome/src/popup/popup.html b/reef-extension-chrome/src/popup/popup.html deleted file mode 100644 index 98dcf11..0000000 --- a/reef-extension-chrome/src/popup/popup.html +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - Reef for Browsers - - - - - - - - diff --git a/reef-extension-chrome/src/popup/popup.js b/reef-extension-chrome/src/popup/popup.js deleted file mode 100644 index 854bfc4..0000000 --- a/reef-extension-chrome/src/popup/popup.js +++ /dev/null @@ -1,745 +0,0 @@ -// src/popup/popup.ts -var activeTabId = null; -var currentMode = "page"; -var pageFilter = "all"; -var pageQuery = ""; -var pageResults = []; -var tabQuery = ""; -var tabResults = []; -var libFilter = "bookmarks"; -var libQuery = ""; -var bookmarksCache = []; -var snippetsCache = []; -var notesCache = []; -var recentsCache = []; -document.addEventListener("DOMContentLoaded", async () => { - if (typeof chrome !== "undefined" && chrome.storage?.local) { - const { theme } = await chrome.storage.local.get(["theme"]); - document.body.setAttribute("data-theme", theme || "light"); - } - const searchInput = document.getElementById("search-input"); - const clearBtn = document.getElementById("btn-clear"); - const optionsBtn = document.getElementById("btn-options"); - const filterTabs = document.getElementById("filter-tabs"); - const resultsContainer = document.getElementById("results-container"); - const manifestBadge = document.getElementById("manifest-badge"); - const statsLabel = document.getElementById("stats-label"); - const modeTabs = document.getElementById("mode-tabs"); - const panels = document.querySelectorAll(".mode-panel"); - const tabSearchInput = document.getElementById("tab-search-input"); - const tabSearchClear = document.getElementById("tab-search-clear"); - const tabsContainer = document.getElementById("tabs-container"); - const libSearchInput = document.getElementById("library-search-input"); - const libSearchClear = document.getElementById("library-search-clear"); - const libTabs = document.getElementById("library-tabs"); - const libContainer = document.getElementById("library-container"); - optionsBtn.addEventListener("click", () => { - if (chrome.runtime.openOptionsPage) chrome.runtime.openOptionsPage(); - else window.open(chrome.runtime.getURL("src/options/options.html")); - }); - modeTabs.addEventListener("click", (e) => { - const t = e.target; - if (!t.classList.contains("mode-tab")) return; - const mode = t.dataset.mode; - if (mode === currentMode) return; - currentMode = mode; - modeTabs.querySelectorAll(".mode-tab").forEach((b) => { - const active = b === t; - b.classList.toggle("active", active); - b.setAttribute("aria-selected", active ? "true" : "false"); - }); - panels.forEach((p) => p.classList.toggle("hidden", p.dataset.panel !== mode)); - if (mode === "page") { - renderPageResults(); - } else if (mode === "tabs") { - runTabSearch(); - } else if (mode === "library") { - loadLibrary(); - } - }); - const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); - if (tab?.id) { - activeTabId = tab.id; - await performPageSearch(); - } else { - resultsContainer.innerHTML = '
No active tab accessible.
'; - } - searchInput.addEventListener("input", () => { - pageQuery = searchInput.value; - clearBtn.classList.toggle("hidden", !pageQuery); - performPageSearch(); - }); - clearBtn.addEventListener("click", () => { - searchInput.value = ""; - pageQuery = ""; - clearBtn.classList.add("hidden"); - searchInput.focus(); - performPageSearch(); - }); - filterTabs.addEventListener("click", (e) => { - const target = e.target; - if (target.classList.contains("tab-btn")) { - filterTabs.querySelectorAll(".tab-btn").forEach((btn) => btn.classList.remove("active")); - target.classList.add("active"); - pageFilter = target.dataset.type || "all"; - renderPageResults(); - } - }); - var pageSuggestion = void 0; - var pageAutocorrected = false; - var pageSuggestionsList = []; - async function performPageSearch() { - if (!activeTabId) return; - resultsContainer.innerHTML = '
Searching page...
'; - chrome.runtime.sendMessage( - { type: "SEARCH_CURRENT_TAB", tabId: activeTabId, query: pageQuery }, - (response) => { - if (!response || !response.success) { - resultsContainer.replaceChildren(); - const empty = document.createElement("div"); - empty.className = "empty-state"; - empty.textContent = `Failed to index page. ${response?.error || ""}`; - resultsContainer.appendChild(empty); - return; - } - if (response.manifest?.version) { - manifestBadge.textContent = "Agent-Ready Site"; - manifestBadge.classList.add("authoritative"); - } else { - manifestBadge.textContent = "Dynamic Extract"; - manifestBadge.classList.remove("authoritative"); - } - pageResults = response.results || []; - pageSuggestion = response.suggestion; - pageAutocorrected = !!response.autocorrected; - pageSuggestionsList = response.suggestions || []; - renderPageResults(); - } - ); - } - function renderPageResults() { - if (currentMode !== "page") return; - let filtered2 = pageResults; - if (pageFilter !== "all") filtered2 = pageResults.filter((r) => r.type === pageFilter); - statsLabel.textContent = `${filtered2.length} items found`; - resultsContainer.innerHTML = ""; - if (pageAutocorrected && pageSuggestion) { - const banner = document.createElement("div"); - banner.className = "autocorrect-banner"; - const label = document.createElement("span"); - label.textContent = `Showing fuzzy matches. Did you mean `; - const link = document.createElement("a"); - link.className = "autocorrect-link"; - link.textContent = pageSuggestion; - link.href = "#"; - link.addEventListener("click", (e) => { - e.preventDefault(); - const searchInput = document.getElementById("search-input"); - if (searchInput) { - searchInput.value = pageSuggestion; - pageQuery = pageSuggestion; - performPageSearch(); - } - }); - label.appendChild(link); - const q = document.createTextNode("?"); - label.appendChild(q); - banner.appendChild(label); - resultsContainer.appendChild(banner); - } - if (pageSuggestionsList.length > 0 && !pageAutocorrected) { - const chips = document.createElement("div"); - chips.className = "suggestion-chips"; - for (const s of pageSuggestionsList.slice(0, 5)) { - const chip = document.createElement("button"); - chip.className = "suggestion-chip"; - chip.textContent = s; - chip.addEventListener("click", () => { - const searchInput = document.getElementById("search-input"); - if (searchInput) { - searchInput.value = s; - pageQuery = s; - performPageSearch(); - } - }); - chips.appendChild(chip); - } - resultsContainer.appendChild(chips); - } - if (filtered2.length === 0) { - const empty = document.createElement("div"); - empty.className = "empty-state"; - empty.textContent = "No matching records found."; - resultsContainer.appendChild(empty); - return; - } - filtered2.forEach((record) => { - const card = document.createElement("div"); - card.className = "result-card"; - const topRow = document.createElement("div"); - topRow.className = "card-top"; - const title = document.createElement("span"); - title.className = "card-title"; - title.textContent = record.headingText || record.label || record.url; - const typePill = document.createElement("span"); - typePill.className = `type-pill ${record.type}`; - typePill.textContent = record.type; - topRow.appendChild(title); - topRow.appendChild(typePill); - card.appendChild(topRow); - if (record.bodyText) { - const snippet = document.createElement("div"); - snippet.className = "card-snippet"; - snippet.textContent = record.bodyText; - card.appendChild(snippet); - } - const actionsRow = document.createElement("div"); - actionsRow.className = "card-actions"; - const bookmarkBtn = document.createElement("button"); - bookmarkBtn.className = "mini-btn"; - bookmarkBtn.title = "Bookmark this record"; - bookmarkBtn.textContent = "\u2605"; - bookmarkBtn.addEventListener("click", (ev) => { - ev.stopPropagation(); - bookmarkRecord(record); - }); - actionsRow.appendChild(bookmarkBtn); - const runBtn = document.createElement("button"); - runBtn.className = `run-btn ${record.destructive ? "destructive" : ""}`; - runBtn.textContent = record.type === "action" || record.type === "link" ? record.destructive ? "\u26A0\uFE0F Run (Confirm)" : "Execute" : record.type === "field" ? "Fill Field" : "View"; - runBtn.addEventListener("click", (e) => { - e.stopPropagation(); - executePageAction(record); - }); - actionsRow.appendChild(runBtn); - card.appendChild(actionsRow); - card.addEventListener("click", () => { - if (activeTabId) { - chrome.tabs.sendMessage(activeTabId, { type: "HIGHLIGHT_RECORD", record }); - } - }); - resultsContainer.appendChild(card); - }); - } - function executePageAction(record) { - if (!activeTabId) return; - let valueToType; - if (record.type === "field") { - const input = prompt(`Enter value for field "${record.headingText || record.label || "Input"}":`, record.value || ""); - if (input === null) return; - valueToType = input; - } - chrome.runtime.sendMessage({ - type: "EXECUTE_TAB_ACTION", - tabId: activeTabId, - record, - actionType: record.type === "field" ? "type" : "click", - value: valueToType - }, (res) => { - if (res && res.success) window.close(); - else alert(`Action failed: ${res?.error || "Unknown error"}`); - }); - } - function bookmarkRecord(record) { - chrome.runtime.sendMessage({ - type: "LIBRARY_BOOKMARK_CREATE", - data: { - url: record.url || location.href, - title: record.headingText || record.label || record.url || "Bookmark", - selectedText: record.bodyText, - selector: record.selector, - note: "", - tags: [] - } - }, (res) => { - flashStats(res?.success ? "Bookmarked" : "Bookmark failed"); - }); - } - let tabDebounce = null; - tabSearchInput.addEventListener("input", () => { - tabQuery = tabSearchInput.value; - tabSearchClear.classList.toggle("hidden", !tabQuery); - if (tabDebounce) window.clearTimeout(tabDebounce); - tabDebounce = window.setTimeout(runTabSearch, 150); - }); - tabSearchClear.addEventListener("click", () => { - tabSearchInput.value = ""; - tabQuery = ""; - tabSearchClear.classList.add("hidden"); - tabResults = []; - runTabSearch(); - tabSearchInput.focus(); - }); - function runTabSearch() { - if (currentMode !== "tabs") return; - if (!tabQuery.trim()) { - fetchOpenTabs(); - return; - } - chrome.runtime.sendMessage({ type: "TAB_SEARCH", query: tabQuery, limit: 30 }, (res) => { - if (!res?.success) { - tabsContainer.innerHTML = '
Tab search unavailable.
'; - return; - } - tabResults = res.items || []; - renderTabResults(); - }); - } - function renderTabResults() { - if (currentMode !== "tabs") return; - statsLabel.textContent = `${tabResults.length} tab${tabResults.length === 1 ? "" : "s"}`; - if (tabResults.length === 0) { - tabsContainer.innerHTML = '
No matching tabs.
'; - return; - } - tabsContainer.innerHTML = ""; - tabResults.forEach((item) => { - const card = document.createElement("div"); - card.className = "result-card"; - const topRow = document.createElement("div"); - topRow.className = "card-top"; - const title = document.createElement("span"); - title.className = "card-title"; - title.textContent = item.title || item.url; - const url = document.createElement("span"); - url.className = "card-url"; - url.textContent = shortHost(item.url); - topRow.appendChild(title); - topRow.appendChild(url); - card.appendChild(topRow); - if (item.matchedRecords?.length) { - const snippet = document.createElement("div"); - snippet.className = "card-snippet"; - snippet.textContent = item.matchedRecords.map((r) => r.headingText).filter(Boolean).join(" \xB7 "); - card.appendChild(snippet); - } - const actionsRow = document.createElement("div"); - actionsRow.className = "card-actions"; - const switchBtn = document.createElement("button"); - switchBtn.className = "run-btn"; - switchBtn.textContent = "Switch to tab"; - switchBtn.addEventListener("click", () => { - chrome.runtime.sendMessage({ - type: "TAB_SWITCH", - tabId: item.tabId, - windowId: item.windowId - }); - window.close(); - }); - actionsRow.appendChild(switchBtn); - card.appendChild(actionsRow); - tabsContainer.appendChild(card); - }); - } - async function fetchOpenTabs() { - tabsContainer.innerHTML = '
Loading tabs...
'; - chrome.runtime.sendMessage({ type: "LIST_OPEN_TABS" }, (res) => { - if (!res?.success) { - tabsContainer.innerHTML = '
Tab list unavailable.
'; - return; - } - tabResults = res.items || []; - renderTabResults(); - }); - } - let libDebounce = null; - libSearchInput.addEventListener("input", () => { - libQuery = libSearchInput.value; - libSearchClear.classList.toggle("hidden", !libQuery); - if (libDebounce) window.clearTimeout(libDebounce); - libDebounce = window.setTimeout(renderLibrary, 120); - }); - libSearchClear.addEventListener("click", () => { - libSearchInput.value = ""; - libQuery = ""; - libSearchClear.classList.add("hidden"); - renderLibrary(); - }); - libTabs.addEventListener("click", (e) => { - const t = e.target; - if (!t.classList.contains("tab-btn")) return; - libTabs.querySelectorAll(".tab-btn").forEach((b) => b.classList.remove("active")); - t.classList.add("active"); - libFilter = t.dataset.lib || "bookmarks"; - renderLibrary(); - }); - async function loadLibrary() { - if (currentMode !== "library") return; - libContainer.innerHTML = '
Loading library\u2026
'; - const req = (type) => new Promise((resolve) => { - chrome.runtime.sendMessage({ type }, (res) => resolve(res)); - }); - const [bm, sn, nt, rc] = await Promise.all([ - req("LIBRARY_BOOKMARK_LIST"), - req("LIBRARY_SNIPPET_LIST"), - req("LIBRARY_NOTE_LIST"), - req("LIBRARY_RECENTS_LIST") - ]); - bookmarksCache = bm?.items || []; - snippetsCache = sn?.items || []; - notesCache = nt?.items || []; - recentsCache = rc?.items || []; - renderLibrary(); - } - function renderLibrary() { - if (currentMode !== "library") return; - if (libFilter === "bookmarks") return renderBookmarks(); - if (libFilter === "snippets") return renderSnippets(); - if (libFilter === "notes") return renderNotes(); - if (libFilter === "recents") return renderRecents(); - } - function filtered(items, hayMaker) { - let out = items; - if (libQuery) { - const q = libQuery.toLowerCase(); - out = out.filter((i) => hayMaker(i).toLowerCase().includes(q)); - } - return out; - } - function renderBookmarks() { - const items = filtered(bookmarksCache, (b) => [b.title, b.selectedText, b.note, (b.tags || []).join(" "), b.url].filter(Boolean).join(" ")); - statsLabel.textContent = `${items.length} bookmark${items.length === 1 ? "" : "s"}`; - if (items.length === 0) { - libContainer.innerHTML = '
No bookmarks yet. Right-click selected text or click \u2605 on a search result to save.
'; - return; - } - libContainer.innerHTML = ""; - items.forEach((b) => { - const card = document.createElement("div"); - card.className = "result-card library-card"; - const topRow = document.createElement("div"); - topRow.className = "card-top"; - const title = document.createElement("span"); - title.className = "card-title"; - title.textContent = b.title || b.url; - const pill = document.createElement("span"); - pill.className = "type-pill link"; - pill.textContent = "bookmark"; - topRow.appendChild(title); - topRow.appendChild(pill); - card.appendChild(topRow); - if (b.selectedText) { - const snip = document.createElement("div"); - snip.className = "card-snippet"; - snip.textContent = b.selectedText.slice(0, 220); - card.appendChild(snip); - } - const meta = document.createElement("div"); - meta.className = "card-meta"; - meta.textContent = `${shortHost(b.url)} \xB7 ${formatDate(b.createdAt)}`; - card.appendChild(meta); - if (b.tags?.length) card.appendChild(tagRow(b.tags)); - const actions = document.createElement("div"); - actions.className = "card-actions"; - const openBtn = document.createElement("button"); - openBtn.className = "run-btn"; - openBtn.textContent = "Open"; - openBtn.addEventListener("click", () => { - chrome.tabs.create({ url: b.url }); - window.close(); - }); - actions.appendChild(openBtn); - const editBtn = document.createElement("button"); - editBtn.className = "mini-btn"; - editBtn.textContent = "\u270E"; - editBtn.title = "Edit note & tags"; - editBtn.addEventListener("click", () => editBookmark(b)); - actions.appendChild(editBtn); - const delBtn = document.createElement("button"); - delBtn.className = "mini-btn danger"; - delBtn.textContent = "\xD7"; - delBtn.title = "Delete bookmark"; - delBtn.addEventListener("click", () => deleteBookmark(b.id)); - actions.appendChild(delBtn); - card.appendChild(actions); - libContainer.appendChild(card); - }); - } - function renderSnippets() { - const items = filtered(snippetsCache, (s) => [s.title, s.text, (s.tags || []).join(" "), s.source?.title || ""].filter(Boolean).join(" ")); - statsLabel.textContent = `${items.length} snippet${items.length === 1 ? "" : "s"}`; - if (items.length === 0) { - libContainer.innerHTML = '
No snippets yet. Right-click selected text \u2192 "Save selection as snippet".
'; - return; - } - libContainer.innerHTML = ""; - items.forEach((s) => { - const card = document.createElement("div"); - card.className = "result-card library-card"; - const topRow = document.createElement("div"); - topRow.className = "card-top"; - const title = document.createElement("span"); - title.className = "card-title"; - title.textContent = s.title || s.text.slice(0, 60); - const pill = document.createElement("span"); - pill.className = "type-pill section"; - pill.textContent = "snippet"; - topRow.appendChild(title); - topRow.appendChild(pill); - card.appendChild(topRow); - const body = document.createElement("pre"); - body.className = "card-pre"; - body.textContent = s.text; - card.appendChild(body); - if (s.source) { - const meta = document.createElement("div"); - meta.className = "card-meta"; - meta.textContent = `from ${s.source.title || shortHost(s.source.url)} \xB7 ${formatDate(s.createdAt)}`; - card.appendChild(meta); - } - if (s.tags?.length) card.appendChild(tagRow(s.tags)); - const actions = document.createElement("div"); - actions.className = "card-actions"; - const copyBtn = document.createElement("button"); - copyBtn.className = "run-btn"; - copyBtn.textContent = "Copy"; - copyBtn.addEventListener("click", async () => { - try { - await navigator.clipboard.writeText(s.text); - flashStats("Copied to clipboard"); - } catch { - flashStats("Copy failed"); - } - }); - actions.appendChild(copyBtn); - const editBtn = document.createElement("button"); - editBtn.className = "mini-btn"; - editBtn.textContent = "\u270E"; - editBtn.title = "Edit snippet"; - editBtn.addEventListener("click", () => editSnippet(s)); - actions.appendChild(editBtn); - const delBtn = document.createElement("button"); - delBtn.className = "mini-btn danger"; - delBtn.textContent = "\xD7"; - delBtn.title = "Delete snippet"; - delBtn.addEventListener("click", () => deleteSnippet(s.id)); - actions.appendChild(delBtn); - card.appendChild(actions); - libContainer.appendChild(card); - }); - } - function renderNotes() { - const items = filtered(notesCache, (n) => [n.text, n.title, n.url].filter(Boolean).join(" ")); - statsLabel.textContent = `${items.length} note${items.length === 1 ? "" : "s"}`; - if (items.length === 0) { - libContainer.innerHTML = '
No page notes yet. Right-click the page \u2192 "Add note to this page".
'; - return; - } - libContainer.innerHTML = ""; - items.forEach((n) => { - const card = document.createElement("div"); - card.className = "result-card library-card"; - const topRow = document.createElement("div"); - topRow.className = "card-top"; - const title = document.createElement("span"); - title.className = "card-title"; - title.textContent = n.title || n.url; - const pill = document.createElement("span"); - pill.className = "type-pill field"; - pill.textContent = "note"; - topRow.appendChild(title); - topRow.appendChild(pill); - card.appendChild(topRow); - const body = document.createElement("div"); - body.className = "card-snippet"; - body.style.webkitLineClamp = "6"; - body.textContent = n.text; - card.appendChild(body); - const meta = document.createElement("div"); - meta.className = "card-meta"; - meta.textContent = `${shortHost(n.url)} \xB7 ${formatDate(n.updatedAt)}`; - card.appendChild(meta); - const actions = document.createElement("div"); - actions.className = "card-actions"; - const openBtn = document.createElement("button"); - openBtn.className = "run-btn"; - openBtn.textContent = "Open page"; - openBtn.addEventListener("click", () => { - chrome.tabs.create({ url: n.url }); - window.close(); - }); - actions.appendChild(openBtn); - const editBtn = document.createElement("button"); - editBtn.className = "mini-btn"; - editBtn.textContent = "\u270E"; - editBtn.title = "Edit note"; - editBtn.addEventListener("click", () => editNote(n)); - actions.appendChild(editBtn); - const delBtn = document.createElement("button"); - delBtn.className = "mini-btn danger"; - delBtn.textContent = "\xD7"; - delBtn.title = "Delete note"; - delBtn.addEventListener("click", () => deleteNote(n.url)); - actions.appendChild(delBtn); - card.appendChild(actions); - libContainer.appendChild(card); - }); - } - function renderRecents() { - const items = recentsCache.slice(0, 25); - statsLabel.textContent = `${items.length} recent`; - if (items.length === 0) { - libContainer.innerHTML = '
No recent pages yet. Visit some pages to build up history.
'; - return; - } - libContainer.innerHTML = ""; - items.forEach((p) => { - const card = document.createElement("div"); - card.className = "result-card library-card"; - const topRow = document.createElement("div"); - topRow.className = "card-top"; - const title = document.createElement("span"); - title.className = "card-title"; - title.textContent = p.title || p.url; - const pill = document.createElement("span"); - pill.className = "type-pill file"; - pill.textContent = `${p.recordCount} idx`; - topRow.appendChild(title); - topRow.appendChild(pill); - card.appendChild(topRow); - const meta = document.createElement("div"); - meta.className = "card-meta"; - meta.textContent = `${shortHost(p.url)} \xB7 ${formatDate(p.visitedAt)}`; - card.appendChild(meta); - const actions = document.createElement("div"); - actions.className = "card-actions"; - const openBtn = document.createElement("button"); - openBtn.className = "run-btn"; - openBtn.textContent = "Open"; - openBtn.addEventListener("click", () => { - chrome.tabs.create({ url: p.url }); - window.close(); - }); - actions.appendChild(openBtn); - const bkBtn = document.createElement("button"); - bkBtn.className = "mini-btn"; - bkBtn.textContent = "\u2605"; - bkBtn.title = "Bookmark this page"; - bkBtn.addEventListener("click", () => { - chrome.runtime.sendMessage({ - type: "LIBRARY_BOOKMARK_CREATE", - data: { url: p.url, title: p.title, note: "", tags: [], favicon: p.favicon } - }, (res) => { - flashStats(res?.success ? "Bookmarked" : "Bookmark failed"); - if (res?.success) loadLibrary(); - }); - }); - actions.appendChild(bkBtn); - card.appendChild(actions); - libContainer.appendChild(card); - }); - } - function editBookmark(b) { - const note = prompt("Note for this bookmark:", b.note || ""); - if (note === null) return; - const tagsRaw = prompt("Tags (comma separated):", (b.tags || []).join(", ")); - if (tagsRaw === null) return; - const tags = tagsRaw.split(",").map((t) => t.trim()).filter(Boolean); - chrome.runtime.sendMessage( - { type: "LIBRARY_BOOKMARK_UPDATE", id: b.id, data: { note, tags } }, - (res) => { - if (res?.success) { - b.note = note; - b.tags = tags; - flashStats("Bookmark updated"); - } - } - ); - } - function deleteBookmark(id) { - if (!confirm("Delete this bookmark?")) return; - chrome.runtime.sendMessage({ type: "LIBRARY_BOOKMARK_DELETE", id }, (res) => { - if (res?.success) { - bookmarksCache = bookmarksCache.filter((b) => b.id !== id); - renderLibrary(); - } - }); - } - function editSnippet(s) { - const title = prompt("Snippet title:", s.title); - if (title === null) return; - const text = prompt("Snippet text:", s.text); - if (text === null) return; - const tagsRaw = prompt("Tags (comma separated):", (s.tags || []).join(", ")); - if (tagsRaw === null) return; - const tags = tagsRaw.split(",").map((t) => t.trim()).filter(Boolean); - chrome.runtime.sendMessage( - { type: "LIBRARY_SNIPPET_UPDATE", id: s.id, data: { title, text, tags } }, - (res) => { - if (res?.success) { - s.title = title; - s.text = text; - s.tags = tags; - flashStats("Snippet updated"); - } - } - ); - } - function deleteSnippet(id) { - if (!confirm("Delete this snippet?")) return; - chrome.runtime.sendMessage({ type: "LIBRARY_SNIPPET_DELETE", id }, (res) => { - if (res?.success) { - snippetsCache = snippetsCache.filter((s) => s.id !== id); - renderLibrary(); - } - }); - } - function editNote(n) { - const text = prompt("Note text:", n.text); - if (text === null) return; - chrome.runtime.sendMessage( - { type: "LIBRARY_NOTE_SET", url: n.url, text, title: n.title }, - (res) => { - if (res?.success) { - n.text = text; - flashStats("Note updated"); - loadLibrary(); - } - } - ); - } - function deleteNote(url) { - if (!confirm("Delete this note?")) return; - chrome.runtime.sendMessage({ type: "LIBRARY_NOTE_DELETE", url }, (res) => { - if (res?.success) { - notesCache = notesCache.filter((n) => n.url !== url); - renderLibrary(); - } - }); - } - function tagRow(tags) { - const row = document.createElement("div"); - row.className = "tag-row"; - tags.forEach((t) => { - const tag = document.createElement("span"); - tag.className = "tag-chip"; - tag.textContent = `#${t}`; - row.appendChild(tag); - }); - return row; - } - function shortHost(url) { - try { - return new URL(url).host; - } catch { - return url; - } - } - function formatDate(ts) { - const diff = Date.now() - ts; - if (diff < 6e4) return "just now"; - if (diff < 36e5) return `${Math.floor(diff / 6e4)}m ago`; - if (diff < 864e5) return `${Math.floor(diff / 36e5)}h ago`; - return new Date(ts).toLocaleDateString(); - } - function flashStats(msg) { - const original = statsLabel.textContent; - statsLabel.textContent = msg; - statsLabel.classList.add("flash"); - setTimeout(() => { - statsLabel.classList.remove("flash"); - statsLabel.textContent = original; - }, 1500); - } -}); -//# sourceMappingURL=popup.js.map diff --git a/reef-extension-chrome/src/popup/popup.js.map b/reef-extension-chrome/src/popup/popup.js.map deleted file mode 100644 index 3d7367f..0000000 --- a/reef-extension-chrome/src/popup/popup.js.map +++ /dev/null @@ -1,7 +0,0 @@ -{ - "version": 3, - "sources": ["../../../src/popup/popup.ts"], - "sourcesContent": ["/**\n * @file Popup logic for Reef for Browsers.\n * Three modes: Page (existing single-page search), Tabs (cross-tab search),\n * Library (bookmarks / snippets / notes / recents).\n */\n\nimport type { IndexRecord } from '../../src/types.js';\nimport type { Bookmark, Snippet, PageNote, RecentPage } from '../storage.js';\n\ntype Mode = 'page' | 'tabs' | 'library';\ntype LibTab = 'bookmarks' | 'snippets' | 'notes' | 'recents';\n\nlet activeTabId: number | null = null;\nlet currentMode: Mode = 'page';\n\n// Page state\nlet pageFilter: string = 'all';\nlet pageQuery: string = '';\nlet pageResults: IndexRecord[] = [];\n\n// Tabs state\nlet tabQuery: string = '';\nlet tabResults: any[] = [];\n\n// Library state\nlet libFilter: LibTab = 'bookmarks';\nlet libQuery: string = '';\nlet bookmarksCache: Bookmark[] = [];\nlet snippetsCache: Snippet[] = [];\nlet notesCache: PageNote[] = [];\nlet recentsCache: RecentPage[] = [];\n\ndocument.addEventListener('DOMContentLoaded', async () => {\n if (typeof chrome !== 'undefined' && chrome.storage?.local) {\n const { theme } = await chrome.storage.local.get(['theme']);\n document.body.setAttribute('data-theme', theme || 'light');\n }\n\n const searchInput = document.getElementById('search-input') as HTMLInputElement;\n const clearBtn = document.getElementById('btn-clear') as HTMLButtonElement;\n const optionsBtn = document.getElementById('btn-options') as HTMLButtonElement;\n const filterTabs = document.getElementById('filter-tabs') as HTMLDivElement;\n const resultsContainer = document.getElementById('results-container') as HTMLDivElement;\n const manifestBadge = document.getElementById('manifest-badge') as HTMLSpanElement;\n const statsLabel = document.getElementById('stats-label') as HTMLSpanElement;\n\n const modeTabs = document.getElementById('mode-tabs') as HTMLDivElement;\n const panels = document.querySelectorAll('.mode-panel') as NodeListOf;\n\n const tabSearchInput = document.getElementById('tab-search-input') as HTMLInputElement;\n const tabSearchClear = document.getElementById('tab-search-clear') as HTMLButtonElement;\n const tabsContainer = document.getElementById('tabs-container') as HTMLDivElement;\n\n const libSearchInput = document.getElementById('library-search-input') as HTMLInputElement;\n const libSearchClear = document.getElementById('library-search-clear') as HTMLButtonElement;\n const libTabs = document.getElementById('library-tabs') as HTMLDivElement;\n const libContainer = document.getElementById('library-container') as HTMLDivElement;\n\n optionsBtn.addEventListener('click', () => {\n if (chrome.runtime.openOptionsPage) chrome.runtime.openOptionsPage();\n else window.open(chrome.runtime.getURL('src/options/options.html'));\n });\n\n // \u2500\u2500\u2500 MODE SWITCHING \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n modeTabs.addEventListener('click', (e) => {\n const t = e.target as HTMLElement;\n if (!t.classList.contains('mode-tab')) return;\n const mode = t.dataset.mode as Mode;\n if (mode === currentMode) return;\n currentMode = mode;\n modeTabs.querySelectorAll('.mode-tab').forEach(b => {\n const active = b === t;\n b.classList.toggle('active', active);\n b.setAttribute('aria-selected', active ? 'true' : 'false');\n });\n panels.forEach(p => p.classList.toggle('hidden', p.dataset.panel !== mode));\n if (mode === 'page') {\n renderPageResults();\n } else if (mode === 'tabs') {\n runTabSearch();\n } else if (mode === 'library') {\n loadLibrary();\n }\n });\n\n // \u2500\u2500\u2500 PAGE MODE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });\n if (tab?.id) {\n activeTabId = tab.id;\n await performPageSearch();\n } else {\n resultsContainer.innerHTML = '
No active tab accessible.
';\n }\n\n searchInput.addEventListener('input', () => {\n pageQuery = searchInput.value;\n clearBtn.classList.toggle('hidden', !pageQuery);\n performPageSearch();\n });\n\n clearBtn.addEventListener('click', () => {\n searchInput.value = '';\n pageQuery = '';\n clearBtn.classList.add('hidden');\n searchInput.focus();\n performPageSearch();\n });\n\n filterTabs.addEventListener('click', (e) => {\n const target = e.target as HTMLElement;\n if (target.classList.contains('tab-btn')) {\n filterTabs.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));\n target.classList.add('active');\n pageFilter = target.dataset.type || 'all';\n renderPageResults();\n }\n });\n\n async function performPageSearch() {\n if (!activeTabId) return;\n resultsContainer.innerHTML = '
Searching page...
';\n chrome.runtime.sendMessage(\n { type: 'SEARCH_CURRENT_TAB', tabId: activeTabId, query: pageQuery },\n (response) => {\n if (!response || !response.success) {\n resultsContainer.replaceChildren();\n const empty = document.createElement('div');\n empty.className = 'empty-state';\n empty.textContent = `Failed to index page. ${response?.error || ''}`;\n resultsContainer.appendChild(empty);\n return;\n }\n if (response.manifest?.version) {\n manifestBadge.textContent = 'Agent-Ready Site';\n manifestBadge.classList.add('authoritative');\n } else {\n manifestBadge.textContent = 'Dynamic Extract';\n manifestBadge.classList.remove('authoritative');\n }\n pageResults = response.results || [];\n renderPageResults();\n }\n );\n }\n\n function renderPageResults() {\n if (currentMode !== 'page') return;\n let filtered = pageResults;\n if (pageFilter !== 'all') filtered = pageResults.filter(r => r.type === pageFilter);\n statsLabel.textContent = `${filtered.length} items found`;\n if (filtered.length === 0) {\n resultsContainer.innerHTML = '
No matching records found.
';\n return;\n }\n resultsContainer.innerHTML = '';\n filtered.forEach(record => {\n const card = document.createElement('div');\n card.className = 'result-card';\n\n const topRow = document.createElement('div');\n topRow.className = 'card-top';\n const title = document.createElement('span');\n title.className = 'card-title';\n title.textContent = record.headingText || record.label || record.url;\n const typePill = document.createElement('span');\n typePill.className = `type-pill ${record.type}`;\n typePill.textContent = record.type;\n topRow.appendChild(title);\n topRow.appendChild(typePill);\n card.appendChild(topRow);\n\n if (record.bodyText) {\n const snippet = document.createElement('div');\n snippet.className = 'card-snippet';\n snippet.textContent = record.bodyText;\n card.appendChild(snippet);\n }\n\n const actionsRow = document.createElement('div');\n actionsRow.className = 'card-actions';\n\n const bookmarkBtn = document.createElement('button');\n bookmarkBtn.className = 'mini-btn';\n bookmarkBtn.title = 'Bookmark this record';\n bookmarkBtn.textContent = '\u2605';\n bookmarkBtn.addEventListener('click', (ev) => {\n ev.stopPropagation();\n bookmarkRecord(record);\n });\n actionsRow.appendChild(bookmarkBtn);\n\n const runBtn = document.createElement('button');\n runBtn.className = `run-btn ${record.destructive ? 'destructive' : ''}`;\n runBtn.textContent =\n record.type === 'action' || record.type === 'link'\n ? (record.destructive ? '\u26A0\uFE0F Run (Confirm)' : 'Execute')\n : record.type === 'field' ? 'Fill Field' : 'View';\n runBtn.addEventListener('click', (e) => { e.stopPropagation(); executePageAction(record); });\n actionsRow.appendChild(runBtn);\n\n card.appendChild(actionsRow);\n\n card.addEventListener('click', () => {\n if (activeTabId) {\n chrome.tabs.sendMessage(activeTabId, { type: 'HIGHLIGHT_RECORD', record });\n }\n });\n resultsContainer.appendChild(card);\n });\n }\n\n function executePageAction(record: IndexRecord) {\n if (!activeTabId) return;\n let valueToType: string | undefined;\n if (record.type === 'field') {\n const input = prompt(`Enter value for field \"${record.headingText || record.label || 'Input'}\":`, record.value || '');\n if (input === null) return;\n valueToType = input;\n }\n chrome.runtime.sendMessage({\n type: 'EXECUTE_TAB_ACTION',\n tabId: activeTabId,\n record,\n actionType: record.type === 'field' ? 'type' : 'click',\n value: valueToType,\n }, (res) => {\n if (res && res.success) window.close();\n else alert(`Action failed: ${res?.error || 'Unknown error'}`);\n });\n }\n\n function bookmarkRecord(record: IndexRecord) {\n chrome.runtime.sendMessage({\n type: 'LIBRARY_BOOKMARK_CREATE',\n data: {\n url: record.url || location.href,\n title: record.headingText || record.label || record.url || 'Bookmark',\n selectedText: record.bodyText,\n selector: record.selector,\n note: '',\n tags: [],\n },\n }, (res: any) => {\n flashStats(res?.success ? 'Bookmarked' : 'Bookmark failed');\n });\n }\n\n // \u2500\u2500\u2500 TABS MODE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n let tabDebounce: number | null = null;\n tabSearchInput.addEventListener('input', () => {\n tabQuery = tabSearchInput.value;\n tabSearchClear.classList.toggle('hidden', !tabQuery);\n if (tabDebounce) window.clearTimeout(tabDebounce);\n tabDebounce = window.setTimeout(runTabSearch, 150) as unknown as number;\n });\n tabSearchClear.addEventListener('click', () => {\n tabSearchInput.value = '';\n tabQuery = '';\n tabSearchClear.classList.add('hidden');\n tabResults = [];\n runTabSearch();\n tabSearchInput.focus();\n });\n\n function runTabSearch() {\n if (currentMode !== 'tabs') return;\n if (!tabQuery.trim()) {\n tabResults = [];\n tabsContainer.innerHTML = '
Type to search all open tabs by title and content.
';\n statsLabel.textContent = '0 tabs';\n return;\n }\n chrome.runtime.sendMessage({ type: 'TAB_SEARCH', query: tabQuery, limit: 30 }, (res: any) => {\n if (!res?.success) {\n tabsContainer.innerHTML = '
Tab search unavailable.
';\n return;\n }\n tabResults = res.items || [];\n renderTabResults();\n });\n }\n\n function renderTabResults() {\n if (currentMode !== 'tabs') return;\n statsLabel.textContent = `${tabResults.length} tab${tabResults.length === 1 ? '' : 's'}`;\n if (tabResults.length === 0) {\n tabsContainer.innerHTML = '
No matching tabs.
';\n return;\n }\n tabsContainer.innerHTML = '';\n tabResults.forEach(item => {\n const card = document.createElement('div');\n card.className = 'result-card';\n const topRow = document.createElement('div');\n topRow.className = 'card-top';\n const title = document.createElement('span');\n title.className = 'card-title';\n title.textContent = item.title || item.url;\n const url = document.createElement('span');\n url.className = 'card-url';\n url.textContent = shortHost(item.url);\n topRow.appendChild(title);\n topRow.appendChild(url);\n card.appendChild(topRow);\n\n if (item.matchedRecords?.length) {\n const snippet = document.createElement('div');\n snippet.className = 'card-snippet';\n snippet.textContent = item.matchedRecords.map((r: any) => r.headingText).filter(Boolean).join(' \u00B7 ');\n card.appendChild(snippet);\n }\n\n const actionsRow = document.createElement('div');\n actionsRow.className = 'card-actions';\n const switchBtn = document.createElement('button');\n switchBtn.className = 'run-btn';\n switchBtn.textContent = 'Switch to tab';\n switchBtn.addEventListener('click', () => {\n chrome.runtime.sendMessage({\n type: 'TAB_SWITCH',\n tabId: item.tabId,\n windowId: item.windowId,\n });\n window.close();\n });\n actionsRow.appendChild(switchBtn);\n card.appendChild(actionsRow);\n\n tabsContainer.appendChild(card);\n });\n }\n\n // \u2500\u2500\u2500 LIBRARY MODE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n let libDebounce: number | null = null;\n libSearchInput.addEventListener('input', () => {\n libQuery = libSearchInput.value;\n libSearchClear.classList.toggle('hidden', !libQuery);\n if (libDebounce) window.clearTimeout(libDebounce);\n libDebounce = window.setTimeout(renderLibrary, 120) as unknown as number;\n });\n libSearchClear.addEventListener('click', () => {\n libSearchInput.value = '';\n libQuery = '';\n libSearchClear.classList.add('hidden');\n renderLibrary();\n });\n libTabs.addEventListener('click', (e) => {\n const t = e.target as HTMLElement;\n if (!t.classList.contains('tab-btn')) return;\n libTabs.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));\n t.classList.add('active');\n libFilter = (t.dataset.lib as LibTab) || 'bookmarks';\n renderLibrary();\n });\n\n async function loadLibrary() {\n if (currentMode !== 'library') return;\n libContainer.innerHTML = '
Loading library\u2026
';\n const req = (type: string) => new Promise((resolve) => {\n chrome.runtime.sendMessage({ type }, (res: any) => resolve(res));\n });\n const [bm, sn, nt, rc] = await Promise.all([\n req('LIBRARY_BOOKMARK_LIST'),\n req('LIBRARY_SNIPPET_LIST'),\n req('LIBRARY_NOTE_LIST'),\n req('LIBRARY_RECENTS_LIST'),\n ]);\n bookmarksCache = (bm?.items || []) as Bookmark[];\n snippetsCache = (sn?.items || []) as Snippet[];\n notesCache = (nt?.items || []) as PageNote[];\n recentsCache = (rc?.items || []) as RecentPage[];\n renderLibrary();\n }\n\n function renderLibrary() {\n if (currentMode !== 'library') return;\n if (libFilter === 'bookmarks') return renderBookmarks();\n if (libFilter === 'snippets') return renderSnippets();\n if (libFilter === 'notes') return renderNotes();\n if (libFilter === 'recents') return renderRecents();\n }\n\n function filtered(items: T[], hayMaker: (i: T) => string): T[] {\n let out = items;\n if (libQuery) {\n const q = libQuery.toLowerCase();\n out = out.filter(i => hayMaker(i).toLowerCase().includes(q));\n }\n return out;\n }\n\n function renderBookmarks() {\n const items = filtered(bookmarksCache, (b) =>\n [b.title, b.selectedText, b.note, (b.tags || []).join(' '), b.url].filter(Boolean).join(' '));\n statsLabel.textContent = `${items.length} bookmark${items.length === 1 ? '' : 's'}`;\n if (items.length === 0) {\n libContainer.innerHTML = '
No bookmarks yet. Right-click selected text or click \u2605 on a search result to save.
';\n return;\n }\n libContainer.innerHTML = '';\n items.forEach((b) => {\n const card = document.createElement('div');\n card.className = 'result-card library-card';\n const topRow = document.createElement('div');\n topRow.className = 'card-top';\n const title = document.createElement('span');\n title.className = 'card-title';\n title.textContent = b.title || b.url;\n const pill = document.createElement('span');\n pill.className = 'type-pill link';\n pill.textContent = 'bookmark';\n topRow.appendChild(title);\n topRow.appendChild(pill);\n card.appendChild(topRow);\n if (b.selectedText) {\n const snip = document.createElement('div');\n snip.className = 'card-snippet';\n snip.textContent = b.selectedText.slice(0, 220);\n card.appendChild(snip);\n }\n const meta = document.createElement('div');\n meta.className = 'card-meta';\n meta.textContent = `${shortHost(b.url)} \u00B7 ${formatDate(b.createdAt)}`;\n card.appendChild(meta);\n if (b.tags?.length) card.appendChild(tagRow(b.tags));\n const actions = document.createElement('div');\n actions.className = 'card-actions';\n const openBtn = document.createElement('button');\n openBtn.className = 'run-btn';\n openBtn.textContent = 'Open';\n openBtn.addEventListener('click', () => {\n chrome.tabs.create({ url: b.url });\n window.close();\n });\n actions.appendChild(openBtn);\n const editBtn = document.createElement('button');\n editBtn.className = 'mini-btn';\n editBtn.textContent = '\u270E';\n editBtn.title = 'Edit note & tags';\n editBtn.addEventListener('click', () => editBookmark(b));\n actions.appendChild(editBtn);\n const delBtn = document.createElement('button');\n delBtn.className = 'mini-btn danger';\n delBtn.textContent = '\u00D7';\n delBtn.title = 'Delete bookmark';\n delBtn.addEventListener('click', () => deleteBookmark(b.id));\n actions.appendChild(delBtn);\n card.appendChild(actions);\n libContainer.appendChild(card);\n });\n }\n\n function renderSnippets() {\n const items = filtered(snippetsCache, (s) =>\n [s.title, s.text, (s.tags || []).join(' '), s.source?.title || ''].filter(Boolean).join(' '));\n statsLabel.textContent = `${items.length} snippet${items.length === 1 ? '' : 's'}`;\n if (items.length === 0) {\n libContainer.innerHTML = '
No snippets yet. Right-click selected text \u2192 \"Save selection as snippet\".
';\n return;\n }\n libContainer.innerHTML = '';\n items.forEach((s) => {\n const card = document.createElement('div');\n card.className = 'result-card library-card';\n const topRow = document.createElement('div');\n topRow.className = 'card-top';\n const title = document.createElement('span');\n title.className = 'card-title';\n title.textContent = s.title || s.text.slice(0, 60);\n const pill = document.createElement('span');\n pill.className = 'type-pill section';\n pill.textContent = 'snippet';\n topRow.appendChild(title);\n topRow.appendChild(pill);\n card.appendChild(topRow);\n const body = document.createElement('pre');\n body.className = 'card-pre';\n body.textContent = s.text;\n card.appendChild(body);\n if (s.source) {\n const meta = document.createElement('div');\n meta.className = 'card-meta';\n meta.textContent = `from ${s.source.title || shortHost(s.source.url)} \u00B7 ${formatDate(s.createdAt)}`;\n card.appendChild(meta);\n }\n if (s.tags?.length) card.appendChild(tagRow(s.tags));\n const actions = document.createElement('div');\n actions.className = 'card-actions';\n const copyBtn = document.createElement('button');\n copyBtn.className = 'run-btn';\n copyBtn.textContent = 'Copy';\n copyBtn.addEventListener('click', async () => {\n try {\n await navigator.clipboard.writeText(s.text);\n flashStats('Copied to clipboard');\n } catch {\n flashStats('Copy failed');\n }\n });\n actions.appendChild(copyBtn);\n const editBtn = document.createElement('button');\n editBtn.className = 'mini-btn';\n editBtn.textContent = '\u270E';\n editBtn.title = 'Edit snippet';\n editBtn.addEventListener('click', () => editSnippet(s));\n actions.appendChild(editBtn);\n const delBtn = document.createElement('button');\n delBtn.className = 'mini-btn danger';\n delBtn.textContent = '\u00D7';\n delBtn.title = 'Delete snippet';\n delBtn.addEventListener('click', () => deleteSnippet(s.id));\n actions.appendChild(delBtn);\n card.appendChild(actions);\n libContainer.appendChild(card);\n });\n }\n\n function renderNotes() {\n const items = filtered(notesCache, (n) => [n.text, n.title, n.url].filter(Boolean).join(' '));\n statsLabel.textContent = `${items.length} note${items.length === 1 ? '' : 's'}`;\n if (items.length === 0) {\n libContainer.innerHTML = '
No page notes yet. Right-click the page \u2192 \"Add note to this page\".
';\n return;\n }\n libContainer.innerHTML = '';\n items.forEach((n) => {\n const card = document.createElement('div');\n card.className = 'result-card library-card';\n const topRow = document.createElement('div');\n topRow.className = 'card-top';\n const title = document.createElement('span');\n title.className = 'card-title';\n title.textContent = n.title || n.url;\n const pill = document.createElement('span');\n pill.className = 'type-pill field';\n pill.textContent = 'note';\n topRow.appendChild(title);\n topRow.appendChild(pill);\n card.appendChild(topRow);\n const body = document.createElement('div');\n body.className = 'card-snippet';\n body.style.webkitLineClamp = '6';\n body.textContent = n.text;\n card.appendChild(body);\n const meta = document.createElement('div');\n meta.className = 'card-meta';\n meta.textContent = `${shortHost(n.url)} \u00B7 ${formatDate(n.updatedAt)}`;\n card.appendChild(meta);\n const actions = document.createElement('div');\n actions.className = 'card-actions';\n const openBtn = document.createElement('button');\n openBtn.className = 'run-btn';\n openBtn.textContent = 'Open page';\n openBtn.addEventListener('click', () => {\n chrome.tabs.create({ url: n.url });\n window.close();\n });\n actions.appendChild(openBtn);\n const editBtn = document.createElement('button');\n editBtn.className = 'mini-btn';\n editBtn.textContent = '\u270E';\n editBtn.title = 'Edit note';\n editBtn.addEventListener('click', () => editNote(n));\n actions.appendChild(editBtn);\n const delBtn = document.createElement('button');\n delBtn.className = 'mini-btn danger';\n delBtn.textContent = '\u00D7';\n delBtn.title = 'Delete note';\n delBtn.addEventListener('click', () => deleteNote(n.url));\n actions.appendChild(delBtn);\n card.appendChild(actions);\n libContainer.appendChild(card);\n });\n }\n\n function renderRecents() {\n const items = recentsCache.slice(0, 25);\n statsLabel.textContent = `${items.length} recent`;\n if (items.length === 0) {\n libContainer.innerHTML = '
No recent pages yet. Visit some pages to build up history.
';\n return;\n }\n libContainer.innerHTML = '';\n items.forEach((p) => {\n const card = document.createElement('div');\n card.className = 'result-card library-card';\n const topRow = document.createElement('div');\n topRow.className = 'card-top';\n const title = document.createElement('span');\n title.className = 'card-title';\n title.textContent = p.title || p.url;\n const pill = document.createElement('span');\n pill.className = 'type-pill file';\n pill.textContent = `${p.recordCount} idx`;\n topRow.appendChild(title);\n topRow.appendChild(pill);\n card.appendChild(topRow);\n const meta = document.createElement('div');\n meta.className = 'card-meta';\n meta.textContent = `${shortHost(p.url)} \u00B7 ${formatDate(p.visitedAt)}`;\n card.appendChild(meta);\n const actions = document.createElement('div');\n actions.className = 'card-actions';\n const openBtn = document.createElement('button');\n openBtn.className = 'run-btn';\n openBtn.textContent = 'Open';\n openBtn.addEventListener('click', () => {\n chrome.tabs.create({ url: p.url });\n window.close();\n });\n actions.appendChild(openBtn);\n const bkBtn = document.createElement('button');\n bkBtn.className = 'mini-btn';\n bkBtn.textContent = '\u2605';\n bkBtn.title = 'Bookmark this page';\n bkBtn.addEventListener('click', () => {\n chrome.runtime.sendMessage({\n type: 'LIBRARY_BOOKMARK_CREATE',\n data: { url: p.url, title: p.title, note: '', tags: [], favicon: p.favicon },\n }, (res: any) => {\n flashStats(res?.success ? 'Bookmarked' : 'Bookmark failed');\n if (res?.success) loadLibrary();\n });\n });\n actions.appendChild(bkBtn);\n card.appendChild(actions);\n libContainer.appendChild(card);\n });\n }\n\n function editBookmark(b: Bookmark) {\n const note = prompt('Note for this bookmark:', b.note || '');\n if (note === null) return;\n const tagsRaw = prompt('Tags (comma separated):', (b.tags || []).join(', '));\n if (tagsRaw === null) return;\n const tags = tagsRaw.split(',').map(t => t.trim()).filter(Boolean);\n chrome.runtime.sendMessage(\n { type: 'LIBRARY_BOOKMARK_UPDATE', id: b.id, data: { note, tags } },\n (res: any) => {\n if (res?.success) {\n b.note = note;\n b.tags = tags;\n flashStats('Bookmark updated');\n }\n }\n );\n }\n\n function deleteBookmark(id: string) {\n if (!confirm('Delete this bookmark?')) return;\n chrome.runtime.sendMessage({ type: 'LIBRARY_BOOKMARK_DELETE', id }, (res: any) => {\n if (res?.success) {\n bookmarksCache = bookmarksCache.filter(b => b.id !== id);\n renderLibrary();\n }\n });\n }\n\n function editSnippet(s: Snippet) {\n const title = prompt('Snippet title:', s.title);\n if (title === null) return;\n const text = prompt('Snippet text:', s.text);\n if (text === null) return;\n const tagsRaw = prompt('Tags (comma separated):', (s.tags || []).join(', '));\n if (tagsRaw === null) return;\n const tags = tagsRaw.split(',').map(t => t.trim()).filter(Boolean);\n chrome.runtime.sendMessage(\n { type: 'LIBRARY_SNIPPET_UPDATE', id: s.id, data: { title, text, tags } },\n (res: any) => {\n if (res?.success) {\n s.title = title; s.text = text; s.tags = tags;\n flashStats('Snippet updated');\n }\n }\n );\n }\n\n function deleteSnippet(id: string) {\n if (!confirm('Delete this snippet?')) return;\n chrome.runtime.sendMessage({ type: 'LIBRARY_SNIPPET_DELETE', id }, (res: any) => {\n if (res?.success) {\n snippetsCache = snippetsCache.filter(s => s.id !== id);\n renderLibrary();\n }\n });\n }\n\n function editNote(n: PageNote) {\n const text = prompt('Note text:', n.text);\n if (text === null) return;\n chrome.runtime.sendMessage(\n { type: 'LIBRARY_NOTE_SET', url: n.url, text, title: n.title },\n (res: any) => {\n if (res?.success) {\n n.text = text;\n flashStats('Note updated');\n loadLibrary();\n }\n }\n );\n }\n\n function deleteNote(url: string) {\n if (!confirm('Delete this note?')) return;\n chrome.runtime.sendMessage({ type: 'LIBRARY_NOTE_DELETE', url }, (res: any) => {\n if (res?.success) {\n notesCache = notesCache.filter(n => n.url !== url);\n renderLibrary();\n }\n });\n }\n\n function tagRow(tags: string[]) {\n const row = document.createElement('div');\n row.className = 'tag-row';\n tags.forEach(t => {\n const tag = document.createElement('span');\n tag.className = 'tag-chip';\n tag.textContent = `#${t}`;\n row.appendChild(tag);\n });\n return row;\n }\n\n function shortHost(url: string): string {\n try { return new URL(url).host; } catch { return url; }\n }\n\n function formatDate(ts: number): string {\n const diff = Date.now() - ts;\n if (diff < 60_000) return 'just now';\n if (diff < 3600_000) return `${Math.floor(diff / 60_000)}m ago`;\n if (diff < 86400_000) return `${Math.floor(diff / 3600_000)}h ago`;\n return new Date(ts).toLocaleDateString();\n }\n\n function flashStats(msg: string) {\n const original = statsLabel.textContent;\n statsLabel.textContent = msg;\n statsLabel.classList.add('flash');\n setTimeout(() => {\n statsLabel.classList.remove('flash');\n statsLabel.textContent = original;\n }, 1500);\n }\n});\n"], - "mappings": "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", - "names": ["filtered"] -} diff --git a/reef-extension-chrome/src/popup/reef-logo.png b/reef-extension-chrome/src/popup/reef-logo.png deleted file mode 100644 index e3ab4f6..0000000 Binary files a/reef-extension-chrome/src/popup/reef-logo.png and /dev/null differ diff --git a/src/reef.ts b/src/reef.ts index 0c07432..203650e 100644 --- a/src/reef.ts +++ b/src/reef.ts @@ -238,10 +238,25 @@ class ReefSearch { if (!results.length) { const suggestion = findClosestWord(query, this.index); + const list = this.ui.getResultsList(); + if (!list) return; + + const makeEmpty = (text: string) => { + const div = document.createElement('div'); + div.className = 'empty'; + div.textContent = text; + return div; + }; + if (suggestion) { - this.ui.getResultsList()!.innerHTML = `
No sections match "${this.escapeHtml(query)}". Did you mean ${this.escapeHtml(suggestion)}?
`; + const div = makeEmpty(`No sections match "${query}". Did you mean `); + const strong = document.createElement('strong'); + strong.textContent = suggestion; + div.appendChild(strong); + div.appendChild(document.createTextNode('?')); + list.replaceChildren(div); } else { - this.ui.getResultsList()!.innerHTML = `
No sections match "${this.escapeHtml(query)}"
`; + list.replaceChildren(makeEmpty(`No sections match "${query}"`)); } const countEl = this.ui.getRoot()?.querySelector('#count'); if (countEl) countEl.textContent = '0 results'; diff --git a/src/ui/inspector.ts b/src/ui/inspector.ts index c1d650e..e711ec2 100644 --- a/src/ui/inspector.ts +++ b/src/ui/inspector.ts @@ -87,7 +87,7 @@ export class VisualInspector { if (!this.active || !this.overlayContainer) return; // Clear existing overlay boxes - this.overlayContainer.innerHTML = ''; + this.overlayContainer.replaceChildren(); const currentUrl = window.location.href.split('#')[0]; diff --git a/src/ui/renderer.ts b/src/ui/renderer.ts index bed78ed..b8029c3 100644 --- a/src/ui/renderer.ts +++ b/src/ui/renderer.ts @@ -3,7 +3,7 @@ * Handles creating the modal DOM, focus trapping, and ARIA attributes. */ -import { getResultTypeIcon, getResultTypeLabel, escapeHtml, highlight, getSnippet } from './ui-helpers.js'; +import { getResultTypeIconNode, getResultTypeLabel, escapeHtml, appendHighlightedText, getSnippet } from './ui-helpers.js'; import type { IndexRecord } from '../types.js'; export class UIRenderer { @@ -66,7 +66,8 @@ renderUI( this.root = host.attachShadow({ mode: 'open' }); const shadow = this.root; - shadow.innerHTML = ` + const parser = new DOMParser(); + const parsedDoc = parser.parseFromString(`