threadshelf 1.2.0 → 1.2.2

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,30 @@
2
2
 
3
3
  All notable changes to ThreadShelf are documented here.
4
4
 
5
- ## Unreleased
5
+ ## 1.2.2 — 2026-09-20
6
+
7
+ ### Packaging review follow-ups
8
+
9
+ - Resolve the package root by looking for the manifest rather than for a
10
+ directory named `dist`, so a repository cloned into a directory called `dist`
11
+ no longer anchors every path one level too high. `dist/` must stay free of a
12
+ stray `package.json` for this, which `test/packaging.test.js` now asserts.
13
+ - Detect a checkout by the presence of `src/` rather than of `src/server.ts`,
14
+ so renaming or splitting an entry point cannot silently move a developer's
15
+ archive out of the repository.
16
+ - Add `npx threadshelf mcp` next to the existing `threadshelf-mcp` executable.
17
+ It starts the server explicitly instead of relying on the MCP module's own
18
+ entry-point detection, which compares URLs in a way that is fragile on
19
+ Windows.
20
+ - Widen the `process.cwd()` guard in `test/packaging.test.js` to cover `bin/`
21
+ (the npx entry point) and `src/paths.ts`. It previously scanned only `.ts`
22
+ files and exempted `paths.ts` outright, so it would have passed while the two
23
+ files most able to break every path did the wrong thing.
24
+ - Exercise the compiled `dist/src/paths.js` from `npm test`, and the `mcp`
25
+ subcommand from `npm run pack:verify`. Both were previously covered only by
26
+ the five-minute packaging run, or not at all.
27
+
28
+ ## 1.2.1 — 2026-09-20
6
29
 
7
30
  ### Install with `npx threadshelf`
8
31
 
@@ -37,6 +60,11 @@ All notable changes to ThreadShelf are documented here.
37
60
  - Add `.github/workflows/publish.yml`: tag-driven release on `v*` that runs the
38
61
  test suite and publishes through npm Trusted Publishing (OIDC), with no npm
39
62
  token stored in the repository.
63
+ - Reach the bundled CLIs from an installed package:
64
+ `npx threadshelf search|ingest|parse`. They were compiled into the tarball but
65
+ had no entry point, so only a clone could run them. Their usage messages now
66
+ name the invocation that applies — `npm run search --` from a checkout,
67
+ `npx threadshelf search` from an install.
40
68
 
41
69
  ## 1.2.0 — 2026-09-13
42
70
 
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
@@ -576,7 +585,7 @@ ThreadShelf exposes your local index to MCP clients (e.g. Claude Desktop, or any
576
585
  MCP-capable agent) over stdio — so a model can search your past chats as a tool.
577
586
 
578
587
  ```bash
579
- npx threadshelf-mcp # installed package
588
+ npx threadshelf mcp # installed package (npx threadshelf-mcp is equivalent)
580
589
  npm run mcp # from a clone
581
590
  ```
582
591
 
@@ -3,30 +3,63 @@
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
+
25
+ // `mcp` is handled separately: dist/mcp/server.js only starts itself when it
26
+ // decides it is the process entry point, and that check compares URLs in a way
27
+ // that is easy to get wrong on Windows. Start it explicitly instead, exactly as
28
+ // bin/threadshelf-mcp.js does.
29
+ const startMcpServer = async () => {
30
+ const [{ runServer }, { startIndexRecovery }] = await Promise.all([
31
+ import('../dist/mcp/server.js'),
32
+ import('../dist/src/store.js'),
33
+ ]);
34
+ const stopRecovery = startIndexRecovery();
35
+ process.stdin.once('end', stopRecovery);
36
+ runServer();
37
+ };
38
+
14
39
  const args = process.argv.slice(2);
15
40
 
