mouaif 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +1 -1
  2. package/bin/mouaif.js +1 -5
  3. package/frontend/dist/assets/{AgentFilePicker-CcKLJorU.js → AgentFilePicker-ZW6vm5Hy.js} +1 -1
  4. package/frontend/dist/assets/{CliModal-Hs5phmNZ.js → CliModal-D2cX4W0a.js} +1 -1
  5. package/frontend/dist/assets/{DictationPage-BI23lp42.js → DictationPage-Dwb6gGNi.js} +1 -1
  6. package/frontend/dist/assets/{FileEditor-DDl31c6d.js → FileEditor-BqgbeJHy.js} +1 -1
  7. package/frontend/dist/assets/{GitModal-3EC_gpJ5.js → GitModal-BPaOnX1a.js} +1 -1
  8. package/frontend/dist/assets/{Inspector-Ba3R1w04.js → Inspector-C88hoEcC.js} +1 -1
  9. package/frontend/dist/assets/{SettingsAbout-bvZGDEDw.js → SettingsAbout-D5gHujOy.js} +1 -1
  10. package/frontend/dist/assets/{SettingsActions-Dk6WX9jv.js → SettingsActions-YrcVF8D3.js} +1 -1
  11. package/frontend/dist/assets/{SettingsAgents-BNV0MgDB.js → SettingsAgents-BVwBxPbO.js} +1 -1
  12. package/frontend/dist/assets/{SettingsDefaults-DbMmQbzc.js → SettingsDefaults-CMSIeSt8.js} +1 -1
  13. package/frontend/dist/assets/{SettingsHiddenContent-BZ2sloH1.js → SettingsHiddenContent-XE1HiHzN.js} +1 -1
  14. package/frontend/dist/assets/{SettingsMcp-DOrfbQd1.js → SettingsMcp-BAK8U0e4.js} +1 -1
  15. package/frontend/dist/assets/{SettingsMcpEdit-BGMQ2CWC.js → SettingsMcpEdit-j21BWCAH.js} +1 -1
  16. package/frontend/dist/assets/{SettingsMcpRegistry-BywXee_A.js → SettingsMcpRegistry-DH5YAigF.js} +1 -1
  17. package/frontend/dist/assets/{SettingsNotifications-B0LEs11a.js → SettingsNotifications-DJt0f8Jn.js} +1 -1
  18. package/frontend/dist/assets/{SettingsPricing-BAg33iVF.js → SettingsPricing-Csy5MjG-.js} +1 -1
  19. package/frontend/dist/assets/{SettingsProject-DNrKhCcZ.js → SettingsProject-Cry5UltF.js} +1 -1
  20. package/frontend/dist/assets/{SettingsProjects-IqkBfDcm.js → SettingsProjects-d3wgiADv.js} +1 -1
  21. package/frontend/dist/assets/{SettingsPrompts-BgeiASuk.js → SettingsPrompts-B9ECDKEa.js} +1 -1
  22. package/frontend/dist/assets/{SettingsProviders-k0xJN0IK.js → SettingsProviders-BalA35c9.js} +1 -1
  23. package/frontend/dist/assets/{SettingsTags-B5kjFdQi.js → SettingsTags-D_1AgCwY.js} +1 -1
  24. package/frontend/dist/assets/index-BDkCsON6.js +61 -0
  25. package/frontend/dist/assets/index-FzhcinOk.css +1 -0
  26. package/frontend/dist/index.html +2 -2
  27. package/frontend/dist/sw.js +1 -1
  28. package/package.json +6 -7
  29. package/src/access-auth.js +1 -86
  30. package/src/http-server.js +2 -9
  31. package/src/pty.js +189 -0
  32. package/src/server-handlers-access.js +1 -101
  33. package/src/server-handlers-tools.js +25 -36
  34. package/src/server-shared.js +1 -14
  35. package/src/statusBar.js +19 -8
  36. package/frontend/dist/assets/index-BGvI4n0T.js +0 -61
  37. package/frontend/dist/assets/index-CANPYzQg.css +0 -1
