botnote 0.1.20 → 0.1.22

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "botnote",
3
- "version": "0.1.20",
3
+ "version": "0.1.22",
4
4
  "description": "Notion for bots. Lightweight project + notes + memory store, agent-first.",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@10.33.0",
@@ -482,39 +482,44 @@ BOTNOTE_URL=http://127.0.0.1:4280 botnote today
482
482
 
483
483
  # Config file (preferred for the daemon host):
484
484
  # ~/.config/botnote/config.json
485
- # { "baseUrl": "http://127.0.0.1:4280" }`})]})}function rU(){return O.jsxs(O.Fragment,{children:[O.jsx(Xl,{title:"Plugin",blurb:"Install the botnote runtime once, then add the plugin for each agent client. The plugin bundles MCP, slash commands, and workflow skills, so Letheia / Plane MCP setup should stay retired."}),O.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-2",children:[O.jsx(kO,{icon:zS,title:"1. Runtime",body:"Install or update the npm binary on every device. Plugins call this binary for MCP."}),O.jsx(kO,{icon:LB,title:"2. Client plugin",body:"Install the marketplace plugin in Claude Code, Codex, Cursor, or another agent client."}),O.jsx(kO,{icon:FT,title:"3. Update both",body:"Runtime and plugin cache update separately. Refresh the client after updating."})]}),O.jsxs("div",{className:"border border-accent/20 bg-accentSoft/50 rounded-md px-4 py-3 text-xs text-accentText leading-relaxed",children:["Use ",O.jsx("code",{className:"text-ink",children:"https://botnote.net"})," with an API token from this settings page on remote devices. On the daemon host, use"," ",O.jsx("code",{className:"text-ink",children:"http://127.0.0.1:4280"})," and skip the token."]}),O.jsx(xo,{icon:zS,title:"Runtime",subtitle:"Required first. Claude Code, Codex, and Cursor all launch this binary for MCP."}),O.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-2 gap-3",children:[O.jsx(on,{title:"Install runtime",code:`# Optional helper CLI for login and manual commands.
485
+ # { "baseUrl": "http://127.0.0.1:4280" }`})]})}function rU(){return O.jsxs(O.Fragment,{children:[O.jsx(Xl,{title:"Plugin",blurb:"Add botnote as the plugin-backed MCP + workflow layer for each agent client. Letheia / Plane MCP setup should stay retired."}),O.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-2",children:[O.jsx(kO,{icon:zS,title:"1. Optional CLI",body:"Install the npm binary when you want botnote login or terminal commands. Plugins can run without it via npx."}),O.jsx(kO,{icon:LB,title:"2. Client plugin",body:"Install the marketplace plugin in Claude Code, Codex, Cursor, or another agent client."}),O.jsx(kO,{icon:FT,title:"3. Reload client",body:"Claude Code reloads plugins in-session. Codex needs a new session after install or update."})]}),O.jsxs("div",{className:"border border-accent/20 bg-accentSoft/50 rounded-md px-4 py-3 text-xs text-accentText leading-relaxed",children:["Use ",O.jsx("code",{className:"text-ink",children:"https://botnote.net"})," with an API token from this settings page on remote devices. On the daemon host, use"," ",O.jsx("code",{className:"text-ink",children:"http://127.0.0.1:4280"})," and skip the token."]}),O.jsx(xo,{icon:zS,title:"Runtime",subtitle:"Optional helper for login, manual CLI commands, and offline fallback. Plugin MCP can run through npx without a global CLI."}),O.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-2 gap-3",children:[O.jsx(on,{title:"Install runtime",code:`# Optional helper CLI for login and manual commands.
486
486
  # Plugins can run without a global CLI by using npx.
487
487
  npm i -g botnote@latest
488
488
 
489
489
  # Remote clients: save https://botnote.net + a bearer token.
490
490
  # Daemon host: use http://127.0.0.1:4280 and skip the token.
