threadshelf 1.2.0 → 1.2.1

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/CHANGELOG.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  All notable changes to ThreadShelf are documented here.
4
4
 
5
- ## Unreleased
5
+ ## 1.2.1 — 2026-09-20
6
6
 
7
7
  ### Install with `npx threadshelf`
8
8
 
@@ -37,6 +37,11 @@ All notable changes to ThreadShelf are documented here.
37
37
  - Add `.github/workflows/publish.yml`: tag-driven release on `v*` that runs the
38
38
  test suite and publishes through npm Trusted Publishing (OIDC), with no npm
39
39
  token stored in the repository.
40
+ - Reach the bundled CLIs from an installed package:
41
+ `npx threadshelf search|ingest|parse`. They were compiled into the tarball but
42
+ had no entry point, so only a clone could run them. Their usage messages now
43
+ name the invocation that applies — `npm run search --` from a checkout,
44
+ `npx threadshelf search` from an install.
40
45
 
41
46
  ## 1.2.0 — 2026-09-13
42
47
 
package/README.md CHANGED
@@ -290,6 +290,15 @@ That is the whole install. The package ships the prebuilt web UI, so there is
290
290
  nothing to compile and no repository to clone. Pick another port with
291
291
  `npx threadshelf 3001`, and see `npx threadshelf --help` for the rest.
292
292
 
293
+ The terminal tools come with it:
294
+
295
+ ```bash
296
+ npx threadshelf search "what did I decide about caching?"
297
+ npx threadshelf ingest ./my-exports my-collection
298
+ npx threadshelf parse ./chatgpt-export.json
299
+ npx threadshelf-mcp # stdio MCP server
300
+ ```
301
+
293
302
  Your archive is **never** stored inside the npm package: an `npx` install
294
303
  directory is disposable and npm may wipe it at any time. Persistent data lives
295
304
  in a per-user directory instead, so upgrading or clearing the npm cache leaves
@@ -3,30 +3,48 @@
3
3
  * `npx threadshelf` entrypoint.
4
4
  *
5
5
  * Plain JavaScript on purpose: the published package must not need tsx or a
6
- * TypeScript toolchain at runtime. It parses a couple of flags, then hands over
7
- * to the compiled server in dist/.
6
+ * TypeScript toolchain at runtime. It dispatches to one of the compiled CLIs in
7
+ * dist/, defaulting to the web server.
8
+ *
9
+ * The subcommands exist so an installed package is not poorer than a clone:
10
+ * from the repository these are `npm run parse|ingest|search|mcp`, and without
11
+ * them the compiled CLIs would ship but be unreachable.
8
12
  */
9
13
  import { createRequire } from 'node:module';
14
+ import { fileURLToPath } from 'node:url';
10
15
 
11
16
  const require = createRequire(import.meta.url);
12
17
  const pkg = require('../package.json');
13
18
 
19
+ const SUBCOMMANDS = {
20
+ parse: '../dist/src/cli.js',
21
+ ingest: '../dist/src/ingest-cli.js',
22
+ search: '../dist/src/search-cli.js',
23
+ };
24
+
14
25
  const args = process.argv.slice(2);
15
26
 
16
27
  const usage = `ThreadShelf ${pkg.version} - local semantic search for your AI chats
17
28
 
18
29
  Usage:
19
- npx threadshelf [port] Start the web UI and API (default port 3000)
30
+ npx threadshelf [port] Start the web UI and API (default port 3000)
31
+ npx threadshelf search "<query>" [...] Search the archive from the terminal
32
+ npx threadshelf ingest <folder> [...] Ingest a folder of exports
33
+ npx threadshelf parse <file> [...] Parse one export to normalized JSON
34
+ npx threadshelf-mcp Start the MCP stdio server
20
35
 
21
36
  Options:
22
37
  -p, --port <port> Port to listen on (default 3000, or $PORT)
23
38
  --host <host> Interface to bind (default 127.0.0.1, loopback only)
24
39
  --data-dir <d> Directory for persistent data
25
- (default: %LOCALAPPDATA%\ThreadShelf on Windows, ~/.threadshelf elsewhere)
40
+ (default: %LOCALAPPDATA%\\ThreadShelf on Windows, ~/.threadshelf elsewhere)
26
41
  --where Print the resolved data and package directories, then exit
27
42
  -v, --version Print the version
28
43
  -h, --help Show this help
29
44
 
45
+ Each subcommand takes its own flags; run it with --help for those. Pass
46
+ --data-dir before the subcommand, e.g. npx threadshelf --data-dir D:\\shelf search "x".
47
+
30
48
  Environment:
31
49
  PORT, HOST, THREADSHELF_DATA_DIR, LANCEDB_PATH and the other documented
32
50
  overrides keep working and take precedence over the defaults.
@@ -34,6 +52,8 @@ Environment:
34
52
 
35
53
  let port = '';
36
54
  let showWhere = false;
55
+ let subcommand = '';
56
+ let subcommandArgs = [];
37
57
 
38
58
  const takeValue = (flag, index) => {
39
59
  const value = args[index + 1];
@@ -46,7 +66,12 @@ const takeValue = (flag, index) => {
46
66
 
47
67
  for (let i = 0; i < args.length; i += 1) {
48
68
  const arg = args[i];
49
- if (arg === '-h' || arg === '--help') {
69
+ if (Object.hasOwn(SUBCOMMANDS, arg)) {
70
+ // Everything after the subcommand belongs to it, untouched.
71
+ subcommand = arg;
72
+ subcommandArgs = args.slice(i + 1);
73
+ break;
74
+ } else if (arg === '-h' || arg === '--help') {
50
75
  console.log(usage);
51
76
  process.exit(0);
52
77
  } else if (arg === '-v' || arg === '--version') {
@@ -80,8 +105,15 @@ if (showWhere) {
80
105
  process.exit(0);
81
106
  }
82
107
 
83
- // server.ts reads process.argv[2] as a port; it is already normalised into
84
- // process.env.PORT above, so hide the raw argv from it.
85
- process.argv = [process.argv[0], process.argv[1]];
86
-
87
- await import('../dist/src/server.js');
108
+ if (subcommand) {
109
+ // The compiled CLIs read process.argv.slice(2) at module load, so present
110
+ // them the argv they would have seen if they had been invoked directly.
111
+ const entry = fileURLToPath(new URL(SUBCOMMANDS[subcommand], import.meta.url));
112
+ process.argv = [process.argv[0], entry, ...subcommandArgs];
113
+ await import(SUBCOMMANDS[subcommand]);
114
+ } else {
115
+ // server.ts reads process.argv[2] as a port; it is already normalised into
116
+ // process.env.PORT above, so hide the raw argv from it.
117
+ process.argv = [process.argv[0], process.argv[1]];
118
+ await import('../dist/src/server.js');
119
+ }
package/dist/src/cli.js CHANGED
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env tsx
2
2
  import { parseFile } from './parser.js';
3
+ import { invocation } from './paths.js';
3
4
  const file = process.argv[2];
4
5
  if (!file) {
5
- console.error('Usage: npm run parse -- <file> -- [--no-user] [--no-thinking] [--no-ai]');
6
+ console.error(`Usage: ${invocation('parse')} <file> -- [--no-user] [--no-thinking] [--no-ai]`);
6
7
  process.exit(1);
7
8
  }
8
9
  const options = {
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env tsx
2
2
  import { basename, resolve } from 'path';
3
3
  import { ingestFolder } from './ingest.js';
4
+ import { invocation } from './paths.js';
4
5
  import { watchFolder } from './watch.js';
5
6
  import { normalizeCollectionName } from './validation.js';
6
7
  import { recoverPendingIndexes, startIndexRecovery } from './store.js';
@@ -33,7 +34,7 @@ for (let i = 0; i < args.length; i++) {
33
34
  const folder = positional[0];
34
35
  const collectionArg = positional[1] ?? 'chunks';
35
36
  if (!folder || positional.length > 2) {
36
- console.error('Usage: npm run ingest -- <folder> [collection] -- [--clear] [--watch] [--debounce <ms>]');
37
+ console.error(`Usage: ${invocation('ingest')} <folder> [collection] -- [--clear] [--watch] [--debounce <ms>]`);
37
38
  process.exit(1);
38
39
  }
39
40
  let collection;
package/dist/src/paths.js CHANGED
@@ -72,6 +72,12 @@ const LAYOUT = {
72
72
  modelCache: { repo: ['.threadshelf', 'model-cache'], user: ['model-cache'] },
73
73
  env: { repo: ['.env'], user: ['.env'] },
74
74
  };
75
+ /**
76
+ * How the user would invoke one of the bundled CLIs, for usage messages.
77
+ * `npm run search --` is right from a clone and meaningless to somebody who
78
+ * installed the package, so print whichever actually applies.
79
+ */
80
+ export const invocation = (command) => isRepoCheckout() ? `npm run ${command} --` : `npx threadshelf ${command}`;
75
81
  /** Absolute path of a persistent file or directory. Callers still apply their own env overrides. */
76
82
  export const dataPath = (key) => {
77
83
  const entry = LAYOUT[key];
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env tsx
2
+ import { invocation } from './paths.js';
2
3
  import { searchAcrossCollections } from './services/search.js';
3
4
  import { recoverPendingIndexes } from './store.js';
4
5
  import { normalizeCollectionSelector, normalizeCount, normalizeDateRange, normalizeOptionalString, normalizeQuery, normalizeRoles, normalizeSearchMode, } from './validation.js';
5
- const USAGE = `Usage: npm run search -- "<query>" -- [options]
6
+ const USAGE = `Usage: ${invocation('search')} "<query>" -- [options]
6
7
 
7
8
  Options:
8
9
  --collection <name> Collection to search, or "all" (default: all)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "threadshelf",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "Local semantic search, backup, and RAG for your AI chats — ChatGPT, Claude, Google AI Studio, OpenRouter, LM Studio, and Grok — searchable from a web UI, HTTP API, or MCP",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -14,7 +14,7 @@ Error generating stack: `+e.message+`
14
14
  `).trim()+`
15
15
  `},_o=e=>(e||`conversation`).toLowerCase().replace(/[^a-z0-9]+/g,`-`).replace(/^-+|-+$/g,``).slice(0,60)||`conversation`,vo=(e,t,n=`text/markdown;charset=utf-8`)=>{let r=new Blob([t],{type:n}),i=URL.createObjectURL(r),a=document.createElement(`a`);a.href=i,a.download=e,document.body.appendChild(a),a.click(),a.remove(),URL.revokeObjectURL(i)},yo=e=>!e||e<=0?`—`:e>=1024**3?`${(e/1024**3).toFixed(e>=10*1024**3?0:1)} GB`:e>=1024**2?`${Math.round(e/1024**2)} MB`:`${Math.round(e/1024)} KB`,bo=e=>!e||e<=0?`0`:e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${Math.round(e/1e3)}k`:String(e);function xo({variant:e=`full`}){let t=B(e=>e.theme),n=B(e=>e.toggleTheme),r=t===`dark`,i=r?`light`:`dark`;return(0,R.jsxs)(`button`,{className:`theme-toggle ${e}`,onClick:n,title:`Switch to ${i} theme`,"aria-label":`Switch to ${i} theme`,children:[(0,R.jsx)(`span`,{className:`tt-ico`,children:r?W.sun:W.moon}),e===`full`&&(0,R.jsx)(`span`,{className:`tt-label`,children:r?`Light`:`Dark`})]})}var So=1,Co=4e3,wo=Ta((e,t)=>({toasts:[],confirmState:null,pushToast:(n,r=`info`)=>{let i=So++;e(e=>({toasts:[...e.toasts,{id:i,type:r,text:n}]})),setTimeout(()=>t().dismissToast(i),Co)},dismissToast:t=>{e(e=>({toasts:e.toasts.filter(e=>e.id!==t)}))},confirm:n=>new Promise(r=>{let i=t().confirmState;i&&i.resolve(!1),e({confirmState:{...n,id:So++,resolve:r}})}),resolveConfirm:n=>{let r=t().confirmState;r&&r.resolve(n),e({confirmState:null})}})),q={info:e=>wo.getState().pushToast(e,`info`),success:e=>wo.getState().pushToast(e,`success`),error:e=>wo.getState().pushToast(e,`error`)},To=e=>wo.getState().confirm(e),Eo=e=>{if(!e)return``;let t=e.split(/[\\/]/);return t[t.length-1]||e};function Do({detailed:e=!1}){let[t,n]=(0,L.useState)(null),[r,i]=(0,L.useState)(null),[a,o]=(0,L.useState)(!1),s=(0,L.useCallback)(async()=>{try{let[e,t]=await Promise.all([H.generationRuntime(),H.generationLlamaLogs().catch(()=>null)]);n(e.runtime),t&&i(t)}catch{n(null)}},[]);(0,L.useEffect)(()=>{let e=window.setTimeout(()=>void s(),0),t=window.setInterval(()=>void s(),3e3),r=e=>{if(e instanceof CustomEvent&&e.detail){n(e.detail),H.generationLlamaLogs().then(i).catch(()=>void 0);return}s()};return window.addEventListener(`threadshelf:generation-runtime-changed`,r),()=>{window.clearTimeout(e),window.clearInterval(t),window.removeEventListener(`threadshelf:generation-runtime-changed`,r)}},[s]);let c=async()=>{o(!0);try{let e=await H.ejectGenerationModel(t?.model);n(e.runtime),window.dispatchEvent(new Event(`threadshelf:generation-runtime-changed`)),q.success(`llama.cpp model ejected from memory.`)}catch(e){q.error(e instanceof Error?e.message:`Could not eject the model.`)}finally{o(!1)}},l=t&&(t.state===`starting`||t.state===`ready`||t.state===`external`&&!!t.model),u=r?.devices[0]?.id.replace(/\d+$/,``).toUpperCase(),d=!t||!r?`unknown`:t.state===`stopped`?r.devices.length>0?`idle · GPU (${u||`accelerator`})`:`idle · CPU`:r.offload.mode===`hybrid`?`hybrid ${r.offload.gpuPercent}% GPU`:r.offload.mode===`gpu`?`GPU · ${u||`accelerator`}`:r.offload.mode===`cpu`?`CPU`:r.devices.length>0?`GPU · ${u||`accelerator`}`:r.deviceDetectionSupported?`CPU`:`unknown`,f=[r?.executable?`Selected executable: ${r.executable}`:void 0,`Detected compute: ${d}`,``,`Check for a newer stable llama.cpp build:`,`npm run setup:llama -- -- --check`,``,`Install CPU:`,`npm run setup:llama -- -- --install --variant cpu`,``,`Install NVIDIA CUDA:`,`npm run setup:llama -- -- --install --variant cuda`,``,`Install Vulkan GPU:`,`npm run setup:llama -- -- --install --variant vulkan`,``,`macOS: use the cpu variant; Metal is included automatically.`].filter(e=>e!==void 0).join(`