@@ -1 +1 @@
1
- import{u as z,k as t,d as _,h as F,S as G,f as A,r as W,n as X}from"./index-BGvI4n0T.js";import{p as R}from"./projectQS-D1cSZ7Gr.js";function Y({error:d,onClose:s}){const l=z({onClose:()=>{s&&s()}});function M(m){m.target===m.currentTarget&&s&&s()}const i=d&&d.result||{},y=i.error||{},k=y.code||"EMCP_RPC",v=Array.isArray(i.content)?i.content.find(m=>m&&m.type==="text"&&m.text):null,S=y.message||v&&v.text||"MCP operation failed",w=d&&d.name||[i.serverSlug,i.toolName].filter(Boolean).join(" / ")||"",u=i&&Object.keys(i).length?JSON.stringify(i,null,2):null;return t("div",{class:"mcp-err__overlay",role:"dialog","aria-modal":"true","aria-label":"MCP error",onClick:M},t("div",{class:"mcp-err__sheet",ref:l},t("div",{class:"mcp-err__head"},t("span",{class:"mcp-err__title"},"MCP error"),t("button",{class:"mcp-err__close",type:"button",onClick:s,"aria-label":"Close error",title:"Close"},t("svg",{viewBox:"0 0 24 24",width:16,height:16,"aria-hidden":"true"},t("path",{d:"M6 6 18 18 M18 6 6 18",fill:"none",stroke:"currentColor","stroke-width":2,"stroke-linecap":"round"})))),t("div",{class:"mcp-err__body"},w?t("div",{class:"mcp-err__tool",title:w},w):null,t("div",{class:"mcp-err__code","data-code":k},k),t("div",{class:"mcp-err__message"},S),u?t("pre",{class:"mcp-err__raw"},u):null),t("div",{class:"mcp-err__foot"},t("button",{class:"btn",type:"button",onClick:s},"Close"))))}function ee(d={}){const s=typeof d.projectDir=="string"?d.projectDir:"",l=typeof d.from=="string"?d.from:"",M=s?s.split(/[/\\]/).filter(Boolean).pop()||s:"",[i,y]=_(!1),[k,v]=_(!1),[S,w]=_(""),[u,m]=_([]),[x,h]=_({text:"",kind:""}),[T,g]=_(new Set),[B,E]=_(null),[H,U]=_({});function b(e,r,o,n){U(a=>{const c=Object.assign({},a);return r?c[e]={text:r,kind:o,error:n||null}:delete c[e],c})}async function C(){v(!0),y(!0),h({text:"loading…",kind:"busy"});const e=s?"?projectDir="+encodeURIComponent(s):"";let r;try{r=await A("/api/mcp/servers"+e)}catch{h({text:"network error",kind:"error"}),y(!1),v(!1);return}if(y(!1),v(!1),r.status!==200){h({text:"HTTP "+r.status+(r.body&&r.body.error?" — "+r.body.error:""),kind:"error"});return}m(r.body.servers||[]),h({text:(r.body.servers||[]).length+" configured",kind:"success"}),U({})}function D(){const e=(S||"").trim();if(!e){h({text:"type a project directory first",kind:"error"});return}W(e),X("settings/mcp"+R(e))}function L(e){const r=e.scope==="app";return t("span",{class:"mcp__scope mcp__scope--"+(r?"app":"project"),title:r?"App-wide: visible to every project":"Project: committed to this project's .mcp.json"},r?"app":"project")}function I(e,r){if(!r)return;const o=typeof r=="string"?{code:"EMCP_START",message:r}:{code:r.code||"EMCP_START",message:r.message||String(r)};E({name:e.name||e.id||"MCP server",result:{serverSlug:e.slug||e.id||"",error:o}})}function q(e){const r=e.status||"stopped",o=e.tools&&e.tools.length?e.tools.length+" tool"+(e.tools.length===1?"":"s"):"no tools",n=T.has(e.id),a=s?R(s)+"&scope="+encodeURIComponent(e.scope||"project")+(l?"&from="+encodeURIComponent(l):""):"?scope=app"+(l?"&from="+encodeURIComponent(l):""),c="#/settings/mcp/"+encodeURIComponent(e.id)+a,f=r==="ready"||r==="errored"||r==="starting",P=r==="starting"||n,N=r==="starting"?"Starting…":r==="ready"?"Restart this server":"Start this server",j=H[e.id],p=j&&j.text?{text:j.text,kind:j.kind||"error",error:j.error||null}:e.status==="errored"&&e.error?{text:(e.error.code||"ERR")+": "+(e.error.message||""),kind:"error",error:e.error}:null;return t("li",{key:e.id,class:"mcp__row"+(n?" mcp__row--busy":"")},t("a",{class:"group__row settings-project__agent-link mcp__row-main",href:c},t("span",{class:"group__row-body"},t("span",{class:"group__row-label mcp__row-name"},e.name," ",L(e)),t("span",{class:"settings-project__link-sub mcp__row-sub"},(e.transport==="http"?"http · ":"")+r)),t("span",{class:"group__row-detail"},o)),t("div",{class:"mcp__row-actions",onClick:V=>V.stopPropagation()},t("button",{class:"btn btn--small",type:"button",disabled:P,title:N,"aria-label":N,onClick:()=>O("start",e.id)},r==="ready"?"Restart":"Start"),f?t("button",{class:"btn btn--small",type:"button",disabled:n,onClick:()=>O("stop",e.id)},"Stop"):null,r==="ready"?t("button",{class:"btn btn--small",type:"button",disabled:n,onClick:()=>J(e.id)},"↻"):null),p?t("button",{class:"mcp__row-err"+(p.kind==="busy"?" mcp__row-busy":""),type:"button","aria-label":p.kind==="error"?"Show full MCP error: "+p.text:p.text,disabled:p.kind!=="error",onClick:()=>I(e,p.error||{code:"EMCP_START",message:p.text})},t("span",{class:"mcp__row-err-text"},p.text),p.kind==="error"?t("span",{class:"mcp__row-err-more"},"Details"):null):null)}async function O(e,r){if(T.has(r))return;g(n=>new Set(n).add(r)),b(r,e==="start"?"starting…":"stopping…","busy");let o;try{o=await A("/api/mcp/servers/"+encodeURIComponent(r)+"/"+e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectDir:s})})}catch{g(f=>{const P=new Set(f);return P.delete(r),P});const a={code:"EMCP_TRANSPORT",message:"Could not reach mouaif — check the app is still running, then try again."};b(r,a.code+": "+a.message,"error",a);const c=u.find(f=>f.id===r)||{id:r};I(c,a);return}if(o.status!==200){g(c=>{const f=new Set(c);return f.delete(r),f});const n={code:o.body&&o.body.code||"EMCP_START",message:o.body&&o.body.error?String(o.body.error):"HTTP "+o.status};await C(),b(r,n.code+": "+n.message,"error",n);const a=u.find(c=>c.id===r)||{id:r};I(a,n);return}g(n=>{const a=new Set(n);return a.delete(r),a}),b(r,""),h({text:e+"ed",kind:"success"}),C()}async function J(e){if(T.has(e))return;g(n=>new Set(n).add(e)),b(e,"refreshing tools…","busy");const r=s?"?projectDir="+encodeURIComponent(s):"";let o;try{o=await A("/api/mcp/servers/"+encodeURIComponent(e)+"/tools"+r)}catch{g(a=>{const c=new Set(a);return c.delete(e),c}),b(e,"Could not reach mouaif — check the app is still running, then try again.","error");return}if(o.status!==200){g(a=>{const c=new Set(a);return c.delete(e),c});const n=o.body&&o.body.error?String(o.body.error):"HTTP "+o.status;b(e,n,"error");return}g(n=>{const a=new Set(n);return a.delete(e),a}),b(e,""),h({text:(o.body.tools||[]).length+" tools",kind:"success"}),C()}F(()=>{C()},[s]);const K="#/settings/mcp/new"+(s?R(s)+"&scope=project":"?scope=app")+(l?"&from="+encodeURIComponent(l):""),Q=s?"#/settings/project?projectDir="+encodeURIComponent(s)+(l?"&from="+encodeURIComponent(l):""):"#/settings";return t(G,null,t("div",{class:"view-head"},t("a",{href:Q,class:"view-back","aria-label":"Back"},"←"),t("h2",{class:"view-title"},s?"MCP servers · "+M:"MCP servers · app defaults")),t("section",null,t("p",{class:"hint hint--compact"},s?"Servers this project can use: the app-wide servers (app badge) plus any servers committed to this project's .mcp.json (project badge). If a project server has the same name as an app one, the project server is the one that runs.":"Model Context Protocol servers available in every project. The AI client discovers each server's tools and advertises them to the model. A project can add its own servers on top of these."),t("div",{class:"group"},t("div",{class:"group__title"},"Servers",t("span",{class:"group__title-note"},u.length+" configured")),t("ul",{class:"group__list mcp__list","aria-label":"MCP servers"},u.length?u.map(q):t("li",{class:"mcp__empty"},s?'No MCP servers for this project yet. Tap "+" to add one. App-wide servers are managed from Settings → App defaults → MCP servers.':'No app-wide MCP servers yet. Tap "+" to add one — it will be available in every project.'))),s?null:t("div",{class:"row"},t("label",{class:"label",for:"mcp-open-project"},"Project servers"),t("div",{class:"row row--inline"},t("input",{class:"input",id:"mcp-open-project",type:"text",placeholder:"C:/path/to/project",value:S,onInput:e=>w(e.target.value),onKeyDown:e=>{e.key==="Enter"&&D()}}),t("button",{class:"btn",type:"button",onClick:D,disabled:i},"Open")),t("span",{class:"hint hint--compact"},"Project servers live with the project (committed to .mcp.json). Open a project to manage them.")),t("div",{class:"page-bar"},t("a",{href:"#/settings/mcp/registry"+R(s)+(l?"&from="+encodeURIComponent(l):""),class:"btn btn--small",type:"button"},"Browse Registry"),t("span",{class:"status page-bar__status"+(x.kind?" status--"+x.kind:""),"aria-live":"polite"},x.text),t("button",{class:"btn btn--small",type:"button","aria-label":"Refresh servers",onClick:()=>C(),disabled:k},"↻"),t("a",{href:K,class:"page-bar__add","aria-label":"Add MCP server"},"+"))),B?t(Y,{error:B,onClose:()=>E(null)}):null)}export{ee as SettingsMcpView};
1
+ import{u as z,k as t,d as _,h as F,S as G,f as A,r as W,n as X}from"./index-BDkCsON6.js";import{p as R}from"./projectQS-D1cSZ7Gr.js";function Y({error:d,onClose:s}){const l=z({onClose:()=>{s&&s()}});function M(m){m.target===m.currentTarget&&s&&s()}const i=d&&d.result||{},y=i.error||{},k=y.code||"EMCP_RPC",v=Array.isArray(i.content)?i.content.find(m=>m&&m.type==="text"&&m.text):null,S=y.message||v&&v.text||"MCP operation failed",w=d&&d.name||[i.serverSlug,i.toolName].filter(Boolean).join(" / ")||"",u=i&&Object.keys(i).length?JSON.stringify(i,null,2):null;return t("div",{class:"mcp-err__overlay",role:"dialog","aria-modal":"true","aria-label":"MCP error",onClick:M},t("div",{class:"mcp-err__sheet",ref:l},t("div",{class:"mcp-err__head"},t("span",{class:"mcp-err__title"},"MCP error"),t("button",{class:"mcp-err__close",type:"button",onClick:s,"aria-label":"Close error",title:"Close"},t("svg",{viewBox:"0 0 24 24",width:16,height:16,"aria-hidden":"true"},t("path",{d:"M6 6 18 18 M18 6 6 18",fill:"none",stroke:"currentColor","stroke-width":2,"stroke-linecap":"round"})))),t("div",{class:"mcp-err__body"},w?t("div",{class:"mcp-err__tool",title:w},w):null,t("div",{class:"mcp-err__code","data-code":k},k),t("div",{class:"mcp-err__message"},S),u?t("pre",{class:"mcp-err__raw"},u):null),t("div",{class:"mcp-err__foot"},t("button",{class:"btn",type:"button",onClick:s},"Close"))))}function ee(d={}){const s=typeof d.projectDir=="string"?d.projectDir:"",l=typeof d.from=="string"?d.from:"",M=s?s.split(/[/\\]/).filter(Boolean).pop()||s:"",[i,y]=_(!1),[k,v]=_(!1),[S,w]=_(""),[u,m]=_([]),[x,h]=_({text:"",kind:""}),[T,g]=_(new Set),[B,E]=_(null),[H,U]=_({});function b(e,r,o,n){U(a=>{const c=Object.assign({},a);return r?c[e]={text:r,kind:o,error:n||null}:delete c[e],c})}async function C(){v(!0),y(!0),h({text:"loading…",kind:"busy"});const e=s?"?projectDir="+encodeURIComponent(s):"";let r;try{r=await A("/api/mcp/servers"+e)}catch{h({text:"network error",kind:"error"}),y(!1),v(!1);return}if(y(!1),v(!1),r.status!==200){h({text:"HTTP "+r.status+(r.body&&r.body.error?" — "+r.body.error:""),kind:"error"});return}m(r.body.servers||[]),h({text:(r.body.servers||[]).length+" configured",kind:"success"}),U({})}function D(){const e=(S||"").trim();if(!e){h({text:"type a project directory first",kind:"error"});return}W(e),X("settings/mcp"+R(e))}function L(e){const r=e.scope==="app";return t("span",{class:"mcp__scope mcp__scope--"+(r?"app":"project"),title:r?"App-wide: visible to every project":"Project: committed to this project's .mcp.json"},r?"app":"project")}function I(e,r){if(!r)return;const o=typeof r=="string"?{code:"EMCP_START",message:r}:{code:r.code||"EMCP_START",message:r.message||String(r)};E({name:e.name||e.id||"MCP server",result:{serverSlug:e.slug||e.id||"",error:o}})}function q(e){const r=e.status||"stopped",o=e.tools&&e.tools.length?e.tools.length+" tool"+(e.tools.length===1?"":"s"):"no tools",n=T.has(e.id),a=s?R(s)+"&scope="+encodeURIComponent(e.scope||"project")+(l?"&from="+encodeURIComponent(l):""):"?scope=app"+(l?"&from="+encodeURIComponent(l):""),c="#/settings/mcp/"+encodeURIComponent(e.id)+a,f=r==="ready"||r==="errored"||r==="starting",P=r==="starting"||n,N=r==="starting"?"Starting…":r==="ready"?"Restart this server":"Start this server",j=H[e.id],p=j&&j.text?{text:j.text,kind:j.kind||"error",error:j.error||null}:e.status==="errored"&&e.error?{text:(e.error.code||"ERR")+": "+(e.error.message||""),kind:"error",error:e.error}:null;return t("li",{key:e.id,class:"mcp__row"+(n?" mcp__row--busy":"")},t("a",{class:"group__row settings-project__agent-link mcp__row-main",href:c},t("span",{class:"group__row-body"},t("span",{class:"group__row-label mcp__row-name"},e.name," ",L(e)),t("span",{class:"settings-project__link-sub mcp__row-sub"},(e.transport==="http"?"http · ":"")+r)),t("span",{class:"group__row-detail"},o)),t("div",{class:"mcp__row-actions",onClick:V=>V.stopPropagation()},t("button",{class:"btn btn--small",type:"button",disabled:P,title:N,"aria-label":N,onClick:()=>O("start",e.id)},r==="ready"?"Restart":"Start"),f?t("button",{class:"btn btn--small",type:"button",disabled:n,onClick:()=>O("stop",e.id)},"Stop"):null,r==="ready"?t("button",{class:"btn btn--small",type:"button",disabled:n,onClick:()=>J(e.id)},"↻"):null),p?t("button",{class:"mcp__row-err"+(p.kind==="busy"?" mcp__row-busy":""),type:"button","aria-label":p.kind==="error"?"Show full MCP error: "+p.text:p.text,disabled:p.kind!=="error",onClick:()=>I(e,p.error||{code:"EMCP_START",message:p.text})},t("span",{class:"mcp__row-err-text"},p.text),p.kind==="error"?t("span",{class:"mcp__row-err-more"},"Details"):null):null)}async function O(e,r){if(T.has(r))return;g(n=>new Set(n).add(r)),b(r,e==="start"?"starting…":"stopping…","busy");let o;try{o=await A("/api/mcp/servers/"+encodeURIComponent(r)+"/"+e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectDir:s})})}catch{g(f=>{const P=new Set(f);return P.delete(r),P});const a={code:"EMCP_TRANSPORT",message:"Could not reach mouaif — check the app is still running, then try again."};b(r,a.code+": "+a.message,"error",a);const c=u.find(f=>f.id===r)||{id:r};I(c,a);return}if(o.status!==200){g(c=>{const f=new Set(c);return f.delete(r),f});const n={code:o.body&&o.body.code||"EMCP_START",message:o.body&&o.body.error?String(o.body.error):"HTTP "+o.status};await C(),b(r,n.code+": "+n.message,"error",n);const a=u.find(c=>c.id===r)||{id:r};I(a,n);return}g(n=>{const a=new Set(n);return a.delete(r),a}),b(r,""),h({text:e+"ed",kind:"success"}),C()}async function J(e){if(T.has(e))return;g(n=>new Set(n).add(e)),b(e,"refreshing tools…","busy");const r=s?"?projectDir="+encodeURIComponent(s):"";let o;try{o=await A("/api/mcp/servers/"+encodeURIComponent(e)+"/tools"+r)}catch{g(a=>{const c=new Set(a);return c.delete(e),c}),b(e,"Could not reach mouaif — check the app is still running, then try again.","error");return}if(o.status!==200){g(a=>{const c=new Set(a);return c.delete(e),c});const n=o.body&&o.body.error?String(o.body.error):"HTTP "+o.status;b(e,n,"error");return}g(n=>{const a=new Set(n);return a.delete(e),a}),b(e,""),h({text:(o.body.tools||[]).length+" tools",kind:"success"}),C()}F(()=>{C()},[s]);const K="#/settings/mcp/new"+(s?R(s)+"&scope=project":"?scope=app")+(l?"&from="+encodeURIComponent(l):""),Q=s?"#/settings/project?projectDir="+encodeURIComponent(s)+(l?"&from="+encodeURIComponent(l):""):"#/settings";return t(G,null,t("div",{class:"view-head"},t("a",{href:Q,class:"view-back","aria-label":"Back"},"←"),t("h2",{class:"view-title"},s?"MCP servers · "+M:"MCP servers · app defaults")),t("section",null,t("p",{class:"hint hint--compact"},s?"Servers this project can use: the app-wide servers (app badge) plus any servers committed to this project's .mcp.json (project badge). If a project server has the same name as an app one, the project server is the one that runs.":"Model Context Protocol servers available in every project. The AI client discovers each server's tools and advertises them to the model. A project can add its own servers on top of these."),t("div",{class:"group"},t("div",{class:"group__title"},"Servers",t("span",{class:"group__title-note"},u.length+" configured")),t("ul",{class:"group__list mcp__list","aria-label":"MCP servers"},u.length?u.map(q):t("li",{class:"mcp__empty"},s?'No MCP servers for this project yet. Tap "+" to add one. App-wide servers are managed from Settings → App defaults → MCP servers.':'No app-wide MCP servers yet. Tap "+" to add one — it will be available in every project.'))),s?null:t("div",{class:"row"},t("label",{class:"label",for:"mcp-open-project"},"Project servers"),t("div",{class:"row row--inline"},t("input",{class:"input",id:"mcp-open-project",type:"text",placeholder:"C:/path/to/project",value:S,onInput:e=>w(e.target.value),onKeyDown:e=>{e.key==="Enter"&&D()}}),t("button",{class:"btn",type:"button",onClick:D,disabled:i},"Open")),t("span",{class:"hint hint--compact"},"Project servers live with the project (committed to .mcp.json). Open a project to manage them.")),t("div",{class:"page-bar"},t("a",{href:"#/settings/mcp/registry"+R(s)+(l?"&from="+encodeURIComponent(l):""),class:"btn btn--small",type:"button"},"Browse Registry"),t("span",{class:"status page-bar__status"+(x.kind?" status--"+x.kind:""),"aria-live":"polite"},x.text),t("button",{class:"btn btn--small",type:"button","aria-label":"Refresh servers",onClick:()=>C(),disabled:k},"↻"),t("a",{href:K,class:"page-bar__add","aria-label":"Add MCP server"},"+"))),B?t(Y,{error:B,onClose:()=>E(null)}):null)}export{ee as SettingsMcpView};
@@ -1,3 +1,3 @@
1
- import{k as e,d as o,h as ee,f as C,b as F,S as K,n as X}from"./index-BGvI4n0T.js";import{p as N}from"./projectQS-D1cSZ7Gr.js";function Se({value:p,onChange:a}){return e("div",{class:"row"},e("span",{class:"label"},"Arguments"),e("span",{class:"hint hint--compact"},"One argument per field. Spaces, quotes, backslashes, and empty values are preserved exactly. Do not add shell quotes around paths."),p.map((v,l)=>e("div",{key:l,class:"row row--inline"},e("input",{class:"input",type:"text",value:v,style:"flex:1;min-width:0","aria-label":"Argument "+(l+1),onInput:w=>a(p.map((r,f)=>f===l?w.target.value:r))}),e("button",{class:"btn",type:"button","aria-label":"Remove argument "+(l+1),onClick:()=>a(p.filter((w,r)=>r!==l))},"Remove"))),e("button",{class:"btn",type:"button",onClick:()=>a([...p,""])},"Add argument"))}function je({id:p,projectDir:a,saved:v,value:l,onChange:w}){const[r,f]=o(null),[L,m]=o(""),[E,A]=o(!1),[_,g]=o(""),I=p?"/api/mcp/servers/"+encodeURIComponent(p)+"/oauth":"",S=a?"?projectDir="+encodeURIComponent(a):"",T=!!(p&&v?.oauth?.enabled&&v.url===l.url&&(v.oauth.clientId||"")===l.clientId.trim()&&(v.oauth.scope||"")===l.scope.trim());ee(()=>{if(f(null),g(""),m(""),!T||!l.enabled)return;let i=!1;async function k(){try{const y=await C(I+S);if(i)return;y.status===200?(f(y.body),y.body.connected?(g(""),m("Signed in. You can start the server from the MCP list.")):y.body.pending||g("")):m(y.body?.error||"Could not read sign-in status.")}catch{i||m("Could not reach mouaif.")}}k();const U=setInterval(k,3e3);return window.addEventListener("focus",k),()=>{i=!0,clearInterval(U),window.removeEventListener("focus",k)}},[I,S,T,l.enabled]);async function x(){A(!0),m("Preparing sign-in…"),g("");try{const i=await C(I+"/start",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectDir:a})});if(i.status!==200)throw new Error(i.body?.error||"Could not start sign-in.");g(i.body.authorizationUrl),f({connected:!1,pending:!0,redirectUrl:i.body.redirectUrl}),m("Open the sign-in page below, then return here. The link expires after 10 minutes.")}catch(i){m(i.message||"Could not reach mouaif.")}finally{A(!1)}}async function O(){if(confirm("Disconnect OAuth for this server? App-wide sign-in is shared with all projects.")){A(!0);try{const i=await C(I+S,{method:"DELETE"});if(i.status!==200)throw new Error(i.body?.error||"Could not disconnect.");f({connected:!1,pending:!1}),g(""),m("Disconnected. Local OAuth credentials were removed.")}catch(i){m(i.message||"Could not reach mouaif.")}finally{A(!1)}}}return e("div",{class:"row"},e("label",{class:"label",for:"mcp-oauth"},"Authentication"),e("select",{class:"input",id:"mcp-oauth",value:l.enabled?"oauth":"headers",onChange:i=>w({...l,enabled:i.target.value==="oauth"})},e("option",{value:"headers"},"None / manual headers"),e("option",{value:"oauth"},"OAuth sign-in (PKCE)")),l.enabled?e("div",{class:"row"},e("label",{class:"label",for:"mcp-oauth-client"},"Client ID (optional)"),e("input",{class:"input",id:"mcp-oauth-client",value:l.clientId,placeholder:"Automatic client registration",onInput:i=>w({...l,clientId:i.target.value})}),e("label",{class:"label",for:"mcp-oauth-scope"},"OAuth scopes (optional)"),e("input",{class:"input",id:"mcp-oauth-scope",value:l.scope,placeholder:"Server-discovered scopes",onInput:i=>w({...l,scope:i.target.value})}),e("p",{class:"hint hint--compact"},"Uses authorization-code OAuth with PKCE. Tokens stay in the OS keychain, not project files. OAuth replaces any manual Authorization header. Servers without automatic client registration need a pre-registered public client ID."),e("p",{class:"hint hint--compact",style:"overflow-wrap:anywhere"},"Register this callback URL if needed: ",r?.redirectUrl||window.location.origin+"/oauth/mcp/callback"),T?e("div",{class:"row"},e("span",{class:"hint"},r?.connected?"Signed in":r?.pending?"Waiting for sign-in":"Not signed in"),e("button",{class:"btn",type:"button",disabled:E,onClick:x},r?.connected?"Sign in again":"Sign in"),_?e("a",{class:"btn btn--primary",href:_,target:"_blank",rel:"noopener noreferrer"},"Open sign-in page"):null,r?.connected||r?.pending?e("button",{class:"btn",type:"button",disabled:E,onClick:O},r.pending?"Cancel sign-in":"Disconnect"):null):e("p",{class:"hint"},"Save the HTTP URL and OAuth settings, then reopen this server to sign in."),e("span",{class:"status",role:"status","aria-live":"polite"},L)):null)}function Ee(p){const a=p.id||"",v=p.projectDir||"",l=typeof p.from=="string"?p.from:"",w=F.value&&F.value.dir||"",r=a?v:v||w,[f,L]=o(p.scope==="app"?"app":p.scope==="project"||v?"project":"app"),[m,E]=o(p.scope==="app"?"app":"project"),[A,_]=o(""),[g,I]=o(""),[S,T]=o(""),[x,O]=o([]),[i,k]=o(""),[U,y]=o(""),[q,B]=o(""),[te,J]=o(`API_TOKEN=...
1
+ import{k as e,d as o,h as ee,f as C,b as F,S as K,n as X}from"./index-BDkCsON6.js";import{p as N}from"./projectQS-D1cSZ7Gr.js";function Se({value:p,onChange:a}){return e("div",{class:"row"},e("span",{class:"label"},"Arguments"),e("span",{class:"hint hint--compact"},"One argument per field. Spaces, quotes, backslashes, and empty values are preserved exactly. Do not add shell quotes around paths."),p.map((v,l)=>e("div",{key:l,class:"row row--inline"},e("input",{class:"input",type:"text",value:v,style:"flex:1;min-width:0","aria-label":"Argument "+(l+1),onInput:w=>a(p.map((r,f)=>f===l?w.target.value:r))}),e("button",{class:"btn",type:"button","aria-label":"Remove argument "+(l+1),onClick:()=>a(p.filter((w,r)=>r!==l))},"Remove"))),e("button",{class:"btn",type:"button",onClick:()=>a([...p,""])},"Add argument"))}function je({id:p,projectDir:a,saved:v,value:l,onChange:w}){const[r,f]=o(null),[L,m]=o(""),[E,A]=o(!1),[_,g]=o(""),I=p?"/api/mcp/servers/"+encodeURIComponent(p)+"/oauth":"",S=a?"?projectDir="+encodeURIComponent(a):"",T=!!(p&&v?.oauth?.enabled&&v.url===l.url&&(v.oauth.clientId||"")===l.clientId.trim()&&(v.oauth.scope||"")===l.scope.trim());ee(()=>{if(f(null),g(""),m(""),!T||!l.enabled)return;let i=!1;async function k(){try{const y=await C(I+S);if(i)return;y.status===200?(f(y.body),y.body.connected?(g(""),m("Signed in. You can start the server from the MCP list.")):y.body.pending||g("")):m(y.body?.error||"Could not read sign-in status.")}catch{i||m("Could not reach mouaif.")}}k();const U=setInterval(k,3e3);return window.addEventListener("focus",k),()=>{i=!0,clearInterval(U),window.removeEventListener("focus",k)}},[I,S,T,l.enabled]);async function x(){A(!0),m("Preparing sign-in…"),g("");try{const i=await C(I+"/start",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectDir:a})});if(i.status!==200)throw new Error(i.body?.error||"Could not start sign-in.");g(i.body.authorizationUrl),f({connected:!1,pending:!0,redirectUrl:i.body.redirectUrl}),m("Open the sign-in page below, then return here. The link expires after 10 minutes.")}catch(i){m(i.message||"Could not reach mouaif.")}finally{A(!1)}}async function O(){if(confirm("Disconnect OAuth for this server? App-wide sign-in is shared with all projects.")){A(!0);try{const i=await C(I+S,{method:"DELETE"});if(i.status!==200)throw new Error(i.body?.error||"Could not disconnect.");f({connected:!1,pending:!1}),g(""),m("Disconnected. Local OAuth credentials were removed.")}catch(i){m(i.message||"Could not reach mouaif.")}finally{A(!1)}}}return e("div",{class:"row"},e("label",{class:"label",for:"mcp-oauth"},"Authentication"),e("select",{class:"input",id:"mcp-oauth",value:l.enabled?"oauth":"headers",onChange:i=>w({...l,enabled:i.target.value==="oauth"})},e("option",{value:"headers"},"None / manual headers"),e("option",{value:"oauth"},"OAuth sign-in (PKCE)")),l.enabled?e("div",{class:"row"},e("label",{class:"label",for:"mcp-oauth-client"},"Client ID (optional)"),e("input",{class:"input",id:"mcp-oauth-client",value:l.clientId,placeholder:"Automatic client registration",onInput:i=>w({...l,clientId:i.target.value})}),e("label",{class:"label",for:"mcp-oauth-scope"},"OAuth scopes (optional)"),e("input",{class:"input",id:"mcp-oauth-scope",value:l.scope,placeholder:"Server-discovered scopes",onInput:i=>w({...l,scope:i.target.value})}),e("p",{class:"hint hint--compact"},"Uses authorization-code OAuth with PKCE. Tokens stay in the OS keychain, not project files. OAuth replaces any manual Authorization header. Servers without automatic client registration need a pre-registered public client ID."),e("p",{class:"hint hint--compact",style:"overflow-wrap:anywhere"},"Register this callback URL if needed: ",r?.redirectUrl||window.location.origin+"/oauth/mcp/callback"),T?e("div",{class:"row"},e("span",{class:"hint"},r?.connected?"Signed in":r?.pending?"Waiting for sign-in":"Not signed in"),e("button",{class:"btn",type:"button",disabled:E,onClick:x},r?.connected?"Sign in again":"Sign in"),_?e("a",{class:"btn btn--primary",href:_,target:"_blank",rel:"noopener noreferrer"},"Open sign-in page"):null,r?.connected||r?.pending?e("button",{class:"btn",type:"button",disabled:E,onClick:O},r.pending?"Cancel sign-in":"Disconnect"):null):e("p",{class:"hint"},"Save the HTTP URL and OAuth settings, then reopen this server to sign in."),e("span",{class:"status",role:"status","aria-live":"polite"},L)):null)}function Ee(p){const a=p.id||"",v=p.projectDir||"",l=typeof p.from=="string"?p.from:"",w=F.value&&F.value.dir||"",r=a?v:v||w,[f,L]=o(p.scope==="app"?"app":p.scope==="project"||v?"project":"app"),[m,E]=o(p.scope==="app"?"app":"project"),[A,_]=o(""),[g,I]=o(""),[S,T]=o(""),[x,O]=o([]),[i,k]=o(""),[U,y]=o(""),[q,B]=o(""),[te,J]=o(`API_TOKEN=...
2
2
  LOG_LEVEL=info`),[ne,V]=o("Authorization: Bearer ..."),[se,j]=o(!1),[oe,R]=o(!1),[z,u]=o({text:"",kind:""}),[b,Q]=o("stdio"),[D,G]=o({enabled:!1,clientId:"",scope:""}),[P,ae]=o(null),[re,W]=o({}),[Y,le]=o([]),[ie,M]=o(""),[Z,ce]=o([]),[$,pe]=o([]),[de,ue]=o(!1),[he,me]=o(!1);async function ve(){if(a){const t=r?"?projectDir="+encodeURIComponent(r):"";let s;try{s=await C("/api/mcp/servers"+t)}catch{u({text:"network error",kind:"error"});return}if(s.status!==200){u({text:"HTTP "+s.status,kind:"error"});return}const n=(s.body.servers||[]).find(d=>d.id===a)||null;if(!n){u({text:"Server not found",kind:"error"});return}ae(n),E(n.scope==="app"?"app":"project"),_(n.name||""),Q(n.transport==="http"?"http":"stdio"),I(n.command||""),T(n.url||""),G({enabled:!!n.oauth?.enabled,clientId:n.oauth?.clientId||"",scope:n.oauth?.scope||""}),O(n.args||[]),k("");const c=Object.keys(n.env||{}).filter(d=>n.env[d]&&n.env[d].configured);ce(c),J(c.length?"Already configured: "+c.join(", ")+". Enter new values or leave blank to keep.":`API_TOKEN=...
3
3
  LOG_LEVEL=info`),y("");const h=Object.keys(n.headers||{}).filter(d=>n.headers[d]&&n.headers[d].configured);if(pe(h),V(h.length?"Already configured: "+h.join(", ")+". Enter new values or leave blank to keep.":"Authorization: Bearer ..."),B(n.cwd||""),le(n.tools||[]),n.scope!=="app"&&r)try{const d=await C("/api/tools/authorization?projectDir="+encodeURIComponent(r)),H=d.status===200&&d.body.mcp,Ie=H&&H.tools&&typeof H.tools=="object"?H.tools:{};W(Ie)}catch{}}u({text:"",kind:""})}async function fe(t,s){M("saving…");const n={};s==="inherit"?n[t]=null:n[t]={mode:s};const c=await C("/api/tools/authorization",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectDir:r,mcp:{tools:n}})});c.status===200?(M("saved"),W(h=>{const d=Object.assign({},h);return s==="inherit"?delete d[t]:d[t]={mode:s},d})):M("HTTP "+c.status)}function be(){return Y.length?Y.map(t=>{const s="mcp__"+(P&&P.slug||"")+"__"+t.name,n=re[s],c=n&&n.mode||"inherit";return e("li",{key:s,class:"mcp__tools-row"},e("div",{class:"mcp__tools-name"},s),t.description?e("div",{class:"mcp__tools-desc"},t.description):null,e("div",{class:"row row--inline"},e("label",{class:"label"},"Authorization"),e("select",{class:"input",value:c,onChange:h=>fe(s,h.target.value)},e("option",{value:"inherit"},"Inherit (shared / server)"),e("option",{value:"off"},"Off"),e("option",{value:"ask"},"Ask"),e("option",{value:"allow"},"Allow"))))}):e("li",{class:"mcp__tools-empty"},P&&P.status==="ready"?"No tools reported by this server.":"Start the server to see its tools.")}function ge(t){if(!t||!t.trim())return{};const s={};for(const n of t.split(/\r?\n/)){const c=n.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);c&&(s[c[1]]=c[2])}return s}function ye(t){if(!t||!t.trim())return{};const s={};for(const n of t.split(/\r?\n/)){const c=n.match(/^([^:=\s][^:=]*?)\s*[:=]\s*(.*)$/);c&&(s[c[1].trim()]=c[2])}return s}async function we(){j(!0),u({text:"saving…",kind:"busy"});const t=a?m:f;if(!a&&t==="project"&&!r){u({text:"a project-scoped server needs an active project — switch to App or open a project first",kind:"error"}),j(!1);return}const s={projectDir:r,scope:t,transport:b,name:(A||"").trim(),command:b==="stdio"?(g||"").trim():"",url:b==="http"?(S||"").trim():"",oauth:b==="http"&&D.enabled?{enabled:!0,clientId:D.clientId.trim(),scope:D.scope.trim()}:null,args:b==="stdio"?x:[],cwd:b==="stdio"?(q||"").trim():""},n=i||"",c=U||"";if(de?s.env={}:n.trim()&&(s.env=ge(n)),he?s.headers={}:c.trim()&&(s.headers=ye(c)),!s.name){u({text:"name is required",kind:"error"}),j(!1);return}if(b==="stdio"&&!s.command){u({text:"command is required",kind:"error"}),j(!1);return}if(b==="http"&&!s.url){u({text:"URL is required",kind:"error"}),j(!1);return}let h;try{h=await C(a?"/api/mcp/servers/"+encodeURIComponent(a):"/api/mcp/servers",{method:a?"PATCH":"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)})}catch{u({text:"network error",kind:"error"}),j(!1);return}if(j(!1),h.status!==200&&h.status!==201){u({text:"HTTP "+h.status+(h.body&&h.body.error?": "+h.body.error:""),kind:"error"});return}u({text:"saved",kind:"success"}),X("settings/mcp"+N(r)+(l?"&from="+encodeURIComponent(l):""))}async function ke(){if(!a||!confirm("Delete this MCP server?"))return;R(!0),u({text:"deleting…",kind:"busy"});const t=r?"?projectDir="+encodeURIComponent(r):"";let s;try{s=await C("/api/mcp/servers/"+encodeURIComponent(a)+t,{method:"DELETE"})}catch{u({text:"network error",kind:"error"}),R(!1);return}if(s.status!==200){u({text:"HTTP "+s.status,kind:"error"}),R(!1);return}X("settings/mcp"+N(r)+(l?"&from="+encodeURIComponent(l):""))}ee(()=>{ve()},[a]);const Ce=a?"Edit MCP server":"Add MCP server",Ae="#/settings/mcp"+N(a?v:r)+(l?"&from="+encodeURIComponent(l):"");return e(K,null,e("div",{class:"view-head"},e("a",{href:Ae,class:"view-back","aria-label":"Back to MCP servers"},"←"),e("h2",{class:"view-title"},Ce)),e("section",null,a?e("p",{class:"hint hint--compact"},m==="app"?"Scope: app-wide — stored in the app store, visible to every project.":"Scope: project — stored in this project's .mcp.json so it can be committed with the repo."," Scope is set at creation; delete and re-add to move a server."):e("div",{class:"row"},e("label",{class:"label"},"Scope"),e("div",{class:"seg",role:"radiogroup","aria-label":"Server scope"},[{value:"app",label:"App (all projects)"},{value:"project",label:"This project"}].map(t=>e("label",{key:t.value,class:"seg__item"+(f===t.value?" seg__item--on":"")},e("input",{type:"radio",name:"mcp-add-scope",value:t.value,checked:f===t.value,onChange:()=>L(t.value)}),e("span",{class:"seg__pill"},t.label)))),f==="project"&&!r?e("span",{class:"hint hint--compact"},"No active project — open a chat in the project first, or pick App scope."):null),e("div",{class:"row"},e("label",{class:"label",for:"mcp-name"},"Name"),e("input",{class:"input",id:"mcp-name",type:"text",placeholder:"e.g. filesystem",value:A,onInput:t=>_(t.target.value)})),e("div",{class:"row"},e("label",{class:"label"},"Transport"),e("div",{class:"seg",role:"radiogroup","aria-label":"MCP transport"},[{value:"stdio",label:"Stdio"},{value:"http",label:"HTTP"}].map(t=>e("label",{key:t.value,class:"seg__item"+(b===t.value?" seg__item--on":"")},e("input",{type:"radio",name:"mcp-transport",value:t.value,checked:b===t.value,onChange:()=>Q(t.value)}),e("span",{class:"seg__pill"},t.label))))),b==="stdio"?e(K,null,e("div",{class:"row"},e("label",{class:"label",for:"mcp-command"},"Command"),e("input",{class:"input",id:"mcp-command",type:"text",placeholder:"node",value:g,onInput:t=>I(t.target.value)})),e(Se,{value:x,onChange:O}),e("div",{class:"row"},e("label",{class:"label",for:"mcp-env"},"Environment (one KEY=value per line)"),e("textarea",{class:"input",id:"mcp-env",rows:4,spellcheck:!1,placeholder:te,"aria-describedby":"mcp-env-hint",value:i,onInput:t=>k(t.target.value)}),e("span",{id:"mcp-env-hint",class:"hint hint--compact"},"Values are write-only and are never returned by the API. Leave blank to preserve existing values."),a&&Z.length?e("div",{class:"row row--inline",style:"margin-top:4px"},e("span",{class:"hint",style:"font-size:0.72rem;color:var(--muted);flex:1"},"Configured keys: "+Z.join(", ")),e("button",{class:"btn btn--small",type:"button",style:"color:var(--danger);border-color:var(--danger)",onClick:()=>{ue(!0),k(""),J("")}},"Clear all")):null),e("div",{class:"row"},e("label",{class:"label",for:"mcp-cwd"},"Working directory (optional, relative to project)"),e("input",{class:"input",id:"mcp-cwd",type:"text",placeholder:"tools/my-mcp",value:q,onInput:t=>B(t.target.value)}))):e(K,null,e("div",{class:"row"},e("label",{class:"label",for:"mcp-url"},"HTTP URL"),e("input",{class:"input",id:"mcp-url",type:"url",placeholder:"https://example.com/mcp",value:S,onInput:t=>T(t.target.value)})),e(je,{id:a,projectDir:r,saved:P,value:{...D,url:S},onChange:G}),e("div",{class:"row"},e("label",{class:"label",for:"mcp-headers"},"HTTP headers (one Name: value per line)"),e("textarea",{class:"input",id:"mcp-headers",rows:4,spellcheck:!1,placeholder:ne,"aria-describedby":"mcp-headers-hint",value:U,onInput:t=>y(t.target.value)}),e("span",{id:"mcp-headers-hint",class:"hint hint--compact"},"Header values are write-only and are never returned by the API. Leave blank to preserve existing values."),a&&$.length?e("div",{class:"row row--inline",style:"margin-top:4px"},e("span",{class:"hint",style:"font-size:0.72rem;color:var(--muted);flex:1"},"Configured keys: "+$.join(", ")),e("button",{class:"btn btn--small",type:"button",style:"color:var(--danger);border-color:var(--danger)",onClick:()=>{me(!0),y(""),V("")}},"Clear all")):null)),a&&m!=="app"&&r?e("div",{class:"row"},e("h3",{class:"mcp__tools-h"},"Discovered tools"),e("p",{class:"hint hint--compact"},'Per-tool authorization overrides. "Inherit" uses the server-level or shared fallback gate; a tool override wins for that call. ',e("span",{class:"settings-project__item-status","aria-live":"polite"},ie)),e("ul",{class:"mcp__tools-list"},be())):null,e("div",{class:"row row--actions"},e("button",{class:"btn btn--primary",type:"button",onClick:we,disabled:se},"Save"),a?e("button",{class:"btn btn--danger",type:"button",onClick:ke,disabled:oe},"Delete"):null),e("div",{class:"row"},e("span",{class:"status"+(z.kind?" status--"+z.kind:""),"aria-live":"polite"},z.text))))}export{Ee as SettingsMcpEditView};
@@ -1 +1 @@
1
- import{d as u,h as q,k as e,S as J,f as T,n as Z}from"./index-BGvI4n0T.js";import{p as I}from"./projectQS-D1cSZ7Gr.js";function E(p){const r=Math.min(100,Math.max(0,p||0));let i="reg-pop";return r>=70?i+=" reg-pop--high":r>=40?i+=" reg-pop--mid":i+=" reg-pop--low",e("span",{class:i,title:"Popularity: "+r+"/100"},e("span",{class:"reg-pop__bar",style:"width:"+r+"%"}),e("span",{class:"reg-pop__label"},r))}function K(p){const r=p&&p.server||{},i=Array.isArray(r.packages)?r.packages:[];if(!i.length)return null;const l=i[0],y=l.type||"uvx",f=Array.isArray(l.arguments)?l.arguments.map(n=>n.value||n).filter(Boolean):[],h={};if(Array.isArray(l.environmentVariables))for(const n of l.environmentVariables)n&&n.name&&n.default?h[n.name]=n.default:n&&n.name&&n.isRequired&&(h[n.name]="");const v=y==="uvx"?"uvx":y==="npx"?"npx":l.command||y,k=y==="uvx"||y==="npx"?[l.packageName||l.name||v].concat(f):f;return{command:v,args:k,env:h}}function G(p={}){const r=typeof p.projectDir=="string"?p.projectDir:"",i=typeof p.from=="string"?p.from:"",[l,y]=u([]),[f,h]=u({count:0,nextCursor:null}),[v,k]=u(""),[n,F]=u("popularity"),[P,B]=u("desc"),[b,x]=u(!1),[D,C]=u(""),[U,N]=u(null),[A,d]=u({message:"",type:""});async function w(s={}){const o=s.search!==void 0?s.search:v,a=s.cursor!==void 0?s.cursor:D,m=s.sortField!==void 0?s.sortField:n,g=s.sortDir!==void 0?s.sortDir:P;x(!0),d({message:"loading…",type:"busy"});const c=new URLSearchParams;o&&c.set("search",o),a&&c.set("cursor",a),c.set("sort",m),c.set("dir",g),c.set("limit","30");try{const t=await T("/api/mcp/registry?"+c.toString());if(t.status!==200){d({message:"Registry error: HTTP "+t.status+(t.body&&t.body.error?": "+t.body.error:""),type:"error"}),x(!1);return}y(t.body.servers||[]),h(t.body.metadata||{count:0,nextCursor:null}),C(a&&t.body.metadata&&t.body.metadata.nextCursor||""),d({message:(t.body.metadata&&t.body.metadata.count)+" servers found",type:"success"})}catch(t){d({message:"Network error: "+(t.message||t),type:"error"})}x(!1)}function R(){C(""),w({search:v,cursor:""})}function L(){f.nextCursor&&w({cursor:f.nextCursor})}function O(){w({cursor:""})}async function H(s){const o=s.server?s.server.name:s.name||"";if(!o)return;const a=o.includes("/")?o.split("/").pop():o,m=K(s);if(!m){d({message:"Cannot install: no package info for "+a,type:"error"});return}N(o),d({message:"Adding "+a+"…",type:"busy"});const g=r?"project":"app",c={projectDir:r||null,scope:g,transport:"stdio",name:a,command:m.command,args:m.args,env:m.env};try{const t=await T("/api/mcp/servers",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(c)});if(t.status===201||t.status===200){d({message:a+" added!",type:"success"});const _=t.body&&t.body.server&&t.body.server.id;_&&setTimeout(()=>{Z("settings/mcp/"+encodeURIComponent(_)+I(r)+"&scope="+g+(i?"&from="+encodeURIComponent(i):""))},600)}else d({message:"HTTP "+t.status+": "+(t.body&&t.body.error||"unknown"),type:"error"})}catch(t){d({message:"Network error: "+(t.message||t),type:"error"})}N(null)}q(()=>{w({})},[]);const V="#/settings/mcp"+I(r)+(i?"&from="+encodeURIComponent(i):"");return e(J,null,e("div",{class:"view-head"},e("a",{href:V,class:"view-back","aria-label":"Back to MCP servers"},"←"),e("h2",{class:"view-title"},"MCP Registry"+(r?" · "+r.split(/[/\\]/).pop():""))),e("section",null,e("p",{class:"hint hint--compact"},"Browse the ",e("a",{href:"https://registry.modelcontextprotocol.io",target:"_blank",rel:"noopener noreferrer"},"official MCP Registry"),". Servers are scored by update recency and package count. Tap a server to add it to ",r?"this project":"the app-wide list","."),e("div",{class:"row row--inline",style:"margin-bottom:8px; gap:8px; flex-wrap:wrap;"},e("input",{class:"input",style:"flex: 1 1 200px;",type:"text",placeholder:"Search servers by name…","aria-label":"Search MCP registry",value:v,onInput:s=>k(s.target.value),onKeyDown:s=>{s.key==="Enter"&&R()}}),e("button",{class:"btn",type:"button",onClick:R,disabled:b},"Search"),e("select",{class:"input",style:"width: auto;",value:n+"|"+P,onChange:s=>{const[o,a]=s.target.value.split("|");F(o),B(a),C(""),w({sortField:o,sortDir:a,cursor:""})},disabled:b},e("option",{value:"popularity|desc"},"Most popular"),e("option",{value:"popularity|asc"},"Least popular"),e("option",{value:"updatedAt|desc"},"Recently updated"),e("option",{value:"updatedAt|asc"},"Oldest updated"),e("option",{value:"name|asc"},"Name (A-Z)"),e("option",{value:"name|desc"},"Name (Z-A)"))),e("ul",{class:"reg-list","aria-label":"Registry servers"},!l.length&&!b?e("li",{class:"mcp__empty"},"No servers found. Try a different search term."):l.map(s=>{const o=s&&s.server||{},a=o.name||s.name||"",m=a.includes("/")?a.split("/").pop():a||"unknown",g=o.description||"",c=s._meta&&s._meta["io.modelcontextprotocol.registry/official"]||{},t=c.status||"active",_=c.updatedAt?new Date(c.updatedAt).toLocaleDateString():null,S=(Array.isArray(o.packages)?o.packages:[]).length,j=s.popularity?s.popularity.score:0,M=U===a;return e("li",{key:a,class:"reg-row"},e("div",{class:"reg-row__head"},e("div",{class:"reg-row__name"},m,e("span",{class:"reg-row__ver"},c.isLatest!==void 0?"latest":""),t!=="active"?e("span",{class:"reg-row__status reg-row__status--"+t},t):null),E(j)),g?e("div",{class:"reg-row__desc"},g.slice(0,200)+(g.length>200?"…":"")):null,e("div",{class:"reg-row__meta"},e("span",{class:"reg-row__meta-item"},a),S>0?e("span",{class:"reg-row__meta-item"},S+" package"+(S===1?"":"s")):null,_?e("span",{class:"reg-row__meta-item"},"Updated "+_):null),e("div",{class:"reg-row__actions"},e("button",{class:"btn btn--small"+(j>=70?" btn--primary":""),type:"button",disabled:M||b,onClick:()=>H(s)},M?"Adding…":"Add to "+(r?"project":"app"))))})),e("div",{class:"page-bar"},e("span",{class:`status page-bar__status${A.type?" status--"+A.type:""}`,"aria-live":"polite"},A.message),e("button",{class:"btn btn--small",type:"button",disabled:!D||b,onClick:O,"aria-label":"First page"},"⇤"),e("button",{class:"btn btn--small",type:"button",disabled:!f.nextCursor||b,onClick:L,"aria-label":"Next page"},"Next →"))))}export{G as SettingsMcpRegistryView};
1
+ import{d as u,h as q,k as e,S as J,f as T,n as Z}from"./index-BDkCsON6.js";import{p as I}from"./projectQS-D1cSZ7Gr.js";function E(p){const r=Math.min(100,Math.max(0,p||0));let i="reg-pop";return r>=70?i+=" reg-pop--high":r>=40?i+=" reg-pop--mid":i+=" reg-pop--low",e("span",{class:i,title:"Popularity: "+r+"/100"},e("span",{class:"reg-pop__bar",style:"width:"+r+"%"}),e("span",{class:"reg-pop__label"},r))}function K(p){const r=p&&p.server||{},i=Array.isArray(r.packages)?r.packages:[];if(!i.length)return null;const l=i[0],y=l.type||"uvx",f=Array.isArray(l.arguments)?l.arguments.map(n=>n.value||n).filter(Boolean):[],h={};if(Array.isArray(l.environmentVariables))for(const n of l.environmentVariables)n&&n.name&&n.default?h[n.name]=n.default:n&&n.name&&n.isRequired&&(h[n.name]="");const v=y==="uvx"?"uvx":y==="npx"?"npx":l.command||y,k=y==="uvx"||y==="npx"?[l.packageName||l.name||v].concat(f):f;return{command:v,args:k,env:h}}function G(p={}){const r=typeof p.projectDir=="string"?p.projectDir:"",i=typeof p.from=="string"?p.from:"",[l,y]=u([]),[f,h]=u({count:0,nextCursor:null}),[v,k]=u(""),[n,F]=u("popularity"),[P,B]=u("desc"),[b,x]=u(!1),[D,C]=u(""),[U,N]=u(null),[A,d]=u({message:"",type:""});async function w(s={}){const o=s.search!==void 0?s.search:v,a=s.cursor!==void 0?s.cursor:D,m=s.sortField!==void 0?s.sortField:n,g=s.sortDir!==void 0?s.sortDir:P;x(!0),d({message:"loading…",type:"busy"});const c=new URLSearchParams;o&&c.set("search",o),a&&c.set("cursor",a),c.set("sort",m),c.set("dir",g),c.set("limit","30");try{const t=await T("/api/mcp/registry?"+c.toString());if(t.status!==200){d({message:"Registry error: HTTP "+t.status+(t.body&&t.body.error?": "+t.body.error:""),type:"error"}),x(!1);return}y(t.body.servers||[]),h(t.body.metadata||{count:0,nextCursor:null}),C(a&&t.body.metadata&&t.body.metadata.nextCursor||""),d({message:(t.body.metadata&&t.body.metadata.count)+" servers found",type:"success"})}catch(t){d({message:"Network error: "+(t.message||t),type:"error"})}x(!1)}function R(){C(""),w({search:v,cursor:""})}function L(){f.nextCursor&&w({cursor:f.nextCursor})}function O(){w({cursor:""})}async function H(s){const o=s.server?s.server.name:s.name||"";if(!o)return;const a=o.includes("/")?o.split("/").pop():o,m=K(s);if(!m){d({message:"Cannot install: no package info for "+a,type:"error"});return}N(o),d({message:"Adding "+a+"…",type:"busy"});const g=r?"project":"app",c={projectDir:r||null,scope:g,transport:"stdio",name:a,command:m.command,args:m.args,env:m.env};try{const t=await T("/api/mcp/servers",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(c)});if(t.status===201||t.status===200){d({message:a+" added!",type:"success"});const _=t.body&&t.body.server&&t.body.server.id;_&&setTimeout(()=>{Z("settings/mcp/"+encodeURIComponent(_)+I(r)+"&scope="+g+(i?"&from="+encodeURIComponent(i):""))},600)}else d({message:"HTTP "+t.status+": "+(t.body&&t.body.error||"unknown"),type:"error"})}catch(t){d({message:"Network error: "+(t.message||t),type:"error"})}N(null)}q(()=>{w({})},[]);const V="#/settings/mcp"+I(r)+(i?"&from="+encodeURIComponent(i):"");return e(J,null,e("div",{class:"view-head"},e("a",{href:V,class:"view-back","aria-label":"Back to MCP servers"},"←"),e("h2",{class:"view-title"},"MCP Registry"+(r?" · "+r.split(/[/\\]/).pop():""))),e("section",null,e("p",{class:"hint hint--compact"},"Browse the ",e("a",{href:"https://registry.modelcontextprotocol.io",target:"_blank",rel:"noopener noreferrer"},"official MCP Registry"),". Servers are scored by update recency and package count. Tap a server to add it to ",r?"this project":"the app-wide list","."),e("div",{class:"row row--inline",style:"margin-bottom:8px; gap:8px; flex-wrap:wrap;"},e("input",{class:"input",style:"flex: 1 1 200px;",type:"text",placeholder:"Search servers by name…","aria-label":"Search MCP registry",value:v,onInput:s=>k(s.target.value),onKeyDown:s=>{s.key==="Enter"&&R()}}),e("button",{class:"btn",type:"button",onClick:R,disabled:b},"Search"),e("select",{class:"input",style:"width: auto;",value:n+"|"+P,onChange:s=>{const[o,a]=s.target.value.split("|");F(o),B(a),C(""),w({sortField:o,sortDir:a,cursor:""})},disabled:b},e("option",{value:"popularity|desc"},"Most popular"),e("option",{value:"popularity|asc"},"Least popular"),e("option",{value:"updatedAt|desc"},"Recently updated"),e("option",{value:"updatedAt|asc"},"Oldest updated"),e("option",{value:"name|asc"},"Name (A-Z)"),e("option",{value:"name|desc"},"Name (Z-A)"))),e("ul",{class:"reg-list","aria-label":"Registry servers"},!l.length&&!b?e("li",{class:"mcp__empty"},"No servers found. Try a different search term."):l.map(s=>{const o=s&&s.server||{},a=o.name||s.name||"",m=a.includes("/")?a.split("/").pop():a||"unknown",g=o.description||"",c=s._meta&&s._meta["io.modelcontextprotocol.registry/official"]||{},t=c.status||"active",_=c.updatedAt?new Date(c.updatedAt).toLocaleDateString():null,S=(Array.isArray(o.packages)?o.packages:[]).length,j=s.popularity?s.popularity.score:0,M=U===a;return e("li",{key:a,class:"reg-row"},e("div",{class:"reg-row__head"},e("div",{class:"reg-row__name"},m,e("span",{class:"reg-row__ver"},c.isLatest!==void 0?"latest":""),t!=="active"?e("span",{class:"reg-row__status reg-row__status--"+t},t):null),E(j)),g?e("div",{class:"reg-row__desc"},g.slice(0,200)+(g.length>200?"…":"")):null,e("div",{class:"reg-row__meta"},e("span",{class:"reg-row__meta-item"},a),S>0?e("span",{class:"reg-row__meta-item"},S+" package"+(S===1?"":"s")):null,_?e("span",{class:"reg-row__meta-item"},"Updated "+_):null),e("div",{class:"reg-row__actions"},e("button",{class:"btn btn--small"+(j>=70?" btn--primary":""),type:"button",disabled:M||b,onClick:()=>H(s)},M?"Adding…":"Add to "+(r?"project":"app"))))})),e("div",{class:"page-bar"},e("span",{class:`status page-bar__status${A.type?" status--"+A.type:""}`,"aria-live":"polite"},A.message),e("button",{class:"btn btn--small",type:"button",disabled:!D||b,onClick:O,"aria-label":"First page"},"⇤"),e("button",{class:"btn btn--small",type:"button",disabled:!f.nextCursor||b,onClick:L,"aria-label":"Next page"},"Next →"))))}export{G as SettingsMcpRegistryView};
@@ -1 +1 @@
1
- import{d as u,h as C,k as s,C as g,E as h,F as c,S as z,l as N,f as w,G as T,H as q,I as E,s as I}from"./index-BGvI4n0T.js";const _=Object.freeze({status:!0,authorization:!0,quickActions:!0,login:!0});function D(l){const t=l||{};return{status:t.status!==void 0?t.status===!0:t.progress!==!1&&t.completion!==!1&&t.errors!==!1,authorization:t.authorization!==void 0?t.authorization===!0:t.askUser!==!1&&t.toolAuthorization!==!1,quickActions:t.quickActions!==!1,login:t.login!==void 0?t.login===!0:_.login}}function x(){const[l,t]=u(_),[f,o]=u(!0),[v,a]=u("Checking this browser…"),[y,i]=u("busy"),[r,k]=u(null);async function m(){o(!0);try{const e=await N({force:!0});t({..._,...D(e.app&&e.app.notifications)});const n=await w("/api/push/config");if(n.status!==200)throw new Error(n.body&&n.body.error||"Push key configuration failed");k(n.body),g.value&&await T(),a(g.value?h.value==="denied"?"Permission is blocked in browser settings.":c.value?"Notifications are enabled on this browser.":"Notifications are not enabled on this browser.":"Web Push is not supported by this browser."),i(c.value?"success":"")}catch(e){a("Could not load notification settings: "+e.message),i("error")}o(!1)}async function P(){o(!0);try{if(c.value)await q(),a("Notifications disabled on this browser."),i("success");else{const e=await E();a(e?"Notifications enabled on this browser.":h.value==="denied"?"Permission is blocked. Allow notifications in browser settings, then try again.":"Could not enable notifications."),i(e?"success":"error")}}finally{o(!1)}}async function A(e,n){const d={...l,[e]:n};t(d),o(!0);try{await I({notifications:d}),a("Notification preferences saved."),i("success")}catch(b){t(l),a("Save failed: "+b.message),i("error")}o(!1)}async function S(){o(!0);try{const e=await w("/api/push/test",{method:"POST"});if(e.status!==200)throw new Error("HTTP "+e.status);a("Test notification sent."),i("success")}catch(e){a("Test failed: "+e.message),i("error")}o(!1)}C(()=>{m()},[]);function p(e,n,d){return s("label",{class:"group__row settings-notifications__event"},s("span",{class:"group__row-body"},s("span",{class:"group__row-label"},n),s("span",{class:"group__row-detail"},d)),s("input",{type:"checkbox",class:"checkbox",checked:l[e]===!0,disabled:f,onChange:b=>A(e,b.currentTarget.checked)}))}return s(z,null,s("div",{class:"view-head"},s("a",{href:"#/settings",class:"view-back","aria-label":"Back to settings"},"←"),s("h2",{class:"view-title"},"Notifications")),s("section",{class:"settings-notifications"},s("p",{class:"hint hint--compact"},"Follow a running chat with one ASCII status notification. Authorization alerts stay visible when the model needs an answer or tool approval. The status bar is sized to this device — a phone shows a compact bar, a desktop a longer one."),s("div",{class:"group"},s("div",{class:"group__title"},"This browser"),s("div",{class:"group__list"},s("div",{class:"group__row"},s("span",{class:"group__row-body"},s("span",{class:"group__row-label"},"Web notifications"),s("span",{class:"group__row-detail"},g.value?h.value==="denied"?"Blocked by browser":c.value?"Enabled":"Disabled":"Not supported")),g.value?s("button",{type:"button",class:"btn btn--small"+(c.value?" btn--danger":" btn--primary"),disabled:f||h.value==="denied",onClick:P},c.value?"Disable":"Enable"):null)),s("div",{class:"settings-notifications__actions"},s("button",{type:"button",class:"btn",disabled:f||!c.value,onClick:S},"Send test notification")),s("p",{class:"status","data-state":y,"aria-live":"polite"},v)),s("div",{class:"group"},s("div",{class:"group__title"},"Server configuration"),s("div",{class:"group__list"},s("div",{class:"group__row"},s("span",{class:"group__row-body"},s("span",{class:"group__row-label"},"Served origin"),s("span",{class:"group__row-detail settings-notifications__value"},r&&r.origin?r.origin:window.location.origin))),s("div",{class:"group__row"},s("span",{class:"group__row-body"},s("span",{class:"group__row-label"},"Web Push keys"),s("span",{class:"group__row-detail"},r&&r.privateKeyConfigured?"Configured automatically":"Not configured"))),s("div",{class:"group__row"},s("span",{class:"group__row-body"},s("span",{class:"group__row-label"},"VAPID contact"),s("span",{class:"group__row-detail settings-notifications__value"},r?r.subject:"Checking…")))),s("p",{class:"hint hint--compact"},"Keys are generated once and reused. iPhone and iPad use these standard Web Push keys through Apple Push Notification service; Apple developer keys are not required.")),s("div",{class:"group"},s("div",{class:"group__title"},"Notification types"),s("div",{class:"group__list"},p("status","ASCII chat status","Progress, completion, and errors. Bar sized to this device."),p("authorization","Authorization","Questions and tool approvals."),p("quickActions","Authorization actions","Answer, allow once, or deny."),p("login","Sign-in alerts","A password or passkey sign-in to this server."))),s("p",{class:"hint hint--compact"},"When the target chat is already focused, the service worker suppresses its OS notification. Longer questions and multi-select answers open the full chat. On iPhone and iPad, install mouaif to the Home Screen before enabling notifications.")))}export{x as SettingsNotificationsView};
1
+ import{d as u,h as C,k as s,C as g,E as h,F as c,S as z,l as N,f as w,G as T,H as q,I as E,s as I}from"./index-BDkCsON6.js";const _=Object.freeze({status:!0,authorization:!0,quickActions:!0,login:!0});function D(l){const t=l||{};return{status:t.status!==void 0?t.status===!0:t.progress!==!1&&t.completion!==!1&&t.errors!==!1,authorization:t.authorization!==void 0?t.authorization===!0:t.askUser!==!1&&t.toolAuthorization!==!1,quickActions:t.quickActions!==!1,login:t.login!==void 0?t.login===!0:_.login}}function x(){const[l,t]=u(_),[f,o]=u(!0),[v,a]=u("Checking this browser…"),[y,i]=u("busy"),[r,k]=u(null);async function m(){o(!0);try{const e=await N({force:!0});t({..._,...D(e.app&&e.app.notifications)});const n=await w("/api/push/config");if(n.status!==200)throw new Error(n.body&&n.body.error||"Push key configuration failed");k(n.body),g.value&&await T(),a(g.value?h.value==="denied"?"Permission is blocked in browser settings.":c.value?"Notifications are enabled on this browser.":"Notifications are not enabled on this browser.":"Web Push is not supported by this browser."),i(c.value?"success":"")}catch(e){a("Could not load notification settings: "+e.message),i("error")}o(!1)}async function P(){o(!0);try{if(c.value)await q(),a("Notifications disabled on this browser."),i("success");else{const e=await E();a(e?"Notifications enabled on this browser.":h.value==="denied"?"Permission is blocked. Allow notifications in browser settings, then try again.":"Could not enable notifications."),i(e?"success":"error")}}finally{o(!1)}}async function A(e,n){const d={...l,[e]:n};t(d),o(!0);try{await I({notifications:d}),a("Notification preferences saved."),i("success")}catch(b){t(l),a("Save failed: "+b.message),i("error")}o(!1)}async function S(){o(!0);try{const e=await w("/api/push/test",{method:"POST"});if(e.status!==200)throw new Error("HTTP "+e.status);a("Test notification sent."),i("success")}catch(e){a("Test failed: "+e.message),i("error")}o(!1)}C(()=>{m()},[]);function p(e,n,d){return s("label",{class:"group__row settings-notifications__event"},s("span",{class:"group__row-body"},s("span",{class:"group__row-label"},n),s("span",{class:"group__row-detail"},d)),s("input",{type:"checkbox",class:"checkbox",checked:l[e]===!0,disabled:f,onChange:b=>A(e,b.currentTarget.checked)}))}return s(z,null,s("div",{class:"view-head"},s("a",{href:"#/settings",class:"view-back","aria-label":"Back to settings"},"←"),s("h2",{class:"view-title"},"Notifications")),s("section",{class:"settings-notifications"},s("p",{class:"hint hint--compact"},"Follow a running chat with one ASCII status notification. Authorization alerts stay visible when the model needs an answer or tool approval. The status bar is sized to this device — a phone shows a compact bar, a desktop a longer one."),s("div",{class:"group"},s("div",{class:"group__title"},"This browser"),s("div",{class:"group__list"},s("div",{class:"group__row"},s("span",{class:"group__row-body"},s("span",{class:"group__row-label"},"Web notifications"),s("span",{class:"group__row-detail"},g.value?h.value==="denied"?"Blocked by browser":c.value?"Enabled":"Disabled":"Not supported")),g.value?s("button",{type:"button",class:"btn btn--small"+(c.value?" btn--danger":" btn--primary"),disabled:f||h.value==="denied",onClick:P},c.value?"Disable":"Enable"):null)),s("div",{class:"settings-notifications__actions"},s("button",{type:"button",class:"btn",disabled:f||!c.value,onClick:S},"Send test notification")),s("p",{class:"status","data-state":y,"aria-live":"polite"},v)),s("div",{class:"group"},s("div",{class:"group__title"},"Server configuration"),s("div",{class:"group__list"},s("div",{class:"group__row"},s("span",{class:"group__row-body"},s("span",{class:"group__row-label"},"Served origin"),s("span",{class:"group__row-detail settings-notifications__value"},r&&r.origin?r.origin:window.location.origin))),s("div",{class:"group__row"},s("span",{class:"group__row-body"},s("span",{class:"group__row-label"},"Web Push keys"),s("span",{class:"group__row-detail"},r&&r.privateKeyConfigured?"Configured automatically":"Not configured"))),s("div",{class:"group__row"},s("span",{class:"group__row-body"},s("span",{class:"group__row-label"},"VAPID contact"),s("span",{class:"group__row-detail settings-notifications__value"},r?r.subject:"Checking…")))),s("p",{class:"hint hint--compact"},"Keys are generated once and reused. iPhone and iPad use these standard Web Push keys through Apple Push Notification service; Apple developer keys are not required.")),s("div",{class:"group"},s("div",{class:"group__title"},"Notification types"),s("div",{class:"group__list"},p("status","ASCII chat status","Progress, completion, and errors. Bar sized to this device."),p("authorization","Authorization","Questions and tool approvals."),p("quickActions","Authorization actions","Answer, allow once, or deny."),p("login","Sign-in alerts","A password or passkey sign-in to this server."))),s("p",{class:"hint hint--compact"},"When the target chat is already focused, the service worker suppresses its OS notification. Longer questions and multi-select answers open the full chat. On iPhone and iPad, install mouaif to the Home Screen before enabling notifications.")))}export{x as SettingsNotificationsView};
@@ -1 +1 @@
1
- import{d as n,h as N,k as e,S as A,l as _,s as P,f}from"./index-BGvI4n0T.js";function j(){const[l,a]=n(""),[h,g]=n([]),[v,k]=n([]),[u,p]=n(!1),[c,s]=n({text:"",kind:""}),[m,b]=n({});async function S(){try{const t=await _({force:!0}),i=t.app&&t.app.modelPricing||{};b(JSON.parse(JSON.stringify(i))),a(JSON.stringify(i,null,2)),await w()}catch(t){s({text:"load failed: "+t.message,kind:"error"})}}async function w(){try{const t=await f("/api/usage/builtin");t.status===200&&t.body&&Array.isArray(t.body.ids)&&g(t.body.ids)}catch{}try{const t=await f("/api/ai/models-all");t.status===200&&t.body&&Array.isArray(t.body.ids)&&k(t.body.ids)}catch{}}function y({title:t,ids:i}){return i.length?e("li",{class:"pricing__group"},e("div",{class:"pricing__group-title"},t),e("ul",{class:"pricing__ids"},i.map(r=>e("li",{key:r},e("code",null,r),e("button",{type:"button",class:"btn btn--small pricing__copy",title:"Copy the id to the table above (as a placeholder entry)",onClick:()=>x(r)},"copy"))))):null}function x(t){let i;try{i=JSON.parse(l||"{}")}catch{s({text:"fix JSON syntax first",kind:"error"});return}(!i||typeof i!="object"||Array.isArray(i))&&(i={}),!i[t]&&(i[t]={inputPer1K:0,outputPer1K:0},a(JSON.stringify(i,null,2)),s({text:"added "+t+" — set its prices and Save",kind:"success"}))}async function O(){let t;try{t=JSON.parse(l||"{}")}catch(i){s({text:"invalid JSON: "+i.message,kind:"error"});return}if(!t||typeof t!="object"||Array.isArray(t)){s({text:"table must be a JSON object",kind:"error"});return}for(const[i,r]of Object.entries(t)){if(!r||typeof r!="object"||Array.isArray(r)){s({text:'entry "'+i+'" must be an object',kind:"error"});return}for(const o of["inputPer1K","outputPer1K"]){if(r[o]===void 0)continue;const d=Number(r[o]);if(!isFinite(d)||d<0){s({text:'entry "'+i+'" has invalid '+o+" (must be a non-negative number)",kind:"error"});return}r[o]=d}}p(!0),s({text:"saving…",kind:"busy"});try{await P({modelPricing:t}),b(JSON.parse(JSON.stringify(t))),s({text:"saved.",kind:"success"})}catch(i){s({text:"save failed: "+i.message,kind:"error"})}p(!1)}function J(){a(JSON.stringify(m,null,2)),s({text:"reverted.",kind:"success"})}return N(()=>{S()},[]),e(A,null,e("div",{class:"view-head"},e("a",{href:"#/settings",class:"view-back","aria-label":"Back to settings"},"←"),e("h2",{class:"view-title"},"Model pricing")),e("section",null,e("p",{class:"hint hint--compact"},"Override the cost-per-1 000 tokens (USD) for any model id. The chat UI uses this to render the per-turn cost line. Verify prices with your provider — the built-in defaults may be outdated."),e("p",{class:"hint hint--compact"},"This is the app-level fallback. A per-model pricing block on a project model record wins over this table; an id missing here falls back to the built-in defaults, then to --."),e("div",{class:"row"},e("label",{class:"label",for:"pricing-table"},"Pricing table (JSON)"),e("textarea",{value:l,onInput:t=>a(t.target.value),class:"input pricing__editor",id:"pricing-table",rows:10,spellcheck:!1}),e("p",{class:"hint"},"Example: ",e("code",null,'{ "gpt-4o-mini": { "inputPer1K": 0.00015, "outputPer1K": 0.00060 } }'))),e("div",{class:"row row--actions"},e("button",{class:"btn btn--primary",type:"button",onClick:O,disabled:u},"Save"),e("button",{class:"btn",type:"button",onClick:J,disabled:u},"Revert"),e("span",{class:"status"+(c.kind?" status--"+c.kind:""),"aria-live":"polite"},c.text)),e("h3",null,"Known model ids"),e("p",{class:"hint hint--compact"},"The chat UI renders a per-turn cost only for ids that resolve through the built-in table or this override. Tap copy to add an id to your table."),e("ul",{class:"pricing__known","aria-label":"Known model ids"},e(y,{title:"Built-in defaults",ids:h}),e(y,{title:"Models you have configured",ids:v}))))}export{j as SettingsPricingView};
1
+ import{d as n,h as N,k as e,S as A,l as _,s as P,f}from"./index-BDkCsON6.js";function j(){const[l,a]=n(""),[h,g]=n([]),[v,k]=n([]),[u,p]=n(!1),[c,s]=n({text:"",kind:""}),[m,b]=n({});async function S(){try{const t=await _({force:!0}),i=t.app&&t.app.modelPricing||{};b(JSON.parse(JSON.stringify(i))),a(JSON.stringify(i,null,2)),await w()}catch(t){s({text:"load failed: "+t.message,kind:"error"})}}async function w(){try{const t=await f("/api/usage/builtin");t.status===200&&t.body&&Array.isArray(t.body.ids)&&g(t.body.ids)}catch{}try{const t=await f("/api/ai/models-all");t.status===200&&t.body&&Array.isArray(t.body.ids)&&k(t.body.ids)}catch{}}function y({title:t,ids:i}){return i.length?e("li",{class:"pricing__group"},e("div",{class:"pricing__group-title"},t),e("ul",{class:"pricing__ids"},i.map(r=>e("li",{key:r},e("code",null,r),e("button",{type:"button",class:"btn btn--small pricing__copy",title:"Copy the id to the table above (as a placeholder entry)",onClick:()=>x(r)},"copy"))))):null}function x(t){let i;try{i=JSON.parse(l||"{}")}catch{s({text:"fix JSON syntax first",kind:"error"});return}(!i||typeof i!="object"||Array.isArray(i))&&(i={}),!i[t]&&(i[t]={inputPer1K:0,outputPer1K:0},a(JSON.stringify(i,null,2)),s({text:"added "+t+" — set its prices and Save",kind:"success"}))}async function O(){let t;try{t=JSON.parse(l||"{}")}catch(i){s({text:"invalid JSON: "+i.message,kind:"error"});return}if(!t||typeof t!="object"||Array.isArray(t)){s({text:"table must be a JSON object",kind:"error"});return}for(const[i,r]of Object.entries(t)){if(!r||typeof r!="object"||Array.isArray(r)){s({text:'entry "'+i+'" must be an object',kind:"error"});return}for(const o of["inputPer1K","outputPer1K"]){if(r[o]===void 0)continue;const d=Number(r[o]);if(!isFinite(d)||d<0){s({text:'entry "'+i+'" has invalid '+o+" (must be a non-negative number)",kind:"error"});return}r[o]=d}}p(!0),s({text:"saving…",kind:"busy"});try{await P({modelPricing:t}),b(JSON.parse(JSON.stringify(t))),s({text:"saved.",kind:"success"})}catch(i){s({text:"save failed: "+i.message,kind:"error"})}p(!1)}function J(){a(JSON.stringify(m,null,2)),s({text:"reverted.",kind:"success"})}return N(()=>{S()},[]),e(A,null,e("div",{class:"view-head"},e("a",{href:"#/settings",class:"view-back","aria-label":"Back to settings"},"←"),e("h2",{class:"view-title"},"Model pricing")),e("section",null,e("p",{class:"hint hint--compact"},"Override the cost-per-1 000 tokens (USD) for any model id. The chat UI uses this to render the per-turn cost line. Verify prices with your provider — the built-in defaults may be outdated."),e("p",{class:"hint hint--compact"},"This is the app-level fallback. A per-model pricing block on a project model record wins over this table; an id missing here falls back to the built-in defaults, then to --."),e("div",{class:"row"},e("label",{class:"label",for:"pricing-table"},"Pricing table (JSON)"),e("textarea",{value:l,onInput:t=>a(t.target.value),class:"input pricing__editor",id:"pricing-table",rows:10,spellcheck:!1}),e("p",{class:"hint"},"Example: ",e("code",null,'{ "gpt-4o-mini": { "inputPer1K": 0.00015, "outputPer1K": 0.00060 } }'))),e("div",{class:"row row--actions"},e("button",{class:"btn btn--primary",type:"button",onClick:O,disabled:u},"Save"),e("button",{class:"btn",type:"button",onClick:J,disabled:u},"Revert"),e("span",{class:"status"+(c.kind?" status--"+c.kind:""),"aria-live":"polite"},c.text)),e("h3",null,"Known model ids"),e("p",{class:"hint hint--compact"},"The chat UI renders a per-turn cost only for ids that resolve through the built-in table or this override. Tap copy to add an id to your table."),e("ul",{class:"pricing__known","aria-label":"Known model ids"},e(y,{title:"Built-in defaults",ids:h}),e(y,{title:"Models you have configured",ids:v}))))}export{j as SettingsPricingView};
@@ -1,4 +1,4 @@
1
- import{k as t,m as Qs,o as Xs,d as n,h as Ys,b as Ee,S as At,W as Zs,P as $s,q as to,f as u,r as eo,t as so,v as oo,n as ao,w as M,x as I,M as io,y as ro}from"./index-BGvI4n0T.js";import{A as no}from"./AgentFilePicker-CcKLJorU.js";import{a as lo}from"./agentNavigation-BiiCpFz5.js";function R(j){const v={class:"settings-project__section-icon settings-project__section-icon--"+j,"aria-hidden":"true"};switch(j){case"general":return t("span",v,t("span",{class:"ico-sliders"}));case"chat":return t("span",v,t("span",{class:"ico-chat"}));case"tools":return t("span",v,t("span",{class:"ico-wrench"}));case"files":return t("span",v,t("span",{class:"ico-doc"}));case"agents":return t("span",v,t("span",{class:"ico-people"}));case"skills":return t("span",v,t("span",{class:"ico-spark"}));case"more":return t("span",v,t("span",{class:"ico-grid"}));default:return t("span",v)}}function z(j,v,U,tt){return t(Xs,{tool:j,name:j,mode:v,namePrefix:"sp",modes:tt||Qs,onPick:nt=>U(nt)})}function fo({projectDir:j,chatId:v,from:U="",page:tt="main"}={}){const[nt,D]=n({text:"",state:""}),[Ne,Kt]=n("…"),[Ie,qt]=n(""),[ze,et]=n("Following the app default until you change it here"),[Tt,Qt]=n("average"),[lt,Xt]=n("tree"),[Je,Ot]=n(""),Be={"very-small":{label:"Very small — quarter cap"},average:{label:"Average — standard cap (default)"},full:{label:"Full — 4× cap"},extensive:{label:"Extensive — never truncate"}},Yt={json:{label:"Full JSON — raw structured result"},tree:{label:"Hierarchical — indented tree"}};function Ct(e){return Object.prototype.hasOwnProperty.call(Be,e)?e:"average"}function ct(e){return Object.prototype.hasOwnProperty.call(Yt,e)?e:"tree"}const[He,Le]=n(!!(v&&v.trim())),[Zt,pt]=n(!1),[We,T]=n(""),[Ge,Pt]=n(!1),[Ve,xt]=n(""),[ut,$t]=n({mode:"ask",allowlist:[]}),[K,Mt]=n({mode:"ask",allowlist:[]}),[Rt,dt]=n({}),[gt,te]=n({mode:"ask",allowlist:[]}),[ft,ee]=n({mode:"ask",allowlist:[]}),[ht,se]=n({mode:"ask",allowlist:[]}),[mt,oe]=n({mode:"ask",allowlist:[]}),[_t,ae]=n({mode:"ask",allowlist:[]}),[Ut,ie]=n("ask"),[re,ne]=n(""),[le,st]=n(""),[ce,pe]=n(""),[ue,de]=n(""),[ge,fe]=n(""),[he,Ke]=n(""),[me,qe]=n(""),[_e,Dt]=n(""),[vt,Qe]=n([]),[Xe,Ye]=n(""),[Ze,Ft]=n(!1),[O,$e]=n(null),[ts,Et]=n(!1),[es,J]=n(""),[ve,ss]=n((v||"").trim()),[F,q]=n({mode:"ask",allowlist:[],servers:{},tools:{}}),[co,B]=n(""),[os,as]=n([]),[bt,be]=n(!1),[Nt,It]=n(""),[is,ot]=n(""),[rs,jt]=n(!1),[je,we]=n(!0),[ye,ns]=n(""),[ls,zt]=n("—"),[cs,Jt]=n("—"),[ke,ps]=n([]),[Se,H]=n("{}"),[us,Bt]=n(!0),[ds,gs]=n(!0),[fs,L]=n(""),[hs,ms]=n(""),[W,Ae]=n(!1),[_s,wt]=n(""),[at,it]=n({}),Ht=Array.isArray(at.hideFileContent)?at.hideFileContent.length:0,[vs,bs]=n(""),[js]=n((v||"").trim());function d(){return vs}function f(){return js}function k(e){const s=d()||j||"",o="projectDir="+encodeURIComponent(s),a=U;return(f()?o+"&chatId="+encodeURIComponent(f()):o)+(a?"&from="+encodeURIComponent(a):"")}async function Lt(e){const s=(e||"").trim();if(!s){D({text:"no project selected",state:"error"});return}bs(s),D({text:"loading…",state:"busy"});const[o,a]=await Promise.all([u("/api/settings/project?projectDir="+encodeURIComponent(s)),u("/api/settings/resolved?projectDir="+encodeURIComponent(s))]);if(o.status!==200){D({text:"project: HTTP "+o.status+(o.body&&o.body.error?" "+o.body.error:""),state:"error"});return}const i=o.body.project||{};it(i),eo(s),Kt(o.body.path||s),Ae(o.body.dbBacked===!0),qt(i.promptSize&&String(i.promptSize)||""),et("");const c=a.status===200&&a.body&&a.body.resolved||{},g=c&&c.toolOutput&&typeof c.toolOutput=="object"?c.toolOutput:{};if(Qt(Ct(g&&g.size)),Xt(ct(g&&g.structure)),Ot(""),Le(!!f()),T(""),xt(""),f())try{const r=await u("/api/chats/"+encodeURIComponent(f())+"?projectDir="+encodeURIComponent(s));if(r.status===200&&r.body&&r.body.chat){const p=r.body.chat.trace===!0;pt(p),T(p?"trace on":"trace off"),Pt(!0)}else T("chat not found"),Pt(!1)}catch{T("failed to load chat"),Pt(!1)}else T("Open from a chat to edit trace.");ne(""),st(""),pe(""),de(""),fe(""),Dt("");try{const r=await u("/api/tools/authorization?projectDir="+encodeURIComponent(s)),p=r.status===200&&r.body.tools&&r.body.tools.shell;$t({mode:p&&p.mode||"ask",allowlist:p&&Array.isArray(p.allowlist)?p.allowlist:[]});const w=r.status===200&&r.body.tools&&r.body.tools.file;Mt({mode:w&&w.mode||"ask",allowlist:w&&Array.isArray(w.allowlist)?w.allowlist:[]}),dt(Object.fromEntries(["read_file","list_files","search_files","write_file","edit_file"].map(x=>[x,r.status===200&&r.body.tools&&r.body.tools[x]])));const y=r.status===200&&r.body.tools&&r.body.tools.subagent;te({mode:y&&y.mode||"ask",allowlist:y&&Array.isArray(y.allowlist)?y.allowlist:[]});const C=r.status===200&&r.body.tools&&r.body.tools.report_progress;ee({mode:C&&C.mode||"ask",allowlist:C&&Array.isArray(C.allowlist)?C.allowlist:[]});const P=r.status===200&&r.body.tools&&r.body.tools.task;se({mode:P&&P.mode||"ask",allowlist:P&&Array.isArray(P.allowlist)?P.allowlist:[]});const l=r.status===200&&r.body.tools&&r.body.tools.webpreview;oe({mode:l&&l.mode||"ask",allowlist:l&&Array.isArray(l.allowlist)?l.allowlist:[]});const _=r.status===200&&r.body.tools&&r.body.tools.restart_app;ae({mode:_&&_.mode||"ask",allowlist:_&&Array.isArray(_.allowlist)?_.allowlist:[]});const G=r.status===200&&r.body.tools&&r.body.tools.ask_user;ie(G&&G.mode==="off"?"off":"ask");const h=r.status===200&&r.body&&r.body.mcp;q({mode:h&&h.mode||"ask",allowlist:h&&Array.isArray(h.allowlist)?h.allowlist:[],servers:h&&h.servers&&typeof h.servers=="object"?h.servers:{},tools:h&&h.tools&&typeof h.tools=="object"?h.tools:{}}),B("")}catch{}try{const r=await u("/api/tools/list?projectDir="+encodeURIComponent(s));r.status===200&&Array.isArray(r.body.tools)&&Qe(r.body.tools)}catch{}ot("");try{const r=await u("/api/mcp/servers?projectDir="+encodeURIComponent(s));if(r.status===200){const p=(r.body.servers||[]).length;Jt(p?p+(p===1?" server":" servers"):"no servers yet"),Array.isArray(r.body.servers)&&as(r.body.servers)}else Jt("—")}catch{Jt("—")}try{const r=await u("/api/prompts?projectDir="+encodeURIComponent(s));if(r.status===200){const p=(r.body.prompts||[]).length;zt(p?p+(p===1?" prompt":" prompts"):"no prompts yet")}else zt("—")}catch{zt("—")}try{const r=await u("/api/agents?projectDir="+encodeURIComponent(s));r.status===200&&ps(Array.isArray(r.body.agents)?r.body.agents:[])}catch{}if(be(i.agentFiles!==!1),It(Array.isArray(i.agentFileNames)?i.agentFileNames.join(`
1
+ import{k as t,m as Qs,o as Xs,d as n,h as Ys,b as Ee,S as At,W as Zs,P as $s,q as to,f as u,r as eo,t as so,v as oo,n as ao,w as M,x as I,M as io,y as ro}from"./index-BDkCsON6.js";import{A as no}from"./AgentFilePicker-ZW6vm5Hy.js";import{a as lo}from"./agentNavigation-BiiCpFz5.js";function R(j){const v={class:"settings-project__section-icon settings-project__section-icon--"+j,"aria-hidden":"true"};switch(j){case"general":return t("span",v,t("span",{class:"ico-sliders"}));case"chat":return t("span",v,t("span",{class:"ico-chat"}));case"tools":return t("span",v,t("span",{class:"ico-wrench"}));case"files":return t("span",v,t("span",{class:"ico-doc"}));case"agents":return t("span",v,t("span",{class:"ico-people"}));case"skills":return t("span",v,t("span",{class:"ico-spark"}));case"more":return t("span",v,t("span",{class:"ico-grid"}));default:return t("span",v)}}function z(j,v,U,tt){return t(Xs,{tool:j,name:j,mode:v,namePrefix:"sp",modes:tt||Qs,onPick:nt=>U(nt)})}function fo({projectDir:j,chatId:v,from:U="",page:tt="main"}={}){const[nt,D]=n({text:"",state:""}),[Ne,Kt]=n("…"),[Ie,qt]=n(""),[ze,et]=n("Following the app default until you change it here"),[Tt,Qt]=n("average"),[lt,Xt]=n("tree"),[Je,Ot]=n(""),Be={"very-small":{label:"Very small — quarter cap"},average:{label:"Average — standard cap (default)"},full:{label:"Full — 4× cap"},extensive:{label:"Extensive — never truncate"}},Yt={json:{label:"Full JSON — raw structured result"},tree:{label:"Hierarchical — indented tree"}};function Ct(e){return Object.prototype.hasOwnProperty.call(Be,e)?e:"average"}function ct(e){return Object.prototype.hasOwnProperty.call(Yt,e)?e:"tree"}const[He,Le]=n(!!(v&&v.trim())),[Zt,pt]=n(!1),[We,T]=n(""),[Ge,Pt]=n(!1),[Ve,xt]=n(""),[ut,$t]=n({mode:"ask",allowlist:[]}),[K,Mt]=n({mode:"ask",allowlist:[]}),[Rt,dt]=n({}),[gt,te]=n({mode:"ask",allowlist:[]}),[ft,ee]=n({mode:"ask",allowlist:[]}),[ht,se]=n({mode:"ask",allowlist:[]}),[mt,oe]=n({mode:"ask",allowlist:[]}),[_t,ae]=n({mode:"ask",allowlist:[]}),[Ut,ie]=n("ask"),[re,ne]=n(""),[le,st]=n(""),[ce,pe]=n(""),[ue,de]=n(""),[ge,fe]=n(""),[he,Ke]=n(""),[me,qe]=n(""),[_e,Dt]=n(""),[vt,Qe]=n([]),[Xe,Ye]=n(""),[Ze,Ft]=n(!1),[O,$e]=n(null),[ts,Et]=n(!1),[es,J]=n(""),[ve,ss]=n((v||"").trim()),[F,q]=n({mode:"ask",allowlist:[],servers:{},tools:{}}),[co,B]=n(""),[os,as]=n([]),[bt,be]=n(!1),[Nt,It]=n(""),[is,ot]=n(""),[rs,jt]=n(!1),[je,we]=n(!0),[ye,ns]=n(""),[ls,zt]=n("—"),[cs,Jt]=n("—"),[ke,ps]=n([]),[Se,H]=n("{}"),[us,Bt]=n(!0),[ds,gs]=n(!0),[fs,L]=n(""),[hs,ms]=n(""),[W,Ae]=n(!1),[_s,wt]=n(""),[at,it]=n({}),Ht=Array.isArray(at.hideFileContent)?at.hideFileContent.length:0,[vs,bs]=n(""),[js]=n((v||"").trim());function d(){return vs}function f(){return js}function k(e){const s=d()||j||"",o="projectDir="+encodeURIComponent(s),a=U;return(f()?o+"&chatId="+encodeURIComponent(f()):o)+(a?"&from="+encodeURIComponent(a):"")}async function Lt(e){const s=(e||"").trim();if(!s){D({text:"no project selected",state:"error"});return}bs(s),D({text:"loading…",state:"busy"});const[o,a]=await Promise.all([u("/api/settings/project?projectDir="+encodeURIComponent(s)),u("/api/settings/resolved?projectDir="+encodeURIComponent(s))]);if(o.status!==200){D({text:"project: HTTP "+o.status+(o.body&&o.body.error?" "+o.body.error:""),state:"error"});return}const i=o.body.project||{};it(i),eo(s),Kt(o.body.path||s),Ae(o.body.dbBacked===!0),qt(i.promptSize&&String(i.promptSize)||""),et("");const c=a.status===200&&a.body&&a.body.resolved||{},g=c&&c.toolOutput&&typeof c.toolOutput=="object"?c.toolOutput:{};if(Qt(Ct(g&&g.size)),Xt(ct(g&&g.structure)),Ot(""),Le(!!f()),T(""),xt(""),f())try{const r=await u("/api/chats/"+encodeURIComponent(f())+"?projectDir="+encodeURIComponent(s));if(r.status===200&&r.body&&r.body.chat){const p=r.body.chat.trace===!0;pt(p),T(p?"trace on":"trace off"),Pt(!0)}else T("chat not found"),Pt(!1)}catch{T("failed to load chat"),Pt(!1)}else T("Open from a chat to edit trace.");ne(""),st(""),pe(""),de(""),fe(""),Dt("");try{const r=await u("/api/tools/authorization?projectDir="+encodeURIComponent(s)),p=r.status===200&&r.body.tools&&r.body.tools.shell;$t({mode:p&&p.mode||"ask",allowlist:p&&Array.isArray(p.allowlist)?p.allowlist:[]});const w=r.status===200&&r.body.tools&&r.body.tools.file;Mt({mode:w&&w.mode||"ask",allowlist:w&&Array.isArray(w.allowlist)?w.allowlist:[]}),dt(Object.fromEntries(["read_file","list_files","search_files","write_file","edit_file"].map(x=>[x,r.status===200&&r.body.tools&&r.body.tools[x]])));const y=r.status===200&&r.body.tools&&r.body.tools.subagent;te({mode:y&&y.mode||"ask",allowlist:y&&Array.isArray(y.allowlist)?y.allowlist:[]});const C=r.status===200&&r.body.tools&&r.body.tools.report_progress;ee({mode:C&&C.mode||"ask",allowlist:C&&Array.isArray(C.allowlist)?C.allowlist:[]});const P=r.status===200&&r.body.tools&&r.body.tools.task;se({mode:P&&P.mode||"ask",allowlist:P&&Array.isArray(P.allowlist)?P.allowlist:[]});const l=r.status===200&&r.body.tools&&r.body.tools.webpreview;oe({mode:l&&l.mode||"ask",allowlist:l&&Array.isArray(l.allowlist)?l.allowlist:[]});const _=r.status===200&&r.body.tools&&r.body.tools.restart_app;ae({mode:_&&_.mode||"ask",allowlist:_&&Array.isArray(_.allowlist)?_.allowlist:[]});const G=r.status===200&&r.body.tools&&r.body.tools.ask_user;ie(G&&G.mode==="off"?"off":"ask");const h=r.status===200&&r.body&&r.body.mcp;q({mode:h&&h.mode||"ask",allowlist:h&&Array.isArray(h.allowlist)?h.allowlist:[],servers:h&&h.servers&&typeof h.servers=="object"?h.servers:{},tools:h&&h.tools&&typeof h.tools=="object"?h.tools:{}}),B("")}catch{}try{const r=await u("/api/tools/list?projectDir="+encodeURIComponent(s));r.status===200&&Array.isArray(r.body.tools)&&Qe(r.body.tools)}catch{}ot("");try{const r=await u("/api/mcp/servers?projectDir="+encodeURIComponent(s));if(r.status===200){const p=(r.body.servers||[]).length;Jt(p?p+(p===1?" server":" servers"):"no servers yet"),Array.isArray(r.body.servers)&&as(r.body.servers)}else Jt("—")}catch{Jt("—")}try{const r=await u("/api/prompts?projectDir="+encodeURIComponent(s));if(r.status===200){const p=(r.body.prompts||[]).length;zt(p?p+(p===1?" prompt":" prompts"):"no prompts yet")}else zt("—")}catch{zt("—")}try{const r=await u("/api/agents?projectDir="+encodeURIComponent(s));r.status===200&&ps(Array.isArray(r.body.agents)?r.body.agents:[])}catch{}if(be(i.agentFiles!==!1),It(Array.isArray(i.agentFileNames)?i.agentFileNames.join(`
2
2
  `):""),we(i.skills!==!1),H(JSON.stringify(i,null,2)),Bt(!1),gs(!1),a.status===200){const r=JSON.parse(JSON.stringify(a.body.resolved||{}));Array.isArray(r.providers)&&(r.providers=r.providers.map(p=>(!p||typeof p!="object"||typeof p.apiKey=="string"&&(p.apiKey=p.apiKey?"•••":""),p))),ms(JSON.stringify(r,null,2))}D({text:"loaded",state:"success"})}async function Wt(e,s,o){const a=d();if(!a)return s&&s("no project"),!1;s&&s("saving…");const i=await u("/api/settings/project",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(Object.assign({projectDir:a},e))});if(i.status!==200)return s&&s("HTTP "+i.status),!1;const c=i.body.project||Object.assign({},at,e);return it(c),H(JSON.stringify(c,null,2)),s&&s(o||"saved"),!0}async function ws(e){const s=e&&e.target?e.target.value:"";if(qt(s),s){await Wt({promptSize:s},et,"set to "+s);return}const o=d();et("saving…");const a=await u("/api/settings/project",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectDir:o,unset:["promptSize"]})});if(a.status===200){const i=a.body.project||{};it(i),H(JSON.stringify(i,null,2)),et("following the app default")}else et("HTTP "+a.status)}async function ys(e,s){const o=Ct(e),a=ct(s);Qt(o),Xt(a),Ot("saving…"),await Wt({toolOutput:{size:o,structure:a}},Ot,"saved")}function ks(e){ys(Tt,e&&e.target?e.target.value:lt)}async function Ss(e){if(!f())return;const s=!!e.target.checked;pt(s),T("saving…");const o=await u("/api/chats/"+encodeURIComponent(f()),{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectDir:d(),trace:s})});if(o.status===200){const a=o.body&&o.body.chat&&o.body.chat.trace===!0;pt(a),T(a?"trace on":"trace off")}else T("HTTP "+o.status),pt(!s)}async function As(){if(!f())return;xt("exporting trace…");const e=await u("/api/chats/"+encodeURIComponent(f())+"/trace/export",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectDir:d()})});xt(e.status===200?"exported: "+e.body.path:"export failed: HTTP "+e.status)}async function Ts(){if(!f()||!confirm("Delete this chat? Its messages will be removed; any exported trace file will be kept."))return;T("deleting chat…");const e=await u("/api/chats/"+encodeURIComponent(f())+"?projectDir="+encodeURIComponent(d()),{method:"DELETE"});e.status===200?(oo.value++,ao("projects")):T("delete failed: HTTP "+e.status)}async function Gt(e,s,o,a){a("saving…");const i=await u("/api/tools/authorization",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectDir:d(),tools:{[e]:{mode:s,allowlist:o}}})});a(i.status===200?"saved":"HTTP "+i.status)}function Q(e,s,o,a,i){const c=i==="allow"?[]:s.allowlist;o({mode:i,allowlist:c}),Gt(e,i,c,a)}function Te(e){Q("shell",ut,$t,ne,e)}function Os(e){const s=e==="allow"?[]:K.allowlist,o={mode:e,allowlist:s};Mt(o),dt(a=>Object.fromEntries(Object.entries(a).map(([i,c])=>[i,c&&c.source==="project-tool"?c:Object.assign({},o,{source:"project"})]))),Gt("file",e,s,st)}function Cs(e,s){const o=Rt[e]||K,a={mode:s,allowlist:s==="allow"?[]:o.allowlist||[],source:"project-tool"};dt(i=>Object.assign({},i,{[e]:a})),Gt(e,a.mode,a.allowlist,st)}async function Ps(e,s){const o=s==="allow"?[]:K.allowlist,a={mode:s,allowlist:o,source:"project-tool"};Mt(g=>Object.assign({},g,{mode:s,allowlist:o})),dt(g=>Object.assign({},g,Object.fromEntries(e.map(r=>[r,a])))),st("saving…");const i=Object.fromEntries(["file",...e].map(g=>[g,{mode:s,allowlist:o}])),c=await u("/api/tools/authorization",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectDir:d(),tools:i})});st(c.status===200?"saved":"HTTP "+c.status)}function Oe(e){Q("subagent",gt,te,pe,e)}function Ce(e){Q("report_progress",ft,ee,de,e)}function Pe(e){Q("task",ht,se,fe,e)}function xe(e){Q("webpreview",mt,oe,Ke,e)}function Me(e){Q("restart_app",_t,ae,qe,e)}async function xs(e){const s=d();if(!s)return"";if(e&&e.trim())return e.trim();try{const o=await u("/api/chats?projectDir="+encodeURIComponent(s)+"&limit=1");if(o.status===200&&Array.isArray(o.body.chats)&&o.body.chats.length){const a=o.body.chats[0].id;return a&&ss(a),a||""}}catch{}return ve||""}async function Re(e,s){const o=d(),a=await xs(ve);if(!o||!a)return J("No chat available for this project."),null;J("Capturing preview…");try{const i=await ro({projectDir:o,chatId:a,url:e,viewport:s||void 0});return i&&i.ok&&i.result&&i.result.thumbnail?($e(i.result),Et(!0),J("")):J(i&&(i.result&&i.result.error||i.error)||"capture failed"),i}catch(i){return i&&i.code==="EAUTH_REQUIRED"?J("Web preview is Ask-gated — approve it in the chat to capture."):J("Preview failed: "+(i&&i.message||String(i))),null}}function Ms(e){return Ye(e),Ft(!1),Re(e)}function Rs(e){const s=Xe||O&&O.url;return s?Re(s,e):Promise.resolve(null)}function Us(){Ft(!0)}function Ue(e){ie(e),Dt("saving…"),u("/api/tools/authorization",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectDir:d(),tools:{ask_user:{mode:e,allowlist:[]}}})}).then(s=>Dt(s.status===200?"saved":"HTTP "+s.status))}async function yt(e){B("saving…");const s=await u("/api/tools/authorization",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectDir:d(),mcp:e})});if(s.status===200){B("saved");const o=s.body&&s.body.mcp;o&&q(a=>({mode:o&&o.mode||a.mode,allowlist:o&&Array.isArray(o.allowlist)?o.allowlist:a.allowlist,servers:o&&o.servers&&typeof o.servers=="object"?o.servers:a.servers,tools:o&&o.tools&&typeof o.tools=="object"?o.tools:a.tools}))}else B("HTTP "+s.status)}function Ds(e,s){q(o=>{const a=o.servers&&o.servers[e],i={mode:s,allowlist:a&&Array.isArray(a.allowlist)?a.allowlist:[]},c=Object.assign({},o.servers,{[e]:i});return yt({servers:{[e]:i}}),Object.assign({},o,{servers:c})})}function De(e){q(s=>{const o=Object.assign({},s.servers);return delete o[e],Object.assign({},s,{servers:o})}),yt({servers:{[e]:null}})}function Fs(e,s){s?(De(e),B("server override cleared (defaults to "+M(F.mode||"ask")+")")):(Ds(e,"off"),B("server override off"))}function Es(e,s){const o=s?null:{mode:"off",allowlist:[]};q(a=>{const i=Object.assign({},a.tools);return s?delete i[e]:i[e]=o,Object.assign({},a,{tools:i})}),yt({tools:{[e]:o}}),B(s?"tool override cleared":"tool override off")}async function Vt(e,s){const o=s.split(/\r?\n/).map(c=>c.trim()).filter(Boolean);ot("saving…");const a={agentFiles:e};o.length?a.agentFileNames=o:a.unset=["agentFileNames"];const i=await u("/api/settings/project",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(Object.assign({projectDir:d()},a))});if(i.status===200){const c=i.body.project||Object.assign({},at,a);it(c),H(JSON.stringify(c,null,2)),ot("saved")}else ot("HTTP "+i.status)}function Ns(e){const s=e.target.checked;be(s),Vt(s,Nt)}function Is(e){if(!e){jt(!1);return}const s=Nt.split(/\r?\n/).map(a=>a.trim()).filter(Boolean);s.includes(e)||s.push(e);const o=s.join(`
3
3
  `);It(o),jt(!1),Vt(bt,o)}const[zs]=n(()=>{let e=null;return s=>{e&&clearTimeout(e),e=setTimeout(s,350)}});function Js(e){const s=e.target.value;It(s),ot("…"),zs(()=>Vt(bt,s))}async function Bs(){const e=d();if(!e){L("no project");return}if(W){L("DB-backed — settings save automatically");return}let s;try{s=JSON.parse(Se||"{}")}catch(a){L("invalid JSON: "+a.message);return}if(!s||typeof s!="object"||Array.isArray(s)){L("must be a JSON object");return}Bt(!0);const o=await u("/api/settings/project",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(Object.assign({projectDir:e},s))});if(Bt(!1),o.status!==200){L("HTTP "+o.status);return}L("saved"),await Lt(e)}function Hs(){H(JSON.stringify(at,null,2)),L("reverted")}async function Ls(e){const s=!!e.target.checked,o=d();if(!o){wt("no project");return}wt("saving…");try{const a=await so(o,s);Ae(a.dbBacked),Kt(a.path||o),it(a.project),H(JSON.stringify(a.project,null,2)),wt(a.dbBacked?"stored in app DB — project folder unchanged":"stored in .mouaif.json")}catch(a){wt("save failed: "+(a&&a.message?a.message:a))}}Ys(()=>{const e=j&&j.trim()||Ee.value&&Ee.value.dir||"";e?Lt(e).catch(s=>D({text:"load failed: "+s.message,state:"error"})):D({text:"open this from a project card",state:"error"})},[j]);function Ws(e){const s=[],o=l=>l!=="off",a=(l,_)=>Object.assign({id:l.name,name:l.name,description:I(l.description),title:l.description||""},_||{});s.push({id:"skills",name:"Skills",description:"Inject matching project skills into chats",title:"Agent Skills stored in .agents/skills/*/SKILL.md. Off locks them out of every chat; on lets each chat opt out, skill by skill.",checked:je,tools:[{id:"skills",name:"Skills",description:"Inject matching project skills into chats",checked:je}],extra:ye?t("div",{class:"settings-project__item-status","aria-live":"polite"},ye):null});const i=e.find(l=>l.name==="shell");i&&s.push({id:"shell",name:"Shell",description:I(i.description),title:i.description||"",checked:o(ut.mode),control:z("Shell commands",M(ut.mode),Te,[{value:"off",label:"Off"},{value:"ask",label:"Ask"},{value:"allow",label:"Allow"}]),tools:[a(i,{checked:o(ut.mode)})],extra:re?t("div",{class:"settings-project__item-status","aria-live":"polite"},re):null});const c=e.find(l=>l.name==="subagent");c&&s.push({id:"subagent",name:"Subagent",description:I(c.description),title:c.description||"",checked:o(gt.mode),control:z("Subagent",M(gt.mode),Oe,[{value:"off",label:"Off"},{value:"ask",label:"Ask"},{value:"allow",label:"Allow"}]),tools:[a(c,{checked:o(gt.mode)})],extra:ce?t("div",{class:"settings-project__item-status","aria-live":"polite"},ce):null});const g=e.find(l=>l.name==="report_progress");g&&s.push({id:"report_progress",name:"Progress updates",description:I(g.description),title:g.description||"",checked:o(ft.mode),control:z("Progress updates",M(ft.mode),Ce,[{value:"off",label:"Off"},{value:"ask",label:"Ask"},{value:"allow",label:"Allow"}]),tools:[a(g,{checked:o(ft.mode)})],extra:ue?t("div",{class:"settings-project__item-status","aria-live":"polite"},ue):null});const r=e.find(l=>l.name==="task");r&&s.push({id:"task",name:"Task",description:I(r.description),title:r.description||"",checked:o(ht.mode),control:z("Task",M(ht.mode),Pe,[{value:"off",label:"Off"},{value:"ask",label:"Ask"},{value:"allow",label:"Allow"}]),tools:[a(r,{checked:o(ht.mode)})],extra:ge?t("div",{class:"settings-project__item-status","aria-live":"polite"},ge):null});const p=e.find(l=>l.name==="webpreview");p&&s.push({id:"webpreview",name:"Web preview",description:I(p.description),title:p.description||"",checked:o(mt.mode),control:z("Web preview",M(mt.mode),xe,[{value:"off",label:"Off"},{value:"ask",label:"Ask"},{value:"allow",label:"Allow"}]),tools:[a(p,{checked:o(mt.mode)})],extra:he?t("div",{class:"settings-project__item-status","aria-live":"polite"},he):null});const w=e.find(l=>l.name==="restart_app");w&&s.push({id:"restart_app",name:"Restart app",description:I(w.description),title:w.description||"",checked:o(_t.mode),control:z("Restart app",M(_t.mode),Me,[{value:"off",label:"Off"},{value:"ask",label:"Ask"},{value:"allow",label:"Allow"}]),tools:[a(w,{checked:o(_t.mode)})],extra:me?t("div",{class:"settings-project__item-status","aria-live":"polite"},me):null});const y=e.find(l=>l.name==="ask_user");y&&s.push({id:"ask_user",name:"Ask user",description:I(y.description),title:y.description||"",checked:o(Ut),control:z("Ask the user",Ut,Ue,[{value:"off",label:"Off"},{value:"ask",label:"Ask"}]),tools:[a(y,{checked:o(Ut)})],extra:_e?t("div",{class:"settings-project__item-status","aria-live":"polite"},_e):null});const C=e.filter(l=>l.kind==="native"&&l.source==="files");C.length&&s.push({id:"files",name:"File tools",description:"read, list, search, write, edit, draw",checked:o(K.mode),control:z("File tools",M(K.mode),Os,[{value:"off",label:"Off"},{value:"ask",label:"Ask"},{value:"allow",label:"Allow"}]),tools:C.map(l=>a(l,{checked:o(Rt[l.name]&&Rt[l.name].mode||K.mode)})),extra:le?t("div",{class:"settings-project__item-status","aria-live":"polite"},le):null});const P=(os||[]).filter(l=>l&&l.id);if(P.length)for(const l of P){const _=l.slug||l.id,G=F.servers&&F.servers[_],h=!!(G&&G.mode),x=h?G.mode:F.mode||"ask",S=(vt||[]).filter(m=>m&&m.kind==="mcp"&&m.source===_),X=S.length||(Array.isArray(l.tools)?l.tools.length:0);s.push({id:"mcp-"+_,name:l.name||l.id,description:(l.status||"stopped")+(X?" · "+X+(X===1?" tool":" tools"):"")+(h?" · override: "+M(x):" · default ("+M(x)+")"),checked:x!=="off",status:l.status||"stopped",enabled:x!=="off",serverId:l.id,control:t(io,{name:l.name||l.id,slug:_,servers:F.servers,shared:F,namePrefix:"sp-mcp",onSave:m=>{if(!m||!m.servers)return;const A=Object.keys(m.servers)[0],E=m.servers[A];E==null?De(A):(q(N=>Object.assign({},N,{servers:Object.assign({},N.servers,{[A]:E})})),yt({servers:{[A]:E}}))}}),tools:S.map(m=>{const A=F.tools&&F.tools[m.name],E=A&&A.mode?A.mode:x,N="mcp__"+_+"__",kt=m.name.startsWith(N)?m.name.slice(N.length):m.name;return a(m,{name:kt,checked:E!=="off"})}),extra:null})}return s}async function Gs(e){const s=d(),o=e&&e.serverId;if(!s||!o)return;e.reloadBusy=!0,D({text:"starting server…",state:"busy"});let a=!1;try{a=(await u("/api/mcp/servers/"+encodeURIComponent(o)+"/start",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectDir:s})})).status===200}catch{}e.reloadBusy=!1,D(a?{text:"server started",state:"ok"}:{text:"start failed — check Server command/URL",state:"error"}),Lt(s)}function Fe(e,s){const o=s?"ask":"off";if(e==="skills")we(s),Wt({skills:s},ns,"saved");else if(e==="shell")Te(o);else if(e==="subagent")Oe(o);else if(e==="task")Pe(o);else if(e==="webpreview")xe(o);else if(e==="restart_app")Me(o);else if(e==="report_progress")Ce(o);else if(e==="ask_user")Ue(o);else if(e==="files"){const a=vt.filter(i=>i&&i.kind==="native"&&i.source==="files").map(i=>i.name);Ps(a,o)}else e.startsWith("mcp-")&&Fs(e.slice(4),s)}function Vs(e,s){const o={"very-small":.25,average:1,full:4,extensive:1/0},a=64*1024,i=4*1024,c=b=>new TextEncoder().encode(b).length,g=[];function r(b,V){g.push("# "+b);for(const rt of V)g.push(" "+rt)}r("bin/",["mouaif.js","mouaif-mcp.js"]),r("src/",["index.js","cli.js","server.js","ai.js","settings.js","toolFeedback.js"]),r("frontend/src/",["main.jsx","api.js","router.js","virtual-list.js"]),r("frontend/src/styles/",["index.css","chat.css","inspector.css"]),r("docs/features/",["file-tools.md","shell-tool.md","tool-output.md","trace.md"]);const p=["auth","chat","billing","search","push","settings","telemetry","caching","uploads","sync","onboarding","metrics","threads","webhooks"],w=["src/features/","src/components/","src/routes/","tests/unit/"],y=["","-core","-ui","-shared"],C=["","data","logic","view"],P=["index.js","actions.js","reducer.js","types.ts","hooks.js","schema.ts"];for(const b of w)for(const V of p)for(const rt of y)for(const Y of C)r(b+V+rt+(Y?"/"+Y:"")+"/",P);const l=[];let _="";for(const b of g)b.startsWith("# ")?_=b.slice(2):l.push({path:_+b.trim()});const h=`# Listing: **/*.{js,jsx,ts,tsx,md,css}
4
4
  # Count: `+l.length+`
@@ -1 +1 @@
1
- import{d as c,h as g,k as t,S as j,f as d,A as _,c as b}from"./index-BGvI4n0T.js";function y({project:n,onRename:l,onUnregister:p}){const[a,r]=c(!1),o=_(null);return b(o,()=>r(!1),a),t("div",{ref:o,class:"sprojects__menu"},t("button",{class:"sprojects__menu-btn",type:"button","aria-haspopup":"true","aria-expanded":String(a),"aria-label":"Project options for "+(n.name||n.path),onClick:i=>{i.stopPropagation(),r(!a)}},"⋯"),t("div",{class:"sprojects__menu-pop",hidden:!a,role:"menu",onClick:i=>i.stopPropagation()},t("button",{type:"button",onClick:()=>{r(!1),l(n)}},"Rename…"),t("button",{type:"button","data-danger":"1",onClick:()=>{r(!1),p(n)}},"Unregister")))}function P(){const[n,l]=c(null),[p,a]=c("loading…"),[r,o]=c("busy");async function i(){a("loading…"),o("busy");try{const e=await d("/api/projects/registered");if(e.status!==200){a("HTTP "+e.status),o("error");return}const s=e.body.projects||[];l(s),a(s.length+(s.length===1?" project":" projects")),o(s.length?"success":"")}catch{a("network error"),o("error")}}g(()=>{i()},[]);async function f(e){const s=prompt("Rename project",e.name||e.path);if(s==null)return;const u=s.trim();if(!u||u===e.name)return;const m=await d("/api/projects/registered/"+encodeURIComponent(e.id),{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:u})});if(m.status!==200){alert("rename failed: HTTP "+m.status);return}i()}async function h(e){if(!confirm('Unregister project "'+(e.name||e.path)+'"? The folder on disk is not touched.'))return;const s=await d("/api/projects/registered/"+encodeURIComponent(e.id),{method:"DELETE"});if(s.status!==200){alert("unregister failed: HTTP "+s.status);return}i()}return t(j,null,t("div",{class:"view-head"},t("a",{href:"#/settings",class:"view-back","aria-label":"Back to settings"},"←"),t("h2",{class:"view-title"},"Projects")),t("p",{class:"hint hint--compact"},"Projects the app remembers. Removing one only unregisters it — the folder on disk is never touched."),t("div",{class:"page-bar"},t("span",{class:"status page-bar__status"+(r?" status--"+r:""),"aria-live":"polite"},p),t("a",{href:"#/projects/new",class:"page-bar__add","aria-label":"Add project"},"+")),t("ul",{class:"sprojects__list","aria-label":"Registered projects"},n===null?null:n.length===0?t("li",{class:"sprojects__empty"},'No projects yet. Tap "+" to register a folder on disk.'):n.map(e=>t("li",{class:"sprojects__row",key:e.id},t("a",{class:"sprojects__main",href:"#/settings/project?projectDir="+encodeURIComponent(e.path)+"&from=settings%2Fprojects","aria-label":"Project settings for "+(e.name||e.path)},t("div",{class:"sprojects__name"},e.name||e.path),t("div",{class:"sprojects__meta"},e.path)),t("span",{class:"sprojects__chev","aria-hidden":"true"},"›"),t(y,{project:e,onRename:f,onUnregister:h})))))}export{P as SettingsProjectsView};
1
+ import{d as c,h as g,k as t,S as j,f as d,A as _,c as b}from"./index-BDkCsON6.js";function y({project:n,onRename:l,onUnregister:p}){const[a,r]=c(!1),o=_(null);return b(o,()=>r(!1),a),t("div",{ref:o,class:"sprojects__menu"},t("button",{class:"sprojects__menu-btn",type:"button","aria-haspopup":"true","aria-expanded":String(a),"aria-label":"Project options for "+(n.name||n.path),onClick:i=>{i.stopPropagation(),r(!a)}},"⋯"),t("div",{class:"sprojects__menu-pop",hidden:!a,role:"menu",onClick:i=>i.stopPropagation()},t("button",{type:"button",onClick:()=>{r(!1),l(n)}},"Rename…"),t("button",{type:"button","data-danger":"1",onClick:()=>{r(!1),p(n)}},"Unregister")))}function P(){const[n,l]=c(null),[p,a]=c("loading…"),[r,o]=c("busy");async function i(){a("loading…"),o("busy");try{const e=await d("/api/projects/registered");if(e.status!==200){a("HTTP "+e.status),o("error");return}const s=e.body.projects||[];l(s),a(s.length+(s.length===1?" project":" projects")),o(s.length?"success":"")}catch{a("network error"),o("error")}}g(()=>{i()},[]);async function f(e){const s=prompt("Rename project",e.name||e.path);if(s==null)return;const u=s.trim();if(!u||u===e.name)return;const m=await d("/api/projects/registered/"+encodeURIComponent(e.id),{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:u})});if(m.status!==200){alert("rename failed: HTTP "+m.status);return}i()}async function h(e){if(!confirm('Unregister project "'+(e.name||e.path)+'"? The folder on disk is not touched.'))return;const s=await d("/api/projects/registered/"+encodeURIComponent(e.id),{method:"DELETE"});if(s.status!==200){alert("unregister failed: HTTP "+s.status);return}i()}return t(j,null,t("div",{class:"view-head"},t("a",{href:"#/settings",class:"view-back","aria-label":"Back to settings"},"←"),t("h2",{class:"view-title"},"Projects")),t("p",{class:"hint hint--compact"},"Projects the app remembers. Removing one only unregisters it — the folder on disk is never touched."),t("div",{class:"page-bar"},t("span",{class:"status page-bar__status"+(r?" status--"+r:""),"aria-live":"polite"},p),t("a",{href:"#/projects/new",class:"page-bar__add","aria-label":"Add project"},"+")),t("ul",{class:"sprojects__list","aria-label":"Registered projects"},n===null?null:n.length===0?t("li",{class:"sprojects__empty"},'No projects yet. Tap "+" to register a folder on disk.'):n.map(e=>t("li",{class:"sprojects__row",key:e.id},t("a",{class:"sprojects__main",href:"#/settings/project?projectDir="+encodeURIComponent(e.path)+"&from=settings%2Fprojects","aria-label":"Project settings for "+(e.name||e.path)},t("div",{class:"sprojects__name"},e.name||e.path),t("div",{class:"sprojects__meta"},e.path)),t("span",{class:"sprojects__chev","aria-hidden":"true"},"›"),t(y,{project:e,onRename:f,onUnregister:h})))))}export{P as SettingsProjectsView};
@@ -1 +1 @@
1
- import{d as c,A as Z,h as D,T as Xe,k as t,K as ve,L as Ze,q as $e,S as et,b as Se,f as P,N as tt}from"./index-BGvI4n0T.js";const h="__new__";async function st(a){try{return await navigator.clipboard.writeText(a||""),!0}catch{try{const k=document.createElement("textarea");k.value=a||"",k.style.position="fixed",k.style.opacity="0",document.body.appendChild(k),k.select();const j=document.execCommand("copy");return document.body.removeChild(k),j}catch{return!1}}}function ot(a){return a&&typeof a.projectDir=="string"?a.projectDir:Se.value&&Se.value.dir||""}function nt(a){return a?typeof a=="string"?a:a.name||a.relPath||"":""}function rt(a){return a?typeof a=="string"?a:a.id||a.name||"":""}function lt(a,k){const j=i=>{if(!i)return null;const L=i.tools instanceof Set?Array.from(i.tools).sort():Array.isArray(i.tools)?i.tools.slice().sort():[];return L.length||i.agentFiles||i.skills?{tools:L,agentFiles:!!i.agentFiles,skills:!!i.skills}:null},w=j(a),l=j(k);if(w===null&&l===null)return!0;if(!w||!l||w.agentFiles!==l.agentFiles||w.skills!==l.skills||w.tools.length!==l.tools.length)return!1;for(let i=0;i<w.tools.length;i++)if(w.tools[i]!==l.tools[i])return!1;return!0}function it(a){const k=ot(a),j=a&&typeof a.from=="string"?a.from:"",l=a&&a.scope==="app"||!k?"":k,[i,L]=c([]),[u,v]=c(h),$=a&&a.initialId||"",[U,q]=c(l?"project":"app"),[ee,B]=c(""),[O,G]=c("sparkles"),[R,z]=c(!1),[A,I]=c(""),[p,_]=c(null),[te,d]=c({text:"",kind:""}),[pe,se]=c(!1),[ue,H]=c(!1),[oe,de]=c("Copy"),[E,Ce]=c([]),[ne,N]=c(!1),[K,M]=c(!1),[g,J]=c({title:"",icon:"sparkles",showOnProjectCard:!1,content:"",preset:null,scope:l?"project":"app"}),[fe,Pe]=c([]),[me,je]=c([]),[Q,Ae]=c([]),[he,Te]=c(!1),[V,Fe]=c([]),[ke,xe]=c(!1),[re,be]=c(!1),S=Z(!1),le=Z(null),F=Z(!1),De=Z(g);De.current=g;async function ae(){const e=l?"?projectDir="+encodeURIComponent(l):"";let s;try{s=await P("/api/prompts"+e)}catch{return}if(s.status!==200)return;const o=s.body.prompts||[];L(o)}async function ie(){try{const e=await P("/api/prompt-profiles");e.status===200&&Array.isArray(e.body.profiles)&&Ce(e.body.profiles.map(s=>({id:s.id,label:s.label||s.id,description:s.description||"",systemMessage:s.systemMessage||""})))}catch{}}async function Le(){be(!1);const e=l?"?projectDir="+encodeURIComponent(l):"",[s,o,n,r]=await Promise.all([P("/api/tools/list"+e).catch(()=>({status:0,body:{}})),P("/api/mcp/servers"+e).catch(()=>({status:0,body:{}})),l?P("/api/features"+e).catch(()=>({status:0,body:{}})):Promise.resolve({status:200,body:{}}),l?P("/api/settings/project"+e).catch(()=>({status:0,body:{}})):Promise.resolve({status:200,body:{}})]);s.status===200&&Array.isArray(s.body.tools)&&Pe(s.body.tools),o.status===200&&Array.isArray(o.body.servers)&&je(o.body.servers);const f=n.status===200&&n.body&&n.body.features||{},b=f.agentFiles||{};Ae(Array.isArray(b.discovered)?b.discovered.map(nt).filter(Boolean):[]);const Y=f.skills||{};Fe(Array.isArray(Y.items)?Y.items.map(rt).filter(Boolean):[]);const y=r.status===200&&r.body&&r.body.project||{};Te(y.agentFiles===!1),xe(y.skills===!1),be(!0)}function C(e){if(!e){const f=l?"project":"app",b={title:"",icon:"sparkles",showOnProjectCard:!1,content:"",preset:null,scope:f};B(b.title),G(b.icon),z(b.showOnProjectCard),I(b.content),_(b.preset),q(f),J(b),S.current=!1;return}const s=e.preset,o=s&&(Array.isArray(s.tools)||typeof s.agentFiles=="boolean"||typeof s.skills=="boolean")?{tools:new Set(Array.isArray(s.tools)?s.tools:[]),agentFiles:s.agentFiles===!0,skills:s.skills===!0}:null,n=e.scope||(l?"project":"app"),r={title:e.title||"",icon:e.icon||"sparkles",showOnProjectCard:e.showOnProjectCard===!0,content:e.content||"",preset:o,scope:n};B(r.title),G(r.icon),z(r.showOnProjectCard),I(r.content),_(o),q(n),J(r),S.current=!1}function Oe(){return ee!==g.title||O!==g.icon||R!==g.showOnProjectCard||A!==g.content||U!==g.scope||!lt(p,g.preset)}function W(){return!!p}function Re(e,s){_(o=>{const n=o||{tools:new Set,agentFiles:!1,skills:!1},r=new Set(n.tools);return s?r.add(e):r.delete(e),{tools:r,agentFiles:!!n.agentFiles,skills:!!n.skills}})}function Ie(e,s){_(o=>{const n=o||{tools:new Set,agentFiles:!1,skills:!1},r=new Set(n.tools);for(const f of e)s?r.add(f):r.delete(f);return{tools:r,agentFiles:!!n.agentFiles,skills:!!n.skills}})}function ye(e){_(s=>{const o=s||{tools:new Set,skills:!1};return{tools:new Set(o.tools),agentFiles:!!e,skills:!!o.skills}})}function ge(e){_(s=>{const o=s||{tools:new Set,agentFiles:!1};return{tools:new Set(o.tools),agentFiles:!!o.agentFiles,skills:!!e}})}function Ee(e){if(e){_({tools:new Set,agentFiles:!1,skills:!1});return}_(null)}D(()=>{ae()},[l]),D(()=>{Le()},[l]),D(()=>{ie()},[]),D(()=>{if(!K)return;function e(o){le.current&&!le.current.contains(o.target)&&M(!1)}function s(o){o.key==="Escape"&&M(!1)}return document.addEventListener("pointerdown",e),document.addEventListener("keydown",s),()=>{document.removeEventListener("pointerdown",e),document.removeEventListener("keydown",s)}},[K]),D(()=>{F.current=!1,L([]),v(h),C(null)},[l]),D(()=>{if(F.current||u!==h)return;if($){const s=i.find(o=>o.id===$);if(!s)return;v(s.id),C(s);return}if(!i.length)return;const e=i[0];v(e.id),C(e)},[i,$,u]);function _e(e){if(M(!1),e===u||S.current&&!confirm("Discard unsaved changes to this prompt?"))return;if(F.current=!0,v(e),d({text:"",kind:""}),N(!1),e===h){C(null);return}const s=i.find(o=>o.id===e);C(s||null)}async function Ne(){const e=ee.trim(),s=A.trim();if(!s){d({text:"prompt content is required",kind:"error"});return}se(!0),d({text:"saving…",kind:"busy"});const o=u===h,n=o?U:g.scope||"project",f={projectDir:n==="app"?"":l||"",scope:n,title:e,icon:O,showOnProjectCard:R,content:s};if(W()){const T=p,Ye=T.tools&&T.tools.size>0||T.agentFiles||T.skills;f.preset=Ye?{tools:Array.from(T.tools||[]),agentFiles:T.agentFiles===!0,skills:T.skills===!0}:null}else f.preset=null;const b=o?"/api/prompts":"/api/prompts/"+encodeURIComponent(u),Y=o?"POST":"PATCH";let y;try{y=await P(b,{method:Y,headers:{"Content-Type":"application/json"},body:JSON.stringify(f)})}catch{d({text:"network error",kind:"error"}),se(!1);return}if(se(!1),y.status!==200&&y.status!==201){d({text:"HTTP "+y.status+(y.body&&y.body.error?": "+y.body.error:""),kind:"error"});return}await ae();const X=y.body.prompt;X&&X.id?(v(X.id),C(X)):(J({title:e,icon:O,showOnProjectCard:R,content:s,preset:p?{...p,tools:new Set(p.tools)}:null,scope:n}),S.current=!1),d({text:"saved.",kind:"success"})}async function Me(){if(u===h||!confirm("Delete this prompt? Chats that referenced it will fall back to no custom prompt."))return;H(!0),d({text:"deleting…",kind:"busy"});const e=i.find(f=>f.id===u),o=(e&&e.scope||g.scope||(l?"project":"app"))==="app"?"":l||"",n=o?"?projectDir="+encodeURIComponent(o):"?scope=app";let r;try{r=await P("/api/prompts/"+encodeURIComponent(u)+n,{method:"DELETE"})}catch{d({text:"network error",kind:"error"}),H(!1);return}if(r.status!==200){d({text:"HTTP "+r.status,kind:"error"}),H(!1);return}await ae(),F.current=!0,v(h),C(null),H(!1),d({text:"deleted.",kind:"success"})}async function Ue(){let e=A,s="prompt";if(u!==h){const n=i.find(r=>r.id===u);n&&(e=n.content||"",s=n.title||n.id)}else e=A;const o=await st(e);de(o?"Copied":"Copy failed"),setTimeout(()=>de("Copy"),1400),o&&d({text:'copied "'+s+'"',kind:"success"})}function qe(){S.current&&!confirm("Discard unsaved changes to this prompt?")||(F.current=!0,v(h),C(null),d({text:"",kind:""}),N(!1))}function Be(e){if(!e||(M(!1),S.current&&!confirm("Discard unsaved changes to this prompt?")))return;F.current=!0,v(h);const s=l?"project":"app",o={title:"",icon:"sparkles",showOnProjectCard:!1,content:"",preset:null,scope:s};B(e.label||""),G("sparkles"),z(!1),I(e.systemMessage||""),_(null),q(s),J(o),S.current=!0,N(!1),d({text:"started from "+(e.label||e.id)+" profile",kind:"success"})}function Ge(e){e&&(A.trim()&&!confirm('Replace the current prompt content with the "'+(e.label||e.id)+'" default?')||(I(e.systemMessage||""),N(!1),d({text:"loaded "+(e.label||e.id)+" profile",kind:"success"})))}function ze(){const e=p&&p.tools?Array.from(p.tools):[],o=tt(fe,me,e,new Set).slice();if(Q.length>0){const n=he,r=Q.length;o.push({id:"agent-files",title:"Agent files",subtitle:r+(r===1?" file":" files")+" · "+Q.join(", "),checked:!n&&!!(p&&p.agentFiles),disabled:n,description:n?"Locked off by project settings":p&&p.agentFiles?"on for chats using this prompt":"off",tools:[]})}if(V.length>0){const n=ke,r=V.length;o.push({id:"skills",title:"Skills",subtitle:r+(r===1?" skill":" skills")+" · "+V.join(", "),checked:!n&&!!(p&&p.skills),disabled:n,description:n?"Locked off by project settings":p&&p.skills?"on for chats using this prompt":"off",tools:[]})}return o}function He(e,s){if(e==="agent-files"){ye(s);return}if(e==="skills"){ge(s);return}const o=we.find(r=>r.id===e);if(!o)return;const n=(o.tools||[]).map(r=>r.id).filter(Boolean);n.length&&Ie(n,s)}function Ke(e,s,o){if(e==="agent-files"){ye(o);return}if(e==="skills"){ge(o);return}Re(s,o)}const we=Xe(()=>re?ze():[],[re,p,fe,me,Q,he,V,ke]),m=u===h,ce=Oe();S.current=ce;const x=m?null:i.find(e=>e.id===u),Je=m?"+ New prompt":x&&(x.title||x.id)||"Choose a prompt",Qe=m&&!A,Ve=!!l,We=l?"#/settings/project?projectDir="+encodeURIComponent(l)+(j?"&from="+encodeURIComponent(j):""):"#/settings";return t(et,null,t("div",{class:"view-head"},t("a",{href:We,class:"view-back","aria-label":"Back"},"←"),t("h2",{class:"view-title"},l?"Custom prompts · this project":"Custom prompts · app defaults")),t("section",null,t("p",{class:"hint hint--compact"},l?"System prompts for this project. Shows project prompts (committed to .mouaif.json) plus app-wide prompts from the SQLite store.":"App-wide system prompts available in every project. Stored in the app SQLite database."),l?t("p",{class:"hint hint--compact"},t("code",null,l)):null,t("div",{class:"row prompts__picker"},t("span",{class:"label",id:"sp-picker-label"},"Prompt"),t("div",{class:"prompts__picker-row"},t("div",{class:"prompts__picker-control",ref:le},t("button",{type:"button",class:"input prompts__select",id:"sp-picker","aria-labelledby":"sp-picker-label sp-picker","aria-haspopup":"listbox","aria-expanded":String(K),onClick:()=>M(e=>{const s=!e;return s&&!E.length&&ie(),s})},t("span",{class:"prompts__select-label"},Je),t("span",{class:"prompts__select-caret","aria-hidden":"true"},"⌄")),K?t("div",{class:"prompts__picker-menu",role:"listbox","aria-labelledby":"sp-picker-label"},t("button",{type:"button",class:"prompts__picker-option"+(m?" is-selected":""),role:"option","aria-selected":String(m),onClick:()=>_e(h)},t("span",{class:"prompts__picker-check","aria-hidden":"true"},m?"✓":""),t("span",null,"+ New prompt")),i.length===0?null:i.map(e=>{const s=e.id===u;return t("button",{type:"button",key:e.id,class:"prompts__picker-option"+(s?" is-selected":""),role:"option","aria-selected":String(s),onClick:()=>_e(e.id)},t("span",{class:"prompts__picker-check","aria-hidden":"true"},s?"✓":""),t("span",{class:"prompts__picker-option-label"},t(ve,{name:e.icon,size:17,class:"prompts__picker-icon"}),t("span",null,e.title||e.id),Ve&&e.scope?t("span",{class:"mcp__scope mcp__scope--"+e.scope},e.scope):null,e.preset?t("span",{class:"prompts__picker-preset"},"preset"):null))}),E.length?t("div",{class:"prompts__picker-section",role:"presentation"},"Start from a default"):null,E.map(e=>t("button",{type:"button",key:"profile:"+e.id,class:"prompts__picker-option prompts__picker-option--profile",role:"option","aria-selected":"false",onClick:()=>Be(e)},t("span",{class:"prompts__picker-check","aria-hidden":"true"},""),t("span",{class:"prompts__picker-option-label"},t("span",null,e.label),t("span",{class:"prompts__picker-preset"},"default"))))):null),t("button",{type:"button",class:"btn prompts__copy"+(oe==="Copied"?" is-copied":oe==="Copy failed"?" is-error":""),disabled:Qe,onClick:Ue,"aria-label":"Copy current prompt to clipboard",title:"Copy this prompt’s content to the clipboard"},oe),t("button",{type:"button",class:"btn",onClick:qe},"New")),ce?t("p",{class:"hint prompts__dirty"},"Unsaved changes — switch prompts to discard or hit Save."):null),m&&l?t("div",{class:"row"},t("label",{class:"label"},"Scope"),t("div",{class:"seg",role:"radiogroup","aria-label":"Prompt scope"},[{value:"project",label:"This project"},{value:"app",label:"App default"}].map(e=>t("label",{key:e.value,class:"seg__item"+(U===e.value?" seg__item--on":"")},t("input",{type:"radio",name:"sp-prompt-scope",value:e.value,checked:U===e.value,onChange:()=>q(e.value)}),t("span",{class:"seg__pill"},e.label))))):!m&&x?t("div",{class:"row"},t("span",{class:"hint hint--compact"},"Scope: ",t("span",{class:"mcp__scope mcp__scope--"+(x.scope==="app"?"app":"project")},x.scope==="app"?"app":"project"))):null,t("div",{class:"row"},t("label",{class:"label",for:"spe-title"},m?"Title (optional until saved)":"Title"),t("input",{value:ee,onInput:e=>B(e.currentTarget.value),class:"input",id:"spe-title",type:"text",placeholder:"My custom prompt"})),t("div",{class:"row"},t("span",{class:"label",id:"spe-icon-label"},"Icon"),t("div",{class:"prompts__icons",role:"radiogroup","aria-labelledby":"spe-icon-label"},Ze.map(e=>t("label",{key:e.id,class:"prompts__icon-choice"+(O===e.id?" is-selected":""),title:e.label},t("input",{type:"radio",name:"spe-icon",value:e.id,checked:O===e.id,"aria-label":e.label,onChange:()=>G(e.id)}),t(ve,{name:e.id,size:21})))),t("label",{class:"prompts__quick-launch"},t("span",{class:"switch"},t("input",{type:"checkbox",role:"switch",checked:R,"aria-checked":String(R),onChange:e=>z(e.currentTarget.checked)}),t("span",{class:"switch__track","aria-hidden":"true"},t("span",{class:"switch__thumb"}))),t("span",null,t("span",{class:"prompts__quick-launch-title"},"Add to project card"),t("span",{class:"prompts__quick-launch-desc"},"Use this icon as a one-tap button that starts a new chat with this prompt.")))),t("div",{class:"row"},t("label",{class:"label",for:"spe-content"},"Prompt content"),t("textarea",{value:A,onInput:e=>I(e.currentTarget.value),class:"input prompts__textarea",id:"spe-content",rows:6,placeholder:"You are a helpful assistant specialized in…"}),t("div",{class:"prompts__from-default"},t("button",{type:"button",class:"btn btn--ghost",onClick:()=>{const e=!ne;N(e),e&&ie()},"aria-expanded":String(ne)},"Copy from default"),t("span",{class:"hint hint--compact"},"Start from a built-in prompt-size profile, then edit.")),ne?t("div",{class:"prompts__profile-pick"},E.length?E.map(e=>t("button",{type:"button",class:"btn btn--ghost prompts__profile-option",key:e.id,onClick:()=>Ge(e)},t("span",{class:"prompts__profile-name"},e.label),t("span",{class:"prompts__profile-desc"},e.description))):t("p",{class:"hint"},"profiles unavailable")):null),t("div",{class:"row prompts__preset"},t("label",{class:"prompts__preset-head"},t("span",{class:"label prompt-label"},"Chat preset"),t("span",{class:"prompts__preset-main"},t("label",{class:"switch"},t("input",{id:"spe-preset-on",type:"checkbox",role:"switch","aria-checked":String(!!W()),checked:!!W(),onChange:e=>Ee(e.currentTarget.checked)}),t("span",{class:"switch__track","aria-hidden":"true"},t("span",{class:"switch__thumb"}))),t("span",{class:"prompts__preset-desc"},"Tools, agent files, and skills a chat gets when it uses this prompt."))),t("p",{class:"hint hint--compact prompts__preset-note"},"Tools are additive — a chat that already has a tool keeps it, and the project’s Off/Ask/Allow still wins. ","Agent files inject AGENTS.md / CLAUDE.md. Skills inject .agents/skills/*/SKILL.md. ","The project can lock any of these off; the preset cannot override that lock.")),W()?t("div",{class:"row prompts__preset-body"},re?t($e,{groups:we,onToggleGroup:He,onToggleTool:Ke,collapsedByDefault:!0,class:"prompts__preset-tree"}):t("div",{class:"prompts__preset-loading"},"loading tools…")):null,t("div",{class:"row row--actions"},t("button",{class:"btn btn--primary",type:"button",onClick:Ne,disabled:pe||!ce&&!m},pe?"Saving…":m?"Create":"Save"),t("button",{class:"btn btn--danger",type:"button",onClick:Me,hidden:m,disabled:ue},ue?"Deleting…":"Delete"),t("span",{class:"status"+(te.kind?" status--"+te.kind:""),"aria-live":"polite"},te.text))))}export{it as SettingsPromptsView};
1
+ import{d as c,A as Z,h as D,T as Xe,k as t,K as ve,L as Ze,q as $e,S as et,b as Se,f as P,N as tt}from"./index-BDkCsON6.js";const h="__new__";async function st(a){try{return await navigator.clipboard.writeText(a||""),!0}catch{try{const k=document.createElement("textarea");k.value=a||"",k.style.position="fixed",k.style.opacity="0",document.body.appendChild(k),k.select();const j=document.execCommand("copy");return document.body.removeChild(k),j}catch{return!1}}}function ot(a){return a&&typeof a.projectDir=="string"?a.projectDir:Se.value&&Se.value.dir||""}function nt(a){return a?typeof a=="string"?a:a.name||a.relPath||"":""}function rt(a){return a?typeof a=="string"?a:a.id||a.name||"":""}function lt(a,k){const j=i=>{if(!i)return null;const L=i.tools instanceof Set?Array.from(i.tools).sort():Array.isArray(i.tools)?i.tools.slice().sort():[];return L.length||i.agentFiles||i.skills?{tools:L,agentFiles:!!i.agentFiles,skills:!!i.skills}:null},w=j(a),l=j(k);if(w===null&&l===null)return!0;if(!w||!l||w.agentFiles!==l.agentFiles||w.skills!==l.skills||w.tools.length!==l.tools.length)return!1;for(let i=0;i<w.tools.length;i++)if(w.tools[i]!==l.tools[i])return!1;return!0}function it(a){const k=ot(a),j=a&&typeof a.from=="string"?a.from:"",l=a&&a.scope==="app"||!k?"":k,[i,L]=c([]),[u,v]=c(h),$=a&&a.initialId||"",[U,q]=c(l?"project":"app"),[ee,B]=c(""),[O,G]=c("sparkles"),[R,z]=c(!1),[A,I]=c(""),[p,_]=c(null),[te,d]=c({text:"",kind:""}),[pe,se]=c(!1),[ue,H]=c(!1),[oe,de]=c("Copy"),[E,Ce]=c([]),[ne,N]=c(!1),[K,M]=c(!1),[g,J]=c({title:"",icon:"sparkles",showOnProjectCard:!1,content:"",preset:null,scope:l?"project":"app"}),[fe,Pe]=c([]),[me,je]=c([]),[Q,Ae]=c([]),[he,Te]=c(!1),[V,Fe]=c([]),[ke,xe]=c(!1),[re,be]=c(!1),S=Z(!1),le=Z(null),F=Z(!1),De=Z(g);De.current=g;async function ae(){const e=l?"?projectDir="+encodeURIComponent(l):"";let s;try{s=await P("/api/prompts"+e)}catch{return}if(s.status!==200)return;const o=s.body.prompts||[];L(o)}async function ie(){try{const e=await P("/api/prompt-profiles");e.status===200&&Array.isArray(e.body.profiles)&&Ce(e.body.profiles.map(s=>({id:s.id,label:s.label||s.id,description:s.description||"",systemMessage:s.systemMessage||""})))}catch{}}async function Le(){be(!1);const e=l?"?projectDir="+encodeURIComponent(l):"",[s,o,n,r]=await Promise.all([P("/api/tools/list"+e).catch(()=>({status:0,body:{}})),P("/api/mcp/servers"+e).catch(()=>({status:0,body:{}})),l?P("/api/features"+e).catch(()=>({status:0,body:{}})):Promise.resolve({status:200,body:{}}),l?P("/api/settings/project"+e).catch(()=>({status:0,body:{}})):Promise.resolve({status:200,body:{}})]);s.status===200&&Array.isArray(s.body.tools)&&Pe(s.body.tools),o.status===200&&Array.isArray(o.body.servers)&&je(o.body.servers);const f=n.status===200&&n.body&&n.body.features||{},b=f.agentFiles||{};Ae(Array.isArray(b.discovered)?b.discovered.map(nt).filter(Boolean):[]);const Y=f.skills||{};Fe(Array.isArray(Y.items)?Y.items.map(rt).filter(Boolean):[]);const y=r.status===200&&r.body&&r.body.project||{};Te(y.agentFiles===!1),xe(y.skills===!1),be(!0)}function C(e){if(!e){const f=l?"project":"app",b={title:"",icon:"sparkles",showOnProjectCard:!1,content:"",preset:null,scope:f};B(b.title),G(b.icon),z(b.showOnProjectCard),I(b.content),_(b.preset),q(f),J(b),S.current=!1;return}const s=e.preset,o=s&&(Array.isArray(s.tools)||typeof s.agentFiles=="boolean"||typeof s.skills=="boolean")?{tools:new Set(Array.isArray(s.tools)?s.tools:[]),agentFiles:s.agentFiles===!0,skills:s.skills===!0}:null,n=e.scope||(l?"project":"app"),r={title:e.title||"",icon:e.icon||"sparkles",showOnProjectCard:e.showOnProjectCard===!0,content:e.content||"",preset:o,scope:n};B(r.title),G(r.icon),z(r.showOnProjectCard),I(r.content),_(o),q(n),J(r),S.current=!1}function Oe(){return ee!==g.title||O!==g.icon||R!==g.showOnProjectCard||A!==g.content||U!==g.scope||!lt(p,g.preset)}function W(){return!!p}function Re(e,s){_(o=>{const n=o||{tools:new Set,agentFiles:!1,skills:!1},r=new Set(n.tools);return s?r.add(e):r.delete(e),{tools:r,agentFiles:!!n.agentFiles,skills:!!n.skills}})}function Ie(e,s){_(o=>{const n=o||{tools:new Set,agentFiles:!1,skills:!1},r=new Set(n.tools);for(const f of e)s?r.add(f):r.delete(f);return{tools:r,agentFiles:!!n.agentFiles,skills:!!n.skills}})}function ye(e){_(s=>{const o=s||{tools:new Set,skills:!1};return{tools:new Set(o.tools),agentFiles:!!e,skills:!!o.skills}})}function ge(e){_(s=>{const o=s||{tools:new Set,agentFiles:!1};return{tools:new Set(o.tools),agentFiles:!!o.agentFiles,skills:!!e}})}function Ee(e){if(e){_({tools:new Set,agentFiles:!1,skills:!1});return}_(null)}D(()=>{ae()},[l]),D(()=>{Le()},[l]),D(()=>{ie()},[]),D(()=>{if(!K)return;function e(o){le.current&&!le.current.contains(o.target)&&M(!1)}function s(o){o.key==="Escape"&&M(!1)}return document.addEventListener("pointerdown",e),document.addEventListener("keydown",s),()=>{document.removeEventListener("pointerdown",e),document.removeEventListener("keydown",s)}},[K]),D(()=>{F.current=!1,L([]),v(h),C(null)},[l]),D(()=>{if(F.current||u!==h)return;if($){const s=i.find(o=>o.id===$);if(!s)return;v(s.id),C(s);return}if(!i.length)return;const e=i[0];v(e.id),C(e)},[i,$,u]);function _e(e){if(M(!1),e===u||S.current&&!confirm("Discard unsaved changes to this prompt?"))return;if(F.current=!0,v(e),d({text:"",kind:""}),N(!1),e===h){C(null);return}const s=i.find(o=>o.id===e);C(s||null)}async function Ne(){const e=ee.trim(),s=A.trim();if(!s){d({text:"prompt content is required",kind:"error"});return}se(!0),d({text:"saving…",kind:"busy"});const o=u===h,n=o?U:g.scope||"project",f={projectDir:n==="app"?"":l||"",scope:n,title:e,icon:O,showOnProjectCard:R,content:s};if(W()){const T=p,Ye=T.tools&&T.tools.size>0||T.agentFiles||T.skills;f.preset=Ye?{tools:Array.from(T.tools||[]),agentFiles:T.agentFiles===!0,skills:T.skills===!0}:null}else f.preset=null;const b=o?"/api/prompts":"/api/prompts/"+encodeURIComponent(u),Y=o?"POST":"PATCH";let y;try{y=await P(b,{method:Y,headers:{"Content-Type":"application/json"},body:JSON.stringify(f)})}catch{d({text:"network error",kind:"error"}),se(!1);return}if(se(!1),y.status!==200&&y.status!==201){d({text:"HTTP "+y.status+(y.body&&y.body.error?": "+y.body.error:""),kind:"error"});return}await ae();const X=y.body.prompt;X&&X.id?(v(X.id),C(X)):(J({title:e,icon:O,showOnProjectCard:R,content:s,preset:p?{...p,tools:new Set(p.tools)}:null,scope:n}),S.current=!1),d({text:"saved.",kind:"success"})}async function Me(){if(u===h||!confirm("Delete this prompt? Chats that referenced it will fall back to no custom prompt."))return;H(!0),d({text:"deleting…",kind:"busy"});const e=i.find(f=>f.id===u),o=(e&&e.scope||g.scope||(l?"project":"app"))==="app"?"":l||"",n=o?"?projectDir="+encodeURIComponent(o):"?scope=app";let r;try{r=await P("/api/prompts/"+encodeURIComponent(u)+n,{method:"DELETE"})}catch{d({text:"network error",kind:"error"}),H(!1);return}if(r.status!==200){d({text:"HTTP "+r.status,kind:"error"}),H(!1);return}await ae(),F.current=!0,v(h),C(null),H(!1),d({text:"deleted.",kind:"success"})}async function Ue(){let e=A,s="prompt";if(u!==h){const n=i.find(r=>r.id===u);n&&(e=n.content||"",s=n.title||n.id)}else e=A;const o=await st(e);de(o?"Copied":"Copy failed"),setTimeout(()=>de("Copy"),1400),o&&d({text:'copied "'+s+'"',kind:"success"})}function qe(){S.current&&!confirm("Discard unsaved changes to this prompt?")||(F.current=!0,v(h),C(null),d({text:"",kind:""}),N(!1))}function Be(e){if(!e||(M(!1),S.current&&!confirm("Discard unsaved changes to this prompt?")))return;F.current=!0,v(h);const s=l?"project":"app",o={title:"",icon:"sparkles",showOnProjectCard:!1,content:"",preset:null,scope:s};B(e.label||""),G("sparkles"),z(!1),I(e.systemMessage||""),_(null),q(s),J(o),S.current=!0,N(!1),d({text:"started from "+(e.label||e.id)+" profile",kind:"success"})}function Ge(e){e&&(A.trim()&&!confirm('Replace the current prompt content with the "'+(e.label||e.id)+'" default?')||(I(e.systemMessage||""),N(!1),d({text:"loaded "+(e.label||e.id)+" profile",kind:"success"})))}function ze(){const e=p&&p.tools?Array.from(p.tools):[],o=tt(fe,me,e,new Set).slice();if(Q.length>0){const n=he,r=Q.length;o.push({id:"agent-files",title:"Agent files",subtitle:r+(r===1?" file":" files")+" · "+Q.join(", "),checked:!n&&!!(p&&p.agentFiles),disabled:n,description:n?"Locked off by project settings":p&&p.agentFiles?"on for chats using this prompt":"off",tools:[]})}if(V.length>0){const n=ke,r=V.length;o.push({id:"skills",title:"Skills",subtitle:r+(r===1?" skill":" skills")+" · "+V.join(", "),checked:!n&&!!(p&&p.skills),disabled:n,description:n?"Locked off by project settings":p&&p.skills?"on for chats using this prompt":"off",tools:[]})}return o}function He(e,s){if(e==="agent-files"){ye(s);return}if(e==="skills"){ge(s);return}const o=we.find(r=>r.id===e);if(!o)return;const n=(o.tools||[]).map(r=>r.id).filter(Boolean);n.length&&Ie(n,s)}function Ke(e,s,o){if(e==="agent-files"){ye(o);return}if(e==="skills"){ge(o);return}Re(s,o)}const we=Xe(()=>re?ze():[],[re,p,fe,me,Q,he,V,ke]),m=u===h,ce=Oe();S.current=ce;const x=m?null:i.find(e=>e.id===u),Je=m?"+ New prompt":x&&(x.title||x.id)||"Choose a prompt",Qe=m&&!A,Ve=!!l,We=l?"#/settings/project?projectDir="+encodeURIComponent(l)+(j?"&from="+encodeURIComponent(j):""):"#/settings";return t(et,null,t("div",{class:"view-head"},t("a",{href:We,class:"view-back","aria-label":"Back"},"←"),t("h2",{class:"view-title"},l?"Custom prompts · this project":"Custom prompts · app defaults")),t("section",null,t("p",{class:"hint hint--compact"},l?"System prompts for this project. Shows project prompts (committed to .mouaif.json) plus app-wide prompts from the SQLite store.":"App-wide system prompts available in every project. Stored in the app SQLite database."),l?t("p",{class:"hint hint--compact"},t("code",null,l)):null,t("div",{class:"row prompts__picker"},t("span",{class:"label",id:"sp-picker-label"},"Prompt"),t("div",{class:"prompts__picker-row"},t("div",{class:"prompts__picker-control",ref:le},t("button",{type:"button",class:"input prompts__select",id:"sp-picker","aria-labelledby":"sp-picker-label sp-picker","aria-haspopup":"listbox","aria-expanded":String(K),onClick:()=>M(e=>{const s=!e;return s&&!E.length&&ie(),s})},t("span",{class:"prompts__select-label"},Je),t("span",{class:"prompts__select-caret","aria-hidden":"true"},"⌄")),K?t("div",{class:"prompts__picker-menu",role:"listbox","aria-labelledby":"sp-picker-label"},t("button",{type:"button",class:"prompts__picker-option"+(m?" is-selected":""),role:"option","aria-selected":String(m),onClick:()=>_e(h)},t("span",{class:"prompts__picker-check","aria-hidden":"true"},m?"✓":""),t("span",null,"+ New prompt")),i.length===0?null:i.map(e=>{const s=e.id===u;return t("button",{type:"button",key:e.id,class:"prompts__picker-option"+(s?" is-selected":""),role:"option","aria-selected":String(s),onClick:()=>_e(e.id)},t("span",{class:"prompts__picker-check","aria-hidden":"true"},s?"✓":""),t("span",{class:"prompts__picker-option-label"},t(ve,{name:e.icon,size:17,class:"prompts__picker-icon"}),t("span",null,e.title||e.id),Ve&&e.scope?t("span",{class:"mcp__scope mcp__scope--"+e.scope},e.scope):null,e.preset?t("span",{class:"prompts__picker-preset"},"preset"):null))}),E.length?t("div",{class:"prompts__picker-section",role:"presentation"},"Start from a default"):null,E.map(e=>t("button",{type:"button",key:"profile:"+e.id,class:"prompts__picker-option prompts__picker-option--profile",role:"option","aria-selected":"false",onClick:()=>Be(e)},t("span",{class:"prompts__picker-check","aria-hidden":"true"},""),t("span",{class:"prompts__picker-option-label"},t("span",null,e.label),t("span",{class:"prompts__picker-preset"},"default"))))):null),t("button",{type:"button",class:"btn prompts__copy"+(oe==="Copied"?" is-copied":oe==="Copy failed"?" is-error":""),disabled:Qe,onClick:Ue,"aria-label":"Copy current prompt to clipboard",title:"Copy this prompt’s content to the clipboard"},oe),t("button",{type:"button",class:"btn",onClick:qe},"New")),ce?t("p",{class:"hint prompts__dirty"},"Unsaved changes — switch prompts to discard or hit Save."):null),m&&l?t("div",{class:"row"},t("label",{class:"label"},"Scope"),t("div",{class:"seg",role:"radiogroup","aria-label":"Prompt scope"},[{value:"project",label:"This project"},{value:"app",label:"App default"}].map(e=>t("label",{key:e.value,class:"seg__item"+(U===e.value?" seg__item--on":"")},t("input",{type:"radio",name:"sp-prompt-scope",value:e.value,checked:U===e.value,onChange:()=>q(e.value)}),t("span",{class:"seg__pill"},e.label))))):!m&&x?t("div",{class:"row"},t("span",{class:"hint hint--compact"},"Scope: ",t("span",{class:"mcp__scope mcp__scope--"+(x.scope==="app"?"app":"project")},x.scope==="app"?"app":"project"))):null,t("div",{class:"row"},t("label",{class:"label",for:"spe-title"},m?"Title (optional until saved)":"Title"),t("input",{value:ee,onInput:e=>B(e.currentTarget.value),class:"input",id:"spe-title",type:"text",placeholder:"My custom prompt"})),t("div",{class:"row"},t("span",{class:"label",id:"spe-icon-label"},"Icon"),t("div",{class:"prompts__icons",role:"radiogroup","aria-labelledby":"spe-icon-label"},Ze.map(e=>t("label",{key:e.id,class:"prompts__icon-choice"+(O===e.id?" is-selected":""),title:e.label},t("input",{type:"radio",name:"spe-icon",value:e.id,checked:O===e.id,"aria-label":e.label,onChange:()=>G(e.id)}),t(ve,{name:e.id,size:21})))),t("label",{class:"prompts__quick-launch"},t("span",{class:"switch"},t("input",{type:"checkbox",role:"switch",checked:R,"aria-checked":String(R),onChange:e=>z(e.currentTarget.checked)}),t("span",{class:"switch__track","aria-hidden":"true"},t("span",{class:"switch__thumb"}))),t("span",null,t("span",{class:"prompts__quick-launch-title"},"Add to project card"),t("span",{class:"prompts__quick-launch-desc"},"Use this icon as a one-tap button that starts a new chat with this prompt.")))),t("div",{class:"row"},t("label",{class:"label",for:"spe-content"},"Prompt content"),t("textarea",{value:A,onInput:e=>I(e.currentTarget.value),class:"input prompts__textarea",id:"spe-content",rows:6,placeholder:"You are a helpful assistant specialized in…"}),t("div",{class:"prompts__from-default"},t("button",{type:"button",class:"btn btn--ghost",onClick:()=>{const e=!ne;N(e),e&&ie()},"aria-expanded":String(ne)},"Copy from default"),t("span",{class:"hint hint--compact"},"Start from a built-in prompt-size profile, then edit.")),ne?t("div",{class:"prompts__profile-pick"},E.length?E.map(e=>t("button",{type:"button",class:"btn btn--ghost prompts__profile-option",key:e.id,onClick:()=>Ge(e)},t("span",{class:"prompts__profile-name"},e.label),t("span",{class:"prompts__profile-desc"},e.description))):t("p",{class:"hint"},"profiles unavailable")):null),t("div",{class:"row prompts__preset"},t("label",{class:"prompts__preset-head"},t("span",{class:"label prompt-label"},"Chat preset"),t("span",{class:"prompts__preset-main"},t("label",{class:"switch"},t("input",{id:"spe-preset-on",type:"checkbox",role:"switch","aria-checked":String(!!W()),checked:!!W(),onChange:e=>Ee(e.currentTarget.checked)}),t("span",{class:"switch__track","aria-hidden":"true"},t("span",{class:"switch__thumb"}))),t("span",{class:"prompts__preset-desc"},"Tools, agent files, and skills a chat gets when it uses this prompt."))),t("p",{class:"hint hint--compact prompts__preset-note"},"Tools are additive — a chat that already has a tool keeps it, and the project’s Off/Ask/Allow still wins. ","Agent files inject AGENTS.md / CLAUDE.md. Skills inject .agents/skills/*/SKILL.md. ","The project can lock any of these off; the preset cannot override that lock.")),W()?t("div",{class:"row prompts__preset-body"},re?t($e,{groups:we,onToggleGroup:He,onToggleTool:Ke,collapsedByDefault:!0,class:"prompts__preset-tree"}):t("div",{class:"prompts__preset-loading"},"loading tools…")):null,t("div",{class:"row row--actions"},t("button",{class:"btn btn--primary",type:"button",onClick:Ne,disabled:pe||!ce&&!m},pe?"Saving…":m?"Create":"Save"),t("button",{class:"btn btn--danger",type:"button",onClick:Me,hidden:m,disabled:ue},ue?"Deleting…":"Delete"),t("span",{class:"status"+(te.kind?" status--"+te.kind:""),"aria-live":"polite"},te.text))))}export{it as SettingsPromptsView};
@@ -1 +1 @@
1
- import{d as i,p as m,h as z,k as e,e as ot,S as nt,l as rt,g as G,i as lt,s as _t,f as q,j as V,n as it}from"./index-BGvI4n0T.js";function Tt(){const[x,n]=i(null),[v,I]=i(""),[f,C]=i("");async function U(){try{await rt({force:!0})}catch(g){I("load failed: "+g.message),C("error");return}const r=lt();n(r),r.length?(I(r.length+(r.length===1?" provider":" providers")),C("success")):(I("0 providers"),C(""))}return z(()=>{U()},[]),e(nt,null,e("div",{class:"view-head"},e("a",{href:"#/settings",class:"view-back","aria-label":"Back to settings"},"‹"),e("h2",{class:"view-title"},"Providers")),e("p",{class:"hint hint--compact"},"Credentials live in the app store. Each project's models reference one of these."),e("ul",{class:"providers__list","aria-label":"Configured providers"},x===null?null:x.length===0?e("li",{class:"providers__empty"},'No providers yet. Tap "Add provider" to configure your first connection.'):x.map(r=>{const g=m(r.id),B=g&&g.label||r.id,D=r.auth||"apikey",b=[];return r.baseUrl&&b.push(r.baseUrl),D==="oauth"?(b.push("OAuth"),r.oauthAccount&&b.push("as "+r.oauthAccount)):r.hasApiKey?b.push("key saved"):b.push("no key"),e("li",{class:"provider-row",key:r.id},e("a",{class:"provider-row__main",href:"#/settings/providers/"+encodeURIComponent(r.id)},e("div",{class:"provider-row__name"},B),e("div",{class:"provider-row__meta"},b.join(" · ")),e("div",{class:"provider-row__chev"},"›")))})),e("div",{class:"page-bar"},e("span",{class:"status page-bar__status"+(f?" status--"+f:""),"aria-live":"polite"},v),e("a",{href:"#/settings/providers/new",class:"page-bar__add","aria-label":"Add provider"},"+")))}function Ot(x){const n=x.id||"",[v,I]=i(n||"openai-compatible"),[f,C]=i(null),U=t=>{const s=m(t);return!!(s&&s.reserved)},[r,g]=i(U(n||"openai-compatible")?"oauth":"apikey"),B=(()=>{const t=m(n||"openai-compatible");return t&&t.defaultBaseUrl||""})(),[D,b]=i(B),[F,Q]=i(""),[W,X]=i(""),[Y,R]=i(""),[ct,ut]=i([]),[pt,_]=i(!1),[dt,E]=i(!1),[ht,c]=i(""),[Z,u]=i(""),[vt,w]=i(""),[$,k]=i(""),[ft,K]=i(""),[tt,N]=i(""),[M,j]=i({}),bt=()=>m(v);async function yt(){let t,s;try{t=await rt({force:!0}),s=await G({force:!0}),j(s)}catch(l){c("load failed: "+l.message),u("error");return}const a=n?lt().find(l=>l&&l.id===n):null;if(n&&!a){c("Provider not found"),u("error");return}C(a||null),t&&t.app&&t.app.githubCopilot&&t.app.githubCopilot.clientId?R(t.app.githubCopilot.clientId):R(""),g(et(v,a)),b(st(v,a,a&&a.baseUrl||"")),L(v,s,a),Q(""),c(""),u("")}const H=t=>{const s=m(t);return!!(s&&(s.oauth||s.reserved))};function et(t,s){return U(t)||s&&s.id===t&&s.auth==="oauth"&&H(t)?"oauth":(s&&s.id===t&&s.auth==="apikey","apikey")}function L(t=v,s=M,a=f){const l=V(t),p=(s[l]||[]).slice();let d=[],h="";p.length===0?d.push({value:"",text:"— no accounts yet; sign in below —",disabled:!0}):p.length===1?d.push({value:"",text:"(auto — "+p[0]+")"}):d.push({value:"",text:"(pick an account)",disabled:!0});for(const y of p)d.push({value:y,text:y});ut(d);const o=a&&a.oauthAccount||"";o&&p.includes(o)&&(h=o),X(h)}function st(t,s,a){const l=m(t),p=s&&s.id===t?s:null,d=p&&p.baseUrl||l&&l.defaultBaseUrl||"",h=(a||"").trim(),o=ot.map(y=>y.defaultBaseUrl).filter(Boolean);return!h||o.includes(h)?d:h}async function gt(){const t=v,s=m(t);if(!s){w("unknown provider"),k("error");return}if(!H(t)){w(s.label+" does not support OAuth sign-in; use an API key."),k("error");return}w("starting sign-in…"),k("busy");let a;try{a=await q("/api/auth/sign-in/"+encodeURIComponent(t),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({redirectUri:new URL("/oauth/callback",window.location.href).toString()})})}catch{w("network error"),k("error");return}if(a.status!==200){w("HTTP "+a.status+(a.body&&a.body.error?" — "+a.body.error:"")),k("error");return}const l=a.body.authorizeUrl;if(!window.open(l,"_blank")){try{sessionStorage.setItem("oauthPending",JSON.stringify({provider:t,started:Date.now()}))}catch{}window.location.href=l;return}w("waiting for "+s.label+" to redirect back…"),k("busy");const d=new Set((M[V(t)]||[]).slice()),h=Date.now();for(;Date.now()-h<5*60*1e3;){await new Promise(o=>setTimeout(o,1500));try{const o=await G({force:!0});j(o);const P=(o[V(t)]||[]).filter(Ct=>!d.has(Ct));if(P.length){L(t,o,f),w("signed in as "+P[0]),k("success");return}}catch{}}w("timed out. Paste the redirect URL or its code below."),k("error")}async function wt(){const t=Y.trim();K("saving…"),N("busy");try{await _t({githubCopilot:{clientId:t||null}}),K(t?"saved — you can sign in now.":"cleared (using default)."),N("success")}catch(s){K("save failed: "+s.message),N("error")}}async function kt(){_(!0),c("saving…"),u("busy");const t=v,s=m(t),a=s&&s.reserved?"oauth":r,l=(D||"").trim(),p=F.trim(),d=W.trim();if(a==="oauth"){let y=M;try{y=await G({force:!0}),j(y)}catch{}const P=y[V(t)]||[];if(!P.length){c("sign in to "+t+" first"),u("error"),_(!1);return}if(P.length>1&&!d){c("pick which signed-in account to use"),u("error"),_(!1);return}}if(a==="apikey"&&t!=="ollama"&&!p&&!(f&&f.hasApiKey)){c("API key is required for "+t),u("error"),_(!1);return}const h={id:t,auth:a};l&&(h.baseUrl=l),a==="apikey"&&p&&(h.apiKey=p),a==="oauth"&&d&&(h.oauthAccount=d);let o;try{o=await q("/api/settings/app/providers",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(h)})}catch{c("network error"),u("error"),_(!1);return}if(_(!1),o.status!==200){c("HTTP "+o.status+(o.body&&o.body.error?": "+o.body.error:"")),u("error");return}c("saved "+t),u("success"),it("settings/providers")}async function mt(){if(!n||!confirm('Delete provider "'+n+'"? Models in your projects that reference it will stop working until you re-add it.'))return;E(!0),c("deleting…"),u("busy");let t;try{t=await q("/api/settings/app/providers/"+encodeURIComponent(n),{method:"DELETE"})}catch{c("network error"),u("error"),E(!1);return}if(t.status!==200){c("HTTP "+t.status),u("error"),E(!1);return}it("settings/providers")}z(()=>{yt()},[n]),z(()=>{I(n||"openai-compatible"),C(null)},[n]);const A=bt(),At=n?A&&A.label||n:"Add provider",T=!!(A&&A.reserved),J=H(v),O=T?"oauth":J?r:"apikey",St=T||!J,S=t=>"row"+(t?" is-hidden":"");function It(t){const s=t.target.value;I(s),g(et(s,f)),b(a=>st(s,f,a)),L(s,M,f)}const at=r==="apikey"&&f&&f.hasApiKey;return e(nt,null,e("div",{class:"view-head"},e("a",{href:"#/settings/providers",class:"view-back","aria-label":"Back to providers"},"←"),e("h2",{class:"view-title"},At)),e("section",null,A&&A.hint?e("p",{class:T?"notice notice--auth":"hint hint--compact"},A.hint):null,e("div",{class:"row"},e("label",{class:"label",for:"sp-id"},"Provider"),e("select",{class:"input",id:"sp-id",disabled:!!n,value:v,onChange:It},ot.map(t=>e("option",{value:t.id,key:t.id},t.label)))),e("div",{class:S(T)+" row--base"},e("label",{class:"label",for:"sp-base"},"API base URL"),e("input",{class:"input",id:"sp-base",type:"url",placeholder:"https://api.openai.com/v1",value:D,onInput:t=>b(t.target.value)})),e("div",{class:S(St)},e("label",{class:"label",for:"sp-auth"},"Authentication"),e("select",{class:"input",id:"sp-auth",value:O,onChange:t=>{g(t.target.value)}},e("option",{value:"apikey"},"API key"),J?e("option",{value:"oauth"},"OAuth"):null)),e("div",{class:S(!T)+" row__static-wrap"},e("div",{class:"row__static"},e("span",{class:"label"},"Authentication"),e("span",{class:"row__static-value"},"OAuth (required)"),e("span",{class:"row__static-note"},"This provider only supports OAuth sign-in."))),e("div",{class:S(O!=="apikey")+" row--apikey"},e("label",{class:"label",for:"sp-key"},"Provider API key"),e("input",{class:"input",id:"sp-key",type:"password",placeholder:"paste key",autocomplete:"off",value:F,onInput:t=>Q(t.target.value)})),e("p",{class:"hint hint--compact key-hint",hidden:!at},at?"a key is already saved for this provider; leave the field empty to keep it":""),e("div",{class:S(O!=="oauth")+" row--oauth"},e("label",{class:"label",for:"sp-account"},"OAuth account"),e("select",{class:"input",id:"sp-account",value:W,onChange:t=>X(t.target.value)},ct.map((t,s)=>e("option",{key:s,value:t.value,disabled:t.disabled},t.text)))),e("div",{class:S(O!=="oauth"||v!=="github-copilot")+" row--oauth row--copilot"},e("label",{class:"label",for:"sp-copilot-id"},"GitHub OAuth app client ID"),e("p",{class:"hint hint--compact"},"GitHub does not allow third-party apps to use the public Copilot client_id with a loopback callback. Create a personal OAuth app at ",e("code",null,"github.com/settings/developers")," (Developer settings → OAuth Apps → New OAuth App) with callback ",e("code",null,"http://127.0.0.1:5732/oauth/callback?provider=github-copilot"),", then paste its client_id here and Save before signing in. Leave blank to use the shipped default."),e("input",{class:"input",id:"sp-copilot-id",type:"text",placeholder:"Iv1.xxxxxxxxxxxxxxxx",autocomplete:"off",value:Y,onInput:t=>R(t.target.value)}),e("div",{class:"row row--actions"},e("button",{class:"btn",type:"button",onClick:wt},"Save client ID"),e("span",{class:"status"+(tt?" status--"+tt:""),"aria-live":"polite"},ft))),e("div",{class:S(O!=="oauth")+" row--oauth"},e("div",{class:"auth__help-inline"},e("p",{class:"hint hint--compact"},"Sign in to this provider below; the OAuth-account list refreshes automatically."),e("div",{class:"row row--actions"},e("button",{class:"btn",type:"button",onClick:gt},"Sign in"),e("span",{class:"status"+($?" status--"+$:""),"aria-live":"polite"},vt)))),e("div",{class:"row row--actions"},e("button",{class:"btn btn--primary",type:"button",disabled:pt,onClick:kt},n?"Save":"Add provider"),e("button",{class:"btn btn--danger",type:"button",disabled:dt,onClick:mt,hidden:!n},"Delete"),e("span",{class:"status"+(Z?" status--"+Z:""),"aria-live":"polite"},ht))))}export{Ot as SettingsProviderEditView,Tt as SettingsProvidersView};
1
+ import{d as i,p as m,h as z,k as e,e as ot,S as nt,l as rt,g as G,i as lt,s as _t,f as q,j as V,n as it}from"./index-BDkCsON6.js";function Tt(){const[x,n]=i(null),[v,I]=i(""),[f,C]=i("");async function U(){try{await rt({force:!0})}catch(g){I("load failed: "+g.message),C("error");return}const r=lt();n(r),r.length?(I(r.length+(r.length===1?" provider":" providers")),C("success")):(I("0 providers"),C(""))}return z(()=>{U()},[]),e(nt,null,e("div",{class:"view-head"},e("a",{href:"#/settings",class:"view-back","aria-label":"Back to settings"},"‹"),e("h2",{class:"view-title"},"Providers")),e("p",{class:"hint hint--compact"},"Credentials live in the app store. Each project's models reference one of these."),e("ul",{class:"providers__list","aria-label":"Configured providers"},x===null?null:x.length===0?e("li",{class:"providers__empty"},'No providers yet. Tap "Add provider" to configure your first connection.'):x.map(r=>{const g=m(r.id),B=g&&g.label||r.id,D=r.auth||"apikey",b=[];return r.baseUrl&&b.push(r.baseUrl),D==="oauth"?(b.push("OAuth"),r.oauthAccount&&b.push("as "+r.oauthAccount)):r.hasApiKey?b.push("key saved"):b.push("no key"),e("li",{class:"provider-row",key:r.id},e("a",{class:"provider-row__main",href:"#/settings/providers/"+encodeURIComponent(r.id)},e("div",{class:"provider-row__name"},B),e("div",{class:"provider-row__meta"},b.join(" · ")),e("div",{class:"provider-row__chev"},"›")))})),e("div",{class:"page-bar"},e("span",{class:"status page-bar__status"+(f?" status--"+f:""),"aria-live":"polite"},v),e("a",{href:"#/settings/providers/new",class:"page-bar__add","aria-label":"Add provider"},"+")))}function Ot(x){const n=x.id||"",[v,I]=i(n||"openai-compatible"),[f,C]=i(null),U=t=>{const s=m(t);return!!(s&&s.reserved)},[r,g]=i(U(n||"openai-compatible")?"oauth":"apikey"),B=(()=>{const t=m(n||"openai-compatible");return t&&t.defaultBaseUrl||""})(),[D,b]=i(B),[F,Q]=i(""),[W,X]=i(""),[Y,R]=i(""),[ct,ut]=i([]),[pt,_]=i(!1),[dt,E]=i(!1),[ht,c]=i(""),[Z,u]=i(""),[vt,w]=i(""),[$,k]=i(""),[ft,K]=i(""),[tt,N]=i(""),[M,j]=i({}),bt=()=>m(v);async function yt(){let t,s;try{t=await rt({force:!0}),s=await G({force:!0}),j(s)}catch(l){c("load failed: "+l.message),u("error");return}const a=n?lt().find(l=>l&&l.id===n):null;if(n&&!a){c("Provider not found"),u("error");return}C(a||null),t&&t.app&&t.app.githubCopilot&&t.app.githubCopilot.clientId?R(t.app.githubCopilot.clientId):R(""),g(et(v,a)),b(st(v,a,a&&a.baseUrl||"")),L(v,s,a),Q(""),c(""),u("")}const H=t=>{const s=m(t);return!!(s&&(s.oauth||s.reserved))};function et(t,s){return U(t)||s&&s.id===t&&s.auth==="oauth"&&H(t)?"oauth":(s&&s.id===t&&s.auth==="apikey","apikey")}function L(t=v,s=M,a=f){const l=V(t),p=(s[l]||[]).slice();let d=[],h="";p.length===0?d.push({value:"",text:"— no accounts yet; sign in below —",disabled:!0}):p.length===1?d.push({value:"",text:"(auto — "+p[0]+")"}):d.push({value:"",text:"(pick an account)",disabled:!0});for(const y of p)d.push({value:y,text:y});ut(d);const o=a&&a.oauthAccount||"";o&&p.includes(o)&&(h=o),X(h)}function st(t,s,a){const l=m(t),p=s&&s.id===t?s:null,d=p&&p.baseUrl||l&&l.defaultBaseUrl||"",h=(a||"").trim(),o=ot.map(y=>y.defaultBaseUrl).filter(Boolean);return!h||o.includes(h)?d:h}async function gt(){const t=v,s=m(t);if(!s){w("unknown provider"),k("error");return}if(!H(t)){w(s.label+" does not support OAuth sign-in; use an API key."),k("error");return}w("starting sign-in…"),k("busy");let a;try{a=await q("/api/auth/sign-in/"+encodeURIComponent(t),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({redirectUri:new URL("/oauth/callback",window.location.href).toString()})})}catch{w("network error"),k("error");return}if(a.status!==200){w("HTTP "+a.status+(a.body&&a.body.error?" — "+a.body.error:"")),k("error");return}const l=a.body.authorizeUrl;if(!window.open(l,"_blank")){try{sessionStorage.setItem("oauthPending",JSON.stringify({provider:t,started:Date.now()}))}catch{}window.location.href=l;return}w("waiting for "+s.label+" to redirect back…"),k("busy");const d=new Set((M[V(t)]||[]).slice()),h=Date.now();for(;Date.now()-h<5*60*1e3;){await new Promise(o=>setTimeout(o,1500));try{const o=await G({force:!0});j(o);const P=(o[V(t)]||[]).filter(Ct=>!d.has(Ct));if(P.length){L(t,o,f),w("signed in as "+P[0]),k("success");return}}catch{}}w("timed out. Paste the redirect URL or its code below."),k("error")}async function wt(){const t=Y.trim();K("saving…"),N("busy");try{await _t({githubCopilot:{clientId:t||null}}),K(t?"saved — you can sign in now.":"cleared (using default)."),N("success")}catch(s){K("save failed: "+s.message),N("error")}}async function kt(){_(!0),c("saving…"),u("busy");const t=v,s=m(t),a=s&&s.reserved?"oauth":r,l=(D||"").trim(),p=F.trim(),d=W.trim();if(a==="oauth"){let y=M;try{y=await G({force:!0}),j(y)}catch{}const P=y[V(t)]||[];if(!P.length){c("sign in to "+t+" first"),u("error"),_(!1);return}if(P.length>1&&!d){c("pick which signed-in account to use"),u("error"),_(!1);return}}if(a==="apikey"&&t!=="ollama"&&!p&&!(f&&f.hasApiKey)){c("API key is required for "+t),u("error"),_(!1);return}const h={id:t,auth:a};l&&(h.baseUrl=l),a==="apikey"&&p&&(h.apiKey=p),a==="oauth"&&d&&(h.oauthAccount=d);let o;try{o=await q("/api/settings/app/providers",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(h)})}catch{c("network error"),u("error"),_(!1);return}if(_(!1),o.status!==200){c("HTTP "+o.status+(o.body&&o.body.error?": "+o.body.error:"")),u("error");return}c("saved "+t),u("success"),it("settings/providers")}async function mt(){if(!n||!confirm('Delete provider "'+n+'"? Models in your projects that reference it will stop working until you re-add it.'))return;E(!0),c("deleting…"),u("busy");let t;try{t=await q("/api/settings/app/providers/"+encodeURIComponent(n),{method:"DELETE"})}catch{c("network error"),u("error"),E(!1);return}if(t.status!==200){c("HTTP "+t.status),u("error"),E(!1);return}it("settings/providers")}z(()=>{yt()},[n]),z(()=>{I(n||"openai-compatible"),C(null)},[n]);const A=bt(),At=n?A&&A.label||n:"Add provider",T=!!(A&&A.reserved),J=H(v),O=T?"oauth":J?r:"apikey",St=T||!J,S=t=>"row"+(t?" is-hidden":"");function It(t){const s=t.target.value;I(s),g(et(s,f)),b(a=>st(s,f,a)),L(s,M,f)}const at=r==="apikey"&&f&&f.hasApiKey;return e(nt,null,e("div",{class:"view-head"},e("a",{href:"#/settings/providers",class:"view-back","aria-label":"Back to providers"},"←"),e("h2",{class:"view-title"},At)),e("section",null,A&&A.hint?e("p",{class:T?"notice notice--auth":"hint hint--compact"},A.hint):null,e("div",{class:"row"},e("label",{class:"label",for:"sp-id"},"Provider"),e("select",{class:"input",id:"sp-id",disabled:!!n,value:v,onChange:It},ot.map(t=>e("option",{value:t.id,key:t.id},t.label)))),e("div",{class:S(T)+" row--base"},e("label",{class:"label",for:"sp-base"},"API base URL"),e("input",{class:"input",id:"sp-base",type:"url",placeholder:"https://api.openai.com/v1",value:D,onInput:t=>b(t.target.value)})),e("div",{class:S(St)},e("label",{class:"label",for:"sp-auth"},"Authentication"),e("select",{class:"input",id:"sp-auth",value:O,onChange:t=>{g(t.target.value)}},e("option",{value:"apikey"},"API key"),J?e("option",{value:"oauth"},"OAuth"):null)),e("div",{class:S(!T)+" row__static-wrap"},e("div",{class:"row__static"},e("span",{class:"label"},"Authentication"),e("span",{class:"row__static-value"},"OAuth (required)"),e("span",{class:"row__static-note"},"This provider only supports OAuth sign-in."))),e("div",{class:S(O!=="apikey")+" row--apikey"},e("label",{class:"label",for:"sp-key"},"Provider API key"),e("input",{class:"input",id:"sp-key",type:"password",placeholder:"paste key",autocomplete:"off",value:F,onInput:t=>Q(t.target.value)})),e("p",{class:"hint hint--compact key-hint",hidden:!at},at?"a key is already saved for this provider; leave the field empty to keep it":""),e("div",{class:S(O!=="oauth")+" row--oauth"},e("label",{class:"label",for:"sp-account"},"OAuth account"),e("select",{class:"input",id:"sp-account",value:W,onChange:t=>X(t.target.value)},ct.map((t,s)=>e("option",{key:s,value:t.value,disabled:t.disabled},t.text)))),e("div",{class:S(O!=="oauth"||v!=="github-copilot")+" row--oauth row--copilot"},e("label",{class:"label",for:"sp-copilot-id"},"GitHub OAuth app client ID"),e("p",{class:"hint hint--compact"},"GitHub does not allow third-party apps to use the public Copilot client_id with a loopback callback. Create a personal OAuth app at ",e("code",null,"github.com/settings/developers")," (Developer settings → OAuth Apps → New OAuth App) with callback ",e("code",null,"http://127.0.0.1:5732/oauth/callback?provider=github-copilot"),", then paste its client_id here and Save before signing in. Leave blank to use the shipped default."),e("input",{class:"input",id:"sp-copilot-id",type:"text",placeholder:"Iv1.xxxxxxxxxxxxxxxx",autocomplete:"off",value:Y,onInput:t=>R(t.target.value)}),e("div",{class:"row row--actions"},e("button",{class:"btn",type:"button",onClick:wt},"Save client ID"),e("span",{class:"status"+(tt?" status--"+tt:""),"aria-live":"polite"},ft))),e("div",{class:S(O!=="oauth")+" row--oauth"},e("div",{class:"auth__help-inline"},e("p",{class:"hint hint--compact"},"Sign in to this provider below; the OAuth-account list refreshes automatically."),e("div",{class:"row row--actions"},e("button",{class:"btn",type:"button",onClick:gt},"Sign in"),e("span",{class:"status"+($?" status--"+$:""),"aria-live":"polite"},vt)))),e("div",{class:"row row--actions"},e("button",{class:"btn btn--primary",type:"button",disabled:pt,onClick:kt},n?"Save":"Add provider"),e("button",{class:"btn btn--danger",type:"button",disabled:dt,onClick:mt,hidden:!n},"Delete"),e("span",{class:"status"+(Z?" status--"+Z:""),"aria-live":"polite"},ht))))}export{Ot as SettingsProviderEditView,Tt as SettingsProvidersView};
@@ -1 +1 @@
1
- import{d as y,h as C,f,k as s,S as P,V as D}from"./index-BGvI4n0T.js";import{c as H}from"./virtual-list-6H9b4K51.js";function U(x){const d=String(x||"").replace(/\\/g,"/").replace(/^\/+|\/+$/g,""),u=d.lastIndexOf("/");return{name:u>=0?d.slice(u+1):d,folder:u>=0?d.slice(0,u):"Project root"}}function V(x){const d=x.projectDir||"",[u,S]=y(x.projectId||""),[k,c]=y({text:"",kind:""}),[o,w]=y({}),[I,T]=y([]),[E,h]=y(!1),[b,M]=y(null);function j(){return u}function _(){return"/api/projects/"+encodeURIComponent(j())+"/tags"}async function R(){if(!j()){c({text:"no project id",kind:"error"});return}h(!0),c({text:"scanning…",kind:"busy"});let e;try{e=await f(_()+"/scan",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({})})}catch{c({text:"network error",kind:"error"}),h(!1);return}if(h(!1),e.status!==200){c({text:"HTTP "+e.status+(e.body&&e.body.error?" — "+e.body.error:""),kind:"error"});return}T(e.body.files||[]);const t=Object.keys(o).length;c({text:e.body.files.length+" files · "+t+" tagged",kind:"success"})}async function m(e){let t;try{t=await f(_(),{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({tags:e})})}catch{return c({text:"network error",kind:"error"}),!1}return t.status!==200?(c({text:"save failed: HTTP "+t.status,kind:"error"}),!1):(w(t.body.tags||{}),!0)}function L(){const e=new Map;for(const t of I)e.set(t.path,{path:t.path,size:t.size,binary:!!t.binary,present:!0});for(const t of Object.keys(o))e.has(t)||e.set(t,{path:t,size:0,binary:!1,present:!1});return[...e.values()].sort((t,p)=>t.path.toLowerCase().localeCompare(p.path.toLowerCase()))}C(()=>{const e=document.getElementById("tags-list-container");if(!e)return;const t=L();if(e.classList.toggle("is-empty",!t.length),!t.length){b&&b.setData([]),e.dataset.emptyText="No files scanned yet. Tap Scan to list the project’s text files.";return}delete e.dataset.emptyText,b&&b.setData(t)},[I,o,b]);function N(e){const t=o[e.path]||null,p=!!t,l=U(e.path);return s("div",{class:"tags__row"+(p?" is-tagged":"")+(e.binary?" is-binary":"")},s("div",{class:"tags__row-head"},s("div",{class:"tags__row-path",title:e.path},s("span",{class:"tags__row-name"},l.name),s("span",{class:"tags__row-folder"},l.folder)),e.present?e.binary?s("span",{class:"tags__badge"},"binary"):null:s("span",{class:"tags__badge tags__badge--missing"},"missing")),e.binary&&!t?null:s(P,null,s("div",{class:"tags__chips"},(t&&t.tags||[]).map(n=>s("span",{key:n,class:"tags__chip"},n,s("button",{type:"button",class:"tags__chip-x","aria-label":"Remove tag "+n,onClick:async()=>{const r={...o},a=r[e.path];a&&(a.tags=a.tags.filter(i=>i!==n),!a.tags.length&&a.excerpt==null&&a.includeInChat&&delete r[e.path],await m(r))}},"×"))),s("input",{class:"tags__chip-input",type:"text",placeholder:t&&t.tags&&t.tags.length?"add tag…":"type a tag, Enter to add",onKeyDown:async n=>{if(n.key==="Enter"||n.key===","){n.preventDefault();const r=n.target.value.trim().replace(/,+$/,"");if(!r)return;const a={...o},i=a[e.path]||{tags:[],excerpt:null,includeInChat:!0};i.tags.includes(r)||i.tags.push(r),a[e.path]=i,n.target.value="",await m(a)}}})),s("div",{class:"tags__controls"},s("label",{class:"tags__toggle"},s("input",{type:"checkbox",class:"checkbox",checked:t?t.includeInChat!==!1:!0,onChange:async n=>{const r={...o},a=r[e.path]||{tags:[],excerpt:null,includeInChat:!0};a.includeInChat=n.target.checked,r[e.path]=a,await m(r)}})," Include in chat"),s("div",{class:"tags__excerpt"},"Lines ",s("input",{type:"number",min:"1",class:"tags__excerpt-num",placeholder:"start",value:t&&t.excerpt?t.excerpt.start:"",onChange:async n=>{const r={...o},a=r[e.path]||{tags:[],excerpt:null,includeInChat:!0},i=parseInt(n.target.value,10),g=document.getElementById(`tags-ex-end-${encodeURIComponent(e.path)}`),O=g?g.value:t&&t.excerpt?t.excerpt.end:"",v=parseInt(O,10);Number.isInteger(i)&&Number.isInteger(v)&&i>=1&&v>=i?a.excerpt={start:i,end:v}:a.excerpt=null,r[e.path]=a,await m(r)}}),"–",s("input",{id:`tags-ex-end-${encodeURIComponent(e.path)}`,type:"number",min:"1",class:"tags__excerpt-num",placeholder:"end",value:t&&t.excerpt?t.excerpt.end:"",onChange:async n=>{const r={...o},a=r[e.path]||{tags:[],excerpt:null,includeInChat:!0},i=parseInt(n.target.value,10),g=t&&t.excerpt?t.excerpt.start:"";Number.isInteger(g)&&Number.isInteger(i)&&g>=1&&i>=g?a.excerpt={start:g,end:i}:a.excerpt=null,r[e.path]=a,await m(r)}})),s("button",{type:"button",class:"btn btn--small","data-danger":"1",disabled:!t,onClick:async()=>{if(!o[e.path])return;let n;try{n=await f(_()+"/files/"+e.path.split("/").map(encodeURIComponent).join("/"),{method:"DELETE"})}catch{c({text:"network error",kind:"error"});return}if(n.status!==200&&n.status!==404){c({text:"remove failed: HTTP "+n.status,kind:"error"});return}const r={...o};delete r[e.path],w(r),c({text:"removed "+e.path,kind:"success"})}},"Remove"))))}return C(()=>{const e=document.getElementById("tags-list-container");if(!e)return;const t=H({scroller:e,itemHeight:248,overscan:3,data:[],render:(p,l)=>{l.className="tags__virtual-slot",D(N(p),l)}});return M(t),(async()=>{let p=u;if(!p&&d){try{const l=await f("/api/projects/registered");if(l.status===200&&Array.isArray(l.body.projects)){const n=l.body.projects.find(r=>r&&r.path===d);n&&(p=n.id,S(n.id))}}catch{}if(!p){c({text:"this folder is not a registered project — register it first",kind:"error"});return}}if(p){const l=()=>"/api/projects/"+encodeURIComponent(p)+"/tags",n=async()=>{try{const a=await f(l());a.status===200?w(a.body.tags||{}):c({text:"HTTP "+a.status,kind:"error"})}catch{c({text:"network error",kind:"error"})}},r=async()=>{h(!0),c({text:"scanning…",kind:"busy"});try{const a=await f(l()+"/scan",{method:"POST",headers:{"Content-Type":"application/json"},body:"{}"});h(!1),a.status===200?(T(a.body.files||[]),c({text:a.body.files.length+" files · "+Object.keys(o).length+" tagged",kind:"success"})):c({text:"HTTP "+a.status,kind:"error"})}catch{c({text:"network error",kind:"error"}),h(!1)}};await n(),await r()}})().catch(()=>c({text:"load failed",kind:"error"})),()=>{t&&t.destroy()}},[d]),s(P,null,s("div",{class:"view-head"},s("a",{href:"#/projects",class:"view-back","aria-label":"Back to projects"},"‹"),s("h2",{class:"view-title"},"File tags")),s("p",{class:"hint hint--compact"},d||"(project)"),s("p",{class:"hint hint--compact"},"Tag project files, then toggle “Include in chat” to auto-inject them into every chat send. Reference one explicitly with @path in the composer."),s("div",{id:"tags-list-container",class:"tags__list",role:"list","aria-label":"Project files"}),s("div",{class:"page-bar"},s("span",{class:"status page-bar__status"+(k.kind?" status--"+k.kind:""),"aria-live":"polite"},k.text),s("button",{class:"btn",type:"button",onClick:R,disabled:E},"Scan")))}export{V as SettingsTagsView};
1
+ import{d as y,h as C,f,k as s,S as P,V as D}from"./index-BDkCsON6.js";import{c as H}from"./virtual-list-6H9b4K51.js";function U(x){const d=String(x||"").replace(/\\/g,"/").replace(/^\/+|\/+$/g,""),u=d.lastIndexOf("/");return{name:u>=0?d.slice(u+1):d,folder:u>=0?d.slice(0,u):"Project root"}}function V(x){const d=x.projectDir||"",[u,S]=y(x.projectId||""),[k,c]=y({text:"",kind:""}),[o,w]=y({}),[I,T]=y([]),[E,h]=y(!1),[b,M]=y(null);function j(){return u}function _(){return"/api/projects/"+encodeURIComponent(j())+"/tags"}async function R(){if(!j()){c({text:"no project id",kind:"error"});return}h(!0),c({text:"scanning…",kind:"busy"});let e;try{e=await f(_()+"/scan",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({})})}catch{c({text:"network error",kind:"error"}),h(!1);return}if(h(!1),e.status!==200){c({text:"HTTP "+e.status+(e.body&&e.body.error?" — "+e.body.error:""),kind:"error"});return}T(e.body.files||[]);const t=Object.keys(o).length;c({text:e.body.files.length+" files · "+t+" tagged",kind:"success"})}async function m(e){let t;try{t=await f(_(),{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({tags:e})})}catch{return c({text:"network error",kind:"error"}),!1}return t.status!==200?(c({text:"save failed: HTTP "+t.status,kind:"error"}),!1):(w(t.body.tags||{}),!0)}function L(){const e=new Map;for(const t of I)e.set(t.path,{path:t.path,size:t.size,binary:!!t.binary,present:!0});for(const t of Object.keys(o))e.has(t)||e.set(t,{path:t,size:0,binary:!1,present:!1});return[...e.values()].sort((t,p)=>t.path.toLowerCase().localeCompare(p.path.toLowerCase()))}C(()=>{const e=document.getElementById("tags-list-container");if(!e)return;const t=L();if(e.classList.toggle("is-empty",!t.length),!t.length){b&&b.setData([]),e.dataset.emptyText="No files scanned yet. Tap Scan to list the project’s text files.";return}delete e.dataset.emptyText,b&&b.setData(t)},[I,o,b]);function N(e){const t=o[e.path]||null,p=!!t,l=U(e.path);return s("div",{class:"tags__row"+(p?" is-tagged":"")+(e.binary?" is-binary":"")},s("div",{class:"tags__row-head"},s("div",{class:"tags__row-path",title:e.path},s("span",{class:"tags__row-name"},l.name),s("span",{class:"tags__row-folder"},l.folder)),e.present?e.binary?s("span",{class:"tags__badge"},"binary"):null:s("span",{class:"tags__badge tags__badge--missing"},"missing")),e.binary&&!t?null:s(P,null,s("div",{class:"tags__chips"},(t&&t.tags||[]).map(n=>s("span",{key:n,class:"tags__chip"},n,s("button",{type:"button",class:"tags__chip-x","aria-label":"Remove tag "+n,onClick:async()=>{const r={...o},a=r[e.path];a&&(a.tags=a.tags.filter(i=>i!==n),!a.tags.length&&a.excerpt==null&&a.includeInChat&&delete r[e.path],await m(r))}},"×"))),s("input",{class:"tags__chip-input",type:"text",placeholder:t&&t.tags&&t.tags.length?"add tag…":"type a tag, Enter to add",onKeyDown:async n=>{if(n.key==="Enter"||n.key===","){n.preventDefault();const r=n.target.value.trim().replace(/,+$/,"");if(!r)return;const a={...o},i=a[e.path]||{tags:[],excerpt:null,includeInChat:!0};i.tags.includes(r)||i.tags.push(r),a[e.path]=i,n.target.value="",await m(a)}}})),s("div",{class:"tags__controls"},s("label",{class:"tags__toggle"},s("input",{type:"checkbox",class:"checkbox",checked:t?t.includeInChat!==!1:!0,onChange:async n=>{const r={...o},a=r[e.path]||{tags:[],excerpt:null,includeInChat:!0};a.includeInChat=n.target.checked,r[e.path]=a,await m(r)}})," Include in chat"),s("div",{class:"tags__excerpt"},"Lines ",s("input",{type:"number",min:"1",class:"tags__excerpt-num",placeholder:"start",value:t&&t.excerpt?t.excerpt.start:"",onChange:async n=>{const r={...o},a=r[e.path]||{tags:[],excerpt:null,includeInChat:!0},i=parseInt(n.target.value,10),g=document.getElementById(`tags-ex-end-${encodeURIComponent(e.path)}`),O=g?g.value:t&&t.excerpt?t.excerpt.end:"",v=parseInt(O,10);Number.isInteger(i)&&Number.isInteger(v)&&i>=1&&v>=i?a.excerpt={start:i,end:v}:a.excerpt=null,r[e.path]=a,await m(r)}}),"–",s("input",{id:`tags-ex-end-${encodeURIComponent(e.path)}`,type:"number",min:"1",class:"tags__excerpt-num",placeholder:"end",value:t&&t.excerpt?t.excerpt.end:"",onChange:async n=>{const r={...o},a=r[e.path]||{tags:[],excerpt:null,includeInChat:!0},i=parseInt(n.target.value,10),g=t&&t.excerpt?t.excerpt.start:"";Number.isInteger(g)&&Number.isInteger(i)&&g>=1&&i>=g?a.excerpt={start:g,end:i}:a.excerpt=null,r[e.path]=a,await m(r)}})),s("button",{type:"button",class:"btn btn--small","data-danger":"1",disabled:!t,onClick:async()=>{if(!o[e.path])return;let n;try{n=await f(_()+"/files/"+e.path.split("/").map(encodeURIComponent).join("/"),{method:"DELETE"})}catch{c({text:"network error",kind:"error"});return}if(n.status!==200&&n.status!==404){c({text:"remove failed: HTTP "+n.status,kind:"error"});return}const r={...o};delete r[e.path],w(r),c({text:"removed "+e.path,kind:"success"})}},"Remove"))))}return C(()=>{const e=document.getElementById("tags-list-container");if(!e)return;const t=H({scroller:e,itemHeight:248,overscan:3,data:[],render:(p,l)=>{l.className="tags__virtual-slot",D(N(p),l)}});return M(t),(async()=>{let p=u;if(!p&&d){try{const l=await f("/api/projects/registered");if(l.status===200&&Array.isArray(l.body.projects)){const n=l.body.projects.find(r=>r&&r.path===d);n&&(p=n.id,S(n.id))}}catch{}if(!p){c({text:"this folder is not a registered project — register it first",kind:"error"});return}}if(p){const l=()=>"/api/projects/"+encodeURIComponent(p)+"/tags",n=async()=>{try{const a=await f(l());a.status===200?w(a.body.tags||{}):c({text:"HTTP "+a.status,kind:"error"})}catch{c({text:"network error",kind:"error"})}},r=async()=>{h(!0),c({text:"scanning…",kind:"busy"});try{const a=await f(l()+"/scan",{method:"POST",headers:{"Content-Type":"application/json"},body:"{}"});h(!1),a.status===200?(T(a.body.files||[]),c({text:a.body.files.length+" files · "+Object.keys(o).length+" tagged",kind:"success"})):c({text:"HTTP "+a.status,kind:"error"})}catch{c({text:"network error",kind:"error"}),h(!1)}};await n(),await r()}})().catch(()=>c({text:"load failed",kind:"error"})),()=>{t&&t.destroy()}},[d]),s(P,null,s("div",{class:"view-head"},s("a",{href:"#/projects",class:"view-back","aria-label":"Back to projects"},"‹"),s("h2",{class:"view-title"},"File tags")),s("p",{class:"hint hint--compact"},d||"(project)"),s("p",{class:"hint hint--compact"},"Tag project files, then toggle “Include in chat” to auto-inject them into every chat send. Reference one explicitly with @path in the composer."),s("div",{id:"tags-list-container",class:"tags__list",role:"list","aria-label":"Project files"}),s("div",{class:"page-bar"},s("span",{class:"status page-bar__status"+(k.kind?" status--"+k.kind:""),"aria-live":"polite"},k.text),s("button",{class:"btn",type:"button",onClick:R,disabled:E},"Scan")))}export{V as SettingsTagsView};