491
- botnote login`}),O.jsx(on,{title:"Update runtime",code:`# Keep the runtime current on every device.
491
+ botnote login`}),O.jsx(on,{title:"Update runtime",code:`# If you installed the optional helper CLI, keep it current.
492
492
  npm i -g botnote@latest
493
- botnote --version`})]}),O.jsx(xo,{icon:NB,title:"Claude Code",subtitle:"Recommended for day-to-day work. Enable marketplace auto-update after installation."}),O.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-2 gap-3",children:[O.jsx(on,{title:"Install",code:`# In Claude Code
493
+ botnote --version`})]}),O.jsx(xo,{icon:NB,title:"Claude Code",subtitle:"Recommended for day-to-day work. Update through /plugin, then reload plugins in-session."}),O.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-2 gap-3",children:[O.jsx(on,{title:"Install",code:`# In Claude Code
494
494
  /plugin marketplace add jianhua-wang/botnote
495
495
  /plugin install botnote@botnote
496
496
 
497
497
  # Claude Code will prompt for:
498
498
  # botnote_url -> default https://botnote.net (use http://127.0.0.1:4280 on daemon host)
499
- # botnote_token -> bearer from Settings → API tokens (skip on loopback)`}),O.jsx(on,{title:"Update",code:`# Preferred: enable marketplace auto-update in Claude Code.
499
+ # botnote_token -> bearer from Settings → API tokens (skip on loopback)`}),O.jsx(on,{title:"Update",code:`# Claude Code does not expose auto-update in every build.
500
500
  /plugin
501
- # Marketplaces botnote Enable auto-update
501
+ # Marketplaces -> botnote -> Update, if your build shows it.
502
502
 
503
- # After Claude reports an updated plugin:
504
- /reload-plugins
503
+ # If there is no Update action:
504
+ # 1. Remove/uninstall botnote from /plugin.
505
+ # 2. Install it again.
506
+ /plugin install botnote@botnote
505
507
 
506
- # Manual update, if available in your Claude Code build:
507
- claude plugin update botnote@botnote`})]}),O.jsx(xo,{icon:GT,title:"Codex",subtitle:"Use the Git marketplace flow; sparse checkout means no full botnote source checkout."}),O.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-2 gap-3",children:[O.jsx(on,{title:"Install",code:`# No full source checkout required.
508
+ # Apply changes in this session:
509
+ /reload-plugins`})]}),O.jsx(xo,{icon:GT,title:"Codex",subtitle:"Use the Git marketplace flow; sparse checkout means no full botnote source checkout. Restart Codex after install or update."}),O.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-2 gap-3",children:[O.jsx(on,{title:"Install",code:`# No full source checkout required.
508
510
  codex plugin marketplace add https://github.com/jianhua-wang/botnote.git \\
509
511
  --sparse .agents/plugins \\
510
512
  --sparse plugins/botnote
511
513
 
512
- codex plugin add botnote@botnote-plugins`}),O.jsx(on,{title:"Update",code:`# Codex refreshes Git marketplaces, then installs from the fresh snapshot.
514
+ codex plugin add botnote@botnote-plugins
515
+
516
+ # Restart Codex: exit this session, then run codex again.
517
+ # In the new session, check /mcp.`}),O.jsx(on,{title:"Update",code:`# Codex refreshes Git marketplaces, then installs from the fresh snapshot.
513
518
  codex plugin marketplace upgrade botnote-plugins
514
519
  codex plugin remove botnote@botnote-plugins
515
520
  codex plugin add botnote@botnote-plugins
516
521
 
