turbollm 1.3.2 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +178 -211
- package/dist/cli.js +664 -232
- package/dist/webdist/assets/{ChatScreen-DY2flMe9.js → ChatScreen-DwW1RFdb.js} +10 -10
- package/dist/webdist/assets/{CustomizeScreen-Cl6H68Sy.js → CustomizeScreen-DSE61GGl.js} +1 -1
- package/dist/webdist/assets/{DeveloperScreen-zC1nePXh.js → DeveloperScreen-BYGLUEKp.js} +1 -1
- package/dist/webdist/assets/{EnginesScreen-CrsziC3B.js → EnginesScreen-D1fV7OUs.js} +1 -1
- package/dist/webdist/assets/{ModelDetailDialog-BOvumdHI.js → ModelDetailDialog-DOWC_r7x.js} +1 -1
- package/dist/webdist/assets/{ModelDirs-Bj-XrX0c.js → ModelDirs-BnEgYeD7.js} +1 -1
- package/dist/webdist/assets/{ModelsScreen-BTir-kDY.js → ModelsScreen-CsIWlTix.js} +1 -1
- package/dist/webdist/assets/{SettingsScreen-B696egCa.js → SettingsScreen-BsoeEYxo.js} +1 -1
- package/dist/webdist/assets/check-BEcwngnz.js +1 -0
- package/dist/webdist/assets/{circle-x-CsuAddI_.js → circle-x-BMU1-bqZ.js} +1 -1
- package/dist/webdist/assets/{common-DIXPIrUd.js → common-CsaPxOo6.js} +1 -1
- package/dist/webdist/assets/{copy-button-BtQZA6P8.js → copy-button-VN55eluX.js} +1 -1
- package/dist/webdist/assets/{dialog-Dpyml8ZT.js → dialog-oym6ZR4Z.js} +1 -1
- package/dist/webdist/assets/{index-sjgdofsd.js → index-DdsLEQg8.js} +2 -2
- package/dist/webdist/assets/{pencil-BuQwoNyj.js → pencil-BUeTGZ2m.js} +1 -1
- package/dist/webdist/assets/{personas-CQapYHeK.js → personas-B44JP8XF.js} +1 -1
- package/dist/webdist/assets/{plus-B1w15FoZ.js → plus-B4N6E4A9.js} +1 -1
- package/dist/webdist/assets/{refresh-cw-BoAcjLtt.js → refresh-cw-35eljdhP.js} +1 -1
- package/dist/webdist/assets/{save-DE1APWTG.js → save-AXon2pXP.js} +1 -1
- package/dist/webdist/assets/{skeleton-Dvq8tYzC.js → skeleton-BqBFt4sB.js} +1 -1
- package/dist/webdist/assets/{sparkles-C54kYWeQ.js → sparkles-C1EIsU4T.js} +1 -1
- package/dist/webdist/assets/{trash-2-DIMVgidH.js → trash-2-BOZ6n1d2.js} +1 -1
- package/dist/webdist/index.html +1 -1
- package/package.json +1 -1
- package/dist/webdist/assets/check-DFwkcl_5.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{j as e}from"./radix-BFvMxQq9.js";import{r as i,d as Ne}from"./vendor-D_-14Whs.js";import{u as ye,g as ke,f as we,P as Ce,i as Se,j as Me}from"./personas-CQapYHeK.js";import{S as Ae,T as Le}from"./common-DIXPIrUd.js";import{c as L,e as de,M as ce,o as Pe,p as Te,B as g,a2 as Ie,t as b,A as k,L as C,u as z,a3 as Ue,X as xe,a4 as Ee,a5 as ze,m as De,a6 as Re}from"./index-sjgdofsd.js";import{C as _e}from"./copy-button-BtQZA6P8.js";import{M as Ge}from"./ModelDirs-Bj-XrX0c.js";import{S as me}from"./save-DE1APWTG.js";import{S as re}from"./sparkles-C54kYWeQ.js";import{C as A}from"./check-DFwkcl_5.js";import{R as ue,E as ie}from"./refresh-cw-BoAcjLtt.js";const Fe=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m16 12-4-4-4 4",key:"177agl"}],["path",{d:"M12 16V8",key:"1sbj14"}]],Oe=L("circle-arrow-up",Fe);const $e=[["rect",{width:"20",height:"14",x:"2",y:"3",rx:"2",key:"48i651"}],["line",{x1:"8",x2:"16",y1:"21",y2:"21",key:"1svkeh"}],["line",{x1:"12",x2:"12",y1:"17",y2:"21",key:"vw1qmm"}]],Be=L("monitor",$e);const qe=[["path",{d:"M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401",key:"kfwtm"}]],He=L("moon",qe);const Ke=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"M12 8v4",key:"1got3b"}],["path",{d:"M12 16h.01",key:"1drbdi"}]],pe=L("shield-alert",Ke);const Ve=[["circle",{cx:"12",cy:"12",r:"4",key:"4exip2"}],["path",{d:"M12 2v2",key:"tus03m"}],["path",{d:"M12 20v2",key:"1lh1kg"}],["path",{d:"m4.93 4.93 1.41 1.41",key:"149t6j"}],["path",{d:"m17.66 17.66 1.41 1.41",key:"ptbguv"}],["path",{d:"M2 12h2",key:"1t8f8n"}],["path",{d:"M20 12h2",key:"1q8mjw"}],["path",{d:"m6.34 17.66-1.41 1.41",key:"1m8zz5"}],["path",{d:"m19.07 4.93-1.41 1.41",key:"1shlcs"}]],Ye=L("sun",Ve),Qe=!1,oe="tllm.thinkingEnabled.default";function y({value:t,min:n,max:a,step:o,onCommit:s,className:l,ariaLabel:c}){const[r,d]=i.useState(String(t));i.useEffect(()=>{d(String(t))},[t]);const h=p=>{let m=p;return n!=null&&(m=Math.max(n,m)),a!=null&&(m=Math.min(a,m)),m};return e.jsx("input",{type:"number",inputMode:"numeric","aria-label":c,min:n,max:a,step:o,value:r,onChange:p=>{const m=p.target.value;if(d(m),m.trim()==="")return;const x=Number(m);Number.isFinite(x)&&s(x)},onBlur:()=>{const p=Number(r),m=r.trim()===""||!Number.isFinite(p)?t:h(p);d(String(m)),s(m)},onKeyDown:p=>{p.key==="Enter"&&p.target.blur()},className:l})}const U=(t,n,a)=>Math.max(n,Math.min(a,Math.round(t)));function gt(){const{theme:t,setTheme:n}=de(),{query:a,save:o}=ce(),s=a.data,l=Pe(),c=Te(),r=l.data?.dirs??[],d=l.data?.primaryDir??"",[h,p]=i.useState(60),[m,x]=i.useState(6996),[P,S]=i.useState(!0),[v,T]=i.useState(!0),[w,f]=i.useState(!1),[j,D]=i.useState(8192),[R,_]=i.useState(99),[G,F]=i.useState(0),[O,$]=i.useState(0),[he,fe]=i.useState("off"),[B,q]=i.useState(!1),[H,K]=i.useState(!0),[V,Y]=i.useState(!1),[Q,W]=i.useState(""),[X,J]=i.useState(!1),[Z,ee]=i.useState(!0),[te,se]=i.useState(1),[E,be]=i.useState(()=>localStorage.getItem(oe)!=="false"),[ge,ae]=i.useState(!1);i.useEffect(()=>{s&&(p(s.idleTtlMinutes),x(s.port??6996),S(s.autoGenerateTitles),T(s.openBrowserOnStart),f(s.autoLoadOnStart??!1),D(s.modelDefaults?.ctx??8192),_(s.modelDefaults?.ngl??99),F(s.modelDefaults?.imageMaxTokens??0),$(s.modelDefaults?.maxTokens??0),fe(s.telemetryLevel??"off"),q(s.lanBind??!1),K(s.requireApiKey??!0),Y(s.comfyui?.enabled??!1),W(s.comfyui?.url??""),J(s.comfyui?.reverseGate??!1),ee(s.gateway?.autoSwap??!0),se(s.gateway?.keepN??1))},[s]),i.useEffect(()=>{localStorage.setItem(oe,E?"true":"false")},[E]);const ne=()=>({idleTtlMinutes:U(h,0,1440),port:U(m,1024,65535),autoGenerateTitles:P,openBrowserOnStart:v,autoLoadOnStart:w,telemetryLevel:he,lanBind:B,requireApiKey:H,modelDefaults:{ctx:Math.max(256,Math.round(j)),ngl:U(R,0,99),imageMaxTokens:Math.max(0,Math.round(G)),maxTokens:Math.max(0,Math.round(O))},comfyui:{enabled:V,url:Q.trim(),reverseGate:X},gateway:{autoSwap:Z,keepN:U(te,1,4)}}),je=()=>{o.mutate(ne(),{onSuccess:u=>{const N=u.rebind;N?.portChanged?(b.success(`Port changed to ${N.port} — reconnecting…`),setTimeout(()=>{const I=new URL(window.location.href);I.port=String(N.port),window.location.href=I.toString()},1300)):N?b.success(N.lanBind?"LAN access enabled — applied (no restart needed)":"LAN access disabled — applied"):b.success("Settings saved")},onError:u=>b.error(u instanceof k?u.message:"Could not save settings.")})},ve=()=>{o.mutate(ne(),{onSuccess:()=>ae(!0),onError:u=>b.error(u instanceof k?u.message:"Could not save settings before restart.")})};return e.jsxs("div",{className:"w-full px-6 py-6",children:[e.jsx(Ae,{title:"Settings",description:"Configure TurboLLM behavior and appearance."}),ge&&e.jsx(st,{onDismiss:()=>ae(!1)}),e.jsxs("div",{className:"flex flex-col gap-6",children:[e.jsx(We,{}),e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-3 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"Appearance"}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Theme"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Choose light, dark, or follow your system setting"})]}),e.jsx("div",{className:"flex overflow-hidden rounded-lg border border-border",children:[{value:"light",label:"Light",Icon:Ye},{value:"system",label:"System",Icon:Be},{value:"dark",label:"Dark",Icon:He}].map(({value:u,label:N,Icon:I})=>e.jsxs("button",{type:"button",onClick:()=>n(u),className:"flex items-center gap-1.5 px-3 py-2 text-[13px] transition-colors",style:{background:t===u?"var(--accent)":"transparent",color:t===u?"var(--on-accent)":"var(--muted)"},children:[e.jsx(I,{size:14}),N]},u))})]}),e.jsxs("label",{className:"mt-2 flex cursor-pointer items-center justify-between border-t border-border py-2 pt-3",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Enable thinking by default"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Let reasoning models think before answering in new chats (you can toggle it per chat). Off = answer directly, faster."})]}),e.jsx("input",{type:"checkbox",checked:E,onChange:u=>be(u.target.checked),className:"h-4 w-4 accent-[var(--accent)]"})]})]}),e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-3 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"Engine"}),e.jsxs("div",{className:"flex items-center justify-between py-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Idle timeout"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Unload model after this many minutes of inactivity (0 = never)"})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(y,{value:h,min:0,max:1440,onCommit:p,ariaLabel:"Idle timeout in minutes",className:"w-20 rounded-md border border-border bg-bg px-2 py-1 text-right text-[13px] text-ink outline-none"}),e.jsx("span",{className:"text-[12px] text-muted",children:"min"})]})]})]}),e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-1 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"Model Defaults"}),e.jsx("p",{className:"mb-3 text-[12px] text-muted",children:"Applied the first time a model is loaded. A model's own saved settings always override these."}),e.jsxs("div",{className:"flex items-center justify-between py-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Context length"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Default context window, capped at each model's native max"})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(y,{value:j,min:256,step:512,onCommit:D,ariaLabel:"Default context length",className:"w-24 rounded-md border border-border bg-bg px-2 py-1 text-right text-[13px] text-ink outline-none"}),e.jsx("span",{className:"text-[12px] text-muted",children:"tok"})]})]}),e.jsxs("div",{className:"flex items-center justify-between py-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"GPU layers"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Layers to offload to the GPU (99 = all); ignored on CPU-only machines"})]}),e.jsx("div",{className:"flex items-center gap-2",children:e.jsx(y,{value:R,min:0,max:99,onCommit:_,ariaLabel:"Default GPU layers",className:"w-20 rounded-md border border-border bg-bg px-2 py-1 text-right text-[13px] text-ink outline-none"})})]}),e.jsxs("div",{className:"flex items-center justify-between py-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Image max tokens"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Per-image token budget for vision models (0 = engine default)"})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(y,{value:G,min:0,step:256,onCommit:F,ariaLabel:"Image max tokens",className:"w-24 rounded-md border border-border bg-bg px-2 py-1 text-right text-[13px] text-ink outline-none"}),e.jsx("span",{className:"text-[12px] text-muted",children:"tok"})]})]}),e.jsxs("div",{className:"flex items-center justify-between py-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Max response tokens"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Hard cap on tokens generated per reply (0 = unlimited). Also caps Claude Code / API requests."})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(y,{value:O,min:0,step:256,onCommit:$,ariaLabel:"Max response tokens",className:"w-24 rounded-md border border-border bg-bg px-2 py-1 text-right text-[13px] text-ink outline-none"}),e.jsx("span",{className:"text-[12px] text-muted",children:"tok"})]})]})]}),e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-3 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"Chat"}),e.jsxs("label",{className:"flex cursor-pointer items-center justify-between py-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Auto-generate chat titles"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Uses the model to create a title after the first exchange"})]}),e.jsx("input",{type:"checkbox",checked:P,onChange:u=>S(u.target.checked),className:"h-4 w-4 accent-[var(--accent)]"})]})]}),e.jsx(nt,{}),e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-3 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"Startup"}),e.jsxs("label",{className:"flex cursor-pointer items-center justify-between py-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Open browser on start"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Automatically open the UI when the daemon starts"})]}),e.jsx("input",{type:"checkbox",checked:v,onChange:u=>T(u.target.checked),className:"h-4 w-4 accent-[var(--accent)]"})]}),e.jsxs("label",{className:"flex cursor-pointer items-center justify-between py-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Auto-load last model"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Reload the last-used model automatically when the daemon starts"})]}),e.jsx("input",{type:"checkbox",checked:w,onChange:u=>f(u.target.checked),className:"h-4 w-4 accent-[var(--accent)]"})]})]}),e.jsx(Xe,{enabled:V,setEnabled:Y,gatePath:s?.comfyui?.gatePath??"",url:Q,setUrl:W,reverseGate:X,setReverseGate:J}),e.jsx(Je,{autoSwap:Z,setAutoSwap:ee,keepN:te,setKeepN:se}),e.jsx(Ze,{lanBind:B,setLanBind:q,requireApiKey:H,setRequireApiKey:K,port:m,setPort:x}),e.jsx(Ge,{dirs:r,primaryDir:d,mut:c}),e.jsx(et,{tokenSet:s?.hfTokenSet??!1,onSaved:()=>{a.refetch()}}),Qe,e.jsx(at,{}),e.jsx("div",{className:"flex justify-end",children:e.jsxs(g,{onClick:je,disabled:o.isPending||a.isLoading,children:[e.jsx(me,{size:14}),o.isPending?"Saving…":"Save settings"]})}),e.jsx(tt,{onRestart:ve}),e.jsx(rt,{}),e.jsx(it,{})]})]})}function We(){const t=Ne(),{data:n}=z(),a=ye(),o=de(c=>c.setPendingConversationId),s=n?.engine.state==="running"&&!!n?.model,l=()=>{s&&a.createExpert.mutate(void 0,{onSuccess:c=>{o(c.id),t("/chat")},onError:c=>b.error(c instanceof k?c.message:"Could not launch the Expert assistant.")})};return e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-1 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"TurboLLM Expert"}),e.jsx("p",{className:"mb-3 text-[12px] text-muted",children:"Chat with a built-in assistant that knows TurboLLM — it can explain features, help configure engines, models, and settings, and troubleshoot. Runs on your loaded model."}),s?e.jsxs(g,{onClick:l,disabled:a.createExpert.isPending,children:[e.jsx(re,{size:14}),a.createExpert.isPending?"Launching…":"Launch Expert"]}):e.jsxs("div",{className:"flex items-start gap-2 rounded-md border p-2.5 text-[12px]",style:{borderColor:"color-mix(in srgb, var(--accent) 40%, var(--border))",background:"color-mix(in srgb, var(--accent) 6%, transparent)"},children:[e.jsx(re,{size:14,className:"mt-0.5 shrink-0",style:{color:"var(--accent)"}}),e.jsxs("div",{className:"text-muted",children:["Load a model first to chat with the Expert assistant. Pick one on the"," ",e.jsx("button",{type:"button",onClick:()=>t("/models"),className:"font-medium text-ink underline-offset-2 hover:underline",children:"Models"})," ","screen."]})]})]})}function Xe({enabled:t,setEnabled:n,gatePath:a,url:o,setUrl:s,reverseGate:l,setReverseGate:c}){const{data:r}=z(),{install:d,uninstall:h}=Ue(),[p,m]=i.useState(""),x=r?.comfyui,P=!!a,S=a.replace(/[\\/]turbollm_gate[\\/]?$/,""),v=f=>{if(!f.trim()){b.error("Enter the path to your ComfyUI folder first.");return}d.mutate(f.trim(),{onSuccess:j=>{b.success(`Gate installed at ${j.path}. ${j.note??""}`.trim()),m("")},onError:j=>b.error(j instanceof k?j.message:"Could not install the gate node.")})},T=()=>{h.mutate(void 0,{onSuccess:()=>b.success("ComfyUI gate removed."),onError:f=>b.error(f instanceof k?f.message:"Could not remove the gate node.")})},w=x?.enabled?x.installed?x.held?{color:"var(--warn)",text:"ComfyUI is rendering — the model is unloaded and loads are paused."}:x.lastSignalAgoMs==null?{color:"var(--muted)",text:"Installed. Restart ComfyUI, then run a job to connect."}:{color:"var(--ok)",text:`ComfyUI idle — connected (last signal ${Math.round(x.lastSignalAgoMs/1e3)}s ago).`}:{color:"var(--muted)",text:"Install the gate node in ComfyUI to activate this."}:null;return e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-1 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"ComfyUI"}),e.jsx("p",{className:"mb-3 text-[12px] text-muted",children:"Share the GPU with ComfyUI. The instant ComfyUI starts a render, TurboLLM unloads its model and pauses new loads so they don't fight over VRAM — then reloads the model when ComfyUI's queue is empty. This needs a small one-time setup node installed in ComfyUI."}),e.jsxs("label",{className:"flex cursor-pointer items-center justify-between py-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Pause for ComfyUI"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Unload the model and block loads while ComfyUI renders (Save to apply)"})]}),e.jsx("input",{type:"checkbox",checked:t,onChange:f=>n(f.target.checked),className:"h-4 w-4 accent-[var(--accent)]"})]}),t&&e.jsxs("div",{className:"mt-2 flex flex-col gap-3 border-t border-border pt-3",children:[e.jsx("div",{className:"text-[13px] font-medium text-ink",children:"One-time setup"}),P?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"text-[12px] text-muted",children:[e.jsxs("span",{className:"inline-flex items-center gap-1.5 text-ink",children:[e.jsx(A,{size:13,style:{color:"var(--ok)"}})," Gate installed"]}),e.jsx("div",{className:"mt-1 break-all font-mono text-[11px] text-faint",children:a})]}),x?.installedVersion!=null&&x.installedVersion<(x.currentVersion??1/0)&&e.jsxs("div",{className:"flex items-center justify-between gap-3 rounded-md border px-3 py-2",style:{borderColor:"var(--warn)",background:"color-mix(in srgb, var(--warn) 10%, transparent)"},children:[e.jsxs("span",{className:"inline-flex items-center gap-1.5 text-[12px]",style:{color:"var(--warn)"},children:[e.jsx(Le,{size:13}),"Update available — node v",x.installedVersion," → v",x.currentVersion]}),e.jsx(g,{size:"sm",onClick:()=>v(S),disabled:d.isPending,children:d.isPending?e.jsx(C,{size:13,className:"animate-spin"}):"Update node"})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs(g,{variant:"outline",size:"sm",onClick:()=>v(S),disabled:d.isPending,children:[d.isPending?e.jsx(C,{size:13,className:"animate-spin"}):e.jsx(ue,{size:13}),"Reinstall / update"]}),e.jsxs(g,{variant:"outline",size:"sm",onClick:T,disabled:h.isPending,children:[h.isPending?e.jsx(C,{size:13,className:"animate-spin"}):e.jsx(xe,{size:13}),"Remove"]})]}),e.jsx("div",{className:"text-[12px] text-faint",children:"Restart ComfyUI after installing or updating for the gate to take effect."})]}):e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"text-[12px] text-muted",children:["Enter your ComfyUI folder (the one containing ",e.jsx("span",{className:"font-mono",children:"custom_nodes"}),"), or the",e.jsx("span",{className:"font-mono",children:" custom_nodes"})," folder itself. TurboLLM writes the gate node there, wired to this daemon."]}),e.jsxs("div",{className:"flex flex-col gap-2 sm:flex-row sm:items-center",children:[e.jsx("input",{type:"text",value:p,onChange:f=>m(f.target.value),placeholder:"e.g. D:\\\\ComfyUI_windows_portable\\\\ComfyUI",spellCheck:!1,autoComplete:"off",onKeyDown:f=>{f.key==="Enter"&&v(p)},className:"flex-1 rounded-md border border-border bg-bg px-2 py-1.5 font-mono text-[12px] text-ink outline-none"}),e.jsx(g,{size:"sm",onClick:()=>v(p),disabled:d.isPending||!p.trim(),children:d.isPending?e.jsx(C,{size:13,className:"animate-spin"}):"Install gate"})]})]}),e.jsxs("div",{className:"flex flex-col gap-3 border-t border-border pt-3",children:[e.jsxs("label",{className:"flex cursor-pointer items-center justify-between",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[13px] font-medium text-ink",children:"Free ComfyUI when TurboLLM loads"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Before loading a model, tell ComfyUI to unload its VRAM (Save to apply)"})]}),e.jsx("input",{type:"checkbox",checked:l,onChange:f=>c(f.target.checked),className:"h-4 w-4 accent-[var(--accent)]"})]}),l&&e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsxs("div",{className:"text-[12px] text-muted",children:["ComfyUI's address — TurboLLM calls its ",e.jsx("span",{className:"font-mono",children:"/free"})," endpoint here."]}),e.jsx("input",{type:"text",value:o,onChange:f=>s(f.target.value),placeholder:"http://127.0.0.1:8188",spellCheck:!1,autoComplete:"off",className:"rounded-md border border-border bg-bg px-2 py-1.5 font-mono text-[12px] text-ink outline-none"})]})]}),w&&e.jsxs("div",{className:"flex items-center gap-2 border-t border-border pt-3 text-[12px] text-muted",children:[e.jsx("span",{className:"inline-block h-2 w-2 shrink-0 rounded-full",style:{background:w.color}}),w.text]})]})]})}function Je({autoSwap:t,setAutoSwap:n,keepN:a,setKeepN:o}){return e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-1 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"Gateway"}),e.jsxs("p",{className:"mb-3 text-[12px] text-muted",children:["Controls how the OpenAI / Anthropic gateway handles the ",e.jsx("code",{className:"font-mono",children:"model"})," field. Auto-swap loads the requested model on demand; keep-N holds multiple models hot simultaneously."]}),e.jsxs("label",{className:"flex cursor-pointer items-center justify-between py-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Auto model-swap"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Auto-load the model named in each API request (Save to apply)"})]}),e.jsx("input",{type:"checkbox",checked:t,onChange:s=>n(s.target.checked),className:"h-4 w-4 accent-[var(--accent)]"})]}),e.jsxs("div",{className:"flex items-center justify-between py-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Keep-N models loaded"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Max simultaneous hot models (1 = pure swap, 2–4 = pool with LRU eviction)"})]}),e.jsx(y,{value:a,min:1,max:4,onCommit:o,ariaLabel:"Keep-N models",className:"w-16 rounded-md border border-border bg-bg px-2 py-1 text-right text-[13px] text-ink outline-none"})]})]})}function Ze({lanBind:t,setLanBind:n,requireApiKey:a,setRequireApiKey:o,port:s,setPort:l}){const{data:c}=Ee(),r=c?.lanUrl??"",d=c?.hasApiKey??!1;return e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-3 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"Network"}),e.jsxs("div",{className:"flex items-center justify-between py-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Port"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Port the daemon listens on (1024–65535)"})]}),e.jsx(y,{value:s,min:1024,max:65535,onCommit:l,ariaLabel:"Daemon port",className:"w-24 rounded-md border border-border bg-bg px-2 py-1 text-right text-[13px] text-ink outline-none"})]}),e.jsxs("label",{className:"flex cursor-pointer items-center justify-between py-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Expose on local network (LAN)"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Allow other devices on your network to reach the API"})]}),e.jsx("input",{type:"checkbox",checked:t,onChange:h=>n(h.target.checked),className:"h-4 w-4 accent-[var(--accent)]"})]}),t&&e.jsxs("div",{className:"mt-2 flex flex-col gap-3 border-t border-border pt-3",children:[r&&e.jsxs("div",{className:"text-[13px]",children:[e.jsx("span",{className:"text-muted",children:"LAN URL: "}),e.jsx("span",{className:"font-mono text-ink",children:r})]}),e.jsxs("label",{className:"flex cursor-pointer items-center justify-between",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Require an API key"}),e.jsx("div",{className:"text-[12px] text-muted",children:"When off, any device on your network can use this TurboLLM with no key"})]}),e.jsx("input",{type:"checkbox",checked:a,onChange:h=>o(h.target.checked),className:"h-4 w-4 accent-[var(--accent)]"})]}),e.jsxs("div",{className:"flex items-start gap-2 rounded-md border p-2.5 text-[12px]",style:{borderColor:"color-mix(in srgb, var(--warn) 40%, var(--border))",background:"color-mix(in srgb, var(--warn) 8%, transparent)"},children:[e.jsx(pe,{size:14,className:"mt-0.5 shrink-0",style:{color:"var(--warn)"}}),e.jsx("div",{className:"text-muted",children:a?e.jsxs(e.Fragment,{children:["Other devices can reach the API, but a valid API key is required.",!d&&e.jsxs(e.Fragment,{children:[" ","No API key exists yet — create one on the"," ",e.jsx("span",{className:"font-medium text-ink",children:"Developer"})," screen."]})]}):e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"font-medium text-ink",children:"Open access:"})," any device on your network can use this TurboLLM with no key. Only enable this on a network you trust."]})})]})]}),e.jsxs("div",{className:"mt-2 border-t border-border pt-3 text-[12px] text-faint",children:["Click ",e.jsx("span",{className:"font-medium text-ink",children:"Save settings"})," to apply. The listener re-binds in place (no restart, model stays loaded); a port change reconnects this page automatically."]})]})}function et({tokenSet:t,onSaved:n}){const{query:a,save:o}=ce(),s=ze(),[l,c]=i.useState(""),[r,d]=i.useState(null),h=x=>{c(x),d(null)},p=()=>{l.trim()&&s.mutate(l.trim(),{onSuccess:x=>d(x),onError:()=>d({ok:!1})})},m=()=>{o.mutate({hfToken:l.trim()},{onSuccess:()=>{b.success(l.trim()?"Hugging Face token saved":"Hugging Face token cleared"),c(""),d(null),n()},onError:x=>b.error(x instanceof k?x.message:"Could not save the token.")})};return e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-1 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"Models"}),e.jsxs("p",{className:"mb-3 text-[12px] text-muted",children:["A Hugging Face access token lets you download gated models (e.g. Llama). Accept the model's license on huggingface.co, then paste a read token here."," ",e.jsx("a",{href:"https://huggingface.co/settings/tokens",target:"_blank",rel:"noopener noreferrer",className:"text-ink underline-offset-2 hover:underline",children:"Create a token"}),"."]}),e.jsx("div",{className:"flex items-center justify-between py-1",children:e.jsx("div",{className:"text-[13px] text-muted",children:t?e.jsxs("span",{className:"inline-flex items-center gap-1.5 text-ink",children:[e.jsx(A,{size:13,style:{color:"var(--ok)"}}),"A token is configured"]}):"No token configured"})}),e.jsxs("div",{className:"mt-1 flex flex-col gap-2 sm:flex-row sm:items-center",children:[e.jsx("input",{type:"password",value:l,onChange:x=>h(x.target.value),placeholder:t?"Enter a new token to replace the current one":"hf_…",autoComplete:"off",className:"flex-1 rounded-md border border-border bg-bg px-2 py-1.5 font-mono text-[13px] text-ink outline-none"}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(g,{variant:"outline",size:"sm",onClick:p,disabled:!l.trim()||s.isPending,children:s.isPending?e.jsx(C,{size:13,className:"animate-spin"}):"Test"}),e.jsx(g,{size:"sm",onClick:m,disabled:o.isPending||a.isFetching,children:l.trim()?"Save token":"Clear token"})]})]}),r&&e.jsx("div",{className:"mt-2 text-[12px]",children:r.ok?e.jsxs("span",{className:"inline-flex items-center gap-1.5",style:{color:"var(--ok)"},children:[e.jsx(A,{size:13}),"Valid",r.name?` — signed in as ${r.name}`:""]}):e.jsxs("span",{className:"inline-flex items-center gap-1.5",style:{color:"var(--err)"},children:[e.jsx(xe,{size:13}),"Invalid or unauthorized token"]})})]})}function tt({onRestart:t}){return e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-1 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"Advanced"}),e.jsx("p",{className:"mb-3 text-[12px] text-muted",children:"Restart the daemon to apply a new port or LAN binding without killing the terminal. Any loaded model is unloaded by a restart and must be reloaded afterward."}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Restart daemon"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Stops the engine, then re-launches the daemon process"})]}),e.jsxs(g,{variant:"outline",size:"sm",onClick:t,children:[e.jsx(ue,{size:13}),"Restart daemon"]})]})]})}function st({onDismiss:t}){const n=Ie(),[a,o]=i.useState("restarting"),s=i.useRef(!1);return i.useEffect(()=>{if(s.current)return;s.current=!0;let l=!1,c,r;const d=async()=>{if(!l){try{if((await fetch("/api/v1/status",{headers:{Accept:"application/json"}})).ok){l||window.location.reload();return}}catch{}l||(c=setTimeout(d,700))}};return n.mutate(void 0,{onSuccess:()=>{c=setTimeout(d,700),r=setTimeout(()=>{l||o("failed")},2e4)},onError:h=>{l||(o("failed"),b.error(h instanceof k?h.message:"Could not restart the daemon."))}}),()=>{l=!0,c&&clearTimeout(c),r&&clearTimeout(r)}},[]),e.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-bg/90 backdrop-blur-sm",children:e.jsx("div",{className:"flex max-w-sm flex-col items-center gap-3 rounded-lg border border-border bg-panel p-6 text-center",children:a==="restarting"?e.jsxs(e.Fragment,{children:[e.jsx(C,{size:28,className:"animate-spin",style:{color:"var(--accent)"}}),e.jsx("div",{className:"text-[15px] font-medium text-ink",children:"Restarting daemon…"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Applying your changes. The page will reload automatically when the daemon is back."})]}):e.jsxs(e.Fragment,{children:[e.jsx(pe,{size:28,style:{color:"var(--warn)"}}),e.jsx("div",{className:"text-[15px] font-medium text-ink",children:"Daemon is taking a while"}),e.jsx("div",{className:"text-[12px] text-muted",children:"It may have moved to a new port. Try reloading, or check the terminal where you started TurboLLM."}),e.jsxs("div",{className:"mt-1 flex gap-2",children:[e.jsx(g,{variant:"outline",size:"sm",onClick:t,children:"Dismiss"}),e.jsx(g,{size:"sm",onClick:()=>window.location.reload(),children:"Reload now"})]})]})})})}function at(){const{data:t,isLoading:n}=De();return e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-3 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"Hardware"}),n||!t?e.jsx("p",{className:"text-[13px] text-faint",children:"Detecting hardware…"}):e.jsxs("dl",{className:"grid grid-cols-[auto_1fr] gap-x-6 gap-y-1.5",children:[t.gpus.length>0?t.gpus.map((a,o)=>e.jsx(M,{label:t.gpus.length>1?`GPU ${o+1}`:"GPU",value:`${a.name}${a.vramMb>0?` · ${(a.vramMb/1e3).toFixed(1)} GB VRAM`:""}`},o)):e.jsx(M,{label:"GPU",value:"None detected (CPU-only)"}),e.jsx(M,{label:"CPU",value:`${t.cpu||"Unknown"} · ${t.cores} cores`}),e.jsx(M,{label:"RAM",value:`${(t.ramMB/1e3).toFixed(1)} GB`}),e.jsx(M,{label:"OS",value:t.os})]})]})}function M({label:t,value:n}){return e.jsxs(e.Fragment,{children:[e.jsx("dt",{className:"text-[13px] text-muted",children:t}),e.jsx("dd",{className:"text-[13px] text-ink",children:n})]})}function nt(){const[t,n]=i.useState(()=>ke()),[a,o]=i.useState(()=>we()),[s,l]=i.useState(!1),c=()=>{Se(t),Me(a),l(!0),setTimeout(()=>l(!1),2e3)};return e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-1 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"Personalization"}),e.jsx("p",{className:"mb-3 text-[12px] text-muted",children:"Applied as hidden context in every new conversation. Persona can also be changed per-chat."}),e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Default persona"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Applied to new chats unless overridden in the chat window"})]}),e.jsx("select",{value:t,onChange:r=>n(r.target.value),className:"rounded-md border border-border bg-bg px-2 py-1.5 text-[13px] text-ink outline-none",children:Ce.map(r=>e.jsxs("option",{value:r.id,children:[r.name," — ",r.description]},r.id))})]}),e.jsxs("div",{className:"flex items-center justify-between gap-4",children:[e.jsxs("div",{className:"shrink-0",children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Assistant name"}),e.jsx("div",{className:"text-[12px] text-muted",children:"What the assistant calls itself (empty = model default)"})]}),e.jsx("input",{type:"text",value:a.assistantName,onChange:r=>o(d=>({...d,assistantName:r.target.value})),placeholder:"e.g. Aria",className:"w-40 rounded-md border border-border bg-bg px-2 py-1 text-[13px] text-ink outline-none placeholder:text-faint"})]}),e.jsxs("div",{className:"flex items-center justify-between gap-4",children:[e.jsxs("div",{className:"shrink-0",children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Your name"}),e.jsx("div",{className:"text-[12px] text-muted",children:"How the assistant addresses you (empty = not set)"})]}),e.jsx("input",{type:"text",value:a.userName,onChange:r=>o(d=>({...d,userName:r.target.value})),placeholder:"e.g. Alex",className:"w-40 rounded-md border border-border bg-bg px-2 py-1 text-[13px] text-ink outline-none placeholder:text-faint"})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Custom instructions"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Extra instructions added to every new conversation"})]}),e.jsx("textarea",{rows:3,value:a.customInstructions,onChange:r=>o(d=>({...d,customInstructions:r.target.value})),placeholder:"e.g. Always respond in Spanish. Prefer functional programming style.",className:"w-full resize-none rounded-md border border-border bg-bg px-2 py-1.5 text-[13px] text-ink outline-none placeholder:text-faint"})]}),e.jsx("div",{className:"flex justify-end",children:e.jsx(g,{onClick:c,size:"sm",children:s?e.jsxs(e.Fragment,{children:[e.jsx(A,{size:13})," Saved"]}):e.jsxs(e.Fragment,{children:[e.jsx(me,{size:13})," Save personalization"]})})})]})]})}const le="npm i -g turbollm";function rt(){const{data:t}=z(),{data:n}=Re(),a=n?.installed||t?.version||"",o=!!n?.hasUpdate&&!!n?.latest;return e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-3 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"About"}),e.jsxs("div",{className:"flex items-center justify-between py-1",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Version"}),e.jsx("div",{className:"text-[12px] text-muted",children:"The TurboLLM version this daemon is running"})]}),e.jsx("span",{className:"font-mono text-[13px] text-ink",children:a?`v${a}`:"—"})]}),o?e.jsxs("div",{className:"mt-2 flex flex-col gap-2 rounded-md border p-3",style:{borderColor:"color-mix(in srgb, var(--accent) 40%, var(--border))",background:"color-mix(in srgb, var(--accent) 6%, transparent)"},children:[e.jsxs("div",{className:"flex items-center gap-2 text-[13px] font-medium",style:{color:"var(--accent)"},children:[e.jsx(Oe,{size:15}),"TurboLLM v",n.latest," is available"]}),e.jsx("div",{className:"text-[12px] text-muted",children:"Update from your terminal:"}),e.jsxs("div",{className:"flex items-center justify-between gap-2 rounded-md border border-border bg-bg px-2.5 py-1.5",children:[e.jsx("code",{className:"select-all font-mono text-[12px] text-ink",children:le}),e.jsx(_e,{text:le})]})]}):n?.latest&&!n.hasUpdate?e.jsxs("div",{className:"mt-1 inline-flex items-center gap-1.5 text-[12px] text-faint",children:[e.jsx(A,{size:13,style:{color:"var(--ok)"}}),"You're on the latest version"]}):null]})}function it(){return e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-3 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"Help"}),e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsxs("a",{href:"https://github.com/bramha-dev/turbollm/issues/new",target:"_blank",rel:"noopener noreferrer",className:"flex items-center gap-2 text-[13px] text-muted hover:text-ink transition-colors",children:[e.jsx(ie,{size:13}),"Report a bug"]}),e.jsxs("a",{href:"https://github.com/bramha-dev/turbollm/discussions",target:"_blank",rel:"noopener noreferrer",className:"flex items-center gap-2 text-[13px] text-muted hover:text-ink transition-colors",children:[e.jsx(ie,{size:13}),"Send feedback"]})]})]})}export{gt as SettingsScreen};
|
|
1
|
+
import{j as e}from"./radix-BFvMxQq9.js";import{r as i,d as Ne}from"./vendor-D_-14Whs.js";import{u as ye,g as ke,f as we,P as Ce,i as Se,j as Me}from"./personas-B44JP8XF.js";import{S as Ae,T as Le}from"./common-CsaPxOo6.js";import{c as L,e as de,M as ce,o as Pe,p as Te,B as g,a2 as Ie,t as b,A as k,L as C,u as z,a3 as Ue,X as xe,a4 as Ee,a5 as ze,m as De,a6 as Re}from"./index-DdsLEQg8.js";import{C as _e}from"./copy-button-VN55eluX.js";import{M as Ge}from"./ModelDirs-BnEgYeD7.js";import{S as me}from"./save-AXon2pXP.js";import{S as re}from"./sparkles-C1EIsU4T.js";import{C as A}from"./check-BEcwngnz.js";import{R as ue,E as ie}from"./refresh-cw-35eljdhP.js";const Fe=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m16 12-4-4-4 4",key:"177agl"}],["path",{d:"M12 16V8",key:"1sbj14"}]],Oe=L("circle-arrow-up",Fe);const $e=[["rect",{width:"20",height:"14",x:"2",y:"3",rx:"2",key:"48i651"}],["line",{x1:"8",x2:"16",y1:"21",y2:"21",key:"1svkeh"}],["line",{x1:"12",x2:"12",y1:"17",y2:"21",key:"vw1qmm"}]],Be=L("monitor",$e);const qe=[["path",{d:"M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401",key:"kfwtm"}]],He=L("moon",qe);const Ke=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"M12 8v4",key:"1got3b"}],["path",{d:"M12 16h.01",key:"1drbdi"}]],pe=L("shield-alert",Ke);const Ve=[["circle",{cx:"12",cy:"12",r:"4",key:"4exip2"}],["path",{d:"M12 2v2",key:"tus03m"}],["path",{d:"M12 20v2",key:"1lh1kg"}],["path",{d:"m4.93 4.93 1.41 1.41",key:"149t6j"}],["path",{d:"m17.66 17.66 1.41 1.41",key:"ptbguv"}],["path",{d:"M2 12h2",key:"1t8f8n"}],["path",{d:"M20 12h2",key:"1q8mjw"}],["path",{d:"m6.34 17.66-1.41 1.41",key:"1m8zz5"}],["path",{d:"m19.07 4.93-1.41 1.41",key:"1shlcs"}]],Ye=L("sun",Ve),Qe=!1,oe="tllm.thinkingEnabled.default";function y({value:t,min:n,max:a,step:o,onCommit:s,className:l,ariaLabel:c}){const[r,d]=i.useState(String(t));i.useEffect(()=>{d(String(t))},[t]);const h=p=>{let m=p;return n!=null&&(m=Math.max(n,m)),a!=null&&(m=Math.min(a,m)),m};return e.jsx("input",{type:"number",inputMode:"numeric","aria-label":c,min:n,max:a,step:o,value:r,onChange:p=>{const m=p.target.value;if(d(m),m.trim()==="")return;const x=Number(m);Number.isFinite(x)&&s(x)},onBlur:()=>{const p=Number(r),m=r.trim()===""||!Number.isFinite(p)?t:h(p);d(String(m)),s(m)},onKeyDown:p=>{p.key==="Enter"&&p.target.blur()},className:l})}const U=(t,n,a)=>Math.max(n,Math.min(a,Math.round(t)));function gt(){const{theme:t,setTheme:n}=de(),{query:a,save:o}=ce(),s=a.data,l=Pe(),c=Te(),r=l.data?.dirs??[],d=l.data?.primaryDir??"",[h,p]=i.useState(60),[m,x]=i.useState(6996),[P,S]=i.useState(!0),[v,T]=i.useState(!0),[w,f]=i.useState(!1),[j,D]=i.useState(8192),[R,_]=i.useState(99),[G,F]=i.useState(0),[O,$]=i.useState(0),[he,fe]=i.useState("off"),[B,q]=i.useState(!1),[H,K]=i.useState(!0),[V,Y]=i.useState(!1),[Q,W]=i.useState(""),[X,J]=i.useState(!1),[Z,ee]=i.useState(!0),[te,se]=i.useState(1),[E,be]=i.useState(()=>localStorage.getItem(oe)!=="false"),[ge,ae]=i.useState(!1);i.useEffect(()=>{s&&(p(s.idleTtlMinutes),x(s.port??6996),S(s.autoGenerateTitles),T(s.openBrowserOnStart),f(s.autoLoadOnStart??!1),D(s.modelDefaults?.ctx??8192),_(s.modelDefaults?.ngl??99),F(s.modelDefaults?.imageMaxTokens??0),$(s.modelDefaults?.maxTokens??0),fe(s.telemetryLevel??"off"),q(s.lanBind??!1),K(s.requireApiKey??!0),Y(s.comfyui?.enabled??!1),W(s.comfyui?.url??""),J(s.comfyui?.reverseGate??!1),ee(s.gateway?.autoSwap??!0),se(s.gateway?.keepN??1))},[s]),i.useEffect(()=>{localStorage.setItem(oe,E?"true":"false")},[E]);const ne=()=>({idleTtlMinutes:U(h,0,1440),port:U(m,1024,65535),autoGenerateTitles:P,openBrowserOnStart:v,autoLoadOnStart:w,telemetryLevel:he,lanBind:B,requireApiKey:H,modelDefaults:{ctx:Math.max(256,Math.round(j)),ngl:U(R,0,99),imageMaxTokens:Math.max(0,Math.round(G)),maxTokens:Math.max(0,Math.round(O))},comfyui:{enabled:V,url:Q.trim(),reverseGate:X},gateway:{autoSwap:Z,keepN:U(te,1,4)}}),je=()=>{o.mutate(ne(),{onSuccess:u=>{const N=u.rebind;N?.portChanged?(b.success(`Port changed to ${N.port} — reconnecting…`),setTimeout(()=>{const I=new URL(window.location.href);I.port=String(N.port),window.location.href=I.toString()},1300)):N?b.success(N.lanBind?"LAN access enabled — applied (no restart needed)":"LAN access disabled — applied"):b.success("Settings saved")},onError:u=>b.error(u instanceof k?u.message:"Could not save settings.")})},ve=()=>{o.mutate(ne(),{onSuccess:()=>ae(!0),onError:u=>b.error(u instanceof k?u.message:"Could not save settings before restart.")})};return e.jsxs("div",{className:"w-full px-6 py-6",children:[e.jsx(Ae,{title:"Settings",description:"Configure TurboLLM behavior and appearance."}),ge&&e.jsx(st,{onDismiss:()=>ae(!1)}),e.jsxs("div",{className:"flex flex-col gap-6",children:[e.jsx(We,{}),e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-3 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"Appearance"}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Theme"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Choose light, dark, or follow your system setting"})]}),e.jsx("div",{className:"flex overflow-hidden rounded-lg border border-border",children:[{value:"light",label:"Light",Icon:Ye},{value:"system",label:"System",Icon:Be},{value:"dark",label:"Dark",Icon:He}].map(({value:u,label:N,Icon:I})=>e.jsxs("button",{type:"button",onClick:()=>n(u),className:"flex items-center gap-1.5 px-3 py-2 text-[13px] transition-colors",style:{background:t===u?"var(--accent)":"transparent",color:t===u?"var(--on-accent)":"var(--muted)"},children:[e.jsx(I,{size:14}),N]},u))})]}),e.jsxs("label",{className:"mt-2 flex cursor-pointer items-center justify-between border-t border-border py-2 pt-3",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Enable thinking by default"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Let reasoning models think before answering in new chats (you can toggle it per chat). Off = answer directly, faster."})]}),e.jsx("input",{type:"checkbox",checked:E,onChange:u=>be(u.target.checked),className:"h-4 w-4 accent-[var(--accent)]"})]})]}),e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-3 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"Engine"}),e.jsxs("div",{className:"flex items-center justify-between py-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Idle timeout"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Unload model after this many minutes of inactivity (0 = never)"})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(y,{value:h,min:0,max:1440,onCommit:p,ariaLabel:"Idle timeout in minutes",className:"w-20 rounded-md border border-border bg-bg px-2 py-1 text-right text-[13px] text-ink outline-none"}),e.jsx("span",{className:"text-[12px] text-muted",children:"min"})]})]})]}),e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-1 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"Model Defaults"}),e.jsx("p",{className:"mb-3 text-[12px] text-muted",children:"Applied the first time a model is loaded. A model's own saved settings always override these."}),e.jsxs("div",{className:"flex items-center justify-between py-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Context length"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Default context window, capped at each model's native max"})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(y,{value:j,min:256,step:512,onCommit:D,ariaLabel:"Default context length",className:"w-24 rounded-md border border-border bg-bg px-2 py-1 text-right text-[13px] text-ink outline-none"}),e.jsx("span",{className:"text-[12px] text-muted",children:"tok"})]})]}),e.jsxs("div",{className:"flex items-center justify-between py-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"GPU layers"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Layers to offload to the GPU (99 = all); ignored on CPU-only machines"})]}),e.jsx("div",{className:"flex items-center gap-2",children:e.jsx(y,{value:R,min:0,max:99,onCommit:_,ariaLabel:"Default GPU layers",className:"w-20 rounded-md border border-border bg-bg px-2 py-1 text-right text-[13px] text-ink outline-none"})})]}),e.jsxs("div",{className:"flex items-center justify-between py-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Image max tokens"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Per-image token budget for vision models (0 = engine default)"})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(y,{value:G,min:0,step:256,onCommit:F,ariaLabel:"Image max tokens",className:"w-24 rounded-md border border-border bg-bg px-2 py-1 text-right text-[13px] text-ink outline-none"}),e.jsx("span",{className:"text-[12px] text-muted",children:"tok"})]})]}),e.jsxs("div",{className:"flex items-center justify-between py-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Max response tokens"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Hard cap on tokens generated per reply (0 = unlimited). Also caps Claude Code / API requests."})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(y,{value:O,min:0,step:256,onCommit:$,ariaLabel:"Max response tokens",className:"w-24 rounded-md border border-border bg-bg px-2 py-1 text-right text-[13px] text-ink outline-none"}),e.jsx("span",{className:"text-[12px] text-muted",children:"tok"})]})]})]}),e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-3 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"Chat"}),e.jsxs("label",{className:"flex cursor-pointer items-center justify-between py-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Auto-generate chat titles"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Uses the model to create a title after the first exchange"})]}),e.jsx("input",{type:"checkbox",checked:P,onChange:u=>S(u.target.checked),className:"h-4 w-4 accent-[var(--accent)]"})]})]}),e.jsx(nt,{}),e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-3 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"Startup"}),e.jsxs("label",{className:"flex cursor-pointer items-center justify-between py-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Open browser on start"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Automatically open the UI when the daemon starts"})]}),e.jsx("input",{type:"checkbox",checked:v,onChange:u=>T(u.target.checked),className:"h-4 w-4 accent-[var(--accent)]"})]}),e.jsxs("label",{className:"flex cursor-pointer items-center justify-between py-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Auto-load last model"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Reload the last-used model automatically when the daemon starts"})]}),e.jsx("input",{type:"checkbox",checked:w,onChange:u=>f(u.target.checked),className:"h-4 w-4 accent-[var(--accent)]"})]})]}),e.jsx(Xe,{enabled:V,setEnabled:Y,gatePath:s?.comfyui?.gatePath??"",url:Q,setUrl:W,reverseGate:X,setReverseGate:J}),e.jsx(Je,{autoSwap:Z,setAutoSwap:ee,keepN:te,setKeepN:se}),e.jsx(Ze,{lanBind:B,setLanBind:q,requireApiKey:H,setRequireApiKey:K,port:m,setPort:x}),e.jsx(Ge,{dirs:r,primaryDir:d,mut:c}),e.jsx(et,{tokenSet:s?.hfTokenSet??!1,onSaved:()=>{a.refetch()}}),Qe,e.jsx(at,{}),e.jsx("div",{className:"flex justify-end",children:e.jsxs(g,{onClick:je,disabled:o.isPending||a.isLoading,children:[e.jsx(me,{size:14}),o.isPending?"Saving…":"Save settings"]})}),e.jsx(tt,{onRestart:ve}),e.jsx(rt,{}),e.jsx(it,{})]})]})}function We(){const t=Ne(),{data:n}=z(),a=ye(),o=de(c=>c.setPendingConversationId),s=n?.engine.state==="running"&&!!n?.model,l=()=>{s&&a.createExpert.mutate(void 0,{onSuccess:c=>{o(c.id),t("/chat")},onError:c=>b.error(c instanceof k?c.message:"Could not launch the Expert assistant.")})};return e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-1 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"TurboLLM Expert"}),e.jsx("p",{className:"mb-3 text-[12px] text-muted",children:"Chat with a built-in assistant that knows TurboLLM — it can explain features, help configure engines, models, and settings, and troubleshoot. Runs on your loaded model."}),s?e.jsxs(g,{onClick:l,disabled:a.createExpert.isPending,children:[e.jsx(re,{size:14}),a.createExpert.isPending?"Launching…":"Launch Expert"]}):e.jsxs("div",{className:"flex items-start gap-2 rounded-md border p-2.5 text-[12px]",style:{borderColor:"color-mix(in srgb, var(--accent) 40%, var(--border))",background:"color-mix(in srgb, var(--accent) 6%, transparent)"},children:[e.jsx(re,{size:14,className:"mt-0.5 shrink-0",style:{color:"var(--accent)"}}),e.jsxs("div",{className:"text-muted",children:["Load a model first to chat with the Expert assistant. Pick one on the"," ",e.jsx("button",{type:"button",onClick:()=>t("/models"),className:"font-medium text-ink underline-offset-2 hover:underline",children:"Models"})," ","screen."]})]})]})}function Xe({enabled:t,setEnabled:n,gatePath:a,url:o,setUrl:s,reverseGate:l,setReverseGate:c}){const{data:r}=z(),{install:d,uninstall:h}=Ue(),[p,m]=i.useState(""),x=r?.comfyui,P=!!a,S=a.replace(/[\\/]turbollm_gate[\\/]?$/,""),v=f=>{if(!f.trim()){b.error("Enter the path to your ComfyUI folder first.");return}d.mutate(f.trim(),{onSuccess:j=>{b.success(`Gate installed at ${j.path}. ${j.note??""}`.trim()),m("")},onError:j=>b.error(j instanceof k?j.message:"Could not install the gate node.")})},T=()=>{h.mutate(void 0,{onSuccess:()=>b.success("ComfyUI gate removed."),onError:f=>b.error(f instanceof k?f.message:"Could not remove the gate node.")})},w=x?.enabled?x.installed?x.held?{color:"var(--warn)",text:"ComfyUI is rendering — the model is unloaded and loads are paused."}:x.lastSignalAgoMs==null?{color:"var(--muted)",text:"Installed. Restart ComfyUI, then run a job to connect."}:{color:"var(--ok)",text:`ComfyUI idle — connected (last signal ${Math.round(x.lastSignalAgoMs/1e3)}s ago).`}:{color:"var(--muted)",text:"Install the gate node in ComfyUI to activate this."}:null;return e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-1 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"ComfyUI"}),e.jsx("p",{className:"mb-3 text-[12px] text-muted",children:"Share the GPU with ComfyUI. The instant ComfyUI starts a render, TurboLLM unloads its model and pauses new loads so they don't fight over VRAM — then reloads the model when ComfyUI's queue is empty. This needs a small one-time setup node installed in ComfyUI."}),e.jsxs("label",{className:"flex cursor-pointer items-center justify-between py-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Pause for ComfyUI"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Unload the model and block loads while ComfyUI renders (Save to apply)"})]}),e.jsx("input",{type:"checkbox",checked:t,onChange:f=>n(f.target.checked),className:"h-4 w-4 accent-[var(--accent)]"})]}),t&&e.jsxs("div",{className:"mt-2 flex flex-col gap-3 border-t border-border pt-3",children:[e.jsx("div",{className:"text-[13px] font-medium text-ink",children:"One-time setup"}),P?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"text-[12px] text-muted",children:[e.jsxs("span",{className:"inline-flex items-center gap-1.5 text-ink",children:[e.jsx(A,{size:13,style:{color:"var(--ok)"}})," Gate installed"]}),e.jsx("div",{className:"mt-1 break-all font-mono text-[11px] text-faint",children:a})]}),x?.installedVersion!=null&&x.installedVersion<(x.currentVersion??1/0)&&e.jsxs("div",{className:"flex items-center justify-between gap-3 rounded-md border px-3 py-2",style:{borderColor:"var(--warn)",background:"color-mix(in srgb, var(--warn) 10%, transparent)"},children:[e.jsxs("span",{className:"inline-flex items-center gap-1.5 text-[12px]",style:{color:"var(--warn)"},children:[e.jsx(Le,{size:13}),"Update available — node v",x.installedVersion," → v",x.currentVersion]}),e.jsx(g,{size:"sm",onClick:()=>v(S),disabled:d.isPending,children:d.isPending?e.jsx(C,{size:13,className:"animate-spin"}):"Update node"})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs(g,{variant:"outline",size:"sm",onClick:()=>v(S),disabled:d.isPending,children:[d.isPending?e.jsx(C,{size:13,className:"animate-spin"}):e.jsx(ue,{size:13}),"Reinstall / update"]}),e.jsxs(g,{variant:"outline",size:"sm",onClick:T,disabled:h.isPending,children:[h.isPending?e.jsx(C,{size:13,className:"animate-spin"}):e.jsx(xe,{size:13}),"Remove"]})]}),e.jsx("div",{className:"text-[12px] text-faint",children:"Restart ComfyUI after installing or updating for the gate to take effect."})]}):e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"text-[12px] text-muted",children:["Enter your ComfyUI folder (the one containing ",e.jsx("span",{className:"font-mono",children:"custom_nodes"}),"), or the",e.jsx("span",{className:"font-mono",children:" custom_nodes"})," folder itself. TurboLLM writes the gate node there, wired to this daemon."]}),e.jsxs("div",{className:"flex flex-col gap-2 sm:flex-row sm:items-center",children:[e.jsx("input",{type:"text",value:p,onChange:f=>m(f.target.value),placeholder:"e.g. D:\\\\ComfyUI_windows_portable\\\\ComfyUI",spellCheck:!1,autoComplete:"off",onKeyDown:f=>{f.key==="Enter"&&v(p)},className:"flex-1 rounded-md border border-border bg-bg px-2 py-1.5 font-mono text-[12px] text-ink outline-none"}),e.jsx(g,{size:"sm",onClick:()=>v(p),disabled:d.isPending||!p.trim(),children:d.isPending?e.jsx(C,{size:13,className:"animate-spin"}):"Install gate"})]})]}),e.jsxs("div",{className:"flex flex-col gap-3 border-t border-border pt-3",children:[e.jsxs("label",{className:"flex cursor-pointer items-center justify-between",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[13px] font-medium text-ink",children:"Free ComfyUI when TurboLLM loads"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Before loading a model, tell ComfyUI to unload its VRAM (Save to apply)"})]}),e.jsx("input",{type:"checkbox",checked:l,onChange:f=>c(f.target.checked),className:"h-4 w-4 accent-[var(--accent)]"})]}),l&&e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsxs("div",{className:"text-[12px] text-muted",children:["ComfyUI's address — TurboLLM calls its ",e.jsx("span",{className:"font-mono",children:"/free"})," endpoint here."]}),e.jsx("input",{type:"text",value:o,onChange:f=>s(f.target.value),placeholder:"http://127.0.0.1:8188",spellCheck:!1,autoComplete:"off",className:"rounded-md border border-border bg-bg px-2 py-1.5 font-mono text-[12px] text-ink outline-none"})]})]}),w&&e.jsxs("div",{className:"flex items-center gap-2 border-t border-border pt-3 text-[12px] text-muted",children:[e.jsx("span",{className:"inline-block h-2 w-2 shrink-0 rounded-full",style:{background:w.color}}),w.text]})]})]})}function Je({autoSwap:t,setAutoSwap:n,keepN:a,setKeepN:o}){return e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-1 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"Gateway"}),e.jsxs("p",{className:"mb-3 text-[12px] text-muted",children:["Controls how the OpenAI / Anthropic gateway handles the ",e.jsx("code",{className:"font-mono",children:"model"})," field. Auto-swap loads the requested model on demand; keep-N holds multiple models hot simultaneously."]}),e.jsxs("label",{className:"flex cursor-pointer items-center justify-between py-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Auto model-swap"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Auto-load the model named in each API request (Save to apply)"})]}),e.jsx("input",{type:"checkbox",checked:t,onChange:s=>n(s.target.checked),className:"h-4 w-4 accent-[var(--accent)]"})]}),e.jsxs("div",{className:"flex items-center justify-between py-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Keep-N models loaded"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Max simultaneous hot models (1 = pure swap, 2–4 = pool with LRU eviction)"})]}),e.jsx(y,{value:a,min:1,max:4,onCommit:o,ariaLabel:"Keep-N models",className:"w-16 rounded-md border border-border bg-bg px-2 py-1 text-right text-[13px] text-ink outline-none"})]})]})}function Ze({lanBind:t,setLanBind:n,requireApiKey:a,setRequireApiKey:o,port:s,setPort:l}){const{data:c}=Ee(),r=c?.lanUrl??"",d=c?.hasApiKey??!1;return e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-3 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"Network"}),e.jsxs("div",{className:"flex items-center justify-between py-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Port"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Port the daemon listens on (1024–65535)"})]}),e.jsx(y,{value:s,min:1024,max:65535,onCommit:l,ariaLabel:"Daemon port",className:"w-24 rounded-md border border-border bg-bg px-2 py-1 text-right text-[13px] text-ink outline-none"})]}),e.jsxs("label",{className:"flex cursor-pointer items-center justify-between py-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Expose on local network (LAN)"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Allow other devices on your network to reach the API"})]}),e.jsx("input",{type:"checkbox",checked:t,onChange:h=>n(h.target.checked),className:"h-4 w-4 accent-[var(--accent)]"})]}),t&&e.jsxs("div",{className:"mt-2 flex flex-col gap-3 border-t border-border pt-3",children:[r&&e.jsxs("div",{className:"text-[13px]",children:[e.jsx("span",{className:"text-muted",children:"LAN URL: "}),e.jsx("span",{className:"font-mono text-ink",children:r})]}),e.jsxs("label",{className:"flex cursor-pointer items-center justify-between",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Require an API key"}),e.jsx("div",{className:"text-[12px] text-muted",children:"When off, any device on your network can use this TurboLLM with no key"})]}),e.jsx("input",{type:"checkbox",checked:a,onChange:h=>o(h.target.checked),className:"h-4 w-4 accent-[var(--accent)]"})]}),e.jsxs("div",{className:"flex items-start gap-2 rounded-md border p-2.5 text-[12px]",style:{borderColor:"color-mix(in srgb, var(--warn) 40%, var(--border))",background:"color-mix(in srgb, var(--warn) 8%, transparent)"},children:[e.jsx(pe,{size:14,className:"mt-0.5 shrink-0",style:{color:"var(--warn)"}}),e.jsx("div",{className:"text-muted",children:a?e.jsxs(e.Fragment,{children:["Other devices can reach the API, but a valid API key is required.",!d&&e.jsxs(e.Fragment,{children:[" ","No API key exists yet — create one on the"," ",e.jsx("span",{className:"font-medium text-ink",children:"Developer"})," screen."]})]}):e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"font-medium text-ink",children:"Open access:"})," any device on your network can use this TurboLLM with no key. Only enable this on a network you trust."]})})]})]}),e.jsxs("div",{className:"mt-2 border-t border-border pt-3 text-[12px] text-faint",children:["Click ",e.jsx("span",{className:"font-medium text-ink",children:"Save settings"})," to apply. The listener re-binds in place (no restart, model stays loaded); a port change reconnects this page automatically."]})]})}function et({tokenSet:t,onSaved:n}){const{query:a,save:o}=ce(),s=ze(),[l,c]=i.useState(""),[r,d]=i.useState(null),h=x=>{c(x),d(null)},p=()=>{l.trim()&&s.mutate(l.trim(),{onSuccess:x=>d(x),onError:()=>d({ok:!1})})},m=()=>{o.mutate({hfToken:l.trim()},{onSuccess:()=>{b.success(l.trim()?"Hugging Face token saved":"Hugging Face token cleared"),c(""),d(null),n()},onError:x=>b.error(x instanceof k?x.message:"Could not save the token.")})};return e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-1 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"Models"}),e.jsxs("p",{className:"mb-3 text-[12px] text-muted",children:["A Hugging Face access token lets you download gated models (e.g. Llama). Accept the model's license on huggingface.co, then paste a read token here."," ",e.jsx("a",{href:"https://huggingface.co/settings/tokens",target:"_blank",rel:"noopener noreferrer",className:"text-ink underline-offset-2 hover:underline",children:"Create a token"}),"."]}),e.jsx("div",{className:"flex items-center justify-between py-1",children:e.jsx("div",{className:"text-[13px] text-muted",children:t?e.jsxs("span",{className:"inline-flex items-center gap-1.5 text-ink",children:[e.jsx(A,{size:13,style:{color:"var(--ok)"}}),"A token is configured"]}):"No token configured"})}),e.jsxs("div",{className:"mt-1 flex flex-col gap-2 sm:flex-row sm:items-center",children:[e.jsx("input",{type:"password",value:l,onChange:x=>h(x.target.value),placeholder:t?"Enter a new token to replace the current one":"hf_…",autoComplete:"off",className:"flex-1 rounded-md border border-border bg-bg px-2 py-1.5 font-mono text-[13px] text-ink outline-none"}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(g,{variant:"outline",size:"sm",onClick:p,disabled:!l.trim()||s.isPending,children:s.isPending?e.jsx(C,{size:13,className:"animate-spin"}):"Test"}),e.jsx(g,{size:"sm",onClick:m,disabled:o.isPending||a.isFetching,children:l.trim()?"Save token":"Clear token"})]})]}),r&&e.jsx("div",{className:"mt-2 text-[12px]",children:r.ok?e.jsxs("span",{className:"inline-flex items-center gap-1.5",style:{color:"var(--ok)"},children:[e.jsx(A,{size:13}),"Valid",r.name?` — signed in as ${r.name}`:""]}):e.jsxs("span",{className:"inline-flex items-center gap-1.5",style:{color:"var(--err)"},children:[e.jsx(xe,{size:13}),"Invalid or unauthorized token"]})})]})}function tt({onRestart:t}){return e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-1 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"Advanced"}),e.jsx("p",{className:"mb-3 text-[12px] text-muted",children:"Restart the daemon to apply a new port or LAN binding without killing the terminal. Any loaded model is unloaded by a restart and must be reloaded afterward."}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Restart daemon"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Stops the engine, then re-launches the daemon process"})]}),e.jsxs(g,{variant:"outline",size:"sm",onClick:t,children:[e.jsx(ue,{size:13}),"Restart daemon"]})]})]})}function st({onDismiss:t}){const n=Ie(),[a,o]=i.useState("restarting"),s=i.useRef(!1);return i.useEffect(()=>{if(s.current)return;s.current=!0;let l=!1,c,r;const d=async()=>{if(!l){try{if((await fetch("/api/v1/status",{headers:{Accept:"application/json"}})).ok){l||window.location.reload();return}}catch{}l||(c=setTimeout(d,700))}};return n.mutate(void 0,{onSuccess:()=>{c=setTimeout(d,700),r=setTimeout(()=>{l||o("failed")},2e4)},onError:h=>{l||(o("failed"),b.error(h instanceof k?h.message:"Could not restart the daemon."))}}),()=>{l=!0,c&&clearTimeout(c),r&&clearTimeout(r)}},[]),e.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-bg/90 backdrop-blur-sm",children:e.jsx("div",{className:"flex max-w-sm flex-col items-center gap-3 rounded-lg border border-border bg-panel p-6 text-center",children:a==="restarting"?e.jsxs(e.Fragment,{children:[e.jsx(C,{size:28,className:"animate-spin",style:{color:"var(--accent)"}}),e.jsx("div",{className:"text-[15px] font-medium text-ink",children:"Restarting daemon…"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Applying your changes. The page will reload automatically when the daemon is back."})]}):e.jsxs(e.Fragment,{children:[e.jsx(pe,{size:28,style:{color:"var(--warn)"}}),e.jsx("div",{className:"text-[15px] font-medium text-ink",children:"Daemon is taking a while"}),e.jsx("div",{className:"text-[12px] text-muted",children:"It may have moved to a new port. Try reloading, or check the terminal where you started TurboLLM."}),e.jsxs("div",{className:"mt-1 flex gap-2",children:[e.jsx(g,{variant:"outline",size:"sm",onClick:t,children:"Dismiss"}),e.jsx(g,{size:"sm",onClick:()=>window.location.reload(),children:"Reload now"})]})]})})})}function at(){const{data:t,isLoading:n}=De();return e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-3 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"Hardware"}),n||!t?e.jsx("p",{className:"text-[13px] text-faint",children:"Detecting hardware…"}):e.jsxs("dl",{className:"grid grid-cols-[auto_1fr] gap-x-6 gap-y-1.5",children:[t.gpus.length>0?t.gpus.map((a,o)=>e.jsx(M,{label:t.gpus.length>1?`GPU ${o+1}`:"GPU",value:`${a.name}${a.vramMb>0?` · ${(a.vramMb/1e3).toFixed(1)} GB VRAM`:""}`},o)):e.jsx(M,{label:"GPU",value:"None detected (CPU-only)"}),e.jsx(M,{label:"CPU",value:`${t.cpu||"Unknown"} · ${t.cores} cores`}),e.jsx(M,{label:"RAM",value:`${(t.ramMB/1e3).toFixed(1)} GB`}),e.jsx(M,{label:"OS",value:t.os})]})]})}function M({label:t,value:n}){return e.jsxs(e.Fragment,{children:[e.jsx("dt",{className:"text-[13px] text-muted",children:t}),e.jsx("dd",{className:"text-[13px] text-ink",children:n})]})}function nt(){const[t,n]=i.useState(()=>ke()),[a,o]=i.useState(()=>we()),[s,l]=i.useState(!1),c=()=>{Se(t),Me(a),l(!0),setTimeout(()=>l(!1),2e3)};return e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-1 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"Personalization"}),e.jsx("p",{className:"mb-3 text-[12px] text-muted",children:"Applied as hidden context in every new conversation. Persona can also be changed per-chat."}),e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Default persona"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Applied to new chats unless overridden in the chat window"})]}),e.jsx("select",{value:t,onChange:r=>n(r.target.value),className:"rounded-md border border-border bg-bg px-2 py-1.5 text-[13px] text-ink outline-none",children:Ce.map(r=>e.jsxs("option",{value:r.id,children:[r.name," — ",r.description]},r.id))})]}),e.jsxs("div",{className:"flex items-center justify-between gap-4",children:[e.jsxs("div",{className:"shrink-0",children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Assistant name"}),e.jsx("div",{className:"text-[12px] text-muted",children:"What the assistant calls itself (empty = model default)"})]}),e.jsx("input",{type:"text",value:a.assistantName,onChange:r=>o(d=>({...d,assistantName:r.target.value})),placeholder:"e.g. Aria",className:"w-40 rounded-md border border-border bg-bg px-2 py-1 text-[13px] text-ink outline-none placeholder:text-faint"})]}),e.jsxs("div",{className:"flex items-center justify-between gap-4",children:[e.jsxs("div",{className:"shrink-0",children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Your name"}),e.jsx("div",{className:"text-[12px] text-muted",children:"How the assistant addresses you (empty = not set)"})]}),e.jsx("input",{type:"text",value:a.userName,onChange:r=>o(d=>({...d,userName:r.target.value})),placeholder:"e.g. Alex",className:"w-40 rounded-md border border-border bg-bg px-2 py-1 text-[13px] text-ink outline-none placeholder:text-faint"})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Custom instructions"}),e.jsx("div",{className:"text-[12px] text-muted",children:"Extra instructions added to every new conversation"})]}),e.jsx("textarea",{rows:3,value:a.customInstructions,onChange:r=>o(d=>({...d,customInstructions:r.target.value})),placeholder:"e.g. Always respond in Spanish. Prefer functional programming style.",className:"w-full resize-none rounded-md border border-border bg-bg px-2 py-1.5 text-[13px] text-ink outline-none placeholder:text-faint"})]}),e.jsx("div",{className:"flex justify-end",children:e.jsx(g,{onClick:c,size:"sm",children:s?e.jsxs(e.Fragment,{children:[e.jsx(A,{size:13})," Saved"]}):e.jsxs(e.Fragment,{children:[e.jsx(me,{size:13})," Save personalization"]})})})]})]})}const le="npm i -g turbollm";function rt(){const{data:t}=z(),{data:n}=Re(),a=n?.installed||t?.version||"",o=!!n?.hasUpdate&&!!n?.latest;return e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-3 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"About"}),e.jsxs("div",{className:"flex items-center justify-between py-1",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[14px] font-medium text-ink",children:"Version"}),e.jsx("div",{className:"text-[12px] text-muted",children:"The TurboLLM version this daemon is running"})]}),e.jsx("span",{className:"font-mono text-[13px] text-ink",children:a?`v${a}`:"—"})]}),o?e.jsxs("div",{className:"mt-2 flex flex-col gap-2 rounded-md border p-3",style:{borderColor:"color-mix(in srgb, var(--accent) 40%, var(--border))",background:"color-mix(in srgb, var(--accent) 6%, transparent)"},children:[e.jsxs("div",{className:"flex items-center gap-2 text-[13px] font-medium",style:{color:"var(--accent)"},children:[e.jsx(Oe,{size:15}),"TurboLLM v",n.latest," is available"]}),e.jsx("div",{className:"text-[12px] text-muted",children:"Update from your terminal:"}),e.jsxs("div",{className:"flex items-center justify-between gap-2 rounded-md border border-border bg-bg px-2.5 py-1.5",children:[e.jsx("code",{className:"select-all font-mono text-[12px] text-ink",children:le}),e.jsx(_e,{text:le})]})]}):n?.latest&&!n.hasUpdate?e.jsxs("div",{className:"mt-1 inline-flex items-center gap-1.5 text-[12px] text-faint",children:[e.jsx(A,{size:13,style:{color:"var(--ok)"}}),"You're on the latest version"]}):null]})}function it(){return e.jsxs("section",{className:"rounded-lg border border-border bg-panel p-4",children:[e.jsx("h2",{className:"mb-3 text-[13px] font-semibold uppercase tracking-wide text-faint",children:"Help"}),e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsxs("a",{href:"https://github.com/bramha-dev/turbollm/issues/new",target:"_blank",rel:"noopener noreferrer",className:"flex items-center gap-2 text-[13px] text-muted hover:text-ink transition-colors",children:[e.jsx(ie,{size:13}),"Report a bug"]}),e.jsxs("a",{href:"https://github.com/bramha-dev/turbollm/discussions",target:"_blank",rel:"noopener noreferrer",className:"flex items-center gap-2 text-[13px] text-muted hover:text-ink transition-colors",children:[e.jsx(ie,{size:13}),"Send feedback"]})]})]})}export{gt as SettingsScreen};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{c}from"./index-DdsLEQg8.js";const e=[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]],t=c("check",e);export{t as C};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c}from"./index-
|
|
1
|
+
import{c}from"./index-DdsLEQg8.js";const e=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m15 9-6 6",key:"1uzhvr"}],["path",{d:"m9 9 6 6",key:"z0biqf"}]],o=c("circle-x",e);export{o as C};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{j as e}from"./radix-BFvMxQq9.js";import{c as a,B as n,w as l}from"./index-
|
|
1
|
+
import{j as e}from"./radix-BFvMxQq9.js";import{c as a,B as n,w as l}from"./index-DdsLEQg8.js";const i=[["path",{d:"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3",key:"wmoenq"}],["path",{d:"M12 9v4",key:"juzpu7"}],["path",{d:"M12 17h.01",key:"p32p05"}]],c=a("triangle-alert",i);function o({message:r,onRetry:t,className:s}){return e.jsxs("div",{className:l("flex items-start gap-3 rounded-md border p-3 text-[13px]",s),style:{borderColor:"var(--err)",background:"color-mix(in srgb, var(--err) 10%, transparent)",color:"var(--ink)"},role:"alert",children:[e.jsx(c,{size:16,style:{color:"var(--err)"},className:"mt-0.5 shrink-0"}),e.jsx("div",{className:"flex-1",children:r}),t&&e.jsx(n,{size:"sm",variant:"outline",onClick:t,children:"Retry"})]})}function d({icon:r,message:t,action:s}){return e.jsxs("div",{className:"flex flex-col items-center gap-3 py-12 text-center",children:[e.jsx("div",{className:"text-muted",children:r}),e.jsx("p",{className:"max-w-sm text-[13px] text-muted",children:t}),s]})}function p({title:r,description:t,actions:s}){return e.jsxs("div",{className:"mb-6 flex items-start justify-between gap-4",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"text-[18px] font-semibold tracking-[-0.01em] text-ink",children:r}),t&&e.jsx("p",{className:"mt-1 text-[13px] text-muted",children:t})]}),s]})}export{d as E,o as I,p as S,c as T};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{j as o}from"./radix-BFvMxQq9.js";import{r as a}from"./vendor-D_-14Whs.js";import{C as d}from"./check-
|
|
1
|
+
import{j as o}from"./radix-BFvMxQq9.js";import{r as a}from"./vendor-D_-14Whs.js";import{C as d}from"./check-BEcwngnz.js";import{c as x}from"./index-DdsLEQg8.js";const l=[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]],u=x("copy",l);function C({text:r,label:e,size:i=13,className:s}){const[t,n]=a.useState(!1),c=()=>{navigator.clipboard.writeText(r).then(()=>{n(!0),setTimeout(()=>n(!1),1500)})},p=e!=null?"px-2 py-1 text-[12px]":"p-1";return o.jsxs("button",{type:"button",onClick:c,title:t?"Copied!":"Copy",className:`inline-flex items-center gap-1.5 rounded transition-colors ${t?"":"text-faint hover:text-ink"} ${p} ${s??""}`,style:t?{color:"var(--ok)"}:void 0,children:[t?o.jsx(d,{size:i}):o.jsx(u,{size:i}),e!=null&&o.jsx("span",{children:t?"Copied!":e})]})}export{C,u as a};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c as l,w as t,_ as i,X as d}from"./index-
|
|
1
|
+
import{c as l,w as t,_ as i,X as d}from"./index-DdsLEQg8.js";import{m,n as x,j as s,o as p,p as f,q as g,I as u,r as D,s as y,t as w,u as N,v as b,w as h,A as j,O as v,b as C,x as A,e as c,c as k,d as R,f as T,D as _,y as $}from"./radix-BFvMxQq9.js";import{r}from"./vendor-D_-14Whs.js";const z=[["path",{d:"m6 9 6 6 6-6",key:"qrunsl"}]],W=l("chevron-down",z);const M=[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]],Y=l("chevron-right",M);const I=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]],Z=l("circle-check",I);const O=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["line",{x1:"9",x2:"15",y1:"15",y2:"9",key:"1dfufj"}]],ee=l("circle-slash",O),ae=m,se=x,oe=({className:e,...a})=>s.jsx(g,{className:t("my-1 h-px bg-border",e),...a}),q=r.forwardRef(({className:e,sideOffset:a=6,...o},n)=>s.jsx(p,{children:s.jsx(f,{ref:n,sideOffset:a,className:t("z-50 min-w-[12rem] rounded-md border border-border bg-panel p-1","shadow-[var(--shadow-2)]",e),...o})}));q.displayName="DropdownMenuContent";const P=r.forwardRef(({className:e,destructive:a,...o},n)=>s.jsx(u,{ref:n,className:t("flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none","focus:bg-panel-2 data-[disabled]:pointer-events-none data-[disabled]:opacity-50",a?"text-[color:var(--err)]":"text-ink",e),...o}));P.displayName="DropdownMenuItem";const te=D;function S({className:e,...a}){return s.jsx(v,{className:t("fixed inset-0 z-50 bg-black/40",e),...a})}const E=r.forwardRef(({className:e,...a},o)=>s.jsxs(y,{children:[s.jsx(S,{}),s.jsx(w,{ref:o,className:t("fixed left-1/2 top-1/2 z-50 w-full max-w-md -translate-x-1/2 -translate-y-1/2","rounded-[var(--radius-lg)] border border-border bg-panel p-5 shadow-[var(--shadow-2)]","focus:outline-none",e),...a})]}));E.displayName="AlertDialogContent";function re({className:e,...a}){return s.jsx("div",{className:t("mb-4 flex flex-col gap-1",e),...a})}function ne({className:e,...a}){return s.jsx("div",{className:t("mt-5 flex justify-end gap-2",e),...a})}const F=r.forwardRef(({className:e,...a},o)=>s.jsx(N,{ref:o,className:t("text-[16px] font-semibold tracking-[-0.01em] text-ink",e),...a}));F.displayName="AlertDialogTitle";const H=r.forwardRef(({className:e,...a},o)=>s.jsx(b,{ref:o,className:t("text-[13px] text-muted",e),...a}));H.displayName="AlertDialogDescription";const L=r.forwardRef(({className:e,...a},o)=>s.jsx(j,{ref:o,className:t(i({variant:"destructive"}),e),...a}));L.displayName="AlertDialogAction";const V=r.forwardRef(({className:e,...a},o)=>s.jsx(h,{ref:o,className:t(i({variant:"outline"}),e),...a}));V.displayName="AlertDialogCancel";const le=C,ie=A,ce=c;function X({className:e,...a}){return s.jsx($,{className:t("fixed inset-0 z-50 bg-black/40",e),...a})}const B=r.forwardRef(({className:e,children:a,...o},n)=>s.jsxs(k,{children:[s.jsx(X,{}),s.jsxs(R,{ref:n,className:t("fixed left-1/2 top-1/2 z-50 w-full max-w-md -translate-x-1/2 -translate-y-1/2","rounded-[var(--radius-lg)] border border-border bg-panel p-5 shadow-[var(--shadow-2)]","focus:outline-none",e),...o,children:[a,s.jsx(c,{className:"absolute right-4 top-4 rounded-sm text-muted hover:text-ink","aria-label":"Close",children:s.jsx(d,{size:16})})]})]}));B.displayName="DialogContent";function de({className:e,...a}){return s.jsx("div",{className:t("mb-4 flex flex-col gap-1",e),...a})}function me({className:e,...a}){return s.jsx("div",{className:t("mt-5 flex justify-end gap-2",e),...a})}const G=r.forwardRef(({className:e,...a},o)=>s.jsx(T,{ref:o,className:t("text-[16px] font-semibold tracking-[-0.01em] text-ink",e),...a}));G.displayName="DialogTitle";const J=r.forwardRef(({className:e,...a},o)=>s.jsx(_,{ref:o,className:t("text-[13px] text-muted",e),...a}));J.displayName="DialogDescription";export{te as A,W as C,ae as D,Y as a,Z as b,se as c,q as d,oe as e,P as f,ee as g,le as h,ie as i,B as j,ce as k,de as l,G as m,J as n,me as o,E as p,re as q,F as r,H as s,ne as t,V as u,L as v};
|