@vivantel/virage-dashboard 0.1.12 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,31 +7,41 @@ React monitoring dashboard for the Virage RAG pipeline, served by `virage dashbo
7
7
 
8
8
  ## Usage
9
9
 
10
- The dashboard is started automatically by the CLI:
11
-
12
10
  ```bash
13
- virage dashboard
11
+ virage dashboard # start on port 3000
12
+ virage dashboard --port 8080 # custom port
13
+ virage dashboard --verbose # log every HTTP request (useful for debugging)
14
14
  ```
15
15
 
16
- This serves the built `dist/` folder on a local HTTP server alongside a `/api` backend.
16
+ The CLI prints the UI path and database status on startup. If the UI is missing it
17
+ serves an actionable build-instructions page instead of a blank screen.
18
+
19
+ ## Pages
20
+
21
+ | Page | Description |
22
+ | ----------- | ---------------------------------------------------------------------------------- |
23
+ | Home | Live chunk count, embedding count, memory usage, chunk histogram, anomaly table |
24
+ | Chunks | Browse and delete individual chunks by source file |
25
+ | Search | Interactive semantic search against the indexed knowledge base |
26
+ | Pipeline | Trigger index updates, generate eval datasets, and run evaluations via WebSocket |
27
+ | Experiments | View and compare saved evaluation runs |
28
+ | Analytics | Query history, top search terms, zero-result queries, queries-per-hour chart |
17
29
 
18
30
  ## Development
19
31
 
20
32
  ```bash
21
33
  # In one terminal — start the virage-cli API server
22
- cd packages/virage-cli && npm run dev
34
+ cd packages/virage-cli && npx tsx src/bin/virage.ts dashboard
23
35
 
24
- # In another terminal — start the Vite dev server (proxies /api → localhost:3000)
36
+ # In another terminal — start the Vite dev server (proxies /api and /ws → localhost:3000)
25
37
  cd packages/virage-dashboard && npm run dev