517
- # Open a new Codex session after updating.`})]}),O.jsx(xo,{icon:qT,title:"Cursor",subtitle:"Uses the same plugin bundle and the same npm runtime."}),O.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-2 gap-3",children:[O.jsx(on,{title:"Install",code:`# Cursor plugin clients can use the repository marketplace.
522
+ # Open a new Codex session after updating.`})]}),O.jsx(xo,{icon:qT,title:"Cursor",subtitle:"Uses the same plugin bundle. Keep the CLI installed only if you want terminal commands or offline fallback."}),O.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-2 gap-3",children:[O.jsx(on,{title:"Install",code:`# Cursor plugin clients can use the repository marketplace.
518
523
  # Marketplace metadata lives at .cursor-plugin/marketplace.json.
519
524
  https://github.com/jianhua-wang/botnote
520
525
 
@@ -537,13 +542,13 @@ npm i -g botnote@latest`})]}),O.jsx(xo,{icon:yx,title:"Advanced",subtitle:"Only
537
542
  "category": "Productivity"
538
543
  }
539
544
  ]
540
- }`}),O.jsx(xo,{icon:VT,title:"Slash commands",subtitle:"Available after the plugin is installed and the client reloads plugins."}),O.jsx(on,{title:"Commands",code:`/botnote:today # today + overdue
545
+ }`}),O.jsx(xo,{icon:VT,title:"Slash commands",subtitle:"Available after Claude Code reloads plugins or Codex starts a new session with the plugin installed."}),O.jsx(on,{title:"Commands",code:`/botnote:today # today + overdue
541
546
  /botnote:show-todo # open work across projects
542
547
  /botnote:add-task "..." # create a task
543
548
  /botnote:start-work DEMO-12 # pick up a task with project context
544
549
  /botnote:remember "..." # capture a note
545
550
  /botnote:recall "..." # hybrid search
546
- /botnote:done # mark current focus done`}),O.jsxs("div",{className:"border border-line rounded-md bg-surface px-4 py-3 text-xs text-muted leading-relaxed flex items-start gap-3",children:[O.jsx(_B,{size:13,className:"mt-0.5 shrink-0 text-accent"}),O.jsxs("div",{children:["Plugin distribution lives at"," ",O.jsx("a",{href:"https://github.com/jianhua-wang/botnote",target:"_blank",className:"text-accent hover:underline",rel:"noreferrer",children:"jianhua-wang/botnote"}),". ","Claude Code can auto-update third-party marketplaces when enabled; after an update, run"," ",O.jsx("code",{className:"text-ink",children:"/reload-plugins"}),". Codex currently refreshes Git marketplace snapshots with"," ",O.jsx("code",{className:"text-ink",children:"codex plugin marketplace upgrade"}),"."]})]})]})}function kO({icon:n,title:e,body:t}){return O.jsxs("div",{className:"border border-line rounded-md bg-surface px-4 py-3 min-h-[104px]",children:[O.jsxs("div",{className:"flex items-center gap-2 text-sm font-semibold text-ink",children:[O.jsx("span",{className:"w-6 h-6 rounded-md bg-accentSoft text-accent flex items-center justify-center shrink-0",children:O.jsx(n,{size:13})}),e]}),O.jsx("p",{className:"mt-2 text-xs text-muted leading-relaxed",children:t})]})}function xo({icon:n,title:e,subtitle:t}){return O.jsxs("div",{className:"pt-2 border-t border-lineSoft flex items-start gap-2",children:[O.jsx("span",{className:"mt-0.5 w-6 h-6 rounded-md bg-sidebar text-muted border border-line flex items-center justify-center shrink-0",children:O.jsx(n,{size:13})}),O.jsxs("div",{children:[O.jsx("h2",{className:"text-sm font-semibold text-ink",children:e}),O.jsx("p",{className:"text-xs text-muted leading-relaxed mt-0.5",children:t})]})]})}function iU(){const{data:n,isLoading:e}=Iq(),t=zq(),r=Zq(),[i,s]=N.useState(!0),[l,o]=N.useState("openai"),[u,h]=N.useState("text-embedding-3-small"),[f,p]=N.useState(""),[m,y]=N.useState(""),[k,b]=N.useState(!1),[w,S]=N.useState(null);N.useEffect(()=>{n&&(s(n.enabled),o(n.provider),h(n.model),p(n.baseUrl??""),y(""),b(!1))},[n]);async function C(T){T.preventDefault(),S(null);const Z=m.trim(),q={enabled:i,provider:l,model:u.trim(),baseUrl:l==="openai_compatible"&&f.trim()||null,...Z?{apiKey:Z}:k?{apiKey:null}:{}},ee=await t.mutateAsync(q);y(""),b(!1),S(ee.effectiveEnabled?"Embedding search is ready.":zP(ee.statusReason))}async function Q(){S(null);const T=Math.min((n==null?void 0:n.missingCount)??xO,xO),Z=await r.mutateAsync(T),q=n&&n.missingCount>xO;S(q?`Queued ${Z.enqueued} item(s), the maximum per request. Pending queue: ${Z.pendingCount}.`:`Queued all ${Z.enqueued} missing item(s). Pending queue: ${Z.pendingCount}.`)}const A=n&&n.totalCount>0?Math.round(n.embeddedCount/n.totalCount*100):0,I=!!(n!=null&&n.effectiveEnabled&&n.missingCount>0);return O.jsxs(O.Fragment,{children:[O.jsx(Xl,{title:"Embeddings",blurb:"Configure semantic search for botnote. The database vector column is fixed at 384 dimensions, so provider models must return 384-dimensional embeddings."}),O.jsx("div",{className:`border rounded-md px-4 py-3 text-xs leading-relaxed ${n!=null&&n.effectiveEnabled?"border-success/30 bg-success/10 text-ink":"border-warn/30 bg-warn/10 text-ink"}`,children:e?"Loading embedding settings...":n!=null&&n.effectiveEnabled?O.jsxs(O.Fragment,{children:["Semantic search is enabled via ",O.jsx("strong",{children:sU(n.provider)})," using"," ",O.jsx("code",{children:n.model}),". Search merges BM25 + cosine + time decay."]}):O.jsxs(O.Fragment,{children:["Semantic search is not active: ",zP((n==null?void 0:n.statusReason)??"not_loaded"),". Text search still works through BM25."]})}),O.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-4 gap-2",children:[O.jsx(Tf,{label:"Status",value:n!=null&&n.effectiveEnabled?"On":"Off"}),O.jsx(Tf,{label:"Embedded",value:`${(n==null?void 0:n.embeddedCount)??0}/${(n==null?void 0:n.totalCount)??0}`}),O.jsx(Tf,{label:"Missing",value:`${(n==null?void 0:n.missingCount)??0}`}),O.jsx(Tf,{label:"Queue",value:`${(n==null?void 0:n.pendingCount)??0}`})]}),O.jsxs("div",{className:"border border-line rounded-md bg-surface px-4 py-3 space-y-2",children:[O.jsxs("div",{className:"flex items-center justify-between gap-3",children:[O.jsxs("div",{children:[O.jsx("div",{className:"text-xs font-semibold text-ink",children:"Vector coverage"}),O.jsxs("div",{className:"text-xxs text-muted",children:[A,"% of task/note records have embeddings."]})]}),O.jsxs("button",{type:"button",className:"btn",disabled:!I||r.isPending,onClick:Q,title:n!=null&&n.effectiveEnabled?"Queue embeddings for all existing records without body_vec":"Enable embeddings before backfilling",children:[O.jsx(FT,{size:11})," ",r.isPending?"Queueing...":"Backfill all missing"]})]}),O.jsx("div",{className:"h-2 rounded bg-sidebar overflow-hidden border border-lineSoft",children:O.jsx("div",{className:"h-full bg-accent",style:{width:`${A}%`}})})]}),O.jsxs("form",{onSubmit:C,className:"border border-line rounded-md bg-surface px-4 py-4 space-y-4",children:[O.jsxs("label",{className:"flex items-center gap-2 text-sm text-ink",children:[O.jsx("input",{type:"checkbox",checked:i,onChange:T=>s(T.target.checked)}),"Enable semantic search"]}),O.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-3",children:[O.jsxs("label",{className:"space-y-1",children:[O.jsx("span",{className:"block text-xxs uppercase tracking-wider text-muted",children:"Provider"}),O.jsxs("select",{className:"input",value:l,onChange:T=>o(T.target.value),children:[O.jsx("option",{value:"openai",children:"OpenAI"}),O.jsx("option",{value:"openai_compatible",children:"OpenAI-compatible"})]})]}),O.jsxs("label",{className:"space-y-1",children:[O.jsx("span",{className:"block text-xxs uppercase tracking-wider text-muted",children:"Model"}),O.jsx("input",{className:"input font-mono",list:"embedding-models",value:u,onChange:T=>h(T.target.value),placeholder:"text-embedding-3-small"}),O.jsxs("datalist",{id:"embedding-models",children:[O.jsx("option",{value:"text-embedding-3-small"}),O.jsx("option",{value:"text-embedding-3-large"})]})]})]}),l==="openai_compatible"&&O.jsxs("label",{className:"space-y-1 block",children:[O.jsx("span",{className:"block text-xxs uppercase tracking-wider text-muted",children:"Base URL"}),O.jsx("input",{className:"input font-mono",value:f,onChange:T=>p(T.target.value),placeholder:"https://api.example.com/v1"})]}),O.jsxs("label",{className:"space-y-1 block",children:[O.jsx("span",{className:"block text-xxs uppercase tracking-wider text-muted",children:"API key"}),O.jsx("input",{className:"input font-mono",type:"password",value:m,onChange:T=>{y(T.target.value),T.target.value&&b(!1)},placeholder:n!=null&&n.apiKeyConfigured?`Configured (${n.apiKeySource??"unknown"}${n.apiKeyPreview?` · ${n.apiKeyPreview}`:""})`:"sk-..."}),O.jsxs("div",{className:"flex items-center justify-between gap-3 text-xxs text-muted",children:[O.jsx("span",{children:"Full keys are stored server-side and are not returned to the browser after save."}),(n==null?void 0:n.settingsApiKeyConfigured)&&O.jsxs("label",{className:"inline-flex items-center gap-1.5",children:[O.jsx("input",{type:"checkbox",checked:k,onChange:T=>{b(T.target.checked),T.target.checked&&y("")}}),"Clear stored key"]})]})]}),O.jsxs("div",{className:"border border-lineSoft rounded-md bg-sidebar/30 px-3 py-2 text-xs text-muted leading-relaxed",children:["OpenAI-compatible providers must accept the OpenAI embeddings API and return"," ",O.jsx("code",{className:"text-ink",children:"384"})," dimensions for the selected model. Existing vectors are not automatically regenerated when provider or model changes; use Backfill all missing to queue every record that does not have a vector yet."]}),w&&O.jsx("div",{className:"text-xs text-accentText bg-accentSoft/50 border border-accent/20 rounded-md px-3 py-2",children:w}),t.error&&O.jsx("div",{className:"text-xs text-danger",children:t.error instanceof Error?t.error.message:String(t.error)}),r.error&&O.jsx("div",{className:"text-xs text-danger",children:r.error instanceof Error?r.error.message:String(r.error)}),O.jsx("div",{className:"flex justify-end",children:O.jsxs("button",{type:"submit",className:"btn btn-primary",disabled:t.isPending||u.trim().length===0,children:[O.jsx(Nc,{size:11})," ",t.isPending?"Saving...":"Save embedding settings"]})})]})]})}function Tf({label:n,value:e}){return O.jsxs("div",{className:"border border-line rounded-md bg-surface px-3 py-2",children:[O.jsx("div",{className:"text-xxs uppercase tracking-wider text-muted",children:n}),O.jsx("div",{className:"text-lg font-semibold text-ink mt-0.5",children:e})]})}function sU(n){return n==="openai_compatible"?"OpenAI-compatible":"OpenAI"}function zP(n){switch(n){case"ready":return"ready";case"disabled":return"disabled";case"missing_api_key":return"missing API key";case"missing_base_url":return"missing base URL";case"injected":return"using injected test embedder";case"not_loaded":return"configuration not loaded yet"}}function lU(){const n=`{
551
+ /botnote:done # mark current focus done`}),O.jsxs("div",{className:"border border-line rounded-md bg-surface px-4 py-3 text-xs text-muted leading-relaxed flex items-start gap-3",children:[O.jsx(_B,{size:13,className:"mt-0.5 shrink-0 text-accent"}),O.jsxs("div",{children:["Plugin distribution lives at"," ",O.jsx("a",{href:"https://github.com/jianhua-wang/botnote",target:"_blank",className:"text-accent hover:underline",rel:"noreferrer",children:"jianhua-wang/botnote"}),". ","Claude Code updates through ",O.jsx("code",{className:"text-ink",children:"/plugin"}),"; after updating or reinstalling, run ",O.jsx("code",{className:"text-ink",children:"/reload-plugins"}),". Codex has no reload command: refresh Git marketplace snapshots with"," ",O.jsx("code",{className:"text-ink",children:"codex plugin marketplace upgrade"}),", reinstall the plugin, then start a new Codex session."]})]})]})}function kO({icon:n,title:e,body:t}){return O.jsxs("div",{className:"border border-line rounded-md bg-surface px-4 py-3 min-h-[104px]",children:[O.jsxs("div",{className:"flex items-center gap-2 text-sm font-semibold text-ink",children:[O.jsx("span",{className:"w-6 h-6 rounded-md bg-accentSoft text-accent flex items-center justify-center shrink-0",children:O.jsx(n,{size:13})}),e]}),O.jsx("p",{className:"mt-2 text-xs text-muted leading-relaxed",children:t})]})}function xo({icon:n,title:e,subtitle:t}){return O.jsxs("div",{className:"pt-2 border-t border-lineSoft flex items-start gap-2",children:[O.jsx("span",{className:"mt-0.5 w-6 h-6 rounded-md bg-sidebar text-muted border border-line flex items-center justify-center shrink-0",children:O.jsx(n,{size:13})}),O.jsxs("div",{children:[O.jsx("h2",{className:"text-sm font-semibold text-ink",children:e}),O.jsx("p",{className:"text-xs text-muted leading-relaxed mt-0.5",children:t})]})]})}function iU(){const{data:n,isLoading:e}=Iq(),t=zq(),r=Zq(),[i,s]=N.useState(!0),[l,o]=N.useState("openai"),[u,h]=N.useState("text-embedding-3-small"),[f,p]=N.useState(""),[m,y]=N.useState(""),[k,b]=N.useState(!1),[w,S]=N.useState(null);N.useEffect(()=>{n&&(s(n.enabled),o(n.provider),h(n.model),p(n.baseUrl??""),y(""),b(!1))},[n]);async function C(T){T.preventDefault(),S(null);const Z=m.trim(),q={enabled:i,provider:l,model:u.trim(),baseUrl:l==="openai_compatible"&&f.trim()||null,...Z?{apiKey:Z}:k?{apiKey:null}:{}},ee=await t.mutateAsync(q);y(""),b(!1),S(ee.effectiveEnabled?"Embedding search is ready.":zP(ee.statusReason))}async function Q(){S(null);const T=Math.min((n==null?void 0:n.missingCount)??xO,xO),Z=await r.mutateAsync(T),q=n&&n.missingCount>xO;S(q?`Queued ${Z.enqueued} item(s), the maximum per request. Pending queue: ${Z.pendingCount}.`:`Queued all ${Z.enqueued} missing item(s). Pending queue: ${Z.pendingCount}.`)}const A=n&&n.totalCount>0?Math.round(n.embeddedCount/n.totalCount*100):0,I=!!(n!=null&&n.effectiveEnabled&&n.missingCount>0);return O.jsxs(O.Fragment,{children:[O.jsx(Xl,{title:"Embeddings",blurb:"Configure semantic search for botnote. The database vector column is fixed at 384 dimensions, so provider models must return 384-dimensional embeddings."}),O.jsx("div",{className:`border rounded-md px-4 py-3 text-xs leading-relaxed ${n!=null&&n.effectiveEnabled?"border-success/30 bg-success/10 text-ink":"border-warn/30 bg-warn/10 text-ink"}`,children:e?"Loading embedding settings...":n!=null&&n.effectiveEnabled?O.jsxs(O.Fragment,{children:["Semantic search is enabled via ",O.jsx("strong",{children:sU(n.provider)})," using"," ",O.jsx("code",{children:n.model}),". Search merges BM25 + cosine + time decay."]}):O.jsxs(O.Fragment,{children:["Semantic search is not active: ",zP((n==null?void 0:n.statusReason)??"not_loaded"),". Text search still works through BM25."]})}),O.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-4 gap-2",children:[O.jsx(Tf,{label:"Status",value:n!=null&&n.effectiveEnabled?"On":"Off"}),O.jsx(Tf,{label:"Embedded",value:`${(n==null?void 0:n.embeddedCount)??0}/${(n==null?void 0:n.totalCount)??0}`}),O.jsx(Tf,{label:"Missing",value:`${(n==null?void 0:n.missingCount)??0}`}),O.jsx(Tf,{label:"Queue",value:`${(n==null?void 0:n.pendingCount)??0}`})]}),O.jsxs("div",{className:"border border-line rounded-md bg-surface px-4 py-3 space-y-2",children:[O.jsxs("div",{className:"flex items-center justify-between gap-3",children:[O.jsxs("div",{children:[O.jsx("div",{className:"text-xs font-semibold text-ink",children:"Vector coverage"}),O.jsxs("div",{className:"text-xxs text-muted",children:[A,"% of task/note records have embeddings."]})]}),O.jsxs("button",{type:"button",className:"btn",disabled:!I||r.isPending,onClick:Q,title:n!=null&&n.effectiveEnabled?"Queue embeddings for all existing records without body_vec":"Enable embeddings before backfilling",children:[O.jsx(FT,{size:11})," ",r.isPending?"Queueing...":"Backfill all missing"]})]}),O.jsx("div",{className:"h-2 rounded bg-sidebar overflow-hidden border border-lineSoft",children:O.jsx("div",{className:"h-full bg-accent",style:{width:`${A}%`}})})]}),O.jsxs("form",{onSubmit:C,className:"border border-line rounded-md bg-surface px-4 py-4 space-y-4",children:[O.jsxs("label",{className:"flex items-center gap-2 text-sm text-ink",children:[O.jsx("input",{type:"checkbox",checked:i,onChange:T=>s(T.target.checked)}),"Enable semantic search"]}),O.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-3",children:[O.jsxs("label",{className:"space-y-1",children:[O.jsx("span",{className:"block text-xxs uppercase tracking-wider text-muted",children:"Provider"}),O.jsxs("select",{className:"input",value:l,onChange:T=>o(T.target.value),children:[O.jsx("option",{value:"openai",children:"OpenAI"}),O.jsx("option",{value:"openai_compatible",children:"OpenAI-compatible"})]})]}),O.jsxs("label",{className:"space-y-1",children:[O.jsx("span",{className:"block text-xxs uppercase tracking-wider text-muted",children:"Model"}),O.jsx("input",{className:"input font-mono",list:"embedding-models",value:u,onChange:T=>h(T.target.value),placeholder:"text-embedding-3-small"}),O.jsxs("datalist",{id:"embedding-models",children:[O.jsx("option",{value:"text-embedding-3-small"}),O.jsx("option",{value:"text-embedding-3-large"})]})]})]}),l==="openai_compatible"&&O.jsxs("label",{className:"space-y-1 block",children:[O.jsx("span",{className:"block text-xxs uppercase tracking-wider text-muted",children:"Base URL"}),O.jsx("input",{className:"input font-mono",value:f,onChange:T=>p(T.target.value),placeholder:"https://api.example.com/v1"})]}),O.jsxs("label",{className:"space-y-1 block",children:[O.jsx("span",{className:"block text-xxs uppercase tracking-wider text-muted",children:"API key"}),O.jsx("input",{className:"input font-mono",type:"password",value:m,onChange:T=>{y(T.target.value),T.target.value&&b(!1)},placeholder:n!=null&&n.apiKeyConfigured?`Configured (${n.apiKeySource??"unknown"}${n.apiKeyPreview?` · ${n.apiKeyPreview}`:""})`:"sk-..."}),O.jsxs("div",{className:"flex items-center justify-between gap-3 text-xxs text-muted",children:[O.jsx("span",{children:"Full keys are stored server-side and are not returned to the browser after save."}),(n==null?void 0:n.settingsApiKeyConfigured)&&O.jsxs("label",{className:"inline-flex items-center gap-1.5",children:[O.jsx("input",{type:"checkbox",checked:k,onChange:T=>{b(T.target.checked),T.target.checked&&y("")}}),"Clear stored key"]})]})]}),O.jsxs("div",{className:"border border-lineSoft rounded-md bg-sidebar/30 px-3 py-2 text-xs text-muted leading-relaxed",children:["OpenAI-compatible providers must accept the OpenAI embeddings API and return"," ",O.jsx("code",{className:"text-ink",children:"384"})," dimensions for the selected model. Existing vectors are not automatically regenerated when provider or model changes; use Backfill all missing to queue every record that does not have a vector yet."]}),w&&O.jsx("div",{className:"text-xs text-accentText bg-accentSoft/50 border border-accent/20 rounded-md px-3 py-2",children:w}),t.error&&O.jsx("div",{className:"text-xs text-danger",children:t.error instanceof Error?t.error.message:String(t.error)}),r.error&&O.jsx("div",{className:"text-xs text-danger",children:r.error instanceof Error?r.error.message:String(r.error)}),O.jsx("div",{className:"flex justify-end",children:O.jsxs("button",{type:"submit",className:"btn btn-primary",disabled:t.isPending||u.trim().length===0,children:[O.jsx(Nc,{size:11})," ",t.isPending?"Saving...":"Save embedding settings"]})})]})]})}function Tf({label:n,value:e}){return O.jsxs("div",{className:"border border-line rounded-md bg-surface px-3 py-2",children:[O.jsx("div",{className:"text-xxs uppercase tracking-wider text-muted",children:n}),O.jsx("div",{className:"text-lg font-semibold text-ink mt-0.5",children:e})]})}function sU(n){return n==="openai_compatible"?"OpenAI-compatible":"OpenAI"}function zP(n){switch(n){case"ready":return"ready";case"disabled":return"disabled";case"missing_api_key":return"missing API key";case"missing_base_url":return"missing base URL";case"injected":return"using injected test embedder";case"not_loaded":return"configuration not loaded yet"}}function lU(){const n=`{
547
552
  "mcpServers": {
548
553
  "botnote": {
549
554
  "command": "botnote",
@@ -8,7 +8,7 @@
8
8
  <link rel="preconnect" href="https://fonts.googleapis.com" />
9
9
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
10
10
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />
11
- <script type="module" crossorigin src="/assets/index-DEzD2c-n.js"></script>
11
+ <script type="module" crossorigin src="/assets/index-DFtd7aGy.js"></script>
12
12
  <link rel="stylesheet" crossorigin href="/assets/index-DBDF6wZm.css">
13
13
  </head>
14
14
  <body>