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