26
38
  ```
27
39
 
28
- ## Panels
40
+ Rebuild the production bundle and embed it in virage-cli:
29
41
 
30
- | Panel | Description |
31
- | --------------- | --------------------------------------------------- |
32
- | Status | Live chunk count, embedding count, and memory usage |
33
- | Chunk histogram | Distribution of chunk sizes across the index |
34
- | Anomalies | Embeddings with high z-scores flagged for review |
42
+ ```bash
43
+ npm run build:with-dashboard -w @vivantel/virage-cli
44
+ ```
35
45
 
36
46
  ## License
37
47
 
@@ -10,4 +10,4 @@ Error generating stack: `+e.message+`
10
10
 
11
11
  Please change the parent <Route path="${e}"> to <Route path="${e===`/`?`*`:`${e}/*`}">.`)}let u=ft(),d;if(t){let e=typeof t==`string`?se(t):t;E(c===`/`||e.pathname?.startsWith(c),`When overriding the location using \`<Routes location>\` or \`useRoutes(routes, location)\`, the location pathname must begin with the portion of the URL pathname that was matched by all parent routes. The current pathname base is "${c}" but pathname "${e.pathname}" was given in the \`location\` prop.`),d=e}else d=u;let f=d.pathname||`/`,p=f;if(c!==`/`){let e=c.replace(/^\//,``).split(`/`);p=`/`+f.replace(/^\//,``).split(`/`).slice(e.length).join(`/`)}let m=n&&n.state.matches.length?n.state.matches.map(e=>Object.assign(e,{route:n.manifest[e.route.id]||e.route})):ce(e,{pathname:p});D(l||m!=null,`No routes matched location "${d.pathname}${d.search}${d.hash}" `),D(m==null||m[m.length-1].route.element!==void 0||m[m.length-1].route.Component!==void 0||m[m.length-1].route.lazy!==void 0,`Matched leaf route at location "${d.pathname}${d.search}${d.hash}" does not have an element or Component. This means it will render an <Outlet /> with a null value by default resulting in an "empty" page.`);let h=Et(m&&m.map(e=>Object.assign({},e,{params:Object.assign({},o,e.params),pathname:Ie([c,r.encodeLocation?r.encodeLocation(e.pathname.replace(/%/g,`%25`).replace(/\?/g,`%3F`).replace(/#/g,`%23`)).pathname:e.pathname]),pathnameBase:e.pathnameBase===`/`?c:Ie([c,r.encodeLocation?r.encodeLocation(e.pathnameBase.replace(/%/g,`%25`).replace(/\?/g,`%3F`).replace(/#/g,`%23`)).pathname:e.pathnameBase])})),i,n);return t&&h?w.createElement(nt.Provider,{value:{location:{pathname:`/`,search:``,hash:``,state:null,key:`default`,mask:void 0,...d},navigationType:`POP`}},h):h}function bt(){let e=Pt(),t=He(e)?`${e.status} ${e.statusText}`:e instanceof Error?e.message:JSON.stringify(e),n=e instanceof Error?e.stack:null,r=`rgba(200,200,200, 0.5)`,i={padding:`0.5rem`,backgroundColor:r},a={padding:`2px 4px`,backgroundColor:r},o=null;return console.error(`Error handled by React Router default ErrorBoundary:`,e),o=w.createElement(w.Fragment,null,w.createElement(`p`,null,`💿 Hey developer 👋`),w.createElement(`p`,null,`You can provide a way better UX than this when your app throws errors by providing your own `,w.createElement(`code`,{style:a},`ErrorBoundary`),` or`,` `,w.createElement(`code`,{style:a},`errorElement`),` prop on your route.`)),w.createElement(w.Fragment,null,w.createElement(`h2`,null,`Unexpected Application Error!`),w.createElement(`h3`,{style:{fontStyle:`italic`}},t),n?w.createElement(`pre`,{style:i},n):null,o)}var xt=w.createElement(bt,null),St=class extends w.Component{constructor(e){super(e),this.state={location:e.location,revalidation:e.revalidation,error:e.error}}static getDerivedStateFromError(e){return{error:e}}static getDerivedStateFromProps(e,t){return t.location!==e.location||t.revalidation!==`idle`&&e.revalidation===`idle`?{error:e.error,location:e.location,revalidation:e.revalidation}:{error:e.error===void 0?t.error:e.error,location:t.location,revalidation:e.revalidation||t.revalidation}}componentDidCatch(e,t){this.props.onError?this.props.onError(e,t):console.error(`React Router caught the following error during render`,e)}render(){let e=this.state.error;if(this.context&&typeof e==`object`&&e&&`digest`in e&&typeof e.digest==`string`){let t=lt(e.digest);t&&(e=t)}let t=e===void 0?this.props.children:w.createElement(rt.Provider,{value:this.props.routeContext},w.createElement(it.Provider,{value:e,children:this.props.component}));return this.context?w.createElement(wt,{error:e},t):t}};St.contextType=Xe;var Ct=new WeakMap;function wt({children:e,error:t}){let{basename:n}=w.useContext(tt);if(typeof t==`object`&&t&&`digest`in t&&typeof t.digest==`string`){let e=ct(t.digest);if(e){let r=Ct.get(t);if(r)throw r;let i=Ge(e.location,n);if(We&&!Ct.get(t))if(i.isExternal||e.reloadDocument)window.location.href=i.absoluteURL||i.to;else{let n=Promise.resolve().then(()=>window.__reactRouterDataRouter.navigate(i.to,{replace:e.replace}));throw Ct.set(t,n),n}return w.createElement(`meta`,{httpEquiv:`refresh`,content:`0;url=${i.absoluteURL||i.to}`})}}return e}function Tt({routeContext:e,match:t,children:n}){let r=w.useContext(Je);return r&&r.static&&r.staticContext&&(t.route.errorElement||t.route.ErrorBoundary)&&(r.staticContext._deepestRenderedBoundaryId=t.route.id),w.createElement(rt.Provider,{value:e},n)}function Et(e,t=[],n){let r=n?.state;if(e==null){if(!r)return null;if(r.errors)e=r.matches;else if(t.length===0&&!r.initialized&&r.matches.length>0)e=r.matches;else return null}let i=e,a=r?.errors;if(a!=null){let e=i.findIndex(e=>e.route.id&&a?.[e.route.id]!==void 0);E(e>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(a).join(`,`)}`),i=i.slice(0,Math.min(i.length,e+1))}let o=!1,s=-1;if(n&&r){o=r.renderFallback;for(let e=0;e<i.length;e++){let t=i[e];if((t.route.HydrateFallback||t.route.hydrateFallbackElement)&&(s=e),t.route.id){let{loaderData:e,errors:a}=r,c=t.route.loader&&!e.hasOwnProperty(t.route.id)&&(!a||a[t.route.id]===void 0);if(t.route.lazy||c){n.isStatic&&(o=!0),i=s>=0?i.slice(0,s+1):[i[0]];break}}}}let c=n?.onError,l=r&&c?(e,t)=>{c(e,{location:r.location,params:r.matches?.[0]?.params??{},pattern:Ue(r.matches),errorInfo:t})}:void 0;return i.reduceRight((e,n,c)=>{let u,d=!1,f=null,p=null;r&&(u=a&&n.route.id?a[n.route.id]:void 0,f=n.route.errorElement||xt,o&&(s<0&&c===0?(Lt(`route-fallback`,!1,"No `HydrateFallback` element provided to render during initial hydration"),d=!0,p=null):s===c&&(d=!0,p=n.route.hydrateFallbackElement||null)));let m=t.concat(i.slice(0,c+1)),h=()=>{let t;return t=u?f:d?p:n.route.Component?w.createElement(n.route.Component,null):n.route.element?n.route.element:e,w.createElement(Tt,{match:n,routeContext:{outlet:e,matches:m,isDataRoute:r!=null},children:t})};return r&&(n.route.ErrorBoundary||n.route.errorElement||c===0)?w.createElement(St,{location:r.location,revalidation:r.revalidation,component:f,error:u,children:h(),routeContext:{outlet:null,matches:m,isDataRoute:!0},onError:l}):h()},null)}function j(e){return`${e} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function Dt(e){let t=w.useContext(Je);return E(t,j(e)),t}function Ot(e){let t=w.useContext(Ye);return E(t,j(e)),t}function kt(e){let t=w.useContext(rt);return E(t,j(e)),t}function At(e){let t=kt(e),n=t.matches[t.matches.length-1];return E(n.route.id,`${e} can only be used on routes that contain a unique "id"`),n.route.id}function jt(){return At(`useRouteId`)}function Mt(){let e=Ot(`useNavigation`);return w.useMemo(()=>{let{matches:t,historyAction:n,...r}=e.navigation;return r},[e.navigation])}function Nt(){let{matches:e,loaderData:t}=Ot(`useMatches`);return w.useMemo(()=>e.map(e=>ue(e,t)),[e,t])}function Pt(){let e=w.useContext(it),t=Ot(`useRouteError`),n=At(`useRouteError`);return e===void 0?t.errors?.[n]:e}function Ft(){let{router:e}=Dt(`useNavigate`),t=At(`useNavigate`),n=w.useRef(!1);return mt(()=>{n.current=!0}),w.useCallback(async(r,i={})=>{D(n.current,pt),n.current&&(typeof r==`number`?await e.navigate(r):await e.navigate(r,{fromRouteId:t,...i}))},[e,t])}var It={};function Lt(e,t,n){!t&&!It[e]&&(It[e]=!0,D(!1,n))}w.memo(Rt);function Rt({routes:e,manifest:t,future:n,state:r,isStatic:i,onError:a}){return yt(e,void 0,{manifest:t,state:r,isStatic:i,onError:a,future:n})}function zt(e){E(!1,`A <Route> is only ever to be used as the child of <Routes> element, never rendered directly. Please wrap your <Route> in a <Routes>.`)}function Bt({basename:e=`/`,children:t=null,location:n,navigationType:r=`POP`,navigator:i,static:a=!1,useTransitions:o}){E(!dt(),`You cannot render a <Router> inside another <Router>. You should never have more than one in your app.`);let s=e.replace(/^\/*/,`/`),c=w.useMemo(()=>({basename:s,navigator:i,static:a,useTransitions:o,future:{}}),[s,i,a,o]);typeof n==`string`&&(n=se(n));let{pathname:l=`/`,search:u=``,hash:d=``,state:f=null,key:p=`default`,mask:m}=n,h=w.useMemo(()=>{let e=De(l,s);return e==null?null:{location:{pathname:e,search:u,hash:d,state:f,key:p,mask:m},navigationType:r}},[s,l,u,d,f,p,r,m]);return D(h!=null,`<Router basename="${s}"> is not able to match the URL "${l}${u}${d}" because it does not start with the basename, so the <Router> won't render anything.`),h==null?null:w.createElement(tt.Provider,{value:c},w.createElement(nt.Provider,{children:t,value:h}))}function Vt({children:e,location:t}){return vt(Ht(e),t)}w.Component;function Ht(e,t=[]){let n=[];return w.Children.forEach(e,(e,r)=>{if(!w.isValidElement(e))return;let i=[...t,r];if(e.type===w.Fragment){n.push.apply(n,Ht(e.props.children,i));return}E(e.type===zt,`[${typeof e.type==`string`?e.type:e.type.name}] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>`),E(!e.props.index||!e.props.children,`An index route cannot have child routes.`);let a={id:e.props.id||i.join(`-`),caseSensitive:e.props.caseSensitive,element:e.props.element,Component:e.props.Component,index:e.props.index,path:e.props.path,middleware:e.props.middleware,loader:e.props.loader,action:e.props.action,hydrateFallbackElement:e.props.hydrateFallbackElement,HydrateFallback:e.props.HydrateFallback,errorElement:e.props.errorElement,ErrorBoundary:e.props.ErrorBoundary,hasErrorBoundary:e.props.hasErrorBoundary===!0||e.props.ErrorBoundary!=null||e.props.errorElement!=null,shouldRevalidate:e.props.shouldRevalidate,handle:e.props.handle,lazy:e.props.lazy};e.props.children&&(a.children=Ht(e.props.children,i)),n.push(a)}),n}var Ut=`get`,Wt=`application/x-www-form-urlencoded`;function Gt(e){return typeof HTMLElement<`u`&&e instanceof HTMLElement}function Kt(e){return Gt(e)&&e.tagName.toLowerCase()===`button`}function qt(e){return Gt(e)&&e.tagName.toLowerCase()===`form`}function Jt(e){return Gt(e)&&e.tagName.toLowerCase()===`input`}function Yt(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function Xt(e,t){return e.button===0&&(!t||t===`_self`)&&!Yt(e)}var Zt=null;function Qt(){if(Zt===null)try{new FormData(document.createElement(`form`),0),Zt=!1}catch{Zt=!0}return Zt}var $t=new Set([`application/x-www-form-urlencoded`,`multipart/form-data`,`text/plain`]);function en(e){return e!=null&&!$t.has(e)?(D(!1,`"${e}" is not a valid \`encType\` for \`<Form>\`/\`<fetcher.Form>\` and will default to "${Wt}"`),null):e}function tn(e,t){let n,r,i,a,o;if(qt(e)){let o=e.getAttribute(`action`);r=o?De(o,t):null,n=e.getAttribute(`method`)||Ut,i=en(e.getAttribute(`enctype`))||Wt,a=new FormData(e)}else if(Kt(e)||Jt(e)&&(e.type===`submit`||e.type===`image`)){let o=e.form;if(o==null)throw Error(`Cannot submit a <button> or <input type="submit"> without a <form>`);let s=e.getAttribute(`formaction`)||o.getAttribute(`action`);if(r=s?De(s,t):null,n=e.getAttribute(`formmethod`)||o.getAttribute(`method`)||Ut,i=en(e.getAttribute(`formenctype`))||en(o.getAttribute(`enctype`))||Wt,a=new FormData(o,e),!Qt()){let{name:t,type:n,value:r}=e;if(n===`image`){let e=t?`${t}.`:``;a.append(`${e}x`,`0`),a.append(`${e}y`,`0`)}else t&&a.append(t,r)}}else if(Gt(e))throw Error(`Cannot submit element that is not <form>, <button>, or <input type="submit|image">`);else n=Ut,r=null,i=Wt,o=e;return a&&i===`text/plain`&&(o=a,a=void 0),{action:r,method:n.toLowerCase(),encType:i,formData:a,body:o}}Object.getOwnPropertyNames(Object.prototype).sort().join(`\0`);var nn={"&":`\\u0026`,">":`\\u003e`,"<":`\\u003c`,"\u2028":`\\u2028`,"\u2029":`\\u2029`},rn=/[&><\u2028\u2029]/g;function an(e){return e.replace(rn,e=>nn[e])}function on(e,t){if(e===!1||e==null)throw Error(t)}function sn(e,t,n,r){let i=typeof e==`string`?new URL(e,typeof window>`u`?`server://singlefetch/`:window.location.origin):e;return n?i.pathname.endsWith(`/`)?i.pathname=`${i.pathname}_.${r}`:i.pathname=`${i.pathname}.${r}`:i.pathname===`/`?i.pathname=`_root.${r}`:t&&De(i.pathname,t)===`/`?i.pathname=`${Le(t)}/_root.${r}`:i.pathname=`${Le(i.pathname)}.${r}`,i}async function cn(e,t){if(e.id in t)return t[e.id];try{let n=await C(()=>import(e.module),[]);return t[e.id]=n,n}catch(t){return console.error(`Error loading route module \`${e.module}\`, reloading page...`),console.error(t),window.__reactRouterContext&&window.__reactRouterContext.isSpaMode,window.location.reload(),new Promise(()=>{})}}function ln(e){return e!=null&&typeof e.page==`string`}function un(e){return e==null?!1:e.href==null?e.rel===`preload`&&typeof e.imageSrcSet==`string`&&typeof e.imageSizes==`string`:typeof e.rel==`string`&&typeof e.href==`string`}async function dn(e,t,n){return gn((await Promise.all(e.map(async e=>{let r=t.routes[e.route.id];if(r){let e=await cn(r,n);return e.links?e.links():[]}return[]}))).flat(1).filter(un).filter(e=>e.rel===`stylesheet`||e.rel===`preload`).map(e=>e.rel===`stylesheet`?{...e,rel:`prefetch`,as:`style`}:{...e,rel:`prefetch`}))}function fn(e,t,n,r,i,a){let o=(e,t)=>n[t]?e.route.id!==n[t].route.id:!0,s=(e,t)=>n[t].pathname!==e.pathname||n[t].route.path?.endsWith(`*`)&&n[t].params[`*`]!==e.params[`*`];return a===`assets`?t.filter((e,t)=>o(e,t)||s(e,t)):a===`data`?t.filter((t,a)=>{let c=r.routes[t.route.id];if(!c||!c.hasLoader)return!1;if(o(t,a)||s(t,a))return!0;if(t.route.shouldRevalidate){let r=t.route.shouldRevalidate({currentUrl:new URL(i.pathname+i.search+i.hash,window.origin),currentParams:n[0]?.params||{},nextUrl:new URL(e,window.origin),nextParams:t.params,defaultShouldRevalidate:!0});if(typeof r==`boolean`)return r}return!0}):[]}function pn(e,t,{includeHydrateFallback:n}={}){return mn(e.map(e=>{let r=t.routes[e.route.id];if(!r)return[];let i=[r.module];return r.clientActionModule&&(i=i.concat(r.clientActionModule)),r.clientLoaderModule&&(i=i.concat(r.clientLoaderModule)),n&&r.hydrateFallbackModule&&(i=i.concat(r.hydrateFallbackModule)),r.imports&&(i=i.concat(r.imports)),i}).flat(1))}function mn(e){return[...new Set(e)]}function hn(e){let t={},n=Object.keys(e).sort();for(let r of n)t[r]=e[r];return t}function gn(e,t){let n=new Set,r=new Set(t);return e.reduce((e,i)=>{if(t&&!ln(i)&&i.as===`script`&&i.href&&r.has(i.href))return e;let a=JSON.stringify(hn(i));return n.has(a)||(n.add(a),e.push({key:a,link:i})),e},[])}function _n(){let e=w.useContext(Je);return on(e,`You must render this element inside a <DataRouterContext.Provider> element`),e}function vn(){let e=w.useContext(Ye);return on(e,`You must render this element inside a <DataRouterStateContext.Provider> element`),e}var yn=w.createContext(void 0);yn.displayName=`FrameworkContext`;function bn(){let e=w.useContext(yn);return on(e,`You must render this element inside a <HydratedRouter> element`),e}function xn(e,t){let n=w.useContext(yn),[r,i]=w.useState(!1),[a,o]=w.useState(!1),{onFocus:s,onBlur:c,onMouseEnter:l,onMouseLeave:u,onTouchStart:d}=t,f=w.useRef(null);w.useEffect(()=>{if(e===`render`&&o(!0),e===`viewport`){let e=new IntersectionObserver(e=>{e.forEach(e=>{o(e.isIntersecting)})},{threshold:.5});return f.current&&e.observe(f.current),()=>{e.disconnect()}}},[e]),w.useEffect(()=>{if(r){let e=setTimeout(()=>{o(!0)},100);return()=>{clearTimeout(e)}}},[r]);let p=()=>{i(!0)},m=()=>{i(!1),o(!1)};return n?e===`intent`?[a,f,{onFocus:Sn(s,p),onBlur:Sn(c,m),onMouseEnter:Sn(l,p),onMouseLeave:Sn(u,m),onTouchStart:Sn(d,p)}]:[a,f,{}]:[!1,f,{}]}function Sn(e,t){return n=>{e&&e(n),n.defaultPrevented||t(n)}}function Cn({page:e,...t}){let n=Ze(),{router:r}=_n(),i=w.useMemo(()=>ce(r.routes,e,r.basename),[r.routes,e,r.basename]);return i?n?w.createElement(Tn,{page:e,matches:i,...t}):w.createElement(En,{page:e,matches:i,...t}):null}function wn(e){let{manifest:t,routeModules:n}=bn(),[r,i]=w.useState([]);return w.useEffect(()=>{let r=!1;return dn(e,t,n).then(e=>{r||i(e)}),()=>{r=!0}},[e,t,n]),r}function Tn({page:e,matches:t,...n}){let r=ft(),{future:i}=bn(),{basename:a}=_n(),o=w.useMemo(()=>{if(e===r.pathname+r.search+r.hash)return[];let n=sn(e,a,i.v8_trailingSlashAwareDataRequests,`rsc`),o=!1,s=[];for(let e of t)typeof e.route.shouldRevalidate==`function`?o=!0:s.push(e.route.id);return o&&s.length>0&&n.searchParams.set(`_routes`,s.join(`,`)),[n.pathname+n.search]},[a,i.v8_trailingSlashAwareDataRequests,e,r,t]);return w.createElement(w.Fragment,null,o.map(e=>w.createElement(`link`,{key:e,rel:`prefetch`,as:`fetch`,href:e,...n})))}function En({page:e,matches:t,...n}){let r=ft(),{future:i,manifest:a,routeModules:o}=bn(),{basename:s}=_n(),{loaderData:c,matches:l}=vn(),u=w.useMemo(()=>fn(e,t,l,a,r,`data`),[e,t,l,a,r]),d=w.useMemo(()=>fn(e,t,l,a,r,`assets`),[e,t,l,a,r]),f=w.useMemo(()=>{if(e===r.pathname+r.search+r.hash)return[];let n=new Set,l=!1;if(t.forEach(e=>{let t=a.routes[e.route.id];!t||!t.hasLoader||(!u.some(t=>t.route.id===e.route.id)&&e.route.id in c&&o[e.route.id]?.shouldRevalidate||t.hasClientLoader?l=!0:n.add(e.route.id))}),n.size===0)return[];let d=sn(e,s,i.v8_trailingSlashAwareDataRequests,`data`);return l&&n.size>0&&d.searchParams.set(`_routes`,t.filter(e=>n.has(e.route.id)).map(e=>e.route.id).join(`,`)),[d.pathname+d.search]},[s,i.v8_trailingSlashAwareDataRequests,c,r,a,u,t,e,o]),p=w.useMemo(()=>pn(d,a),[d,a]),m=wn(d);return w.createElement(w.Fragment,null,f.map(e=>w.createElement(`link`,{key:e,rel:`prefetch`,as:`fetch`,href:e,...n})),p.map(e=>w.createElement(`link`,{key:e,rel:`modulepreload`,href:e,...n})),m.map(({key:e,link:t})=>w.createElement(`link`,{key:e,nonce:n.nonce,...t,crossOrigin:t.crossOrigin??n.crossOrigin})))}function Dn(...e){return t=>{e.forEach(e=>{typeof e==`function`?e(t):e!=null&&(e.current=t)})}}w.Component;var On=typeof window<`u`&&window.document!==void 0&&window.document.createElement!==void 0;try{On&&(window.__reactRouterVersion=`7.17.0`)}catch{}function kn({basename:e,children:t,useTransitions:n,window:r}){let i=w.useRef();i.current??=T({window:r,v5Compat:!0});let a=i.current,[o,s]=w.useState({action:a.action,location:a.location}),c=w.useCallback(e=>{n===!1?s(e):w.startTransition(()=>s(e))},[n]);return w.useLayoutEffect(()=>a.listen(c),[a,c]),w.createElement(Bt,{basename:e,children:t,location:o.location,navigationType:o.action,navigator:a,useTransitions:n})}function An({basename:e,children:t,history:n,useTransitions:r}){let[i,a]=w.useState({action:n.action,location:n.location}),o=w.useCallback(e=>{r===!1?a(e):w.startTransition(()=>a(e))},[r]);return w.useLayoutEffect(()=>n.listen(o),[n,o]),w.createElement(Bt,{basename:e,children:t,location:i.location,navigationType:i.action,navigator:n,useTransitions:r})}An.displayName=`unstable_HistoryRouter`;var jn=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,Mn=w.forwardRef(function({onClick:e,discover:t=`render`,prefetch:n=`none`,relative:r,reloadDocument:i,replace:a,mask:o,state:s,target:c,to:l,preventScrollReset:u,viewTransition:d,defaultShouldRevalidate:f,...p},m){let{basename:h,navigator:g,useTransitions:_}=w.useContext(tt),v=typeof l==`string`&&jn.test(l),y=Ge(l,h);l=y.to;let b=ut(l,{relative:r}),x=ft(),ee=null;if(o){let e=Pe(o,[],x.mask?x.mask.pathname:`/`,!0);h!==`/`&&(e.pathname=e.pathname===`/`?h:Ie([h,e.pathname])),ee=g.createHref(e)}let[S,C,te]=xn(n,p),ne=zn(l,{replace:a,mask:o,state:s,target:c,preventScrollReset:u,relative:r,viewTransition:d,defaultShouldRevalidate:f,useTransitions:_});function T(t){e&&e(t),t.defaultPrevented||ne(t)}let E=!(y.isExternal||i),D=w.createElement(`a`,{...p,...te,href:(E?ee:void 0)||y.absoluteURL||b,onClick:E?T:e,ref:Dn(m,C),target:c,"data-discover":!v&&t===`render`?`true`:void 0});return S&&!v?w.createElement(w.Fragment,null,D,w.createElement(Cn,{page:b})):D});Mn.displayName=`Link`;var Nn=w.forwardRef(function({"aria-current":e=`page`,caseSensitive:t=!1,className:n=``,end:r=!1,style:i,to:a,viewTransition:o,children:s,...c},l){let u=_t(a,{relative:c.relative}),d=ft(),f=w.useContext(Ye),{navigator:p,basename:m}=w.useContext(tt),h=f!=null&&Yn(u)&&o===!0,g=p.encodeLocation?p.encodeLocation(u).pathname:u.pathname,_=d.pathname,v=f&&f.navigation&&f.navigation.location?f.navigation.location.pathname:null;t||(_=_.toLowerCase(),v=v?v.toLowerCase():null,g=g.toLowerCase()),v&&m&&(v=De(v,m)||v);let y=g!==`/`&&g.endsWith(`/`)?g.length-1:g.length,b=_===g||!r&&_.startsWith(g)&&_.charAt(y)===`/`,x=v!=null&&(v===g||!r&&v.startsWith(g)&&v.charAt(g.length)===`/`),ee={isActive:b,isPending:x,isTransitioning:h},S=b?e:void 0,C;C=typeof n==`function`?n(ee):[n,b?`active`:null,x?`pending`:null,h?`transitioning`:null].filter(Boolean).join(` `);let te=typeof i==`function`?i(ee):i;return w.createElement(Mn,{...c,"aria-current":S,className:C,ref:l,style:te,to:a,viewTransition:o},typeof s==`function`?s(ee):s)});Nn.displayName=`NavLink`;var Pn=w.forwardRef(({discover:e=`render`,fetcherKey:t,navigate:n,reloadDocument:r,replace:i,state:a,method:o=Ut,action:s,onSubmit:c,relative:l,preventScrollReset:u,viewTransition:d,defaultShouldRevalidate:f,...p},m)=>{let{useTransitions:h}=w.useContext(tt),g=Hn(),_=Un(s,{relative:l}),v=o.toLowerCase()===`get`?`get`:`post`,y=typeof s==`string`&&jn.test(s);return w.createElement(`form`,{ref:m,method:v,action:_,onSubmit:r?c:e=>{if(c&&c(e),e.defaultPrevented)return;e.preventDefault();let r=e.nativeEvent.submitter,s=r?.getAttribute(`formmethod`)||o,p=()=>g(r||e.currentTarget,{fetcherKey:t,method:s,navigate:n,replace:i,state:a,relative:l,preventScrollReset:u,viewTransition:d,defaultShouldRevalidate:f});h&&n!==!1?w.startTransition(()=>p()):p()},...p,"data-discover":!y&&e===`render`?`true`:void 0})});Pn.displayName=`Form`;function Fn({getKey:e,storageKey:t,...n}){let r=w.useContext(yn),{basename:i}=w.useContext(tt),a=ft(),o=Nt();qn({getKey:e,storageKey:t});let s=w.useMemo(()=>{if(!r||!e)return null;let t=Kn(a,o,i,e);return t===a.key?null:t},[]);if(!r||r.isSpaMode)return null;let c=((e,t)=>{if(!window.history.state||!window.history.state.key){let e=Math.random().toString(32).slice(2);window.history.replaceState({key:e},``)}try{let n=JSON.parse(sessionStorage.getItem(e)||`{}`)[t||window.history.state.key];typeof n==`number`&&window.scrollTo(0,n)}catch(t){console.error(t),sessionStorage.removeItem(e)}}).toString();return w.createElement(`script`,{...n,suppressHydrationWarning:!0,dangerouslySetInnerHTML:{__html:`(${c})(${an(JSON.stringify(t||Wn))}, ${an(JSON.stringify(s))})`}})}Fn.displayName=`ScrollRestoration`;function In(e){return`${e} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function Ln(e){let t=w.useContext(Je);return E(t,In(e)),t}function Rn(e){let t=w.useContext(Ye);return E(t,In(e)),t}function zn(e,{target:t,replace:n,mask:r,state:i,preventScrollReset:a,relative:o,viewTransition:s,defaultShouldRevalidate:c,useTransitions:l}={}){let u=ht(),d=ft(),f=_t(e,{relative:o});return w.useCallback(p=>{if(Xt(p,t)){p.preventDefault();let t=n===void 0?oe(d)===oe(f):n,m=()=>u(e,{replace:t,mask:r,state:i,preventScrollReset:a,relative:o,viewTransition:s,defaultShouldRevalidate:c});l?w.startTransition(()=>m()):m()}},[d,u,f,n,r,i,t,e,a,o,s,c,l])}var Bn=0,Vn=()=>`__${String(++Bn)}__`;function Hn(){let{router:e}=Ln(`useSubmit`),{basename:t}=w.useContext(tt),n=jt(),r=e.fetch,i=e.navigate;return w.useCallback(async(e,a={})=>{let{action:o,method:s,encType:c,formData:l,body:u}=tn(e,t);a.navigate===!1?await r(a.fetcherKey||Vn(),n,a.action||o,{defaultShouldRevalidate:a.defaultShouldRevalidate,preventScrollReset:a.preventScrollReset,formData:l,body:u,formMethod:a.method||s,formEncType:a.encType||c,flushSync:a.flushSync}):await i(a.action||o,{defaultShouldRevalidate:a.defaultShouldRevalidate,preventScrollReset:a.preventScrollReset,formData:l,body:u,formMethod:a.method||s,formEncType:a.encType||c,replace:a.replace,state:a.state,fromRouteId:n,flushSync:a.flushSync,viewTransition:a.viewTransition})},[r,i,t,n])}function Un(e,{relative:t}={}){let{basename:n}=w.useContext(tt),r=w.useContext(rt);E(r,`useFormAction must be used inside a RouteContext`);let[i]=r.matches.slice(-1),a={..._t(e||`.`,{relative:t})},o=ft();if(e==null){a.search=o.search;let e=new URLSearchParams(a.search),t=e.getAll(`index`);if(t.some(e=>e===``)){e.delete(`index`),t.filter(e=>e).forEach(t=>e.append(`index`,t));let n=e.toString();a.search=n?`?${n}`:``}}return(!e||e===`.`)&&i.route.index&&(a.search=a.search?a.search.replace(/^\?/,`?index&`):`?index`),n!==`/`&&(a.pathname=a.pathname===`/`?n:Ie([n,a.pathname])),oe(a)}var Wn=`react-router-scroll-positions`,Gn={};function Kn(e,t,n,r){let i=null;return r&&(i=r(n===`/`?e:{...e,pathname:De(e.pathname,n)||e.pathname},t)),i??=e.key,i}function qn({getKey:e,storageKey:t}={}){let{router:n}=Ln(`useScrollRestoration`),{restoreScrollPosition:r,preventScrollReset:i}=Rn(`useScrollRestoration`),{basename:a}=w.useContext(tt),o=ft(),s=Nt(),c=Mt();w.useEffect(()=>(window.history.scrollRestoration=`manual`,()=>{window.history.scrollRestoration=`auto`}),[]),Jn(w.useCallback(()=>{if(c.state===`idle`){let t=Kn(o,s,a,e);Gn[t]=window.scrollY}try{sessionStorage.setItem(t||Wn,JSON.stringify(Gn))}catch(e){D(!1,`Failed to save scroll positions in sessionStorage, <ScrollRestoration /> will not work properly (${e}).`)}window.history.scrollRestoration=`auto`},[c.state,e,a,o,s,t])),typeof document<`u`&&(w.useLayoutEffect(()=>{try{let e=sessionStorage.getItem(t||Wn);e&&(Gn=JSON.parse(e))}catch{}},[t]),w.useLayoutEffect(()=>{let t=n?.enableScrollRestoration(Gn,()=>window.scrollY,e?(t,n)=>Kn(t,n,a,e):void 0);return()=>t&&t()},[n,a,e]),w.useLayoutEffect(()=>{if(r!==!1){if(typeof r==`number`){window.scrollTo(0,r);return}try{if(o.hash){let e=document.getElementById(decodeURIComponent(o.hash.slice(1)));if(e){e.scrollIntoView();return}}}catch{D(!1,`"${o.hash.slice(1)}" is not a decodable element ID. The view will not scroll to it.`)}i!==!0&&window.scrollTo(0,0)}},[o,r,i]))}function Jn(e,t){let{capture:n}=t||{};w.useEffect(()=>{let t=n==null?void 0:{capture:n};return window.addEventListener(`pagehide`,e,t),()=>{window.removeEventListener(`pagehide`,e,t)}},[e,n])}function Yn(e,{relative:t}={}){let n=w.useContext(Qe);E(n!=null,"`useViewTransitionState` must be used within `react-router-dom`'s `RouterProvider`. Did you accidentally import `RouterProvider` from `react-router`?");let{basename:r}=Ln(`useViewTransitionState`),i=_t(e,{relative:t});if(!n.isTransitioning)return!1;let a=De(n.currentLocation.pathname,r)||n.currentLocation.pathname,o=De(n.nextLocation.pathname,r)||n.nextLocation.pathname;return we(i.pathname,o)!=null||we(i.pathname,a)!=null}async function Xn(e){let t=await fetch(e);if(!t.ok)throw Error(`${e}: ${t.status} ${t.statusText}`);return t.json()}async function Zn(e,t){let n=await fetch(e,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(t)});if(!n.ok){let t=await n.json().catch(()=>({error:n.statusText}));throw Error(t.error??`${e}: ${n.status}`)}return n.json()}async function Qn(e,t){let n=await fetch(e,{method:`DELETE`,headers:t?{"Content-Type":`application/json`}:{},body:t?JSON.stringify(t):void 0});if(!n.ok){let t=await n.json().catch(()=>({error:n.statusText}));throw Error(t.error??`${e}: ${n.status}`)}return n.json()}var M={status:()=>Xn(`/api/status`),chunks:()=>Xn(`/api/chunks`),anomalies:()=>Xn(`/api/embeddings/anomalies`),projects:()=>Xn(`/api/projects`),addProject:e=>Zn(`/api/projects/add`,{rootPath:e}),switchProject:e=>Zn(`/api/projects/switch`,{index:e}),chunksAll:e=>Xn(`/api/chunks/all${e?`?sourceFile=${encodeURIComponent(e)}`:``}`),deleteChunksFile:e=>Qn(`/api/chunks/file`,{sourceFile:e}),deleteChunksAll:()=>Qn(`/api/chunks/all`),metaCheck:()=>Xn(`/api/meta-check`),search:(e,t)=>Zn(`/api/search`,{query:e,topK:t}),experiments:()=>Xn(`/api/experiments`),experiment:e=>Xn(`/api/experiments/${e}`),deleteExperiment:e=>Qn(`/api/experiments/${e}`),compareExperiments:(e,t)=>Zn(`/api/experiments/compare`,{baseline:e,candidate:t}),analytics:{queries:e=>Xn(`/api/analytics/queries${e?`?limit=${e}`:``}`),topTerms:e=>Xn(`/api/analytics/top-terms${e?`?limit=${e}`:``}`),zeroResults:e=>Xn(`/api/analytics/zero-results${e==null?``:`?threshold=${e}`}`),stats:()=>Xn(`/api/analytics/stats`),perHour:e=>Xn(`/api/analytics/queries-per-hour${e?`?hours=${e}`:``}`)}},$n=o((e=>{var t=Symbol.for(`react.transitional.element`),n=Symbol.for(`react.fragment`);function r(e,n,r){var i=null;if(r!==void 0&&(i=``+r),n.key!==void 0&&(i=``+n.key),`key`in n)for(var a in r={},n)a!==`key`&&(r[a]=n[a]);else r=n;return n=r.ref,{$$typeof:t,type:e,key:i,ref:n===void 0?null:n,props:r}}e.Fragment=n,e.jsx=r,e.jsxs=r})),N=o(((e,t)=>{t.exports=$n()}))();function er({data:e}){return(0,N.jsxs)(`div`,{className:`card`,children:[(0,N.jsx)(`h2`,{children:`System Status`}),(0,N.jsxs)(`div`,{className:`metrics`,children:[(0,N.jsx)(tr,{value:e.totalChunks,label:`Total Chunks`}),(0,N.jsx)(tr,{value:e.totalEmbeddings,label:`Embeddings`}),(0,N.jsx)(tr,{value:`${e.memoryMB} MB`,label:`Heap Used`})]})]})}function tr({value:e,label:t}){return(0,N.jsxs)(`div`,{className:`metric`,children:[(0,N.jsx)(`div`,{className:`value`,children:e}),(0,N.jsx)(`div`,{className:`label`,children:t})]})}function nr({buckets:e}){if(e.length===0)return null;let t=Math.max(...e.map(e=>e.count));return(0,N.jsxs)(`div`,{className:`card`,children:[(0,N.jsx)(`h2`,{children:`Chunk Size Distribution`}),e.map(e=>{let n=t>0?Math.round(e.count/t*100):0;return(0,N.jsxs)(`div`,{children:[(0,N.jsx)(`div`,{style:{fontSize:12},children:e.label}),(0,N.jsx)(`div`,{className:`bar`,children:(0,N.jsx)(`div`,{className:`bar-fill`,style:{width:`${n}%`}})}),(0,N.jsxs)(`div`,{style:{fontSize:11,color:`#888`},children:[e.count,` chunks`]})]},e.label)})]})}function rr({anomalies:e}){return e.length===0?(0,N.jsx)(`div`,{className:`card`,children:`✅ No embedding anomalies detected`}):(0,N.jsxs)(`div`,{className:`card`,children:[(0,N.jsxs)(`h2`,{children:[`⚠️ Embedding Anomalies (`,e.length,`)`]}),(0,N.jsxs)(`table`,{children:[(0,N.jsx)(`thead`,{children:(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`th`,{children:`File`}),(0,N.jsx)(`th`,{children:`z-score`}),(0,N.jsx)(`th`,{children:`Preview`})]})}),(0,N.jsx)(`tbody`,{children:e.slice(0,10).map((e,t)=>(0,N.jsxs)(`tr`,{className:`anomaly`,children:[(0,N.jsx)(`td`,{children:e.sourceFile}),(0,N.jsx)(`td`,{children:e.zscore.toFixed(2)}),(0,N.jsx)(`td`,{children:e.preview})]},t))})]})]})}function ir(e){let t=Date.now()-e,n=Math.floor(t/6e4);if(n<1)return`just now`;if(n<60)return`${n}m ago`;let r=Math.floor(n/60);return r<24?`${r}h ago`:`${Math.floor(r/24)}d ago`}function ar({projects:e,activeIndex:t,onSwitch:n,onAdd:r,addError:i}){let[a,o]=(0,_.useState)(!1),[s,c]=(0,_.useState)(``),l=e[t];function u(){let e=s.trim();e&&(r(e),c(``),o(!1))}function d(e){e.key===`Enter`&&u()}return(0,N.jsxs)(`div`,{className:`project-switcher`,children:[(0,N.jsxs)(`div`,{className:`project-header-row`,children:[(0,N.jsx)(`span`,{className:`project-current`,children:l?.label??`—`}),e.length>1&&(0,N.jsx)(`button`,{className:`project-dropdown-toggle`,onClick:()=>o(e=>!e),children:a?`▲ close`:`▼ switch`})]}),a&&(0,N.jsx)(`ul`,{className:`project-list`,children:e.map((e,r)=>(0,N.jsxs)(`li`,{className:r===t?`active`:``,onClick:()=>{r!==t&&(n(r),o(!1))},children:[(0,N.jsx)(`span`,{children:e.label}),(0,N.jsx)(`span`,{className:`project-path`,children:ir(e.lastUsed)})]},e.rootPath))}),(0,N.jsxs)(`div`,{className:`project-add-form`,children:[(0,N.jsx)(`input`,{type:`text`,placeholder:`Absolute path to project root`,value:s,onChange:e=>c(e.target.value),onKeyDown:d}),(0,N.jsx)(`button`,{onClick:u,children:`Add project`})]}),i&&(0,N.jsx)(`span`,{className:`project-add-error`,children:i})]})}var or=5e3;function sr(){let[e,t]=(0,_.useState)({status:null,chunks:null,anomalies:null,projects:null,metaMismatch:null,error:null,addProjectError:null});async function n(){try{let[e,n,r,i,a]=await Promise.all([M.status(),M.chunks(),M.anomalies(),M.projects(),M.metaCheck()]);t(t=>({...t,status:e,chunks:n,anomalies:r,projects:i,metaMismatch:a.status===`mismatch`?a.message??null:null,error:null}))}catch(e){t(t=>({...t,error:e instanceof Error?e.message:String(e)}))}}async function r(e){try{let r=await M.switchProject(e);t(e=>({...e,projects:r,addProjectError:null})),n()}catch(e){t(t=>({...t,error:e instanceof Error?e.message:String(e)}))}}async function i(e){try{let r=await M.addProject(e);t(e=>({...e,projects:r,addProjectError:null})),n()}catch(e){t(t=>({...t,addProjectError:e instanceof Error?e.message:String(e)}))}}return(0,_.useEffect)(()=>{n();let e=setInterval(()=>void n(),or);return()=>clearInterval(e)},[]),(0,N.jsxs)(N.Fragment,{children:[e.projects&&(0,N.jsx)(ar,{projects:e.projects.projects,activeIndex:e.projects.activeIndex,onSwitch:r,onAdd:i,addError:e.addProjectError}),(0,N.jsx)(`h1`,{children:`RAG Dashboard`}),e.metaMismatch&&(0,N.jsxs)(`div`,{className:`card warning`,children:[`⚠️ `,e.metaMismatch]}),e.error&&(0,N.jsxs)(`div`,{className:`card error`,children:[`⚠️ `,e.error]}),e.status&&(0,N.jsx)(er,{data:e.status}),e.chunks&&(0,N.jsx)(nr,{buckets:e.chunks.histogram}),e.anomalies&&(0,N.jsx)(rr,{anomalies:e.anomalies.anomalies}),!e.status&&!e.error&&(0,N.jsx)(`div`,{className:`card`,children:`Loading...`})]})}function cr(){let[e,t]=(0,_.useState)([]),[n,r]=(0,_.useState)(``),[i,a]=(0,_.useState)(!1),[o,s]=(0,_.useState)(null),[c,l]=(0,_.useState)(!1);async function u(e){a(!0),s(null);try{t((await M.chunksAll(e||void 0)).chunks)}catch(e){s(e instanceof Error?e.message:String(e))}finally{a(!1)}}(0,_.useEffect)(()=>{u()},[]);let d=Array.from(new Set(e.map(e=>e.sourceFile))).sort();async function f(e){r(e),await u(e||void 0)}async function p(){if(n)try{await M.deleteChunksFile(n),r(``),await u()}catch(e){s(e instanceof Error?e.message:String(e))}}async function m(){try{await M.deleteChunksAll(),l(!1),r(``),await u()}catch(e){s(e instanceof Error?e.message:String(e))}}let h=n?e.filter(e=>e.sourceFile===n):e;return(0,N.jsxs)(`div`,{children:[(0,N.jsx)(`h2`,{children:`Chunk Browser`}),o&&(0,N.jsxs)(`div`,{className:`card error`,children:[`⚠️ `,o]}),(0,N.jsxs)(`div`,{className:`toolbar`,children:[(0,N.jsxs)(`select`,{value:n,onChange:e=>void f(e.target.value),children:[(0,N.jsxs)(`option`,{value:``,children:[`All files (`,e.length,` chunks)`]}),d.map(e=>(0,N.jsx)(`option`,{value:e,children:e},e))]}),n&&(0,N.jsx)(`button`,{className:`btn-danger`,onClick:()=>void p(),children:`Delete file chunks`}),c?(0,N.jsxs)(`span`,{className:`confirm-inline`,children:[`Sure?\xA0`,(0,N.jsx)(`button`,{className:`btn-danger`,onClick:()=>void m(),children:`Yes, clear all`}),`\xA0`,(0,N.jsx)(`button`,{onClick:()=>l(!1),children:`Cancel`})]}):(0,N.jsx)(`button`,{className:`btn-danger`,onClick:()=>l(!0),children:`Clear all`})]}),i?(0,N.jsx)(`div`,{className:`card`,children:`Loading...`}):(0,N.jsxs)(`table`,{className:`chunk-table`,children:[(0,N.jsx)(`thead`,{children:(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`th`,{children:`Source file`}),(0,N.jsx)(`th`,{children:`Preview`}),(0,N.jsx)(`th`,{children:`Hash`})]})}),(0,N.jsxs)(`tbody`,{children:[h.map(e=>(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`td`,{className:`source-file`,children:e.sourceFile}),(0,N.jsxs)(`td`,{className:`content-preview`,children:[e.content.slice(0,80),e.content.length>80?`…`:``]}),(0,N.jsx)(`td`,{className:`hash`,children:e.contentHash})]},e.contentHash)),h.length===0&&(0,N.jsx)(`tr`,{children:(0,N.jsx)(`td`,{colSpan:3,style:{textAlign:`center`,color:`#888`},children:`No chunks found`})})]})]})]})}function lr(){let[e,t]=(0,_.useState)(``),[n,r]=(0,_.useState)(5),[i,a]=(0,_.useState)([]),[o,s]=(0,_.useState)(!1),[c,l]=(0,_.useState)(null),[u,d]=(0,_.useState)(!1);async function f(t){if(t.preventDefault(),e.trim()){s(!0),l(null);try{a((await M.search(e.trim(),n)).results),d(!0)}catch(e){l(e instanceof Error?e.message:String(e))}finally{s(!1)}}}return(0,N.jsxs)(`div`,{children:[(0,N.jsx)(`h2`,{children:`RAG Search`}),(0,N.jsxs)(`form`,{onSubmit:e=>void f(e),className:`search-form`,children:[(0,N.jsx)(`input`,{type:`text`,className:`search-input`,placeholder:`Enter a search query…`,value:e,onChange:e=>t(e.target.value)}),(0,N.jsxs)(`div`,{className:`search-controls`,children:[(0,N.jsxs)(`label`,{children:[`Top-K:\xA0`,(0,N.jsx)(`input`,{type:`range`,min:1,max:20,value:n,onChange:e=>r(Number(e.target.value))}),`\xA0`,(0,N.jsx)(`strong`,{children:n})]}),(0,N.jsx)(`button`,{type:`submit`,disabled:o||!e.trim(),children:o?`Searching…`:`Search`})]})]}),c&&(0,N.jsxs)(`div`,{className:`card error`,children:[`⚠️ `,c]}),u&&i.length===0&&!o&&(0,N.jsx)(`div`,{className:`card`,children:`No results found.`}),(0,N.jsx)(`div`,{className:`search-results`,children:i.map(e=>(0,N.jsxs)(`div`,{className:`search-result-card card`,children:[(0,N.jsxs)(`div`,{className:`result-header`,children:[(0,N.jsx)(`span`,{className:`source-badge`,children:e.sourceFile??e.metadata.source_file??e.id}),(0,N.jsxs)(`span`,{className:`similarity-badge`,children:[(e.similarity*100).toFixed(1),`% match`]})]}),(0,N.jsx)(`p`,{className:`result-content`,children:e.content})]},e.id))})]})}var ur=new Set([`done`,`error`,`busy`]),dr=5,fr=1e3,pr=(0,_.createContext)({status:`disconnected`,operationRunning:!1,messages:[],startOp:()=>void 0});function mr({children:e}){let[t,n]=(0,_.useState)(`disconnected`),[r,i]=(0,_.useState)([]),[a,o]=(0,_.useState)(!1),s=(0,_.useRef)(null),c=(0,_.useRef)(0),l=(0,_.useRef)(null),u=(0,_.useRef)(null),d=(0,_.useCallback)(()=>{if(s.current?.readyState===WebSocket.OPEN)return;n(`connecting`);let e=new WebSocket(`/ws`);s.current=e,e.onopen=()=>{c.current=0,n(`connected`),u.current!==null&&(e.send(JSON.stringify(u.current)),u.current=null)},e.onmessage=e=>{let t;try{t=JSON.parse(e.data)}catch{t={type:`raw`,text:e.data}}i(e=>[...e,t]),ur.has(t.type)&&o(!1)},e.onclose=()=>{if(s.current===e)if(s.current=null,n(`disconnected`),c.current<dr){let e=fr*2**c.current;c.current+=1,l.current=setTimeout(()=>{d()},e)}else n(`error`)},e.onerror=()=>{}},[]);(0,_.useEffect)(()=>(d(),()=>{l.current&&clearTimeout(l.current);let e=s.current;e&&(e.onclose=null,e.close(),s.current=null)}),[d]);let f=(0,_.useCallback)(e=>{i([]),o(!0),s.current?.readyState===WebSocket.OPEN?s.current.send(JSON.stringify(e)):(u.current=e,d())},[d]);return(0,N.jsx)(pr.Provider,{value:{status:t,operationRunning:a,messages:r,startOp:f},children:e})}function hr(){return(0,_.useContext)(pr)}function gr(e){return e.type===`progress`?e.message?`[${String(e.stage??`info`)}] ${String(e.message)}`:`[${String(e.stage??`progress`)}] ${String(e.done??0)} / ${String(e.total??`?`)}`:e.type===`done`?`✓ Completed${e.message?` — ${String(e.message)}`:``}`:e.type===`error`?`✗ Error: ${String(e.message??`unknown`)}`:e.type===`busy`?`⚠ Server busy — another operation is running`:e.type===`raw`?String(e.text??``):JSON.stringify(e)}function _r(){let[e,t]=(0,_.useState)(`update`),{startOp:n,messages:r,status:i,operationRunning:a}=hr(),o=(0,_.useRef)(null);(0,_.useEffect)(()=>{o.current&&(o.current.scrollTop=o.current.scrollHeight)},[r]);let s={disconnected:`Idle`,connecting:`Connecting…`,connected:a?`Running`:`Idle`,error:`Error`};return(0,N.jsxs)(`div`,{children:[(0,N.jsx)(`h2`,{children:`Pipeline`}),(0,N.jsxs)(`div`,{className:`pipeline-controls`,children:[(0,N.jsxs)(`select`,{value:e,onChange:e=>t(e.target.value),disabled:a,children:[(0,N.jsx)(`option`,{value:`update`,children:`Update index (virage update)`}),(0,N.jsx)(`option`,{value:`eval-generate`,children:`Generate eval dataset`}),(0,N.jsx)(`option`,{value:`evaluate`,children:`Run evaluation`})]}),(0,N.jsx)(`button`,{onClick:()=>n({op:e}),disabled:a,children:a?`Running…`:`Run`}),(0,N.jsx)(`span`,{className:`status-badge status-${i}`,children:s[i]})]}),(0,N.jsx)(`pre`,{ref:o,className:`pipeline-log`,children:r.length===0?`— Select an operation and click Run —`:r.map(gr).join(`
12
12
  `)})]})}function vr(e){return e.type===`progress`?e.message?`[${String(e.stage??`info`)}] ${String(e.message)}`:`[${String(e.stage??`progress`)}] ${String(e.done??0)} / ${String(e.total??`?`)}`:e.type===`done`?`✓ Completed${e.message?` — ${String(e.message)}`:``}`:e.type===`error`?`✗ Error: ${String(e.message??`unknown`)}`:e.type===`busy`?`⚠ Server busy — another operation is running`:e.type===`raw`?String(e.text??``):JSON.stringify(e)}function yr(){let[e,t]=(0,_.useState)([]),[n,r]=(0,_.useState)(null),[i,a]=(0,_.useState)([]),[o,s]=(0,_.useState)(null),[c,l]=(0,_.useState)(!1),[u,d]=(0,_.useState)(null),[f,p]=(0,_.useState)(``),{startOp:m,messages:h,operationRunning:g}=hr(),v=(0,_.useRef)(null);async function y(){l(!0),d(null);try{t((await M.experiments()).runs)}catch(e){d(e instanceof Error?e.message:String(e))}finally{l(!1)}}(0,_.useEffect)(()=>{y()},[]),(0,_.useEffect)(()=>{h[h.length-1]?.type===`done`&&y()},[h]),(0,_.useEffect)(()=>{v.current&&(v.current.scrollTop=v.current.scrollHeight)},[h]);async function b(e){try{await M.deleteExperiment(e),a(t=>t.filter(t=>t!==e)),n===e&&r(null),await y()}catch(e){d(e instanceof Error?e.message:String(e))}}function x(e){a(t=>t.includes(e)?t.filter(t=>t!==e):t.length<2?[...t,e]:t),s(null)}async function ee(){if(i.length===2)try{s(await M.compareExperiments(i[0],i[1]))}catch(e){d(e instanceof Error?e.message:String(e))}}let S=e=>(e*100).toFixed(1)+`%`;return(0,N.jsxs)(`div`,{children:[(0,N.jsx)(`h2`,{children:`Experiments`}),u&&(0,N.jsxs)(`div`,{className:`card error`,children:[`⚠️ `,u]}),(0,N.jsxs)(`div`,{className:`card`,children:[(0,N.jsx)(`h3`,{style:{marginTop:0},children:`New Experiment`}),(0,N.jsxs)(`div`,{className:`pipeline-controls`,children:[(0,N.jsx)(`input`,{type:`text`,placeholder:`Experiment name`,value:f,onChange:e=>p(e.target.value),disabled:g,style:{flex:1}}),(0,N.jsx)(`button`,{onClick:()=>m({op:`experiment-run`,name:f}),disabled:g||!f.trim(),children:g?`Running…`:`Run`})]}),h.length>0&&(0,N.jsx)(`pre`,{ref:v,className:`pipeline-log`,style:{marginTop:8},children:h.map(vr).join(`
13
- `)})]}),i.length===2&&(0,N.jsxs)(`div`,{className:`toolbar`,children:[(0,N.jsxs)(`button`,{onClick:()=>void ee(),children:[`Compare selected (`,i.length,`/2)`]}),(0,N.jsx)(`button`,{onClick:()=>{a([]),s(null)},children:`Clear selection`})]}),o&&(0,N.jsxs)(`div`,{className:`card compare-result badge-${o.recommendation}`,children:[(0,N.jsx)(`strong`,{children:`Comparison result`}),(0,N.jsx)(`table`,{children:(0,N.jsxs)(`tbody`,{children:[(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`td`,{children:`Baseline MRR`}),(0,N.jsx)(`td`,{children:S(o.baselineMrr)})]}),(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`td`,{children:`Candidate MRR`}),(0,N.jsx)(`td`,{children:S(o.candidateMrr)})]}),(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`td`,{children:`Delta`}),(0,N.jsxs)(`td`,{children:[o.mrrDelta>0?`+`:``,S(o.mrrDelta)]})]}),(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`td`,{children:`p-value`}),(0,N.jsx)(`td`,{children:o.pValue.toFixed(4)})]}),(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`td`,{children:`95% CI`}),(0,N.jsxs)(`td`,{children:[`[`,S(o.confidenceInterval95[0]),`,`,` `,S(o.confidenceInterval95[1]),`]`]})]}),(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`td`,{children:`Verdict`}),(0,N.jsx)(`td`,{children:(0,N.jsx)(`span`,{className:`badge badge-${o.recommendation}`,children:o.recommendation.toUpperCase()})})]})]})})]}),c?(0,N.jsx)(`div`,{className:`card`,children:`Loading…`}):e.length===0?(0,N.jsx)(`div`,{className:`card`,children:`No experiments found. Enter a name above and click Run to create one.`}):(0,N.jsxs)(`table`,{className:`experiment-table`,children:[(0,N.jsx)(`thead`,{children:(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`th`,{children:`Select`}),(0,N.jsx)(`th`,{children:`Name`}),(0,N.jsx)(`th`,{children:`Date`}),(0,N.jsx)(`th`,{children:`MRR`}),(0,N.jsx)(`th`,{children:`P@5`}),(0,N.jsx)(`th`,{children:`R@10`}),(0,N.jsx)(`th`,{children:`Hit@5`}),(0,N.jsx)(`th`,{})]})}),(0,N.jsx)(`tbody`,{children:e.map(e=>(0,N.jsxs)(N.Fragment,{children:[(0,N.jsxs)(`tr`,{className:n===e.id?`expanded-row`:``,children:[(0,N.jsx)(`td`,{children:(0,N.jsx)(`input`,{type:`checkbox`,checked:i.includes(e.id),onChange:()=>x(e.id),disabled:!i.includes(e.id)&&i.length>=2})}),(0,N.jsx)(`td`,{children:e.name}),(0,N.jsx)(`td`,{children:new Date(e.timestamp).toLocaleString()}),(0,N.jsx)(`td`,{children:S(e.evalResult.mrr)}),(0,N.jsx)(`td`,{children:S(e.evalResult.precisionAt5)}),(0,N.jsx)(`td`,{children:S(e.evalResult.recallAt10)}),(0,N.jsx)(`td`,{children:S(e.evalResult.hitRateAt5)}),(0,N.jsxs)(`td`,{className:`row-actions`,children:[(0,N.jsx)(`button`,{className:`btn-sm`,onClick:()=>r(n===e.id?null:e.id),children:n===e.id?`▲`:`▼`}),(0,N.jsx)(`button`,{className:`btn-sm btn-danger`,onClick:()=>void b(e.id),children:`✕`})]})]},e.id),n===e.id&&(0,N.jsx)(`tr`,{children:(0,N.jsx)(`td`,{colSpan:8,children:(0,N.jsxs)(`div`,{className:`experiment-detail card`,children:[(0,N.jsxs)(`p`,{children:[(0,N.jsx)(`strong`,{children:`ID:`}),` `,e.id]}),(0,N.jsxs)(`p`,{children:[(0,N.jsx)(`strong`,{children:`Queries evaluated:`}),` `,e.evalResult.queriesEvaluated]}),e.ragasResult&&(0,N.jsx)(`pre`,{children:JSON.stringify(e.ragasResult,null,2)})]})})},`${e.id}-detail`)]}))})]})]})}function br({label:e,value:t}){return(0,N.jsxs)(`div`,{className:`stat-card`,children:[(0,N.jsx)(`div`,{className:`stat-value`,children:t}),(0,N.jsx)(`div`,{className:`stat-label`,children:e})]})}function xr(e){return`${(e*100).toFixed(1)}%`}function Sr(e){return e==null?`—`:e.toFixed(3)}function Cr(e){let t=Date.now()-new Date(e).getTime();return t<6e4?`${Math.round(t/1e3)}s ago`:t<36e5?`${Math.round(t/6e4)}m ago`:`${Math.round(t/36e5)}h ago`}function wr(){let[e,t]=(0,_.useState)(null),[n,r]=(0,_.useState)([]),[i,a]=(0,_.useState)([]),[o,s]=(0,_.useState)([]),[c,l]=(0,_.useState)(!0),[u,d]=(0,_.useState)(null);async function f(){l(!0),d(null);try{let[e,n,i,o]=await Promise.all([M.analytics.stats(),M.analytics.perHour(24),M.analytics.topTerms(20),M.analytics.zeroResults(.5)]);t(e),r(n.buckets),a(i.terms),s(o.queries)}catch(e){d(e instanceof Error?e.message:String(e))}finally{l(!1)}}if((0,_.useEffect)(()=>{f()},[]),c)return(0,N.jsx)(`p`,{children:`Loading analytics…`});if(u)return(0,N.jsxs)(`p`,{className:`error`,children:[`Error: `,u]});let p=Math.max(...n.map(e=>e.count),1);return(0,N.jsxs)(`div`,{children:[(0,N.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:12,marginBottom:20},children:[(0,N.jsx)(`h2`,{style:{margin:0},children:`Search Activity`}),(0,N.jsx)(`button`,{onClick:()=>void f(),style:{marginLeft:`auto`},children:`Refresh`})]}),e&&(0,N.jsxs)(`div`,{className:`stat-grid`,style:{marginBottom:24},children:[(0,N.jsx)(br,{label:`Queries (last hour)`,value:e.queriesLastHour}),(0,N.jsx)(br,{label:`Queries (24 h)`,value:e.queriesLast24h}),(0,N.jsx)(br,{label:`Avg top similarity`,value:e.queriesLast24h>0?Sr(e.avgTopSimilarity):`—`}),(0,N.jsx)(br,{label:`Zero-result rate`,value:e.queriesLast24h>0?xr(e.zeroResultRate):`—`})]}),(0,N.jsxs)(`section`,{style:{marginBottom:28},children:[(0,N.jsx)(`h3`,{children:`Queries per Hour (last 24 h)`}),n.length===0?(0,N.jsx)(`p`,{className:`muted`,children:`No data yet.`}):(0,N.jsxs)(`table`,{className:`data-table`,children:[(0,N.jsx)(`thead`,{children:(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`th`,{children:`Hour`}),(0,N.jsx)(`th`,{children:`Queries`}),(0,N.jsx)(`th`,{style:{width:`50%`}})]})}),(0,N.jsx)(`tbody`,{children:n.map(e=>(0,N.jsxs)(`tr`,{children:[(0,N.jsxs)(`td`,{style:{fontFamily:`monospace`,whiteSpace:`nowrap`},children:[e.hour.slice(11,16),` UTC`]}),(0,N.jsx)(`td`,{children:e.count}),(0,N.jsx)(`td`,{children:(0,N.jsx)(`div`,{style:{background:`var(--accent, #6366f1)`,height:10,borderRadius:3,width:`${e.count/p*100}%`,minWidth:e.count>0?4:0}})})]},e.hour))})]})]}),(0,N.jsxs)(`section`,{style:{marginBottom:28},children:[(0,N.jsx)(`h3`,{children:`Top 20 Search Terms`}),i.length===0?(0,N.jsx)(`p`,{className:`muted`,children:`No searches recorded yet.`}):(0,N.jsxs)(`table`,{className:`data-table`,children:[(0,N.jsx)(`thead`,{children:(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`th`,{children:`#`}),(0,N.jsx)(`th`,{children:`Query`}),(0,N.jsx)(`th`,{children:`Count`})]})}),(0,N.jsx)(`tbody`,{children:i.map((e,t)=>(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`td`,{children:t+1}),(0,N.jsx)(`td`,{children:e.query_text}),(0,N.jsx)(`td`,{children:e.count})]},e.query_text))})]})]}),(0,N.jsxs)(`section`,{children:[(0,N.jsx)(`h3`,{children:`Low-Quality Queries (top similarity < 0.5)`}),o.length===0?(0,N.jsx)(`p`,{className:`muted`,children:`No low-quality queries detected.`}):(0,N.jsxs)(`table`,{className:`data-table`,children:[(0,N.jsx)(`thead`,{children:(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`th`,{children:`Query`}),(0,N.jsx)(`th`,{children:`Results`}),(0,N.jsx)(`th`,{children:`Top similarity`}),(0,N.jsx)(`th`,{children:`When`}),(0,N.jsx)(`th`,{children:`Hybrid`})]})}),(0,N.jsx)(`tbody`,{children:o.map(e=>(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`td`,{style:{maxWidth:320,overflow:`hidden`,textOverflow:`ellipsis`,whiteSpace:`nowrap`},children:e.query_text}),(0,N.jsx)(`td`,{children:e.result_count}),(0,N.jsx)(`td`,{children:Sr(e.top_similarity)}),(0,N.jsx)(`td`,{style:{whiteSpace:`nowrap`},children:Cr(e.occurred_at)}),(0,N.jsx)(`td`,{children:e.hybrid_used?`yes`:`no`})]},e.id))})]})]})]})}function Tr(){return(0,N.jsx)(mr,{children:(0,N.jsx)(kn,{children:(0,N.jsxs)(`div`,{className:`layout`,children:[(0,N.jsxs)(`nav`,{className:`sidebar`,children:[(0,N.jsx)(`div`,{className:`sidebar-logo`,children:`Virage`}),(0,N.jsx)(Nn,{to:`/`,end:!0,children:`Home`}),(0,N.jsx)(Nn,{to:`/chunks`,children:`Chunks`}),(0,N.jsx)(Nn,{to:`/search`,children:`Search`}),(0,N.jsx)(Nn,{to:`/pipeline`,children:`Pipeline`}),(0,N.jsx)(Nn,{to:`/experiments`,children:`Experiments`}),(0,N.jsx)(Nn,{to:`/analytics`,children:`Analytics`})]}),(0,N.jsx)(`main`,{className:`main-content`,children:(0,N.jsxs)(Vt,{children:[(0,N.jsx)(zt,{path:`/`,element:(0,N.jsx)(sr,{})}),(0,N.jsx)(zt,{path:`/chunks`,element:(0,N.jsx)(cr,{})}),(0,N.jsx)(zt,{path:`/search`,element:(0,N.jsx)(lr,{})}),(0,N.jsx)(zt,{path:`/pipeline`,element:(0,N.jsx)(_r,{})}),(0,N.jsx)(zt,{path:`/experiments`,element:(0,N.jsx)(yr,{})}),(0,N.jsx)(zt,{path:`/analytics`,element:(0,N.jsx)(wr,{})})]})})]})})})}var Er=document.getElementById(`root`);if(!Er)throw Error(`No #root element found`);(0,v.createRoot)(Er).render((0,N.jsx)(_.StrictMode,{children:(0,N.jsx)(Tr,{})}));
13
+ `)})]}),i.length===2&&(0,N.jsxs)(`div`,{className:`toolbar`,children:[(0,N.jsxs)(`button`,{onClick:()=>void ee(),children:[`Compare selected (`,i.length,`/2)`]}),(0,N.jsx)(`button`,{onClick:()=>{a([]),s(null)},children:`Clear selection`})]}),o&&(0,N.jsxs)(`div`,{className:`card compare-result badge-${o.recommendation}`,children:[(0,N.jsx)(`strong`,{children:`Comparison result`}),(0,N.jsx)(`table`,{children:(0,N.jsxs)(`tbody`,{children:[(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`td`,{children:`Baseline MRR`}),(0,N.jsx)(`td`,{children:S(o.baselineMrr)})]}),(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`td`,{children:`Candidate MRR`}),(0,N.jsx)(`td`,{children:S(o.candidateMrr)})]}),(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`td`,{children:`Delta`}),(0,N.jsxs)(`td`,{children:[o.mrrDelta>0?`+`:``,S(o.mrrDelta)]})]}),(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`td`,{children:`p-value`}),(0,N.jsx)(`td`,{children:o.pValue.toFixed(4)})]}),(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`td`,{children:`95% CI`}),(0,N.jsxs)(`td`,{children:[`[`,S(o.confidenceInterval95[0]),`,`,` `,S(o.confidenceInterval95[1]),`]`]})]}),(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`td`,{children:`Verdict`}),(0,N.jsx)(`td`,{children:(0,N.jsx)(`span`,{className:`badge badge-${o.recommendation}`,children:o.recommendation.toUpperCase()})})]})]})})]}),c?(0,N.jsx)(`div`,{className:`card`,children:`Loading…`}):e.length===0?(0,N.jsx)(`div`,{className:`card`,children:`No experiments found. Enter a name above and click Run to create one.`}):(0,N.jsxs)(`table`,{className:`experiment-table`,children:[(0,N.jsx)(`thead`,{children:(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`th`,{children:`Select`}),(0,N.jsx)(`th`,{children:`Name`}),(0,N.jsx)(`th`,{children:`Date`}),(0,N.jsx)(`th`,{children:`MRR`}),(0,N.jsx)(`th`,{children:`P@5`}),(0,N.jsx)(`th`,{children:`R@10`}),(0,N.jsx)(`th`,{children:`Hit@5`}),(0,N.jsx)(`th`,{})]})}),(0,N.jsx)(`tbody`,{children:e.map(e=>(0,N.jsxs)(N.Fragment,{children:[(0,N.jsxs)(`tr`,{className:n===e.id?`expanded-row`:``,children:[(0,N.jsx)(`td`,{children:(0,N.jsx)(`input`,{type:`checkbox`,checked:i.includes(e.id),onChange:()=>x(e.id),disabled:!i.includes(e.id)&&i.length>=2})}),(0,N.jsx)(`td`,{children:e.name}),(0,N.jsx)(`td`,{children:new Date(e.timestamp).toLocaleString()}),(0,N.jsx)(`td`,{children:S(e.evalResult.mrr)}),(0,N.jsx)(`td`,{children:S(e.evalResult.precisionAt5)}),(0,N.jsx)(`td`,{children:S(e.evalResult.recallAt10)}),(0,N.jsx)(`td`,{children:S(e.evalResult.hitRateAt5)}),(0,N.jsxs)(`td`,{className:`row-actions`,children:[(0,N.jsx)(`button`,{className:`btn-sm`,onClick:()=>r(n===e.id?null:e.id),children:n===e.id?`▲`:`▼`}),(0,N.jsx)(`button`,{className:`btn-sm btn-danger`,onClick:()=>void b(e.id),children:`✕`})]})]},e.id),n===e.id&&(0,N.jsx)(`tr`,{children:(0,N.jsx)(`td`,{colSpan:8,children:(0,N.jsxs)(`div`,{className:`experiment-detail card`,children:[(0,N.jsxs)(`p`,{children:[(0,N.jsx)(`strong`,{children:`ID:`}),` `,e.id]}),(0,N.jsxs)(`p`,{children:[(0,N.jsx)(`strong`,{children:`Queries evaluated:`}),` `,e.evalResult.queriesEvaluated]}),e.ragasResult&&(0,N.jsx)(`pre`,{children:JSON.stringify(e.ragasResult,null,2)})]})})},`${e.id}-detail`)]}))})]})]})}function br({label:e,value:t}){return(0,N.jsxs)(`div`,{className:`stat-card`,children:[(0,N.jsx)(`div`,{className:`stat-value`,children:t}),(0,N.jsx)(`div`,{className:`stat-label`,children:e})]})}function xr(e){return`${(e*100).toFixed(1)}%`}function Sr(e){return e==null?`—`:e.toFixed(3)}function Cr(e){let t=Date.now()-new Date(e).getTime();return t<6e4?`${Math.round(t/1e3)}s ago`:t<36e5?`${Math.round(t/6e4)}m ago`:`${Math.round(t/36e5)}h ago`}function wr(){let[e,t]=(0,_.useState)(null),[n,r]=(0,_.useState)([]),[i,a]=(0,_.useState)([]),[o,s]=(0,_.useState)([]),[c,l]=(0,_.useState)(!0),[u,d]=(0,_.useState)(null);async function f(){l(!0),d(null);try{let[e,n,i,o]=await Promise.all([M.analytics.stats(),M.analytics.perHour(24),M.analytics.topTerms(20),M.analytics.zeroResults(.5)]);t(e),r(n.buckets),a(i.terms),s(o.queries)}catch(e){d(e instanceof Error?e.message:String(e))}finally{l(!1)}}if((0,_.useEffect)(()=>{f()},[]),c)return(0,N.jsx)(`p`,{children:`Loading analytics…`});if(u)return(0,N.jsxs)(`p`,{className:`error`,children:[`Error: `,u]});let p=Math.max(...n.map(e=>e.count),1);return(0,N.jsxs)(`div`,{children:[(0,N.jsxs)(`div`,{style:{display:`flex`,alignItems:`center`,gap:12,marginBottom:20},children:[(0,N.jsx)(`h2`,{style:{margin:0},children:`Search Activity`}),(0,N.jsx)(`button`,{onClick:()=>void f(),style:{marginLeft:`auto`},children:`Refresh`})]}),e&&(0,N.jsxs)(`div`,{className:`stat-grid`,style:{marginBottom:24},children:[(0,N.jsx)(br,{label:`Queries (last hour)`,value:e.queriesLastHour}),(0,N.jsx)(br,{label:`Queries (24 h)`,value:e.queriesLast24h}),(0,N.jsx)(br,{label:`Avg top similarity`,value:e.queriesLast24h>0?Sr(e.avgTopSimilarity):`—`}),(0,N.jsx)(br,{label:`Zero-result rate`,value:e.queriesLast24h>0?xr(e.zeroResultRate):`—`})]}),(0,N.jsxs)(`section`,{style:{marginBottom:28},children:[(0,N.jsx)(`h3`,{children:`Queries per Hour (last 24 h)`}),n.length===0?(0,N.jsx)(`p`,{className:`muted`,children:`No data yet.`}):(0,N.jsxs)(`table`,{className:`data-table`,children:[(0,N.jsx)(`thead`,{children:(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`th`,{children:`Hour`}),(0,N.jsx)(`th`,{children:`Queries`}),(0,N.jsx)(`th`,{style:{width:`50%`}})]})}),(0,N.jsx)(`tbody`,{children:n.map(e=>(0,N.jsxs)(`tr`,{children:[(0,N.jsxs)(`td`,{style:{fontFamily:`monospace`,whiteSpace:`nowrap`},children:[e.hour.slice(11,16),` UTC`]}),(0,N.jsx)(`td`,{children:e.count}),(0,N.jsx)(`td`,{children:(0,N.jsx)(`div`,{style:{background:`var(--accent, #6366f1)`,height:10,borderRadius:3,width:`${e.count/p*100}%`,minWidth:e.count>0?4:0}})})]},e.hour))})]})]}),(0,N.jsxs)(`section`,{style:{marginBottom:28},children:[(0,N.jsx)(`h3`,{children:`Top 20 Search Terms`}),i.length===0?(0,N.jsx)(`p`,{className:`muted`,children:`No searches recorded yet.`}):(0,N.jsxs)(`table`,{className:`data-table`,children:[(0,N.jsx)(`thead`,{children:(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`th`,{children:`#`}),(0,N.jsx)(`th`,{children:`Query`}),(0,N.jsx)(`th`,{children:`Count`})]})}),(0,N.jsx)(`tbody`,{children:i.map((e,t)=>(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`td`,{children:t+1}),(0,N.jsx)(`td`,{children:e.query_text}),(0,N.jsx)(`td`,{children:e.count})]},e.query_text))})]})]}),(0,N.jsxs)(`section`,{children:[(0,N.jsx)(`h3`,{children:`Low-Quality Queries (top similarity < 0.5)`}),o.length===0?(0,N.jsx)(`p`,{className:`muted`,children:`No low-quality queries detected.`}):(0,N.jsxs)(`table`,{className:`data-table`,children:[(0,N.jsx)(`thead`,{children:(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`th`,{children:`Query`}),(0,N.jsx)(`th`,{children:`Results`}),(0,N.jsx)(`th`,{children:`Top similarity`}),(0,N.jsx)(`th`,{children:`When`}),(0,N.jsx)(`th`,{children:`Hybrid`})]})}),(0,N.jsx)(`tbody`,{children:o.map(e=>(0,N.jsxs)(`tr`,{children:[(0,N.jsx)(`td`,{style:{maxWidth:320,overflow:`hidden`,textOverflow:`ellipsis`,whiteSpace:`nowrap`},children:e.query_text}),(0,N.jsx)(`td`,{children:e.result_count}),(0,N.jsx)(`td`,{children:Sr(e.top_similarity)}),(0,N.jsx)(`td`,{style:{whiteSpace:`nowrap`},children:Cr(e.occurred_at)}),(0,N.jsx)(`td`,{children:e.hybrid_used?`yes`:`no`})]},e.id))})]})]})]})}var Tr=class extends _.Component{state={error:null};static getDerivedStateFromError(e){return{error:e}}render(){return this.state.error?(0,N.jsxs)(`div`,{style:{padding:32,fontFamily:`monospace`,maxWidth:640,margin:`48px auto`},children:[(0,N.jsx)(`h2`,{style:{color:`#c00`},children:`Dashboard error`}),(0,N.jsx)(`pre`,{style:{background:`#f5f5f5`,padding:16,borderRadius:4,overflowX:`auto`},children:this.state.error.message}),(0,N.jsxs)(`p`,{children:[`Check the terminal where `,(0,N.jsx)(`code`,{children:`virage dashboard`}),` is running for server logs. Run with `,(0,N.jsx)(`code`,{children:`--verbose`}),` for request-level detail.`]}),(0,N.jsx)(`button`,{onClick:()=>this.setState({error:null}),children:`Try again`})]}):this.props.children}};function Er(){return(0,N.jsx)(Tr,{children:(0,N.jsx)(mr,{children:(0,N.jsx)(kn,{children:(0,N.jsxs)(`div`,{className:`layout`,children:[(0,N.jsxs)(`nav`,{className:`sidebar`,children:[(0,N.jsx)(`div`,{className:`sidebar-logo`,children:`Virage`}),(0,N.jsx)(Nn,{to:`/`,end:!0,children:`Home`}),(0,N.jsx)(Nn,{to:`/chunks`,children:`Chunks`}),(0,N.jsx)(Nn,{to:`/search`,children:`Search`}),(0,N.jsx)(Nn,{to:`/pipeline`,children:`Pipeline`}),(0,N.jsx)(Nn,{to:`/experiments`,children:`Experiments`}),(0,N.jsx)(Nn,{to:`/analytics`,children:`Analytics`})]}),(0,N.jsx)(`main`,{className:`main-content`,children:(0,N.jsxs)(Vt,{children:[(0,N.jsx)(zt,{path:`/`,element:(0,N.jsx)(sr,{})}),(0,N.jsx)(zt,{path:`/chunks`,element:(0,N.jsx)(cr,{})}),(0,N.jsx)(zt,{path:`/search`,element:(0,N.jsx)(lr,{})}),(0,N.jsx)(zt,{path:`/pipeline`,element:(0,N.jsx)(_r,{})}),(0,N.jsx)(zt,{path:`/experiments`,element:(0,N.jsx)(yr,{})}),(0,N.jsx)(zt,{path:`/analytics`,element:(0,N.jsx)(wr,{})})]})})]})})})})}var Dr=document.getElementById(`root`);if(!Dr)throw Error(`No #root element found`);(0,v.createRoot)(Dr).render((0,N.jsx)(_.StrictMode,{children:(0,N.jsx)(Er,{})}));
package/dist/index.html CHANGED
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>RAG Dashboard</title>
7
- <script type="module" crossorigin src="/assets/index-BbURVT4r.js"></script>
7
+ <script type="module" crossorigin src="/assets/index-CiRJKgxZ.js"></script>
8
8
  <link rel="stylesheet" crossorigin href="/assets/index-CzLlIPEZ.css">
9
9
  </head>
10
10
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vivantel/virage-dashboard",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "type": "module",
5
5
  "description": "React dashboard web app for the Virage RAG pipeline — served by virage-cli",
6
6
  "author": "Vivantel",