16
16
  `),p=Object.entries(r?.offload.deviceBufferMiB??{}).filter(([e])=>!e.toUpperCase().startsWith(`CPU`)).reduce((e,[,t])=>e+t,0),m=(r?.profile??[]).filter(e=>e.applied),h=m.length>0&&t?.state!==`stopped`?[d,...m.map(e=>`${e.setting} ${e.value}`),p>0?`GPU weights ${(p/1024).toFixed(1)} GiB`:``].filter(Boolean).join(` · `):``,g=(r?.profile??[]).filter(e=>e.note).map(e=>`${e.setting}: ${e.applied?``:`skipped, `}${e.note}`).join(`
17
- `);return(0,R.jsxs)(`div`,{className:`global-runtime`,"data-state":t?.state||`unknown`,title:t?.detail,children:[(0,R.jsx)(`span`,{className:`runtime-dot`,"aria-hidden":`true`}),(0,R.jsxs)(`span`,{className:`global-runtime-copy`,children:[(0,R.jsxs)(`b`,{children:[(0,R.jsx)(`span`,{children:t?.state===`starting`?`llama.cpp · loading`:`llama.cpp`}),(0,R.jsx)(`em`,{children:d}),(0,R.jsx)(`span`,{className:`runtime-install-help`,tabIndex:0,role:`img`,"aria-label":`llama.cpp CPU and GPU install commands`,title:f,children:`?`})]}),(0,R.jsx)(`span`,{children:t?.model?Eo(t.model):t?`no model loaded`:`status unavailable`}),e&&t?.detail&&(0,R.jsx)(`small`,{children:t.detail}),e&&h&&(0,R.jsx)(`small`,{className:`runtime-profile`,title:g||void 0,children:h})]}),l&&(0,R.jsx)(`button`,{type:`button`,disabled:a,onClick:()=>void c(),children:a?`…`:`Eject`})]})}var Oo=[{id:`/search`,viewKey:`search`,label:`Search archive`,icon:W.search},{id:`/insights`,viewKey:`insights`,label:`Insights`,icon:W.chart}],ko=[{id:`/indexing`,viewKey:`indexing`,label:`Add data`,icon:W.download},{id:`/mcp`,viewKey:`mcp`,label:`MCP`,icon:W.plug},{id:`/settings`,viewKey:`settings`,label:`Settings`,icon:W.settings}];function Ao({view:e,setView:t,activeColl:n,setActiveColl:r,onNewChat:i,onNewPrivateChat:a,onOpenChat:o,activeChatId:s,onCmdK:c,onNewColl:l,onDeleteCollection:u,collections:d,stats:f}){let[p,m]=(0,L.useState)(!1),[h,g]=(0,L.useState)(e===`chat`),{data:_,isLoading:v,refetch:y}=Ha(),b=_?.threads??[],x=d.filter(e=>e!==`all`),S=x.reduce((e,t)=>e+(f[t]?.files??0),0),C=x.reduce((e,t)=>e+(f[t]?.chunks??0),0),w=e=>{let t=f[e];return(t?.chunks??0)===0&&(t?.files??0)===0&&(t?.conversations??0)===0},T=x.filter(e=>w(e)&&e!==n),E=x.filter(e=>p||!w(e)||e===n),D=e=>{let t=e?.conversations??0;return t>0?`${t.toLocaleString()} thread${t===1?``:`s`}`:`${(e?.files??0).toLocaleString()} files`},O=e=>{let t=e?.conversations??0;return`${t>0?`${t.toLocaleString()} conversation${t===1?``:`s`}`:`${(e?.files??0).toLocaleString()} files`} · ${(e?.chunks??0).toLocaleString()} indexed chunks`};return(0,R.jsxs)(`aside`,{className:`sidebar`,children:[(0,R.jsxs)(`div`,{className:`sb-brand`,children:[(0,R.jsx)(`div`,{className:`sb-logo`,"aria-hidden":`true`}),(0,R.jsxs)(`div`,{className:`sb-name`,children:[(0,R.jsx)(`b`,{children:`ThreadShelf`}),(0,R.jsxs)(`span`,{children:[`v`,`1.2.0`,` · local`]})]})]}),(0,R.jsxs)(`section`,{className:`sb-chat-panel`,"aria-label":`Conversations`,children:[(0,R.jsxs)(`div`,{className:`sb-chat-create`,children:[(0,R.jsxs)(`button`,{id:`sidebarNewChatButton`,className:`sb-new-chat`,onClick:i,children:[W.spark,(0,R.jsx)(`span`,{children:`New chat`})]}),(0,R.jsx)(`button`,{id:`sidebarPrivateChatButton`,className:`sb-private-chat`,"aria-label":`Start private conversation`,title:`Start a private chat that is cleared with this tab`,onClick:a,children:W.ghost})]}),(0,R.jsxs)(`div`,{className:`sb-chat-history`,children:[(0,R.jsxs)(`button`,{id:`sidebarChatHistoryToggle`,className:`sb-chat-history-toggle`,"aria-expanded":h,onClick:()=>{let e=!h;g(e),e&&y()},children:[(0,R.jsx)(`span`,{className:`ico`,children:W.chat}),(0,R.jsx)(`span`,{className:`label`,children:`Your chats`}),b.length>0&&(0,R.jsx)(`em`,{children:b.length}),(0,R.jsx)(`span`,{className:`sb-chat-chevron`,"aria-hidden":`true`,children:`▾`})]}),h&&(0,R.jsxs)(`div`,{className:`sb-chat-history-list`,children:[v&&(0,R.jsx)(`span`,{className:`sb-chat-history-empty`,children:`Loading chats…`}),!v&&b.length===0&&(0,R.jsx)(`span`,{className:`sb-chat-history-empty`,children:`No saved chats yet.`}),b.map(e=>(0,R.jsxs)(`button`,{className:`sb-chat-item`,"data-active":e.id===s,title:e.title,onClick:()=>o(e.id),children:[(0,R.jsx)(`strong`,{children:e.title}),(0,R.jsx)(`span`,{title:`${e.model?`${no(e.model)} · `:``}${e.turnCount} messages`,children:oo(e.updatedAt)||`just now`})]},e.id))]})]})]}),(0,R.jsxs)(`button`,{className:`sb-cmdk`,onClick:c,children:[W.search,(0,R.jsx)(`span`,{children:`Quick jump…`}),(0,R.jsx)(`kbd`,{children:`Ctrl+K`})]}),(0,R.jsx)(`div`,{className:`sb-section`,children:(0,R.jsx)(`span`,{children:`Library`})}),(0,R.jsx)(`nav`,{className:`sb-list`,style:{flex:`0 0 auto`},children:Oo.map(n=>(0,R.jsxs)(`button`,{id:n.viewKey===`indexing`?`indexingNavBtn`:void 0,className:`sb-item`,"data-active":e===n.viewKey,onClick:()=>t(n.id),children:[(0,R.jsx)(`span`,{className:`ico`,children:n.icon}),(0,R.jsx)(`span`,{className:`label`,children:n.label})]},n.id))}),(0,R.jsxs)(`div`,{className:`sb-section`,children:[(0,R.jsx)(`span`,{children:`Archive collections`}),(0,R.jsx)(`button`,{title:`New collection`,onClick:l,children:W.plus})]}),(0,R.jsxs)(`div`,{className:`sb-list sb-collections`,children:[(0,R.jsxs)(`button`,{id:`collection-all`,className:`sb-coll`,"data-active":n===`all`,onClick:()=>r(`all`),children:[(0,R.jsx)(`span`,{className:`sb-coll-dot`,style:{background:`var(--accent)`}}),(0,R.jsxs)(`span`,{className:`sb-coll-meta`,children:[(0,R.jsx)(`b`,{children:`All collections`}),(0,R.jsxs)(`span`,{children:[S,` files · `,C.toLocaleString(),` chunks`]})]})]}),E.map(e=>{let t=f[e];return(0,R.jsxs)(`div`,{className:`sb-coll-row`,children:[(0,R.jsxs)(`button`,{id:`collection-${e}`,className:`sb-coll`,"data-active":n===e,onClick:()=>r(e),children:[(0,R.jsx)(`span`,{className:`sb-coll-dot`}),(0,R.jsxs)(`span`,{className:`sb-coll-meta`,children:[(0,R.jsx)(`b`,{children:Za(e)}),(0,R.jsxs)(`span`,{title:O(t),children:[D(t),` · `,(t?.chunks??0).toLocaleString(),` chunks`]})]})]}),e!==`chunks`&&e!==`threadshelf_conversations`&&(0,R.jsx)(`button`,{id:`delete-collection-${e}`,className:`sb-coll-delete`,title:`Delete ${Za(e)}`,"aria-label":`Delete ${Za(e)}`,onClick:t=>{t.stopPropagation(),u(e)},children:W.trash})]},e)}),T.length>0&&(0,R.jsx)(`button`,{id:`toggleEmptyCollections`,className:`sb-coll-empty-toggle`,onClick:()=>m(e=>!e),children:p?`Hide empty collections`:`Show ${T.length} empty`})]}),(0,R.jsx)(`div`,{className:`sb-section sb-manage-section`,children:(0,R.jsx)(`span`,{children:`Manage`})}),(0,R.jsx)(`nav`,{className:`sb-list sb-manage-list`,children:ko.map(n=>(0,R.jsxs)(`button`,{id:n.viewKey===`indexing`?`indexingNavBtn`:void 0,className:`sb-item`,"data-active":e===n.viewKey,onClick:()=>t(n.id),children:[(0,R.jsx)(`span`,{className:`ico`,children:n.icon}),(0,R.jsx)(`span`,{className:`label`,children:n.label})]},n.id))}),(0,R.jsxs)(`div`,{className:`sb-footer`,children:[(0,R.jsx)(Do,{}),(0,R.jsxs)(`div`,{className:`sb-footer-row`,children:[(0,R.jsxs)(`span`,{className:`sb-status`,title:`Local backend · paraphrase-multilingual embeddings · LanceDB storage. Model runtime and eject live in the chat model menu.`,children:[`local · :`,window.location.port||`80`]}),(0,R.jsx)(xo,{variant:`icon`})]})]})]})}function jo({open:e,onClose:t,setView:n,onNewChat:r,setActiveColl:i,collections:a,stats:o,onNewColl:s,onRefresh:c}){let[l,u]=(0,L.useState)(``),[d,f]=(0,L.useState)(0),p=(0,L.useRef)(null);if((0,L.useEffect)(()=>{if(e){let e=window.setTimeout(()=>{u(``),f(0),p.current?.focus()},30);return()=>window.clearTimeout(e)}},[e]),!e)return null;let m=[{label:`Go to`,items:[{id:`go-search`,label:`Search archive`,icon:W.search,action:()=>n(`/search`)},{id:`go-chat`,label:`Chat history`,icon:W.chat,action:()=>n(`/chat`)},{id:`go-insights`,label:`Insights`,icon:W.chart,action:()=>n(`/insights`)},{id:`go-indexing`,label:`Indexing`,icon:W.download,action:()=>n(`/indexing`)},{id:`go-mcp`,label:`MCP`,icon:W.plug,action:()=>n(`/mcp`)},{id:`go-settings`,label:`Settings`,icon:W.settings,action:()=>n(`/settings`)}]},{label:`Collections`,items:[{id:`coll-all`,label:`All collections`,icon:W.scope,action:()=>i(`all`)},...a.filter(e=>e!==`all`).map(e=>({id:`coll-${e}`,label:Za(e),icon:W.database,meta:`${o[e]?.files??0} files`,action:()=>i(e)}))]},{label:`Actions`,items:[{id:`a-chat`,label:`New chat`,icon:W.spark,action:r},{id:`a-new`,label:`New collection…`,icon:W.plus,action:s},{id:`a-index`,label:`Index a folder`,icon:W.folder,action:()=>n(`/indexing`)},{id:`a-refresh`,label:`Refresh stats`,icon:W.refresh,action:c}]}].map(e=>({...e,items:e.items.filter(e=>!l||e.label.toLowerCase().includes(l.toLowerCase()))})).filter(e=>e.items.length>0),h=m.flatMap(e=>e.items),g=Math.min(d,h.length-1),_=e=>{e.key===`ArrowDown`?(e.preventDefault(),f(e=>(e+1)%Math.max(1,h.length))):e.key===`ArrowUp`?(e.preventDefault(),f(e=>(e-1+h.length)%Math.max(1,h.length))):e.key===`Enter`?(e.preventDefault(),h[g]?.action(),t()):e.key===`Escape`&&t()},v=0;return(0,R.jsx)(`div`,{className:`scrim`,onClick:t,children:(0,R.jsxs)(`div`,{className:`cmdk`,onClick:e=>e.stopPropagation(),children:[(0,R.jsxs)(`div`,{className:`cmdk-input`,children:[W.searchLg,(0,R.jsx)(`input`,{ref:p,value:l,onChange:e=>{u(e.target.value),f(0)},onKeyDown:_,placeholder:`Search commands, collections, or actions…`}),(0,R.jsx)(`kbd`,{children:`ESC`})]}),(0,R.jsxs)(`div`,{className:`cmdk-list`,children:[m.map(e=>(0,R.jsxs)(`div`,{children:[(0,R.jsx)(`div`,{className:`cmdk-group-h`,children:e.label}),e.items.map(e=>{let n=v++;return(0,R.jsxs)(`button`,{className:`cmdk-item`,"data-active":n===g,onMouseEnter:()=>f(n),onClick:()=>{e.action(),t()},children:[(0,R.jsx)(`span`,{className:`ico`,children:e.icon}),(0,R.jsx)(`span`,{children:e.label}),e.meta&&(0,R.jsx)(`span`,{className:`meta`,children:e.meta})]},e.id)})]},e.label)),h.length===0&&(0,R.jsx)(`div`,{style:{padding:24,textAlign:`center`,color:`var(--text-3)`,fontSize:12},children:`No matches`})]})]})})}function Mo({open:e,onClose:t,onCreated:n}){let[r,i]=(0,L.useState)(``),a=Ya(),o=(0,L.useCallback)(async()=>{if(r)try{let e=await a.mutateAsync(r);i(``),t(),n(e.collection??r),q.success(`Created collection "${e.collection??r}".`)}catch(e){q.error(e instanceof Error?e.message:`Unknown error`)}},[r,t,n,a]);return e?(0,R.jsx)(`div`,{className:`scrim`,onClick:t,children:(0,R.jsxs)(`div`,{className:`modal-card`,onClick:e=>e.stopPropagation(),children:[(0,R.jsxs)(`div`,{className:`modal-head`,children:[(0,R.jsx)(`h3`,{children:`New collection`}),(0,R.jsx)(`p`,{children:`Collections are local LanceDB tables. Use lowercase, alphanumeric, hyphens, underscores.`})]}),(0,R.jsxs)(`div`,{className:`modal-body`,children:[(0,R.jsx)(`label`,{children:`Name`}),(0,R.jsx)(`input`,{autoFocus:!0,value:r,onChange:e=>i(e.target.value.replace(/[^a-z0-9_-]/g,``).toLowerCase()),onKeyDown:e=>e.key===`Enter`&&r&&void o(),placeholder:`my-archive`})]}),(0,R.jsxs)(`div`,{className:`modal-foot`,children:[(0,R.jsx)(`button`,{className:`btn ghost`,onClick:t,children:`Cancel`}),(0,R.jsx)(`button`,{className:`btn primary`,onClick:()=>void o(),disabled:!r,children:`Create`})]})]})}):null}var No={info:W.info,success:W.check,error:W.warn};function Po(){let e=wo(e=>e.toasts),t=wo(e=>e.dismissToast);return e.length===0?null:(0,R.jsx)(`div`,{className:`toaster`,role:`status`,"aria-live":`polite`,children:e.map(e=>(0,R.jsxs)(`button`,{className:`toast`,"data-type":e.type,onClick:()=>t(e.id),title:`Dismiss`,children:[(0,R.jsx)(`span`,{className:`toast-ico`,children:No[e.type]}),(0,R.jsx)(`span`,{className:`toast-text`,children:e.text})]},e.id))})}function Fo(){let e=wo(e=>e.confirmState),t=wo(e=>e.resolveConfirm);if((0,L.useEffect)(()=>{if(!e)return;let n=e=>{e.key===`Escape`&&(e.preventDefault(),e.stopPropagation(),t(!1))};return window.addEventListener(`keydown`,n,!0),()=>window.removeEventListener(`keydown`,n,!0)},[e,t]),!e)return null;let{title:n,message:r,confirmLabel:i,cancelLabel:a,danger:o}=e;return(0,R.jsx)(`div`,{className:`scrim`,onClick:()=>t(!1),children:(0,R.jsxs)(`div`,{className:`modal-card`,onClick:e=>e.stopPropagation(),role:`alertdialog`,"aria-modal":`true`,children:[(0,R.jsxs)(`div`,{className:`modal-head`,children:[(0,R.jsx)(`h3`,{children:n}),r&&(0,R.jsx)(`p`,{children:r})]}),(0,R.jsxs)(`div`,{className:`modal-foot`,children:[(0,R.jsx)(`button`,{className:`btn ghost`,onClick:()=>t(!1),children:a??`Cancel`}),(0,R.jsx)(`button`,{className:`btn ${o?`danger`:`primary`}`,onClick:()=>t(!0),autoFocus:!0,children:i??`Confirm`})]})]})})}function Io(){let e=Qr(),t=Ke(),n=fa(),r=n.some(e=>e.pathname===`/thread`),i=B(e=>e.theme),a=B(e=>e.activeColl),o=B(e=>e.setActiveColl),s=B(e=>e.sidebarOpen),c=B(e=>e.setSidebarOpen),l=B(e=>e.cmdkOpen),u=B(e=>e.setCmdkOpen),d=B(e=>e.newCollOpen),f=B(e=>e.setNewCollOpen),{data:p}=za(),m=p?.collections??[`all`,`chunks`],h=p?.stats??{},g=qa(),_=(0,L.useCallback)(()=>c(!1),[c]),v=(0,L.useCallback)(async()=>{await Promise.all([t.invalidateQueries({queryKey:[`collections`]}),t.invalidateQueries({queryKey:[`files`]}),t.invalidateQueries({queryKey:[`search`]}),t.invalidateQueries({queryKey:[`thread`]})]),q.success(`Local index data refreshed.`)},[t]);(0,L.useEffect)(()=>{document.documentElement.dataset.theme=i},[i]);let y=(0,L.useCallback)(async t=>{if(t!==`chunks`&&t!==`threadshelf_conversations`&&await To({title:`Delete collection "${Za(t)}"?`,message:`The index and copies previously uploaded through ThreadShelf are deleted. Files in the original folder you selected remain untouched.`,confirmLabel:`Delete`,danger:!0}))try{await g.mutateAsync(t),a===t&&(o(`all`),e.navigate({to:`/search/$collection`,params:{collection:`all`}})),q.success(`Deleted collection "${Za(t)}".`)}catch(e){q.error(e instanceof Error?e.message:`Unknown error`)}},[a,o,g,e]),b=(0,L.useCallback)(t=>{t===`/search`?e.navigate({to:`/search/$collection`,params:{collection:a}}):t===`/chat`?e.navigate({to:`/chat`,search:{}}):e.navigate({to:t}),_()},[e,_,a]),x=(0,L.useCallback)(()=>{e.navigate({to:`/chat`,search:{draft:Date.now().toString(36)}}),_()},[e,_]),S=(0,L.useCallback)(()=>{e.navigate({to:`/chat`,search:{private:Date.now().toString(36)}}),_()},[e,_]),C=(0,L.useCallback)(t=>{e.navigate({to:`/chat`,search:{thread:t}}),_()},[e,_]),w=(0,L.useCallback)(t=>{o(t),e.navigate({to:`/search/$collection`,params:{collection:t}}),_()},[o,e,_]);(0,L.useEffect)(()=>{let t=t=>{let n=t.target.tagName.toLowerCase(),i=n===`input`||n===`textarea`;if((t.metaKey||t.ctrlKey)&&t.key.toLowerCase()===`k`){t.preventDefault(),u(!l);return}if(t.key===`Escape`){l?u(!1):d?f(!1):r?e.history.back():s&&_();return}!i&&t.key===`/`&&(t.preventDefault(),document.querySelector(`.search-card input`)?.focus())};return window.addEventListener(`keydown`,t),()=>window.removeEventListener(`keydown`,t)},[l,d,r,u,f,e,s,_]);let T=n[n.length-1]?.pathname??`/search`,E=r||T.startsWith(`/search`)?`search`:T.split(`/`)[1]||`search`,D=n.find(e=>e.pathname===`/chat`)?.search,O=typeof D?.thread==`string`?D.thread:``;return(0,R.jsxs)(`div`,{className:`app`,"data-reading":r,"data-sidebar-open":s,children:[(0,R.jsx)(Ao,{view:E,setView:b,activeColl:a,setActiveColl:w,onNewChat:x,onNewPrivateChat:S,onOpenChat:C,activeChatId:O,onCmdK:()=>{u(!0),_()},onNewColl:()=>{f(!0),_()},onDeleteCollection:y,collections:m,stats:h}),(0,R.jsx)(`button`,{className:`sidebar-scrim`,onClick:_,"aria-label":`Close sidebar`,tabIndex:s?0:-1}),(0,R.jsx)(`div`,{className:`main`,children:(0,R.jsx)(ca,{})}),(0,R.jsx)(jo,{open:l,onClose:()=>u(!1),setView:b,onNewChat:x,setActiveColl:w,collections:m,stats:h,onNewColl:()=>{u(!1),f(!0)},onRefresh:()=>void v()}),d&&(0,R.jsx)(Mo,{open:!0,onClose:()=>f(!1),onCreated:e=>{w(e)}}),(0,R.jsx)(Fo,{}),(0,R.jsx)(Po,{})]})}var Lo={"google-ai-studio":{label:`Google AI Studio`,short:`AI Studio`,color:`var(--p-google)`},google:{label:`Google AI Studio`,short:`AI Studio`,color:`var(--p-google)`},openrouter:{label:`OpenRouter`,short:`OpenRouter`,color:`var(--p-openrouter)`},openai:{label:`ChatGPT`,short:`ChatGPT`,color:`var(--p-openai)`},anthropic:{label:`Claude`,short:`Claude`,color:`var(--p-claude)`},claude:{label:`Claude`,short:`Claude`,color:`var(--p-claude)`},"lm-studio":{label:`LM Studio`,short:`LM Studio`,color:`var(--p-lmstudio)`},grok:{label:`Grok`,short:`Grok`,color:`var(--p-grok)`},threadshelf:{label:`ThreadShelf`,short:`ThreadShelf`,color:`oklch(0.74 0.16 165)`}},Ro={label:`Unknown`,short:`—`,color:`var(--border-2)`},zo=e=>e?Lo[e]??Ro:Ro,Bo=[`paper chromatography household experiment`,`fact-checking workflow with citations`,`sauna rules and temperature`,`openrouter export json schema`,`ablation study sample size`,`polish translation tone register`];function Vo({onPick:e}){return(0,R.jsxs)(`div`,{className:`empty`,children:[(0,R.jsx)(`h3`,{children:`Ask your archive in plain language.`}),(0,R.jsx)(`p`,{children:`ThreadShelf runs locally — your queries embed on this machine and search a local LanceDB. Try a topic, a draft you half-remember, or the shape of an answer you're looking for.`}),(0,R.jsx)(`div`,{className:`examples`,children:Bo.map(t=>(0,R.jsxs)(`button`,{className:`example-chip`,onClick:()=>e(t),children:[W.spark,(0,R.jsx)(`span`,{children:t})]},t))})]})}function Ho(e,t){let n=G(t);if(!n)return e;try{return K(e,t).map((e,t)=>n.test(e)?(0,R.jsx)(`mark`,{children:e},t):e)}catch{return e}}function Uo({result:e,query:t,onClick:n,selected:r,onMoreLikeThis:i}){let{metadata:a}=e,o=a.role??`ai`,s={user:`user`,thinking:`reasoning`,ai:`response`}[o]??o,c=zo(a.provider),l=$a(a.model),u=ao(a.createdAt),d=e.distance==null?``:(1-e.distance).toFixed(3),f=e.distance==null?0:Math.round((1-e.distance)*100),p=(0,L.useMemo)(()=>Ho(e.document,t),[e.document,t]),m=a.title?.trim()||Qa(a.sourceFile);return(0,R.jsxs)(`button`,{className:`result`,"data-selected":r,"aria-selected":r,onClick:n,children:[(0,R.jsxs)(`div`,{className:`result-head`,children:[(0,R.jsx)(`span`,{className:`r-role`,"data-role":o,children:s}),(0,R.jsxs)(`span`,{className:`r-provider`,children:[(0,R.jsx)(`span`,{className:`pdot`,style:{background:c.color}}),(0,R.jsx)(`span`,{children:c.short})]}),a.createdInThreadShelf&&(0,R.jsx)(`span`,{className:`threadshelf-turn-badge`,children:`ThreadShelf`}),e.distance!=null&&(0,R.jsx)(`div`,{className:`r-meta-right`,children:(0,R.jsxs)(`span`,{className:`r-score`,children:[(0,R.jsx)(`span`,{className:`r-score-bar`,children:(0,R.jsx)(`i`,{style:{width:`${f}%`}})}),(0,R.jsx)(`span`,{children:d})]})})]}),(0,R.jsx)(`div`,{className:`r-title`,title:m,children:m}),(0,R.jsx)(`div`,{className:`r-snippet`,children:p}),(0,R.jsxs)(`div`,{className:`r-foot`,children:[(0,R.jsxs)(`span`,{className:`r-source`,title:a.sourceFile,children:[W.folder,(0,R.jsx)(`span`,{className:`r-source-coll`,children:Za(a.collection??``)}),(0,R.jsx)(`span`,{className:`slash`,children:`/`}),(0,R.jsx)(`span`,{className:`r-source-file`,children:Qa(a.sourceFile)})]}),(0,R.jsx)(`span`,{className:`dot`}),l&&(0,R.jsxs)(R.Fragment,{children:[(0,R.jsx)(`span`,{className:`mono`,children:l}),(0,R.jsx)(`span`,{className:`dot`})]}),u&&(0,R.jsxs)(R.Fragment,{children:[(0,R.jsx)(`span`,{children:u}),(0,R.jsx)(`span`,{className:`dot`})]}),a.turnIndex!=null&&(0,R.jsxs)(R.Fragment,{children:[(0,R.jsxs)(`span`,{children:[`turn `,(0,R.jsxs)(`b`,{className:`mono`,children:[`#`,a.turnIndex]})]}),(0,R.jsx)(`span`,{className:`dot`})]}),i&&(0,R.jsx)(`span`,{className:`more-like-this`,role:`button`,tabIndex:0,title:`Search for similar passages`,onClick:e=>{e.stopPropagation(),i()},onKeyDown:e=>{(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),e.stopPropagation(),i())},children:`more like this`}),(0,R.jsxs)(`span`,{className:`open-thread`,children:[`open thread `,W.arrowRight]})]})]})}var Wo={search:`Archive`,insights:`Insights`,indexing:`Add data`,mcp:`MCP`,settings:`Settings`,chat:`Chat`};function Go({view:e,activeColl:t,onMenu:n,actions:r}){return(0,R.jsxs)(`div`,{className:`topbar`,children:[(0,R.jsx)(`button`,{className:`icon-btn mobile-menu-btn`,onClick:n,"aria-label":`Open sidebar`,children:W.menu}),(0,R.jsxs)(`div`,{className:`crumbs`,children:[(0,R.jsx)(`span`,{children:Wo[e]}),e===`search`&&(0,R.jsxs)(R.Fragment,{children:[(0,R.jsx)(`span`,{className:`sep`,children:`/`}),(0,R.jsx)(`b`,{children:Za(t)})]})]}),(0,R.jsx)(`div`,{className:`topbar-spacer`}),r&&(0,R.jsx)(`div`,{className:`topbar-actions`,children:r})]})}var Ko=100,qo=e=>{switch(e){case`recent`:return(e,t)=>(t.lastTurnAt??``).localeCompare(e.lastTurnAt??``);case`longest`:return(e,t)=>(t.turnCount??0)-(e.turnCount??0);case`title`:return(e,t)=>(e.title||e.sourceFile).localeCompare(t.title||t.sourceFile,void 0,{sensitivity:`base`})}},Jo=[{id:`user`,label:`User`,key:`user`},{id:`reasoning`,label:`Reasoning`,key:`thinking`},{id:`response`,label:`Response`,key:`ai`}];function Yo(){return(0,R.jsx)(`div`,{className:`result-list`,style:{marginTop:16},children:[0,1,2].map(e=>(0,R.jsx)(`div`,{className:`shimmer-card`},e))})}function Xo(){let e=Ni(),{collection:t}=ji({from:`/search/$collection`}),{q:n,model:r,from:i,to:a,mode:o}=Mi({from:`/search/$collection`}),s=t?decodeURIComponent(t):`all`,c=B(e=>e.setActiveColl);(0,L.useEffect)(()=>{c(s)},[s,c]);let l=B(e=>e.roles),u=B(e=>e.toggleRole),d=B(e=>e.modelFilter),f=B(e=>e.setModelFilter),p=B(e=>e.setSidebarOpen),m=B(e=>e.savedSearches),h=B(e=>e.addSavedSearch),g=B(e=>e.removeSavedSearch),_=B(e=>e.pinnedConversations),v=B(e=>e.togglePinned),[y,b]=(0,L.useState)(n??``),[x,S]=(0,L.useState)(n??``),[C,w]=(0,L.useState)(i??``),[T,E]=(0,L.useState)(a??``),[D,O]=(0,L.useState)(o===`keyword`?`keyword`:`semantic`),[ee,te]=(0,L.useState)(15),[k,ne]=(0,L.useState)(-1),[re,ie]=(0,L.useState)(``),[A,j]=(0,L.useState)(`recent`),[M,ae]=(0,L.useState)(Ko),[oe,se]=(0,L.useState)(`all`),N=(0,L.useRef)(n??``),P=(0,L.useRef)(null),F=(0,L.useRef)(null),{data:ce}=Ra(),le=ce??!0,ue=(0,L.useMemo)(()=>{let e=[];return l.user&&e.push(`user`),l.thinking&&e.push(`thinking`),l.ai&&e.push(`ai`),e.length>0&&e.length<3?e.join(`,`):void 0},[l]),{data:de,isFetching:fe}=Ua((0,L.useMemo)(()=>({q:x.trim(),collection:s,n:ee,roles:ue,keywordBoost:D===`semantic`&&x.trim().length<=40,model:(r??d).trim()||void 0,from:C.trim()||void 0,to:T.trim()||void 0,mode:D,origin:oe===`all`?void 0:oe}),[x,s,ee,ue,r,d,C,T,D,oe])),{data:pe,isLoading:me}=Va(s,!x.trim()&&le),he=(0,L.useMemo)(()=>{let e=pe?.files??[],t=re.trim().toLowerCase();return[...(t?e.filter(e=>[e.title,e.sourceFile,e.conversationKey,e.collection].some(e=>(e??``).toLowerCase().includes(t))):e).filter(e=>oe===`threadshelf`?e.createdInThreadShelf===!0||e.hasThreadShelfTurns===!0:oe!==`archive`||e.hasThreadShelfTurns!==!0)].sort(qo(A))},[pe?.files,re,A,oe]),I=(0,L.useMemo)(()=>he.slice(0,M),[he,M]);(0,L.useEffect)(()=>{let e=window.setTimeout(()=>ae(Ko),0);return()=>window.clearTimeout(e)},[s,re,A,oe]),(0,L.useEffect)(()=>{P.current?.focus()},[]),(0,L.useEffect)(()=>{let e=n??``;if(e!==N.current){let t=window.setTimeout(()=>{N.current=e,b(e),S(e),w(i??``),E(a??``),O(o===`keyword`?`keyword`:`semantic`)},0);return()=>window.clearTimeout(t)}},[n,i,a,o]),(0,L.useEffect)(()=>{let e=window.setTimeout(()=>{w(i??``),E(a??``)},0);return()=>window.clearTimeout(e)},[i,a]),(0,L.useEffect)(()=>{let e=window.setTimeout(()=>te(15),0);return()=>window.clearTimeout(e)},[x,s,ue,r,d,C,T,D,oe]);let ge=(0,L.useCallback)(t=>{let n=y.trim(),r=t??D;N.current=n,S(n),e({to:`/search/$collection`,params:{collection:s},search:n?{q:n,model:d.trim()||void 0,from:C.trim()||void 0,to:T.trim()||void 0,mode:r===`keyword`?`keyword`:void 0}:{},replace:!0})},[y,d,C,T,e,s,D]),_e=(0,L.useCallback)(()=>{N.current=``,b(``),S(``),P.current?.focus(),e({to:`/search/$collection`,params:{collection:s},search:{},replace:!0})},[e,s]),ve=(0,L.useCallback)(e=>{O(e),ge(e)},[ge]),ye=(0,L.useCallback)(t=>{e({to:`/thread`,search:{sourceFile:t.metadata.sourceFile,collection:t.metadata.collection??s,conversationKey:t.metadata.conversationKey,q:x.trim()||void 0,title:t.metadata.title,matchIdx:t.metadata.turnIndex,provider:t.metadata.provider,model:t.metadata.model}})},[s,x,e]),be=(0,L.useCallback)(t=>{let n=lo(t.document);n&&(O(`semantic`),N.current=n,b(n),S(n),e({to:`/search/$collection`,params:{collection:s},search:{q:n,model:d.trim()||void 0,from:C.trim()||void 0,to:T.trim()||void 0}}))},[s,d,C,T,e]),xe=(0,L.useCallback)(t=>{e({to:`/thread`,search:{sourceFile:t.sourceFile,collection:t.collection,conversationKey:t.conversationKey,title:t.title}})},[e]),Se=x.trim().length>0,Ce=(0,L.useMemo)(()=>({q:x.trim(),collection:s,mode:D,model:(r??d).trim()||void 0,from:C.trim()||void 0,to:T.trim()||void 0}),[x,s,D,r,d,C,T]),we=(0,L.useMemo)(()=>{let e=ka(Ce);return m.find(t=>ka(t)===e)},[m,Ce]),Te=(0,L.useCallback)(()=>{we?g(we.id):h(Ce)},[we,g,h,Ce]),Ee=(0,L.useCallback)(t=>{O(t.mode),N.current=t.q,b(t.q),S(t.q),e({to:`/search/$collection`,params:{collection:t.collection},search:{q:t.q,model:t.model,from:t.from,to:t.to,mode:t.mode===`keyword`?`keyword`:void 0}})},[e]),De=(0,L.useMemo)(()=>s===`all`?_:_.filter(e=>e.collection===s),[_,s]),Oe=(0,L.useMemo)(()=>new Set(_.map(e=>Aa(e))),[_]),ke=e=>({collection:e.collection,sourceFile:e.sourceFile,conversationKey:e.conversationKey,title:e.title,provider:e.provider}),Ae=(0,L.useMemo)(()=>de?.results??[],[de?.results]),je=Se&&!fe&&Ae.length>=ee&&ee<50,Me=s===`all`?`Conversations`:`${Za(s)} conversations`,Ne=Se?Ae.length:I.length,Pe=(0,L.useCallback)(e=>{if(Se){let t=Ae[e];t&&ye(t)}else{let t=I[e];t&&xe(t)}},[Se,Ae,I,ye,xe]);return(0,L.useEffect)(()=>{let e=window.setTimeout(()=>ne(-1),0);return()=>window.clearTimeout(e)},[x,s,Se]),(0,L.useEffect)(()=>{let e=e=>{let t=e.target,n=t.tagName?.toLowerCase(),r=t===P.current;if((n===`input`||n===`textarea`)&&!r||Ne===0)return;let i=e.key===`ArrowDown`||e.key===`j`&&!r,a=e.key===`ArrowUp`||e.key===`k`&&!r;i?(e.preventDefault(),r&&P.current?.blur(),ne(e=>Math.min(Ne-1,e+1))):a?(e.preventDefault(),ne(e=>e<=0?0:e-1)):e.key===`Enter`&&!r&&k>=0&&(e.preventDefault(),Pe(k))};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[Ne,k,Pe]),(0,L.useEffect)(()=>{k<0||F.current?.querySelector(`[data-selected="true"]`)?.scrollIntoView({block:`nearest`})},[k]),(0,R.jsxs)(R.Fragment,{children:[(0,R.jsx)(Go,{view:`search`,activeColl:s,onMenu:()=>p(!0)}),(0,R.jsx)(`div`,{className:`main-scroll`,children:(0,R.jsxs)(`div`,{className:`view`,children:[!le&&(0,R.jsxs)(`div`,{className:`banner err`,children:[(0,R.jsx)(`span`,{className:`ico`,children:W.warn}),(0,R.jsxs)(`div`,{className:`grow`,children:[(0,R.jsx)(`b`,{style:{fontWeight:500},children:`Backend unreachable.`}),` `,(0,R.jsxs)(`span`,{style:{color:`var(--text-2)`},children:[`Run `,(0,R.jsx)(`code`,{children:`npm start`}),` in the project root.`]})]})]}),(0,R.jsxs)(`div`,{className:`search-card`,children:[(0,R.jsx)(`span`,{className:`search-ico`,children:W.searchLg}),(0,R.jsx)(`input`,{id:`searchInput`,ref:P,value:y,onChange:e=>b(e.target.value),onKeyDown:e=>{e.key===`Enter`?ge():e.key===`Escape`&&y&&(e.preventDefault(),e.stopPropagation(),_e())},placeholder:D===`keyword`?`Exact match: identifiers, error strings, code…`:`Search by meaning across your archive…`}),y&&(0,R.jsx)(`button`,{id:`clearSearch`,type:`button`,className:`search-clear`,"aria-label":`Clear search`,title:`Clear search (Esc)`,onClick:_e,children:W.close})]}),(0,R.jsxs)(`div`,{className:`search-toolbar`,children:[(0,R.jsxs)(`div`,{className:`mode-toggle`,role:`group`,"aria-label":`Search mode`,children:[(0,R.jsx)(`button`,{type:`button`,className:`mode-btn`,"data-on":D===`semantic`,title:`Rank by meaning (local embeddings)`,onClick:()=>ve(`semantic`),children:`Semantic`}),(0,R.jsx)(`button`,{type:`button`,className:`mode-btn`,"data-on":D===`keyword`,title:`Exact substring match (case-insensitive)`,onClick:()=>ve(`keyword`),children:`Exact`})]}),(0,R.jsx)(`div`,{className:`role-chips`,children:Jo.map(e=>(0,R.jsxs)(`button`,{className:`role-chip`,"data-role":e.id,"data-on":l[e.key],onClick:()=>u(e.key),children:[(0,R.jsx)(`span`,{className:`dot`}),e.label]},e.id))}),(0,R.jsxs)(`label`,{className:`origin-filter`,children:[(0,R.jsx)(`span`,{children:`origin`}),(0,R.jsxs)(`select`,{"aria-label":`Filter by conversation origin`,value:oe,onChange:e=>se(e.target.value),children:[(0,R.jsx)(`option`,{value:`all`,children:`All`}),(0,R.jsx)(`option`,{value:`threadshelf`,children:`ThreadShelf`}),(0,R.jsx)(`option`,{value:`archive`,children:`Clean archive`})]})]}),(0,R.jsxs)(`label`,{className:`model-filter`,children:[(0,R.jsx)(`span`,{children:`model`}),(0,R.jsx)(`input`,{value:d,onChange:e=>f(e.target.value),onKeyDown:e=>e.key===`Enter`&&ge(),placeholder:`gpt-5, claude, gemini...`})]}),(0,R.jsxs)(`label`,{className:`date-filter`,children:[(0,R.jsx)(`span`,{children:`from`}),(0,R.jsx)(`input`,{type:`date`,value:C,onChange:e=>w(e.target.value),onKeyDown:e=>e.key===`Enter`&&ge()})]}),(0,R.jsxs)(`label`,{className:`date-filter`,children:[(0,R.jsx)(`span`,{children:`to`}),(0,R.jsx)(`input`,{type:`date`,value:T,onChange:e=>E(e.target.value),onKeyDown:e=>e.key===`Enter`&&ge()})]}),(0,R.jsx)(`span`,{className:`toolbar-spacer`}),(0,R.jsxs)(`span`,{className:`toolbar-hint`,children:[(0,R.jsx)(`kbd`,{children:`/`}),` focus · `,(0,R.jsx)(`kbd`,{children:`↑↓`}),` navigate · `,(0,R.jsx)(`kbd`,{children:`↵`}),` open · `,(0,R.jsx)(`kbd`,{children:`Ctrl+K`}),` `,`commands`]})]}),!Se&&(0,R.jsxs)(R.Fragment,{children:[s===`all`&&(0,R.jsx)(Vo,{onPick:t=>{N.current=t,b(t),S(t),e({to:`/search/$collection`,params:{collection:s},search:{q:t},replace:!0})}}),m.length>0&&(0,R.jsxs)(`div`,{className:`saved-searches`,children:[(0,R.jsxs)(`span`,{className:`ss-label`,children:[W.star,` Saved`]}),m.map(e=>(0,R.jsxs)(`span`,{className:`ss-chip`,children:[(0,R.jsxs)(`button`,{type:`button`,className:`ss-run`,title:`Run: ${e.q}`,onClick:()=>Ee(e),children:[(0,R.jsx)(`span`,{className:`ss-q`,children:e.q}),e.collection!==`all`&&(0,R.jsx)(`span`,{className:`ss-tag`,children:Za(e.collection)}),e.mode===`keyword`&&(0,R.jsx)(`span`,{className:`ss-tag`,children:`exact`})]}),(0,R.jsx)(`button`,{type:`button`,className:`ss-del`,"aria-label":`Delete saved search "${e.q}"`,onClick:()=>g(e.id),children:`×`})]},e.id))]}),De.length>0&&(0,R.jsxs)(R.Fragment,{children:[(0,R.jsx)(`div`,{className:`results-meta-bar`,children:(0,R.jsxs)(`span`,{className:`h`,children:[`Pinned `,(0,R.jsx)(`b`,{children:De.length})]})}),(0,R.jsx)(`div`,{className:`result-list`,children:De.map(e=>{let t=e.provider?zo(e.provider):null,n=Qa(e.sourceFile);return(0,R.jsxs)(`button`,{className:`result`,onClick:()=>xe(e),children:[(0,R.jsxs)(`div`,{className:`result-head`,children:[(0,R.jsx)(`span`,{className:`r-role`,"data-role":`user`,children:`pinned`}),(0,R.jsxs)(`span`,{className:`r-provider`,children:[(0,R.jsx)(`span`,{className:`pdot`,style:{background:t?.color??`var(--accent)`}}),(0,R.jsx)(`span`,{children:t?.short??Za(e.collection)})]}),(0,R.jsx)(`span`,{className:`pin-toggle`,role:`button`,tabIndex:0,"data-on":`true`,title:`Unpin`,onClick:t=>{t.stopPropagation(),v(e)},onKeyDown:t=>{(t.key===`Enter`||t.key===` `)&&(t.preventDefault(),t.stopPropagation(),v(e))},children:W.pinFilled})]}),(0,R.jsx)(`div`,{className:`r-title`,title:e.title||n,children:e.title||n}),(0,R.jsxs)(`div`,{className:`r-foot`,children:[(0,R.jsxs)(`span`,{className:`r-source`,title:e.sourceFile,children:[W.folder,(0,R.jsx)(`span`,{className:`r-source-coll`,children:Za(e.collection)}),(0,R.jsx)(`span`,{className:`slash`,children:`/`}),(0,R.jsx)(`span`,{className:`r-source-file`,children:n})]}),(0,R.jsxs)(`span`,{className:`open-thread`,children:[`open thread `,W.arrowRight]})]})]},Aa(e))})})]}),(0,R.jsxs)(`div`,{className:`results-meta-bar`,children:[(0,R.jsxs)(`span`,{className:`h`,children:[Me,` `,(0,R.jsx)(`b`,{children:he.length})]}),(0,R.jsxs)(`label`,{className:`conv-sort`,children:[(0,R.jsx)(`span`,{children:`sort`}),(0,R.jsxs)(`select`,{value:A,onChange:e=>j(e.target.value),"aria-label":`Sort conversations`,children:[(0,R.jsx)(`option`,{value:`recent`,children:`Recent`}),(0,R.jsx)(`option`,{value:`longest`,children:`Longest`}),(0,R.jsx)(`option`,{value:`title`,children:`Title`})]})]}),(0,R.jsx)(`input`,{className:`conv-filter`,value:re,onChange:e=>ie(e.target.value),placeholder:`Filter by title or file…`,"aria-label":`Filter conversations`})]}),me?(0,R.jsx)(Yo,{}):he.length>0?(0,R.jsxs)(`div`,{className:`result-list`,ref:F,children:[I.map((e,t)=>{let n=Qa(e.sourceFile),r=e.title||n,i=e.provider?zo(e.provider):null,a=ao(e.lastTurnAt),o=ke(e),s=Oe.has(Aa(o));return(0,R.jsxs)(`button`,{className:`result`,"data-selected":t===k,"aria-selected":t===k,onClick:()=>xe(e),children:[(0,R.jsxs)(`div`,{className:`result-head`,children:[(0,R.jsx)(`span`,{className:`r-role`,"data-role":`ai`,children:`thread`}),e.hasThreadShelfTurns&&(0,R.jsx)(`span`,{className:`threadshelf-turn-badge`,children:e.createdInThreadShelf?`Created in ThreadShelf`:`Continued in ThreadShelf`}),(0,R.jsxs)(`span`,{className:`r-provider`,children:[(0,R.jsx)(`span`,{className:`pdot`,style:{background:i?.color??`var(--accent)`}}),(0,R.jsx)(`span`,{children:i?.short??Za(e.collection)})]}),(0,R.jsx)(`span`,{className:`pin-toggle`,role:`button`,tabIndex:0,"data-on":s,title:s?`Unpin`:`Pin conversation`,onClick:e=>{e.stopPropagation(),v(o)},onKeyDown:e=>{(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),e.stopPropagation(),v(o))},children:s?W.pinFilled:W.pin})]}),(0,R.jsx)(`div`,{className:`r-title`,title:r,children:r}),(0,R.jsxs)(`div`,{className:`r-foot`,children:[(0,R.jsxs)(`span`,{className:`r-source`,title:e.sourceFile,children:[W.folder,(0,R.jsx)(`span`,{className:`r-source-coll`,children:Za(e.collection)}),(0,R.jsx)(`span`,{className:`slash`,children:`/`}),(0,R.jsx)(`span`,{className:`r-source-file`,children:n})]}),e.turnCount!=null&&(0,R.jsxs)(R.Fragment,{children:[(0,R.jsx)(`span`,{className:`dot`}),(0,R.jsxs)(`span`,{children:[e.turnCount,` turns`]})]}),a&&(0,R.jsxs)(R.Fragment,{children:[(0,R.jsx)(`span`,{className:`dot`}),(0,R.jsx)(`span`,{children:a})]}),(0,R.jsxs)(`span`,{className:`open-thread`,children:[`open thread `,W.arrowRight]})]})]},`${e.collection}:${e.sourceFile}:${t}`)}),he.length>M&&(0,R.jsx)(`div`,{className:`load-more-row`,children:(0,R.jsxs)(`button`,{type:`button`,className:`load-more-button`,onClick:()=>ae(e=>e+Ko),children:[`Show more (`,he.length-M,` left)`]})})]}):re.trim()?(0,R.jsxs)(`div`,{className:`empty`,children:[(0,R.jsx)(`h3`,{children:`No conversations match the filter.`}),(0,R.jsx)(`p`,{children:`Try a different phrase or clear the filter box.`})]}):s===`all`?null:(0,R.jsxs)(`div`,{className:`empty`,children:[(0,R.jsx)(`h3`,{children:`No conversations indexed.`}),(0,R.jsx)(`p`,{children:`Index a folder into this collection or switch to another collection.`})]})]}),Se&&fe&&(0,R.jsx)(Yo,{}),Se&&!fe&&Ae.length===0&&(0,R.jsxs)(`div`,{className:`empty`,children:[(0,R.jsx)(`h3`,{children:`No results found.`}),(0,R.jsx)(`p`,{children:`Try another query, broaden your role filters, or index more files.`})]}),Se&&!fe&&Ae.length>0&&(0,R.jsxs)(R.Fragment,{children:[(0,R.jsxs)(`div`,{className:`results-meta-bar`,children:[(0,R.jsxs)(`span`,{className:`h`,children:[`Results `,(0,R.jsx)(`b`,{children:Ae.length}),(0,R.jsxs)(`span`,{style:{marginLeft:14,color:`var(--text-3)`},children:[`in `,Za(s)]})]}),(0,R.jsxs)(`button`,{type:`button`,className:`save-search`,"data-on":!!we,title:we?`Remove from saved searches`:`Save this search`,onClick:Te,children:[we?W.starFilled:W.star,(0,R.jsx)(`span`,{children:we?`Saved`:`Save search`})]})]}),(0,R.jsx)(`div`,{className:`result-list`,ref:F,children:Ae.map((e,t)=>(0,R.jsx)(Uo,{result:e,query:x,selected:t===k,onClick:()=>ye(e),onMoreLikeThis:()=>be(e)},t))}),je&&(0,R.jsx)(`div`,{className:`load-more-row`,children:(0,R.jsx)(`button`,{type:`button`,className:`load-more-button`,onClick:()=>te(e=>Math.min(e+15,50)),children:`Load more`})})]})]})})]})}var Zo=/^(\s*)([-*+]|\d+[.)])\s+(.*)$/,Qo=/^\s*\d+[.)]\s+/,$o=e=>{let t=e.trim();return/^(?:https?:\/\/|mailto:|#|\/)/i.test(t)?t:null},es=e=>{let t=[],n=``,r=()=>{n&&=(t.push({type:`text`,value:n}),``)},i=0;for(;i<e.length;){let a=e[i];if(a==="`"){let n=e.indexOf("`",i+1);if(n>i){r(),t.push({type:`code`,value:e.slice(i+1,n)}),i=n+1;continue}}if(a===`[`){let n=e.indexOf(`]`,i+1);if(n>i&&e[n+1]===`(`){let a=e.indexOf(`)`,n+2);if(a>n){let o=$o(e.slice(n+2,a));if(o){r(),t.push({type:`link`,href:o,children:es(e.slice(i+1,n))}),i=a+1;continue}}}}if(a===`*`&&e[i+1]===`*`||a===`_`&&e[i+1]===`_`){let n=e.slice(i,i+2),a=e.indexOf(n,i+2);if(a>i+1){r(),t.push({type:`strong`,children:es(e.slice(i+2,a))}),i=a+2;continue}}if(a===`*`){let n=e.indexOf(`*`,i+1);if(n>i+1){r(),t.push({type:`em`,children:es(e.slice(i+1,n))}),i=n+1;continue}}n+=a,i+=1}return r(),t},ts=e=>{let t=e.replace(/\r\n?/g,`
17
+ `);return(0,R.jsxs)(`div`,{className:`global-runtime`,"data-state":t?.state||`unknown`,title:t?.detail,children:[(0,R.jsx)(`span`,{className:`runtime-dot`,"aria-hidden":`true`}),(0,R.jsxs)(`span`,{className:`global-runtime-copy`,children:[(0,R.jsxs)(`b`,{children:[(0,R.jsx)(`span`,{children:t?.state===`starting`?`llama.cpp · loading`:`llama.cpp`}),(0,R.jsx)(`em`,{children:d}),(0,R.jsx)(`span`,{className:`runtime-install-help`,tabIndex:0,role:`img`,"aria-label":`llama.cpp CPU and GPU install commands`,title:f,children:`?`})]}),(0,R.jsx)(`span`,{children:t?.model?Eo(t.model):t?`no model loaded`:`status unavailable`}),e&&t?.detail&&(0,R.jsx)(`small`,{children:t.detail}),e&&h&&(0,R.jsx)(`small`,{className:`runtime-profile`,title:g||void 0,children:h})]}),l&&(0,R.jsx)(`button`,{type:`button`,disabled:a,onClick:()=>void c(),children:a?`…`:`Eject`})]})}var Oo=[{id:`/search`,viewKey:`search`,label:`Search archive`,icon:W.search},{id:`/insights`,viewKey:`insights`,label:`Insights`,icon:W.chart}],ko=[{id:`/indexing`,viewKey:`indexing`,label:`Add data`,icon:W.download},{id:`/mcp`,viewKey:`mcp`,label:`MCP`,icon:W.plug},{id:`/settings`,viewKey:`settings`,label:`Settings`,icon:W.settings}];function Ao({view:e,setView:t,activeColl:n,setActiveColl:r,onNewChat:i,onNewPrivateChat:a,onOpenChat:o,activeChatId:s,onCmdK:c,onNewColl:l,onDeleteCollection:u,collections:d,stats:f}){let[p,m]=(0,L.useState)(!1),[h,g]=(0,L.useState)(e===`chat`),{data:_,isLoading:v,refetch:y}=Ha(),b=_?.threads??[],x=d.filter(e=>e!==`all`),S=x.reduce((e,t)=>e+(f[t]?.files??0),0),C=x.reduce((e,t)=>e+(f[t]?.chunks??0),0),w=e=>{let t=f[e];return(t?.chunks??0)===0&&(t?.files??0)===0&&(t?.conversations??0)===0},T=x.filter(e=>w(e)&&e!==n),E=x.filter(e=>p||!w(e)||e===n),D=e=>{let t=e?.conversations??0;return t>0?`${t.toLocaleString()} thread${t===1?``:`s`}`:`${(e?.files??0).toLocaleString()} files`},O=e=>{let t=e?.conversations??0;return`${t>0?`${t.toLocaleString()} conversation${t===1?``:`s`}`:`${(e?.files??0).toLocaleString()} files`} · ${(e?.chunks??0).toLocaleString()} indexed chunks`};return(0,R.jsxs)(`aside`,{className:`sidebar`,children:[(0,R.jsxs)(`div`,{className:`sb-brand`,children:[(0,R.jsx)(`div`,{className:`sb-logo`,"aria-hidden":`true`}),(0,R.jsxs)(`div`,{className:`sb-name`,children:[(0,R.jsx)(`b`,{children:`ThreadShelf`}),(0,R.jsxs)(`span`,{children:[`v`,`1.2.1`,` · local`]})]})]}),(0,R.jsxs)(`section`,{className:`sb-chat-panel`,"aria-label":`Conversations`,children:[(0,R.jsxs)(`div`,{className:`sb-chat-create`,children:[(0,R.jsxs)(`button`,{id:`sidebarNewChatButton`,className:`sb-new-chat`,onClick:i,children:[W.spark,(0,R.jsx)(`span`,{children:`New chat`})]}),(0,R.jsx)(`button`,{id:`sidebarPrivateChatButton`,className:`sb-private-chat`,"aria-label":`Start private conversation`,title:`Start a private chat that is cleared with this tab`,onClick:a,children:W.ghost})]}),(0,R.jsxs)(`div`,{className:`sb-chat-history`,children:[(0,R.jsxs)(`button`,{id:`sidebarChatHistoryToggle`,className:`sb-chat-history-toggle`,"aria-expanded":h,onClick:()=>{let e=!h;g(e),e&&y()},children:[(0,R.jsx)(`span`,{className:`ico`,children:W.chat}),(0,R.jsx)(`span`,{className:`label`,children:`Your chats`}),b.length>0&&(0,R.jsx)(`em`,{children:b.length}),(0,R.jsx)(`span`,{className:`sb-chat-chevron`,"aria-hidden":`true`,children:`▾`})]}),h&&(0,R.jsxs)(`div`,{className:`sb-chat-history-list`,children:[v&&(0,R.jsx)(`span`,{className:`sb-chat-history-empty`,children:`Loading chats…`}),!v&&b.length===0&&(0,R.jsx)(`span`,{className:`sb-chat-history-empty`,children:`No saved chats yet.`}),b.map(e=>(0,R.jsxs)(`button`,{className:`sb-chat-item`,"data-active":e.id===s,title:e.title,onClick:()=>o(e.id),children:[(0,R.jsx)(`strong`,{children:e.title}),(0,R.jsx)(`span`,{title:`${e.model?`${no(e.model)} · `:``}${e.turnCount} messages`,children:oo(e.updatedAt)||`just now`})]},e.id))]})]})]}),(0,R.jsxs)(`button`,{className:`sb-cmdk`,onClick:c,children:[W.search,(0,R.jsx)(`span`,{children:`Quick jump…`}),(0,R.jsx)(`kbd`,{children:`Ctrl+K`})]}),(0,R.jsx)(`div`,{className:`sb-section`,children:(0,R.jsx)(`span`,{children:`Library`})}),(0,R.jsx)(`nav`,{className:`sb-list`,style:{flex:`0 0 auto`},children:Oo.map(n=>(0,R.jsxs)(`button`,{id:n.viewKey===`indexing`?`indexingNavBtn`:void 0,className:`sb-item`,"data-active":e===n.viewKey,onClick:()=>t(n.id),children:[(0,R.jsx)(`span`,{className:`ico`,children:n.icon}),(0,R.jsx)(`span`,{className:`label`,children:n.label})]},n.id))}),(0,R.jsxs)(`div`,{className:`sb-section`,children:[(0,R.jsx)(`span`,{children:`Archive collections`}),(0,R.jsx)(`button`,{title:`New collection`,onClick:l,children:W.plus})]}),(0,R.jsxs)(`div`,{className:`sb-list sb-collections`,children:[(0,R.jsxs)(`button`,{id:`collection-all`,className:`sb-coll`,"data-active":n===`all`,onClick:()=>r(`all`),children:[(0,R.jsx)(`span`,{className:`sb-coll-dot`,style:{background:`var(--accent)`}}),(0,R.jsxs)(`span`,{className:`sb-coll-meta`,children:[(0,R.jsx)(`b`,{children:`All collections`}),(0,R.jsxs)(`span`,{children:[S,` files · `,C.toLocaleString(),` chunks`]})]})]}),E.map(e=>{let t=f[e];return(0,R.jsxs)(`div`,{className:`sb-coll-row`,children:[(0,R.jsxs)(`button`,{id:`collection-${e}`,className:`sb-coll`,"data-active":n===e,onClick:()=>r(e),children:[(0,R.jsx)(`span`,{className:`sb-coll-dot`}),(0,R.jsxs)(`span`,{className:`sb-coll-meta`,children:[(0,R.jsx)(`b`,{children:Za(e)}),(0,R.jsxs)(`span`,{title:O(t),children:[D(t),` · `,(t?.chunks??0).toLocaleString(),` chunks`]})]})]}),e!==`chunks`&&e!==`threadshelf_conversations`&&(0,R.jsx)(`button`,{id:`delete-collection-${e}`,className:`sb-coll-delete`,title:`Delete ${Za(e)}`,"aria-label":`Delete ${Za(e)}`,onClick:t=>{t.stopPropagation(),u(e)},children:W.trash})]},e)}),T.length>0&&(0,R.jsx)(`button`,{id:`toggleEmptyCollections`,className:`sb-coll-empty-toggle`,onClick:()=>m(e=>!e),children:p?`Hide empty collections`:`Show ${T.length} empty`})]}),(0,R.jsx)(`div`,{className:`sb-section sb-manage-section`,children:(0,R.jsx)(`span`,{children:`Manage`})}),(0,R.jsx)(`nav`,{className:`sb-list sb-manage-list`,children:ko.map(n=>(0,R.jsxs)(`button`,{id:n.viewKey===`indexing`?`indexingNavBtn`:void 0,className:`sb-item`,"data-active":e===n.viewKey,onClick:()=>t(n.id),children:[(0,R.jsx)(`span`,{className:`ico`,children:n.icon}),(0,R.jsx)(`span`,{className:`label`,children:n.label})]},n.id))}),(0,R.jsxs)(`div`,{className:`sb-footer`,children:[(0,R.jsx)(Do,{}),(0,R.jsxs)(`div`,{className:`sb-footer-row`,children:[(0,R.jsxs)(`span`,{className:`sb-status`,title:`Local backend · paraphrase-multilingual embeddings · LanceDB storage. Model runtime and eject live in the chat model menu.`,children:[`local · :`,window.location.port||`80`]}),(0,R.jsx)(xo,{variant:`icon`})]})]})]})}function jo({open:e,onClose:t,setView:n,onNewChat:r,setActiveColl:i,collections:a,stats:o,onNewColl:s,onRefresh:c}){let[l,u]=(0,L.useState)(``),[d,f]=(0,L.useState)(0),p=(0,L.useRef)(null);if((0,L.useEffect)(()=>{if(e){let e=window.setTimeout(()=>{u(``),f(0),p.current?.focus()},30);return()=>window.clearTimeout(e)}},[e]),!e)return null;let m=[{label:`Go to`,items:[{id:`go-search`,label:`Search archive`,icon:W.search,action:()=>n(`/search`)},{id:`go-chat`,label:`Chat history`,icon:W.chat,action:()=>n(`/chat`)},{id:`go-insights`,label:`Insights`,icon:W.chart,action:()=>n(`/insights`)},{id:`go-indexing`,label:`Indexing`,icon:W.download,action:()=>n(`/indexing`)},{id:`go-mcp`,label:`MCP`,icon:W.plug,action:()=>n(`/mcp`)},{id:`go-settings`,label:`Settings`,icon:W.settings,action:()=>n(`/settings`)}]},{label:`Collections`,items:[{id:`coll-all`,label:`All collections`,icon:W.scope,action:()=>i(`all`)},...a.filter(e=>e!==`all`).map(e=>({id:`coll-${e}`,label:Za(e),icon:W.database,meta:`${o[e]?.files??0} files`,action:()=>i(e)}))]},{label:`Actions`,items:[{id:`a-chat`,label:`New chat`,icon:W.spark,action:r},{id:`a-new`,label:`New collection…`,icon:W.plus,action:s},{id:`a-index`,label:`Index a folder`,icon:W.folder,action:()=>n(`/indexing`)},{id:`a-refresh`,label:`Refresh stats`,icon:W.refresh,action:c}]}].map(e=>({...e,items:e.items.filter(e=>!l||e.label.toLowerCase().includes(l.toLowerCase()))})).filter(e=>e.items.length>0),h=m.flatMap(e=>e.items),g=Math.min(d,h.length-1),_=e=>{e.key===`ArrowDown`?(e.preventDefault(),f(e=>(e+1)%Math.max(1,h.length))):e.key===`ArrowUp`?(e.preventDefault(),f(e=>(e-1+h.length)%Math.max(1,h.length))):e.key===`Enter`?(e.preventDefault(),h[g]?.action(),t()):e.key===`Escape`&&t()},v=0;return(0,R.jsx)(`div`,{className:`scrim`,onClick:t,children:(0,R.jsxs)(`div`,{className:`cmdk`,onClick:e=>e.stopPropagation(),children:[(0,R.jsxs)(`div`,{className:`cmdk-input`,children:[W.searchLg,(0,R.jsx)(`input`,{ref:p,value:l,onChange:e=>{u(e.target.value),f(0)},onKeyDown:_,placeholder:`Search commands, collections, or actions…`}),(0,R.jsx)(`kbd`,{children:`ESC`})]}),(0,R.jsxs)(`div`,{className:`cmdk-list`,children:[m.map(e=>(0,R.jsxs)(`div`,{children:[(0,R.jsx)(`div`,{className:`cmdk-group-h`,children:e.label}),e.items.map(e=>{let n=v++;return(0,R.jsxs)(`button`,{className:`cmdk-item`,"data-active":n===g,onMouseEnter:()=>f(n),onClick:()=>{e.action(),t()},children:[(0,R.jsx)(`span`,{className:`ico`,children:e.icon}),(0,R.jsx)(`span`,{children:e.label}),e.meta&&(0,R.jsx)(`span`,{className:`meta`,children:e.meta})]},e.id)})]},e.label)),h.length===0&&(0,R.jsx)(`div`,{style:{padding:24,textAlign:`center`,color:`var(--text-3)`,fontSize:12},children:`No matches`})]})]})})}function Mo({open:e,onClose:t,onCreated:n}){let[r,i]=(0,L.useState)(``),a=Ya(),o=(0,L.useCallback)(async()=>{if(r)try{let e=await a.mutateAsync(r);i(``),t(),n(e.collection??r),q.success(`Created collection "${e.collection??r}".`)}catch(e){q.error(e instanceof Error?e.message:`Unknown error`)}},[r,t,n,a]);return e?(0,R.jsx)(`div`,{className:`scrim`,onClick:t,children:(0,R.jsxs)(`div`,{className:`modal-card`,onClick:e=>e.stopPropagation(),children:[(0,R.jsxs)(`div`,{className:`modal-head`,children:[(0,R.jsx)(`h3`,{children:`New collection`}),(0,R.jsx)(`p`,{children:`Collections are local LanceDB tables. Use lowercase, alphanumeric, hyphens, underscores.`})]}),(0,R.jsxs)(`div`,{className:`modal-body`,children:[(0,R.jsx)(`label`,{children:`Name`}),(0,R.jsx)(`input`,{autoFocus:!0,value:r,onChange:e=>i(e.target.value.replace(/[^a-z0-9_-]/g,``).toLowerCase()),onKeyDown:e=>e.key===`Enter`&&r&&void o(),placeholder:`my-archive`})]}),(0,R.jsxs)(`div`,{className:`modal-foot`,children:[(0,R.jsx)(`button`,{className:`btn ghost`,onClick:t,children:`Cancel`}),(0,R.jsx)(`button`,{className:`btn primary`,onClick:()=>void o(),disabled:!r,children:`Create`})]})]})}):null}var No={info:W.info,success:W.check,error:W.warn};function Po(){let e=wo(e=>e.toasts),t=wo(e=>e.dismissToast);return e.length===0?null:(0,R.jsx)(`div`,{className:`toaster`,role:`status`,"aria-live":`polite`,children:e.map(e=>(0,R.jsxs)(`button`,{className:`toast`,"data-type":e.type,onClick:()=>t(e.id),title:`Dismiss`,children:[(0,R.jsx)(`span`,{className:`toast-ico`,children:No[e.type]}),(0,R.jsx)(`span`,{className:`toast-text`,children:e.text})]},e.id))})}function Fo(){let e=wo(e=>e.confirmState),t=wo(e=>e.resolveConfirm);if((0,L.useEffect)(()=>{if(!e)return;let n=e=>{e.key===`Escape`&&(e.preventDefault(),e.stopPropagation(),t(!1))};return window.addEventListener(`keydown`,n,!0),()=>window.removeEventListener(`keydown`,n,!0)},[e,t]),!e)return null;let{title:n,message:r,confirmLabel:i,cancelLabel:a,danger:o}=e;return(0,R.jsx)(`div`,{className:`scrim`,onClick:()=>t(!1),children:(0,R.jsxs)(`div`,{className:`modal-card`,onClick:e=>e.stopPropagation(),role:`alertdialog`,"aria-modal":`true`,children:[(0,R.jsxs)(`div`,{className:`modal-head`,children:[(0,R.jsx)(`h3`,{children:n}),r&&(0,R.jsx)(`p`,{children:r})]}),(0,R.jsxs)(`div`,{className:`modal-foot`,children:[(0,R.jsx)(`button`,{className:`btn ghost`,onClick:()=>t(!1),children:a??`Cancel`}),(0,R.jsx)(`button`,{className:`btn ${o?`danger`:`primary`}`,onClick:()=>t(!0),autoFocus:!0,children:i??`Confirm`})]})]})})}function Io(){let e=Qr(),t=Ke(),n=fa(),r=n.some(e=>e.pathname===`/thread`),i=B(e=>e.theme),a=B(e=>e.activeColl),o=B(e=>e.setActiveColl),s=B(e=>e.sidebarOpen),c=B(e=>e.setSidebarOpen),l=B(e=>e.cmdkOpen),u=B(e=>e.setCmdkOpen),d=B(e=>e.newCollOpen),f=B(e=>e.setNewCollOpen),{data:p}=za(),m=p?.collections??[`all`,`chunks`],h=p?.stats??{},g=qa(),_=(0,L.useCallback)(()=>c(!1),[c]),v=(0,L.useCallback)(async()=>{await Promise.all([t.invalidateQueries({queryKey:[`collections`]}),t.invalidateQueries({queryKey:[`files`]}),t.invalidateQueries({queryKey:[`search`]}),t.invalidateQueries({queryKey:[`thread`]})]),q.success(`Local index data refreshed.`)},[t]);(0,L.useEffect)(()=>{document.documentElement.dataset.theme=i},[i]);let y=(0,L.useCallback)(async t=>{if(t!==`chunks`&&t!==`threadshelf_conversations`&&await To({title:`Delete collection "${Za(t)}"?`,message:`The index and copies previously uploaded through ThreadShelf are deleted. Files in the original folder you selected remain untouched.`,confirmLabel:`Delete`,danger:!0}))try{await g.mutateAsync(t),a===t&&(o(`all`),e.navigate({to:`/search/$collection`,params:{collection:`all`}})),q.success(`Deleted collection "${Za(t)}".`)}catch(e){q.error(e instanceof Error?e.message:`Unknown error`)}},[a,o,g,e]),b=(0,L.useCallback)(t=>{t===`/search`?e.navigate({to:`/search/$collection`,params:{collection:a}}):t===`/chat`?e.navigate({to:`/chat`,search:{}}):e.navigate({to:t}),_()},[e,_,a]),x=(0,L.useCallback)(()=>{e.navigate({to:`/chat`,search:{draft:Date.now().toString(36)}}),_()},[e,_]),S=(0,L.useCallback)(()=>{e.navigate({to:`/chat`,search:{private:Date.now().toString(36)}}),_()},[e,_]),C=(0,L.useCallback)(t=>{e.navigate({to:`/chat`,search:{thread:t}}),_()},[e,_]),w=(0,L.useCallback)(t=>{o(t),e.navigate({to:`/search/$collection`,params:{collection:t}}),_()},[o,e,_]);(0,L.useEffect)(()=>{let t=t=>{let n=t.target.tagName.toLowerCase(),i=n===`input`||n===`textarea`;if((t.metaKey||t.ctrlKey)&&t.key.toLowerCase()===`k`){t.preventDefault(),u(!l);return}if(t.key===`Escape`){l?u(!1):d?f(!1):r?e.history.back():s&&_();return}!i&&t.key===`/`&&(t.preventDefault(),document.querySelector(`.search-card input`)?.focus())};return window.addEventListener(`keydown`,t),()=>window.removeEventListener(`keydown`,t)},[l,d,r,u,f,e,s,_]);let T=n[n.length-1]?.pathname??`/search`,E=r||T.startsWith(`/search`)?`search`:T.split(`/`)[1]||`search`,D=n.find(e=>e.pathname===`/chat`)?.search,O=typeof D?.thread==`string`?D.thread:``;return(0,R.jsxs)(`div`,{className:`app`,"data-reading":r,"data-sidebar-open":s,children:[(0,R.jsx)(Ao,{view:E,setView:b,activeColl:a,setActiveColl:w,onNewChat:x,onNewPrivateChat:S,onOpenChat:C,activeChatId:O,onCmdK:()=>{u(!0),_()},onNewColl:()=>{f(!0),_()},onDeleteCollection:y,collections:m,stats:h}),(0,R.jsx)(`button`,{className:`sidebar-scrim`,onClick:_,"aria-label":`Close sidebar`,tabIndex:s?0:-1}),(0,R.jsx)(`div`,{className:`main`,children:(0,R.jsx)(ca,{})}),(0,R.jsx)(jo,{open:l,onClose:()=>u(!1),setView:b,onNewChat:x,setActiveColl:w,collections:m,stats:h,onNewColl:()=>{u(!1),f(!0)},onRefresh:()=>void v()}),d&&(0,R.jsx)(Mo,{open:!0,onClose:()=>f(!1),onCreated:e=>{w(e)}}),(0,R.jsx)(Fo,{}),(0,R.jsx)(Po,{})]})}var Lo={"google-ai-studio":{label:`Google AI Studio`,short:`AI Studio`,color:`var(--p-google)`},google:{label:`Google AI Studio`,short:`AI Studio`,color:`var(--p-google)`},openrouter:{label:`OpenRouter`,short:`OpenRouter`,color:`var(--p-openrouter)`},openai:{label:`ChatGPT`,short:`ChatGPT`,color:`var(--p-openai)`},anthropic:{label:`Claude`,short:`Claude`,color:`var(--p-claude)`},claude:{label:`Claude`,short:`Claude`,color:`var(--p-claude)`},"lm-studio":{label:`LM Studio`,short:`LM Studio`,color:`var(--p-lmstudio)`},grok:{label:`Grok`,short:`Grok`,color:`var(--p-grok)`},threadshelf:{label:`ThreadShelf`,short:`ThreadShelf`,color:`oklch(0.74 0.16 165)`}},Ro={label:`Unknown`,short:`—`,color:`var(--border-2)`},zo=e=>e?Lo[e]??Ro:Ro,Bo=[`paper chromatography household experiment`,`fact-checking workflow with citations`,`sauna rules and temperature`,`openrouter export json schema`,`ablation study sample size`,`polish translation tone register`];function Vo({onPick:e}){return(0,R.jsxs)(`div`,{className:`empty`,children:[(0,R.jsx)(`h3`,{children:`Ask your archive in plain language.`}),(0,R.jsx)(`p`,{children:`ThreadShelf runs locally — your queries embed on this machine and search a local LanceDB. Try a topic, a draft you half-remember, or the shape of an answer you're looking for.`}),(0,R.jsx)(`div`,{className:`examples`,children:Bo.map(t=>(0,R.jsxs)(`button`,{className:`example-chip`,onClick:()=>e(t),children:[W.spark,(0,R.jsx)(`span`,{children:t})]},t))})]})}function Ho(e,t){let n=G(t);if(!n)return e;try{return K(e,t).map((e,t)=>n.test(e)?(0,R.jsx)(`mark`,{children:e},t):e)}catch{return e}}function Uo({result:e,query:t,onClick:n,selected:r,onMoreLikeThis:i}){let{metadata:a}=e,o=a.role??`ai`,s={user:`user`,thinking:`reasoning`,ai:`response`}[o]??o,c=zo(a.provider),l=$a(a.model),u=ao(a.createdAt),d=e.distance==null?``:(1-e.distance).toFixed(3),f=e.distance==null?0:Math.round((1-e.distance)*100),p=(0,L.useMemo)(()=>Ho(e.document,t),[e.document,t]),m=a.title?.trim()||Qa(a.sourceFile);return(0,R.jsxs)(`button`,{className:`result`,"data-selected":r,"aria-selected":r,onClick:n,children:[(0,R.jsxs)(`div`,{className:`result-head`,children:[(0,R.jsx)(`span`,{className:`r-role`,"data-role":o,children:s}),(0,R.jsxs)(`span`,{className:`r-provider`,children:[(0,R.jsx)(`span`,{className:`pdot`,style:{background:c.color}}),(0,R.jsx)(`span`,{children:c.short})]}),a.createdInThreadShelf&&(0,R.jsx)(`span`,{className:`threadshelf-turn-badge`,children:`ThreadShelf`}),e.distance!=null&&(0,R.jsx)(`div`,{className:`r-meta-right`,children:(0,R.jsxs)(`span`,{className:`r-score`,children:[(0,R.jsx)(`span`,{className:`r-score-bar`,children:(0,R.jsx)(`i`,{style:{width:`${f}%`}})}),(0,R.jsx)(`span`,{children:d})]})})]}),(0,R.jsx)(`div`,{className:`r-title`,title:m,children:m}),(0,R.jsx)(`div`,{className:`r-snippet`,children:p}),(0,R.jsxs)(`div`,{className:`r-foot`,children:[(0,R.jsxs)(`span`,{className:`r-source`,title:a.sourceFile,children:[W.folder,(0,R.jsx)(`span`,{className:`r-source-coll`,children:Za(a.collection??``)}),(0,R.jsx)(`span`,{className:`slash`,children:`/`}),(0,R.jsx)(`span`,{className:`r-source-file`,children:Qa(a.sourceFile)})]}),(0,R.jsx)(`span`,{className:`dot`}),l&&(0,R.jsxs)(R.Fragment,{children:[(0,R.jsx)(`span`,{className:`mono`,children:l}),(0,R.jsx)(`span`,{className:`dot`})]}),u&&(0,R.jsxs)(R.Fragment,{children:[(0,R.jsx)(`span`,{children:u}),(0,R.jsx)(`span`,{className:`dot`})]}),a.turnIndex!=null&&(0,R.jsxs)(R.Fragment,{children:[(0,R.jsxs)(`span`,{children:[`turn `,(0,R.jsxs)(`b`,{className:`mono`,children:[`#`,a.turnIndex]})]}),(0,R.jsx)(`span`,{className:`dot`})]}),i&&(0,R.jsx)(`span`,{className:`more-like-this`,role:`button`,tabIndex:0,title:`Search for similar passages`,onClick:e=>{e.stopPropagation(),i()},onKeyDown:e=>{(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),e.stopPropagation(),i())},children:`more like this`}),(0,R.jsxs)(`span`,{className:`open-thread`,children:[`open thread `,W.arrowRight]})]})]})}var Wo={search:`Archive`,insights:`Insights`,indexing:`Add data`,mcp:`MCP`,settings:`Settings`,chat:`Chat`};function Go({view:e,activeColl:t,onMenu:n,actions:r}){return(0,R.jsxs)(`div`,{className:`topbar`,children:[(0,R.jsx)(`button`,{className:`icon-btn mobile-menu-btn`,onClick:n,"aria-label":`Open sidebar`,children:W.menu}),(0,R.jsxs)(`div`,{className:`crumbs`,children:[(0,R.jsx)(`span`,{children:Wo[e]}),e===`search`&&(0,R.jsxs)(R.Fragment,{children:[(0,R.jsx)(`span`,{className:`sep`,children:`/`}),(0,R.jsx)(`b`,{children:Za(t)})]})]}),(0,R.jsx)(`div`,{className:`topbar-spacer`}),r&&(0,R.jsx)(`div`,{className:`topbar-actions`,children:r})]})}var Ko=100,qo=e=>{switch(e){case`recent`:return(e,t)=>(t.lastTurnAt??``).localeCompare(e.lastTurnAt??``);case`longest`:return(e,t)=>(t.turnCount??0)-(e.turnCount??0);case`title`:return(e,t)=>(e.title||e.sourceFile).localeCompare(t.title||t.sourceFile,void 0,{sensitivity:`base`})}},Jo=[{id:`user`,label:`User`,key:`user`},{id:`reasoning`,label:`Reasoning`,key:`thinking`},{id:`response`,label:`Response`,key:`ai`}];function Yo(){return(0,R.jsx)(`div`,{className:`result-list`,style:{marginTop:16},children:[0,1,2].map(e=>(0,R.jsx)(`div`,{className:`shimmer-card`},e))})}function Xo(){let e=Ni(),{collection:t}=ji({from:`/search/$collection`}),{q:n,model:r,from:i,to:a,mode:o}=Mi({from:`/search/$collection`}),s=t?decodeURIComponent(t):`all`,c=B(e=>e.setActiveColl);(0,L.useEffect)(()=>{c(s)},[s,c]);let l=B(e=>e.roles),u=B(e=>e.toggleRole),d=B(e=>e.modelFilter),f=B(e=>e.setModelFilter),p=B(e=>e.setSidebarOpen),m=B(e=>e.savedSearches),h=B(e=>e.addSavedSearch),g=B(e=>e.removeSavedSearch),_=B(e=>e.pinnedConversations),v=B(e=>e.togglePinned),[y,b]=(0,L.useState)(n??``),[x,S]=(0,L.useState)(n??``),[C,w]=(0,L.useState)(i??``),[T,E]=(0,L.useState)(a??``),[D,O]=(0,L.useState)(o===`keyword`?`keyword`:`semantic`),[ee,te]=(0,L.useState)(15),[k,ne]=(0,L.useState)(-1),[re,ie]=(0,L.useState)(``),[A,j]=(0,L.useState)(`recent`),[M,ae]=(0,L.useState)(Ko),[oe,se]=(0,L.useState)(`all`),N=(0,L.useRef)(n??``),P=(0,L.useRef)(null),F=(0,L.useRef)(null),{data:ce}=Ra(),le=ce??!0,ue=(0,L.useMemo)(()=>{let e=[];return l.user&&e.push(`user`),l.thinking&&e.push(`thinking`),l.ai&&e.push(`ai`),e.length>0&&e.length<3?e.join(`,`):void 0},[l]),{data:de,isFetching:fe}=Ua((0,L.useMemo)(()=>({q:x.trim(),collection:s,n:ee,roles:ue,keywordBoost:D===`semantic`&&x.trim().length<=40,model:(r??d).trim()||void 0,from:C.trim()||void 0,to:T.trim()||void 0,mode:D,origin:oe===`all`?void 0:oe}),[x,s,ee,ue,r,d,C,T,D,oe])),{data:pe,isLoading:me}=Va(s,!x.trim()&&le),he=(0,L.useMemo)(()=>{let e=pe?.files??[],t=re.trim().toLowerCase();return[...(t?e.filter(e=>[e.title,e.sourceFile,e.conversationKey,e.collection].some(e=>(e??``).toLowerCase().includes(t))):e).filter(e=>oe===`threadshelf`?e.createdInThreadShelf===!0||e.hasThreadShelfTurns===!0:oe!==`archive`||e.hasThreadShelfTurns!==!0)].sort(qo(A))},[pe?.files,re,A,oe]),I=(0,L.useMemo)(()=>he.slice(0,M),[he,M]);(0,L.useEffect)(()=>{let e=window.setTimeout(()=>ae(Ko),0);return()=>window.clearTimeout(e)},[s,re,A,oe]),(0,L.useEffect)(()=>{P.current?.focus()},[]),(0,L.useEffect)(()=>{let e=n??``;if(e!==N.current){let t=window.setTimeout(()=>{N.current=e,b(e),S(e),w(i??``),E(a??``),O(o===`keyword`?`keyword`:`semantic`)},0);return()=>window.clearTimeout(t)}},[n,i,a,o]),(0,L.useEffect)(()=>{let e=window.setTimeout(()=>{w(i??``),E(a??``)},0);return()=>window.clearTimeout(e)},[i,a]),(0,L.useEffect)(()=>{let e=window.setTimeout(()=>te(15),0);return()=>window.clearTimeout(e)},[x,s,ue,r,d,C,T,D,oe]);let ge=(0,L.useCallback)(t=>{let n=y.trim(),r=t??D;N.current=n,S(n),e({to:`/search/$collection`,params:{collection:s},search:n?{q:n,model:d.trim()||void 0,from:C.trim()||void 0,to:T.trim()||void 0,mode:r===`keyword`?`keyword`:void 0}:{},replace:!0})},[y,d,C,T,e,s,D]),_e=(0,L.useCallback)(()=>{N.current=``,b(``),S(``),P.current?.focus(),e({to:`/search/$collection`,params:{collection:s},search:{},replace:!0})},[e,s]),ve=(0,L.useCallback)(e=>{O(e),ge(e)},[ge]),ye=(0,L.useCallback)(t=>{e({to:`/thread`,search:{sourceFile:t.metadata.sourceFile,collection:t.metadata.collection??s,conversationKey:t.metadata.conversationKey,q:x.trim()||void 0,title:t.metadata.title,matchIdx:t.metadata.turnIndex,provider:t.metadata.provider,model:t.metadata.model}})},[s,x,e]),be=(0,L.useCallback)(t=>{let n=lo(t.document);n&&(O(`semantic`),N.current=n,b(n),S(n),e({to:`/search/$collection`,params:{collection:s},search:{q:n,model:d.trim()||void 0,from:C.trim()||void 0,to:T.trim()||void 0}}))},[s,d,C,T,e]),xe=(0,L.useCallback)(t=>{e({to:`/thread`,search:{sourceFile:t.sourceFile,collection:t.collection,conversationKey:t.conversationKey,title:t.title}})},[e]),Se=x.trim().length>0,Ce=(0,L.useMemo)(()=>({q:x.trim(),collection:s,mode:D,model:(r??d).trim()||void 0,from:C.trim()||void 0,to:T.trim()||void 0}),[x,s,D,r,d,C,T]),we=(0,L.useMemo)(()=>{let e=ka(Ce);return m.find(t=>ka(t)===e)},[m,Ce]),Te=(0,L.useCallback)(()=>{we?g(we.id):h(Ce)},[we,g,h,Ce]),Ee=(0,L.useCallback)(t=>{O(t.mode),N.current=t.q,b(t.q),S(t.q),e({to:`/search/$collection`,params:{collection:t.collection},search:{q:t.q,model:t.model,from:t.from,to:t.to,mode:t.mode===`keyword`?`keyword`:void 0}})},[e]),De=(0,L.useMemo)(()=>s===`all`?_:_.filter(e=>e.collection===s),[_,s]),Oe=(0,L.useMemo)(()=>new Set(_.map(e=>Aa(e))),[_]),ke=e=>({collection:e.collection,sourceFile:e.sourceFile,conversationKey:e.conversationKey,title:e.title,provider:e.provider}),Ae=(0,L.useMemo)(()=>de?.results??[],[de?.results]),je=Se&&!fe&&Ae.length>=ee&&ee<50,Me=s===`all`?`Conversations`:`${Za(s)} conversations`,Ne=Se?Ae.length:I.length,Pe=(0,L.useCallback)(e=>{if(Se){let t=Ae[e];t&&ye(t)}else{let t=I[e];t&&xe(t)}},[Se,Ae,I,ye,xe]);return(0,L.useEffect)(()=>{let e=window.setTimeout(()=>ne(-1),0);return()=>window.clearTimeout(e)},[x,s,Se]),(0,L.useEffect)(()=>{let e=e=>{let t=e.target,n=t.tagName?.toLowerCase(),r=t===P.current;if((n===`input`||n===`textarea`)&&!r||Ne===0)return;let i=e.key===`ArrowDown`||e.key===`j`&&!r,a=e.key===`ArrowUp`||e.key===`k`&&!r;i?(e.preventDefault(),r&&P.current?.blur(),ne(e=>Math.min(Ne-1,e+1))):a?(e.preventDefault(),ne(e=>e<=0?0:e-1)):e.key===`Enter`&&!r&&k>=0&&(e.preventDefault(),Pe(k))};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[Ne,k,Pe]),(0,L.useEffect)(()=>{k<0||F.current?.querySelector(`[data-selected="true"]`)?.scrollIntoView({block:`nearest`})},[k]),(0,R.jsxs)(R.Fragment,{children:[(0,R.jsx)(Go,{view:`search`,activeColl:s,onMenu:()=>p(!0)}),(0,R.jsx)(`div`,{className:`main-scroll`,children:(0,R.jsxs)(`div`,{className:`view`,children:[!le&&(0,R.jsxs)(`div`,{className:`banner err`,children:[(0,R.jsx)(`span`,{className:`ico`,children:W.warn}),(0,R.jsxs)(`div`,{className:`grow`,children:[(0,R.jsx)(`b`,{style:{fontWeight:500},children:`Backend unreachable.`}),` `,(0,R.jsxs)(`span`,{style:{color:`var(--text-2)`},children:[`Run `,(0,R.jsx)(`code`,{children:`npm start`}),` in the project root.`]})]})]}),(0,R.jsxs)(`div`,{className:`search-card`,children:[(0,R.jsx)(`span`,{className:`search-ico`,children:W.searchLg}),(0,R.jsx)(`input`,{id:`searchInput`,ref:P,value:y,onChange:e=>b(e.target.value),onKeyDown:e=>{e.key===`Enter`?ge():e.key===`Escape`&&y&&(e.preventDefault(),e.stopPropagation(),_e())},placeholder:D===`keyword`?`Exact match: identifiers, error strings, code…`:`Search by meaning across your archive…`}),y&&(0,R.jsx)(`button`,{id:`clearSearch`,type:`button`,className:`search-clear`,"aria-label":`Clear search`,title:`Clear search (Esc)`,onClick:_e,children:W.close})]}),(0,R.jsxs)(`div`,{className:`search-toolbar`,children:[(0,R.jsxs)(`div`,{className:`mode-toggle`,role:`group`,"aria-label":`Search mode`,children:[(0,R.jsx)(`button`,{type:`button`,className:`mode-btn`,"data-on":D===`semantic`,title:`Rank by meaning (local embeddings)`,onClick:()=>ve(`semantic`),children:`Semantic`}),(0,R.jsx)(`button`,{type:`button`,className:`mode-btn`,"data-on":D===`keyword`,title:`Exact substring match (case-insensitive)`,onClick:()=>ve(`keyword`),children:`Exact`})]}),(0,R.jsx)(`div`,{className:`role-chips`,children:Jo.map(e=>(0,R.jsxs)(`button`,{className:`role-chip`,"data-role":e.id,"data-on":l[e.key],onClick:()=>u(e.key),children:[(0,R.jsx)(`span`,{className:`dot`}),e.label]},e.id))}),(0,R.jsxs)(`label`,{className:`origin-filter`,children:[(0,R.jsx)(`span`,{children:`origin`}),(0,R.jsxs)(`select`,{"aria-label":`Filter by conversation origin`,value:oe,onChange:e=>se(e.target.value),children:[(0,R.jsx)(`option`,{value:`all`,children:`All`}),(0,R.jsx)(`option`,{value:`threadshelf`,children:`ThreadShelf`}),(0,R.jsx)(`option`,{value:`archive`,children:`Clean archive`})]})]}),(0,R.jsxs)(`label`,{className:`model-filter`,children:[(0,R.jsx)(`span`,{children:`model`}),(0,R.jsx)(`input`,{value:d,onChange:e=>f(e.target.value),onKeyDown:e=>e.key===`Enter`&&ge(),placeholder:`gpt-5, claude, gemini...`})]}),(0,R.jsxs)(`label`,{className:`date-filter`,children:[(0,R.jsx)(`span`,{children:`from`}),(0,R.jsx)(`input`,{type:`date`,value:C,onChange:e=>w(e.target.value),onKeyDown:e=>e.key===`Enter`&&ge()})]}),(0,R.jsxs)(`label`,{className:`date-filter`,children:[(0,R.jsx)(`span`,{children:`to`}),(0,R.jsx)(`input`,{type:`date`,value:T,onChange:e=>E(e.target.value),onKeyDown:e=>e.key===`Enter`&&ge()})]}),(0,R.jsx)(`span`,{className:`toolbar-spacer`}),(0,R.jsxs)(`span`,{className:`toolbar-hint`,children:[(0,R.jsx)(`kbd`,{children:`/`}),` focus · `,(0,R.jsx)(`kbd`,{children:`↑↓`}),` navigate · `,(0,R.jsx)(`kbd`,{children:`↵`}),` open · `,(0,R.jsx)(`kbd`,{children:`Ctrl+K`}),` `,`commands`]})]}),!Se&&(0,R.jsxs)(R.Fragment,{children:[s===`all`&&(0,R.jsx)(Vo,{onPick:t=>{N.current=t,b(t),S(t),e({to:`/search/$collection`,params:{collection:s},search:{q:t},replace:!0})}}),m.length>0&&(0,R.jsxs)(`div`,{className:`saved-searches`,children:[(0,R.jsxs)(`span`,{className:`ss-label`,children:[W.star,` Saved`]}),m.map(e=>(0,R.jsxs)(`span`,{className:`ss-chip`,children:[(0,R.jsxs)(`button`,{type:`button`,className:`ss-run`,title:`Run: ${e.q}`,onClick:()=>Ee(e),children:[(0,R.jsx)(`span`,{className:`ss-q`,children:e.q}),e.collection!==`all`&&(0,R.jsx)(`span`,{className:`ss-tag`,children:Za(e.collection)}),e.mode===`keyword`&&(0,R.jsx)(`span`,{className:`ss-tag`,children:`exact`})]}),(0,R.jsx)(`button`,{type:`button`,className:`ss-del`,"aria-label":`Delete saved search "${e.q}"`,onClick:()=>g(e.id),children:`×`})]},e.id))]}),De.length>0&&(0,R.jsxs)(R.Fragment,{children:[(0,R.jsx)(`div`,{className:`results-meta-bar`,children:(0,R.jsxs)(`span`,{className:`h`,children:[`Pinned `,(0,R.jsx)(`b`,{children:De.length})]})}),(0,R.jsx)(`div`,{className:`result-list`,children:De.map(e=>{let t=e.provider?zo(e.provider):null,n=Qa(e.sourceFile);return(0,R.jsxs)(`button`,{className:`result`,onClick:()=>xe(e),children:[(0,R.jsxs)(`div`,{className:`result-head`,children:[(0,R.jsx)(`span`,{className:`r-role`,"data-role":`user`,children:`pinned`}),(0,R.jsxs)(`span`,{className:`r-provider`,children:[(0,R.jsx)(`span`,{className:`pdot`,style:{background:t?.color??`var(--accent)`}}),(0,R.jsx)(`span`,{children:t?.short??Za(e.collection)})]}),(0,R.jsx)(`span`,{className:`pin-toggle`,role:`button`,tabIndex:0,"data-on":`true`,title:`Unpin`,onClick:t=>{t.stopPropagation(),v(e)},onKeyDown:t=>{(t.key===`Enter`||t.key===` `)&&(t.preventDefault(),t.stopPropagation(),v(e))},children:W.pinFilled})]}),(0,R.jsx)(`div`,{className:`r-title`,title:e.title||n,children:e.title||n}),(0,R.jsxs)(`div`,{className:`r-foot`,children:[(0,R.jsxs)(`span`,{className:`r-source`,title:e.sourceFile,children:[W.folder,(0,R.jsx)(`span`,{className:`r-source-coll`,children:Za(e.collection)}),(0,R.jsx)(`span`,{className:`slash`,children:`/`}),(0,R.jsx)(`span`,{className:`r-source-file`,children:n})]}),(0,R.jsxs)(`span`,{className:`open-thread`,children:[`open thread `,W.arrowRight]})]})]},Aa(e))})})]}),(0,R.jsxs)(`div`,{className:`results-meta-bar`,children:[(0,R.jsxs)(`span`,{className:`h`,children:[Me,` `,(0,R.jsx)(`b`,{children:he.length})]}),(0,R.jsxs)(`label`,{className:`conv-sort`,children:[(0,R.jsx)(`span`,{children:`sort`}),(0,R.jsxs)(`select`,{value:A,onChange:e=>j(e.target.value),"aria-label":`Sort conversations`,children:[(0,R.jsx)(`option`,{value:`recent`,children:`Recent`}),(0,R.jsx)(`option`,{value:`longest`,children:`Longest`}),(0,R.jsx)(`option`,{value:`title`,children:`Title`})]})]}),(0,R.jsx)(`input`,{className:`conv-filter`,value:re,onChange:e=>ie(e.target.value),placeholder:`Filter by title or file…`,"aria-label":`Filter conversations`})]}),me?(0,R.jsx)(Yo,{}):he.length>0?(0,R.jsxs)(`div`,{className:`result-list`,ref:F,children:[I.map((e,t)=>{let n=Qa(e.sourceFile),r=e.title||n,i=e.provider?zo(e.provider):null,a=ao(e.lastTurnAt),o=ke(e),s=Oe.has(Aa(o));return(0,R.jsxs)(`button`,{className:`result`,"data-selected":t===k,"aria-selected":t===k,onClick:()=>xe(e),children:[(0,R.jsxs)(`div`,{className:`result-head`,children:[(0,R.jsx)(`span`,{className:`r-role`,"data-role":`ai`,children:`thread`}),e.hasThreadShelfTurns&&(0,R.jsx)(`span`,{className:`threadshelf-turn-badge`,children:e.createdInThreadShelf?`Created in ThreadShelf`:`Continued in ThreadShelf`}),(0,R.jsxs)(`span`,{className:`r-provider`,children:[(0,R.jsx)(`span`,{className:`pdot`,style:{background:i?.color??`var(--accent)`}}),(0,R.jsx)(`span`,{children:i?.short??Za(e.collection)})]}),(0,R.jsx)(`span`,{className:`pin-toggle`,role:`button`,tabIndex:0,"data-on":s,title:s?`Unpin`:`Pin conversation`,onClick:e=>{e.stopPropagation(),v(o)},onKeyDown:e=>{(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),e.stopPropagation(),v(o))},children:s?W.pinFilled:W.pin})]}),(0,R.jsx)(`div`,{className:`r-title`,title:r,children:r}),(0,R.jsxs)(`div`,{className:`r-foot`,children:[(0,R.jsxs)(`span`,{className:`r-source`,title:e.sourceFile,children:[W.folder,(0,R.jsx)(`span`,{className:`r-source-coll`,children:Za(e.collection)}),(0,R.jsx)(`span`,{className:`slash`,children:`/`}),(0,R.jsx)(`span`,{className:`r-source-file`,children:n})]}),e.turnCount!=null&&(0,R.jsxs)(R.Fragment,{children:[(0,R.jsx)(`span`,{className:`dot`}),(0,R.jsxs)(`span`,{children:[e.turnCount,` turns`]})]}),a&&(0,R.jsxs)(R.Fragment,{children:[(0,R.jsx)(`span`,{className:`dot`}),(0,R.jsx)(`span`,{children:a})]}),(0,R.jsxs)(`span`,{className:`open-thread`,children:[`open thread `,W.arrowRight]})]})]},`${e.collection}:${e.sourceFile}:${t}`)}),he.length>M&&(0,R.jsx)(`div`,{className:`load-more-row`,children:(0,R.jsxs)(`button`,{type:`button`,className:`load-more-button`,onClick:()=>ae(e=>e+Ko),children:[`Show more (`,he.length-M,` left)`]})})]}):re.trim()?(0,R.jsxs)(`div`,{className:`empty`,children:[(0,R.jsx)(`h3`,{children:`No conversations match the filter.`}),(0,R.jsx)(`p`,{children:`Try a different phrase or clear the filter box.`})]}):s===`all`?null:(0,R.jsxs)(`div`,{className:`empty`,children:[(0,R.jsx)(`h3`,{children:`No conversations indexed.`}),(0,R.jsx)(`p`,{children:`Index a folder into this collection or switch to another collection.`})]})]}),Se&&fe&&(0,R.jsx)(Yo,{}),Se&&!fe&&Ae.length===0&&(0,R.jsxs)(`div`,{className:`empty`,children:[(0,R.jsx)(`h3`,{children:`No results found.`}),(0,R.jsx)(`p`,{children:`Try another query, broaden your role filters, or index more files.`})]}),Se&&!fe&&Ae.length>0&&(0,R.jsxs)(R.Fragment,{children:[(0,R.jsxs)(`div`,{className:`results-meta-bar`,children:[(0,R.jsxs)(`span`,{className:`h`,children:[`Results `,(0,R.jsx)(`b`,{children:Ae.length}),(0,R.jsxs)(`span`,{style:{marginLeft:14,color:`var(--text-3)`},children:[`in `,Za(s)]})]}),(0,R.jsxs)(`button`,{type:`button`,className:`save-search`,"data-on":!!we,title:we?`Remove from saved searches`:`Save this search`,onClick:Te,children:[we?W.starFilled:W.star,(0,R.jsx)(`span`,{children:we?`Saved`:`Save search`})]})]}),(0,R.jsx)(`div`,{className:`result-list`,ref:F,children:Ae.map((e,t)=>(0,R.jsx)(Uo,{result:e,query:x,selected:t===k,onClick:()=>ye(e),onMoreLikeThis:()=>be(e)},t))}),je&&(0,R.jsx)(`div`,{className:`load-more-row`,children:(0,R.jsx)(`button`,{type:`button`,className:`load-more-button`,onClick:()=>te(e=>Math.min(e+15,50)),children:`Load more`})})]})]})})]})}var Zo=/^(\s*)([-*+]|\d+[.)])\s+(.*)$/,Qo=/^\s*\d+[.)]\s+/,$o=e=>{let t=e.trim();return/^(?:https?:\/\/|mailto:|#|\/)/i.test(t)?t:null},es=e=>{let t=[],n=``,r=()=>{n&&=(t.push({type:`text`,value:n}),``)},i=0;for(;i<e.length;){let a=e[i];if(a==="`"){let n=e.indexOf("`",i+1);if(n>i){r(),t.push({type:`code`,value:e.slice(i+1,n)}),i=n+1;continue}}if(a===`[`){let n=e.indexOf(`]`,i+1);if(n>i&&e[n+1]===`(`){let a=e.indexOf(`)`,n+2);if(a>n){let o=$o(e.slice(n+2,a));if(o){r(),t.push({type:`link`,href:o,children:es(e.slice(i+1,n))}),i=a+1;continue}}}}if(a===`*`&&e[i+1]===`*`||a===`_`&&e[i+1]===`_`){let n=e.slice(i,i+2),a=e.indexOf(n,i+2);if(a>i+1){r(),t.push({type:`strong`,children:es(e.slice(i+2,a))}),i=a+2;continue}}if(a===`*`){let n=e.indexOf(`*`,i+1);if(n>i+1){r(),t.push({type:`em`,children:es(e.slice(i+1,n))}),i=n+1;continue}}n+=a,i+=1}return r(),t},ts=e=>{let t=e.replace(/\r\n?/g,`
18
18
  `).split(`