16
41
  const usage = `ThreadShelf ${pkg.version} - local semantic search for your AI chats
17
42
 
18
43
  Usage:
19
- npx threadshelf [port] Start the web UI and API (default port 3000)
44
+ npx threadshelf [port] Start the web UI and API (default port 3000)
45
+ npx threadshelf search "<query>" [...] Search the archive from the terminal
46
+ npx threadshelf ingest <folder> [...] Ingest a folder of exports
47
+ npx threadshelf parse <file> [...] Parse one export to normalized JSON
48
+ npx threadshelf mcp Start the MCP stdio server
49
+ (npx threadshelf-mcp is equivalent)
20
50
 
21
51
  Options:
22
52
  -p, --port <port> Port to listen on (default 3000, or $PORT)
23
53
  --host <host> Interface to bind (default 127.0.0.1, loopback only)
24
54
  --data-dir <d> Directory for persistent data
25
- (default: %LOCALAPPDATA%\ThreadShelf on Windows, ~/.threadshelf elsewhere)
55
+ (default: %LOCALAPPDATA%\\ThreadShelf on Windows, ~/.threadshelf elsewhere)
26
56
  --where Print the resolved data and package directories, then exit
27
57
  -v, --version Print the version
28
58
  -h, --help Show this help
29
59
 
60
+ Each subcommand takes its own flags; run it with --help for those. Pass
61
+ --data-dir before the subcommand, e.g. npx threadshelf --data-dir D:\\shelf search "x".
62
+
30
63
  Environment:
31
64
  PORT, HOST, THREADSHELF_DATA_DIR, LANCEDB_PATH and the other documented
32
65
  overrides keep working and take precedence over the defaults.
@@ -34,6 +67,8 @@ Environment:
34
67
 
35
68
  let port = '';
36
69
  let showWhere = false;
70
+ let subcommand = '';
71
+ let subcommandArgs = [];
37
72
 
38
73
  const takeValue = (flag, index) => {
39
74
  const value = args[index + 1];
@@ -46,7 +81,12 @@ const takeValue = (flag, index) => {
46
81
 
47
82
  for (let i = 0; i < args.length; i += 1) {
48
83
  const arg = args[i];
49
- if (arg === '-h' || arg === '--help') {
84
+ if (arg === 'mcp' || Object.hasOwn(SUBCOMMANDS, arg)) {
85
+ // Everything after the subcommand belongs to it, untouched.
86
+ subcommand = arg;
87
+ subcommandArgs = args.slice(i + 1);
88
+ break;
89
+ } else if (arg === '-h' || arg === '--help') {
50
90
  console.log(usage);
51
91
  process.exit(0);
52
92
  } else if (arg === '-v' || arg === '--version') {
@@ -80,8 +120,17 @@ if (showWhere) {
80
120
  process.exit(0);
81
121
  }
82
122
 
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');
123
+ if (subcommand === 'mcp') {
124
+ await startMcpServer();
125
+ } else if (subcommand) {
126
+ // The compiled CLIs read process.argv.slice(2) at module load, so present
127
+ // them the argv they would have seen if they had been invoked directly.
128
+ const entry = fileURLToPath(new URL(SUBCOMMANDS[subcommand], import.meta.url));
129
+ process.argv = [process.argv[0], entry, ...subcommandArgs];
130
+ await import(SUBCOMMANDS[subcommand]);
131
+ } else {
132
+ // server.ts reads process.argv[2] as a port; it is already normalised into
133
+ // process.env.PORT above, so hide the raw argv from it.
134
+ process.argv = [process.argv[0], process.argv[1]];
135
+ await import('../dist/src/server.js');
136
+ }
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
@@ -1,6 +1,6 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { homedir } from 'node:os';
3
- import { basename, dirname, join, resolve } from 'node:path';
3
+ import { dirname, join, resolve } from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  /**
6
6
  * Single source of truth for "where does ThreadShelf read and write things".
@@ -24,10 +24,16 @@ const moduleDir = dirname(fileURLToPath(import.meta.url));
24
24
  * This module lives at `<root>/src/paths.ts` in development and at
25
25
  * `<root>/dist/src/paths.js` once compiled, so the package root is one or two
26
26
  * levels up depending on which copy is running.
27
+ *
28
+ * The two cases are told apart by which candidate holds the manifest, not by
29
+ * the directory being named `dist`: a repository cloned into a directory that
30
+ * happens to be called `dist` would fool a name check and send every path one
31
+ * level too high. The build must therefore keep `dist/` free of a stray
32
+ * `package.json` — `test/packaging.test.js` asserts that.
27
33
  */
28
34
  const resolvePackageRoot = (dir) => {
29
35
  const parent = dirname(dir);
30
- return basename(parent) === 'dist' ? dirname(parent) : parent;
36
+ return existsSync(join(parent, 'package.json')) ? parent : dirname(parent);
31
37
  };
32
38
  const PACKAGE_ROOT = resolvePackageRoot(moduleDir);
33
39
  /** Root of the installed package (or the repo in development). Static assets only. */
@@ -36,10 +42,12 @@ export const packageRoot = () => PACKAGE_ROOT;
36
42
  export const packagePath = (...segments) => join(PACKAGE_ROOT, ...segments);
37
43
  /**
38
44
  * True when running from a source checkout rather than an installed package.
39
- * The published tarball ships `dist/`, never `src/*.ts`, so this cannot be
40
- * confused by an end user's install.
45
+ * The published tarball ships `dist/` and never `src/`, so an end user's
46
+ * install cannot be mistaken for a checkout. The whole directory is the marker
47
+ * rather than one file inside it, so renaming or splitting an entry point
48
+ * cannot silently move every developer's data out of their repository.
41
49
  */
42
- export const isRepoCheckout = () => existsSync(join(PACKAGE_ROOT, 'src', 'server.ts'));
50
+ export const isRepoCheckout = () => existsSync(join(PACKAGE_ROOT, 'src'));
43
51
  /** Per-user data directory for an installed package. */
44
52
  export const userDataDir = (env = process.env, platform = process.platform, home = homedir()) => {
45
53
  if (platform === 'win32') {
@@ -72,6 +80,12 @@ const LAYOUT = {
72
80
  modelCache: { repo: ['.threadshelf', 'model-cache'], user: ['model-cache'] },
73
81
  env: { repo: ['.env'], user: ['.env'] },
74
82
  };
83
+ /**
84
+ * How the user would invoke one of the bundled CLIs, for usage messages.
85
+ * `npm run search --` is right from a clone and meaningless to somebody who
86
+ * installed the package, so print whichever actually applies.
87
+ */
88
+ export const invocation = (command) => isRepoCheckout() ? `npm run ${command} --` : `npx threadshelf ${command}`;
75
89
  /** Absolute path of a persistent file or directory. Callers still apply their own env overrides. */
76
90
  export const dataPath = (key) => {
77
91
  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.2",
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.2`,` · 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-TIPW3iTR.js"></script>
13
13
  <link rel="stylesheet" crossorigin href="/assets/index-Dv09K2vS.css">
14
14
  </head>
15
15
  <body>