19
19
  `),n=[],r=0;for(;r<t.length;){let e=t[r];if(!e.trim()){r+=1;continue}let i=/^```(\w+)?\s*$/.exec(e);if(i){let e=[];for(r+=1;r<t.length&&!/^```\s*$/.test(t[r]);)e.push(t[r]),r+=1;r+=1,n.push({type:`code`,value:e.join(`
20
20
  `),lang:i[1]});continue}let a=/^(#{1,6})\s+(.*)$/.exec(e);if(a){n.push({type:`heading`,level:a[1].length,children:es(a[2].trim())}),r+=1;continue}if(/^>\s?/.test(e)){let e=[];for(;r<t.length&&/^>\s?/.test(t[r]);)e.push(t[r].replace(/^>\s?/,``)),r+=1;n.push({type:`quote`,children:es(e.join(`
package/public/index.html CHANGED
@@ -9,7 +9,7 @@
9
9
  name="description"
10
10
  content="Local-first semantic search and backup tool for AI chat exports (Google AI Studio, OpenRouter, LM Studio, ChatGPT, Claude)."
11
11
  />
12
- <script type="module" crossorigin src="/assets/index-CIm_Idqi.js"></script>
12
+ <script type="module" crossorigin src="/assets/index-B7GRXu5E.js"></script>
13
13
  <link rel="stylesheet" crossorigin href="/assets/index-Dv09K2vS.css">
14
14
  </head>
15
15
  <body>