mouaif 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +140 -0
  3. package/bin/mouaif.js +281 -0
  4. package/frontend/dist/assets/AgentFilePicker-CcKLJorU.js +1 -0
  5. package/frontend/dist/assets/CliModal-Hs5phmNZ.js +7 -0
  6. package/frontend/dist/assets/DictationPage-BI23lp42.js +2 -0
  7. package/frontend/dist/assets/FileEditor-DDl31c6d.js +2 -0
  8. package/frontend/dist/assets/GitModal-3EC_gpJ5.js +2 -0
  9. package/frontend/dist/assets/Inspector-Ba3R1w04.js +73 -0
  10. package/frontend/dist/assets/SettingsAbout-bvZGDEDw.js +1 -0
  11. package/frontend/dist/assets/SettingsActions-Dk6WX9jv.js +1 -0
  12. package/frontend/dist/assets/SettingsAgents-BNV0MgDB.js +1 -0
  13. package/frontend/dist/assets/SettingsDefaults-DbMmQbzc.js +1 -0
  14. package/frontend/dist/assets/SettingsHiddenContent-BZ2sloH1.js +1 -0
  15. package/frontend/dist/assets/SettingsMcp-DOrfbQd1.js +1 -0
  16. package/frontend/dist/assets/SettingsMcpEdit-BGMQ2CWC.js +3 -0
  17. package/frontend/dist/assets/SettingsMcpRegistry-BywXee_A.js +1 -0
  18. package/frontend/dist/assets/SettingsNotifications-B0LEs11a.js +1 -0
  19. package/frontend/dist/assets/SettingsPricing-BAg33iVF.js +1 -0
  20. package/frontend/dist/assets/SettingsProject-DNrKhCcZ.js +14 -0
  21. package/frontend/dist/assets/SettingsProjects-IqkBfDcm.js +1 -0
  22. package/frontend/dist/assets/SettingsPrompts-BgeiASuk.js +1 -0
  23. package/frontend/dist/assets/SettingsProviders-k0xJN0IK.js +1 -0
  24. package/frontend/dist/assets/SettingsTags-B5kjFdQi.js +1 -0
  25. package/frontend/dist/assets/agentNavigation-BiiCpFz5.js +1 -0
  26. package/frontend/dist/assets/codemirror-Bp6CUUFk.js +30 -0
  27. package/frontend/dist/assets/index-BGvI4n0T.js +61 -0
  28. package/frontend/dist/assets/index-Bgg1gnDf.css +1 -0
  29. package/frontend/dist/assets/index-C1sQFIC-.css +1 -0
  30. package/frontend/dist/assets/index-CANPYzQg.css +1 -0
  31. package/frontend/dist/assets/index-Crn1LdzK.css +1 -0
  32. package/frontend/dist/assets/index-FbCWDPiB.css +1 -0
  33. package/frontend/dist/assets/projectQS-D1cSZ7Gr.js +1 -0
  34. package/frontend/dist/assets/virtual-list-6H9b4K51.js +1 -0
  35. package/frontend/dist/icons/favicon-32.png +0 -0
  36. package/frontend/dist/icons/icon-180-apple.png +0 -0
  37. package/frontend/dist/icons/icon-192.png +0 -0
  38. package/frontend/dist/icons/icon-512.png +0 -0
  39. package/frontend/dist/icons/icon-maskable-512.png +0 -0
  40. package/frontend/dist/index.html +83 -0
  41. package/frontend/dist/manifest.webmanifest +33 -0
  42. package/frontend/dist/sw.js +482 -0
  43. package/package.json +98 -0
  44. package/scripts/patch-zimmerframe.js +58 -0
  45. package/src/access-auth.js +515 -0
  46. package/src/agentFeatures.js +294 -0
  47. package/src/agentFiles.js +164 -0
  48. package/src/agentSkills.js +147 -0
  49. package/src/agents.js +230 -0
  50. package/src/ai-chat.js +21 -0
  51. package/src/ai-endpoints.js +1880 -0
  52. package/src/ai-stream.js +2048 -0
  53. package/src/ai.js +68 -0
  54. package/src/auth.js +391 -0
  55. package/src/chatdb.js +816 -0
  56. package/src/chats.js +275 -0
  57. package/src/custom-actions.js +65 -0
  58. package/src/files.js +431 -0
  59. package/src/hideFileContent.js +327 -0
  60. package/src/http-server.js +535 -0
  61. package/src/index.js +15 -0
  62. package/src/inspector.js +731 -0
  63. package/src/inspectorProfiles.js +503 -0
  64. package/src/live-chat.js +107 -0
  65. package/src/mcp.js +1517 -0
  66. package/src/messages.js +238 -0
  67. package/src/modelList.js +137 -0
  68. package/src/notifications.js +52 -0
  69. package/src/oauth-anthropic.js +280 -0
  70. package/src/oauth-github-copilot.js +417 -0
  71. package/src/oauth-mcp.js +216 -0
  72. package/src/oauth-openrouter.js +285 -0
  73. package/src/package-version.js +20 -0
  74. package/src/projects.js +285 -0
  75. package/src/promptProfiles.js +256 -0
  76. package/src/prompts.js +384 -0
  77. package/src/providerShapes.js +44 -0
  78. package/src/providers/base.js +41 -0
  79. package/src/providers/index.js +25 -0
  80. package/src/push.js +315 -0
  81. package/src/qr.js +192 -0
  82. package/src/restart.js +47 -0
  83. package/src/server-handlers-access.js +306 -0
  84. package/src/server-handlers-actions.js +100 -0
  85. package/src/server-handlers-ai.js +248 -0
  86. package/src/server-handlers-auth.js +273 -0
  87. package/src/server-handlers-chats.js +1436 -0
  88. package/src/server-handlers-git.js +467 -0
  89. package/src/server-handlers-mcp-oauth.js +56 -0
  90. package/src/server-handlers-misc.js +783 -0
  91. package/src/server-handlers-projects.js +289 -0
  92. package/src/server-handlers-prompts.js +259 -0
  93. package/src/server-handlers-push.js +102 -0
  94. package/src/server-handlers-settings.js +406 -0
  95. package/src/server-handlers-tools.js +654 -0
  96. package/src/server-handlers-transcribe.js +399 -0
  97. package/src/server-shared.js +780 -0
  98. package/src/server-web-static.js +191 -0
  99. package/src/settings.js +898 -0
  100. package/src/statusBar.js +541 -0
  101. package/src/tags.js +414 -0
  102. package/src/toolFeedback.js +225 -0
  103. package/src/tools/ask.js +154 -0
  104. package/src/tools/authorization.js +932 -0
  105. package/src/tools/files.js +1150 -0
  106. package/src/tools/progress.js +71 -0
  107. package/src/tools/restart.js +32 -0
  108. package/src/tools/searchEngine.js +957 -0
  109. package/src/tools/shell.js +341 -0
  110. package/src/tools/subagent.js +47 -0
  111. package/src/tools/task.js +234 -0
  112. package/src/tools/webpreview.js +448 -0
  113. package/src/trace.js +103 -0
  114. package/src/transcribe.js +683 -0
  115. package/src/usage.js +389 -0
  116. package/src/util.js +151 -0
@@ -0,0 +1 @@
1
+ import{d as s,h as d,k as e,S as f,l as g,J as m}from"./index-BGvI4n0T.js";function v(){const[n,c]=s("—"),[r,p]=s("—"),[u,o]=s(!1),[a,l]=s({message:"",type:""});async function i(){try{const t=await g({force:!0});t.home&&c(t.home),t.defaults&&p(JSON.stringify(t.defaults,null,2))}catch{}}async function h(){if(confirm("Reset ALL app-level settings to defaults? Every provider, model, account, and project you registered at the app level will be cleared. Project files on disk are not touched.")){o(!0),l({message:"resetting…",type:"busy"});try{await m(["providers","models","authAccounts","projects","promptSize","flags"]),l({message:"reset.",type:"success"}),await i()}catch(t){l({message:"reset failed: "+t.message,type:"error"})}o(!1)}}return d(()=>{i()},[]),e(f,null,e("div",{class:"view-head"},e("a",{href:"#/settings",class:"view-back","aria-label":"Back to settings"},"←"),e("h2",{class:"view-title"},"About")),e("section",null,e("h3",null,"Storage"),e("p",{class:"hint hint--compact"},"App-level settings and the account index live in this SQLite database:"),e("pre",{class:"settings__out"},n),e("h3",null,"Default values"),e("p",{class:"hint hint--compact"},"The merge floor for every project. Anything not set in app or project falls back to these."),e("pre",{class:"settings__out"},r),e("h3",null,"Destructive actions"),e("p",{class:"hint hint--compact"},"Reset all app-level keys. Project files on disk are not touched."),e("div",{class:"row row--actions"},e("button",{class:"btn btn--danger",type:"button",onClick:h,disabled:u},"Reset all app settings"),e("span",{class:`status${a.type?" status--"+a.type:""}`,"aria-live":"polite"},a.message))))}export{v as SettingsAboutView};
@@ -0,0 +1 @@
1
+ import{d as b,h as w,f as v,k as t,S as y,n as T}from"./index-BGvI4n0T.js";function k(n,a=0){if(!n||typeof n!="object"||a>12)return null;if(Object.prototype.hasOwnProperty.call(n,"default"))return n.default;if(Object.prototype.hasOwnProperty.call(n,"const"))return n.const;if(Array.isArray(n.examples)&&n.examples.length)return n.examples[0];if(Array.isArray(n.enum)&&n.enum.length)return n.enum[0];const u=Array.isArray(n.oneOf)&&n.oneOf[0]||Array.isArray(n.anyOf)&&n.anyOf[0];if(u)return k(u,a+1);const i=Array.isArray(n.type)?n.type.find(c=>c!=="null"):n.type;if(i==="object"||n.properties){const c={};for(const[p,o]of Object.entries(n.properties||{}))c[p]=k(o,a+1);return c}return i==="array"?[]:i==="boolean"?!1:i==="integer"||i==="number"?0:i==="null"?null:""}function R(n){return JSON.stringify(k(n)||{},null,2)}function f(n){return"?projectDir="+encodeURIComponent(n||"")}function I(){return{id:"",label:"",description:"",kind:"cli",command:"",timeoutMs:"",serverId:"",toolName:"",argsText:"{}"}}function J(n){return{...I(),...n,timeoutMs:n.timeoutMs||"",argsText:JSON.stringify(n.args||{},null,2)}}function j({projectDir:n="",from:a=""}){const[u,i]=b([]),[c,p]=b("loading…");return w(()=>{let o=!1;return v("/api/actions?projectDir="+encodeURIComponent(n)).then(l=>{o||(l.status===200?(i(l.body.actions||[]),p("")):p(l.body&&l.body.error||"HTTP "+l.status))}).catch(l=>{o||p(String(l))}),()=>{o=!0}},[n]),t(y,null,t("div",{class:"view-head"},t("a",{href:"#/settings/project"+f(n)+(a?"&from="+encodeURIComponent(a):""),class:"view-back","aria-label":"Back to project settings"},"←"),t("h2",{class:"view-title"},"Custom actions")),t("section",null,t("p",{class:"hint hint--compact"},"Project shortcuts backed by a CLI command or MCP tool. Pick one by name from the composer menu or type ",t("code",null,"@action-id"),"."),t("ul",{class:"prompts__list","aria-label":"Custom actions"},u.length?u.map(o=>t("li",{key:o.id,class:"prompt-row"},t("a",{class:"prompt-row__main",href:"#/settings/actions/"+encodeURIComponent(o.id)+f(n)},t("div",{class:"prompt-row__title"},o.label||o.id),t("div",{class:"prompt-row__meta"},"@"+o.id+" · "+(o.kind==="mcp"?o.serverId+" / "+o.toolName:o.command)),t("div",{class:"prompt-row__chev","aria-hidden":"true"},"›")))):t("li",{class:"prompts__empty"},c||"No custom actions yet.")),t("div",{class:"row row--actions"},t("a",{class:"btn btn--primary",href:"#/settings/actions/new"+f(n)},"+ Add action"),t("span",{class:"status","aria-live":"polite"},c))))}function E({id:n="",projectDir:a="",from:u=""}){const i=!n||n==="new",c=u?"&from="+encodeURIComponent(u):"",p="#/settings/actions"+f(a)+c,[o,l]=b(I),[g,A]=b([]),[h,S]=b([]),[_,d]=b(i?"":"loading…");w(()=>{let e=!1;return Promise.all([v("/api/actions?projectDir="+encodeURIComponent(a)),v("/api/mcp/servers?projectDir="+encodeURIComponent(a))]).then(([s,r])=>{if(!e&&(r.status===200&&A(r.body.servers||[]),!i&&s.status===200)){const C=(s.body.actions||[]).find(P=>P.id===n);C?(l(J(C)),d("")):d("Action not found")}}).catch(s=>{e||d(String(s))}),()=>{e=!0}},[a,n,i]),w(()=>{const e=g.find(s=>s.id===o.serverId);S(e&&Array.isArray(e.tools)?e.tools:[])},[g,o.serverId]);function m(e,s){l(r=>({...r,[e]:s}))}function O(e){l(s=>({...s,serverId:e,toolName:"",argsText:"{}"}))}function M(e){const s=h.find(r=>r&&r.name===e);l(r=>({...r,toolName:e,argsText:s?R(s.inputSchema||s.parameters):"{}"}))}async function x(){let e={};if(o.kind==="mcp"){try{e=JSON.parse(o.argsText||"{}")}catch{d("MCP arguments must be valid JSON");return}if(!e||Array.isArray(e)||typeof e!="object"){d("MCP arguments must be a JSON object");return}}const s={id:o.id.trim(),label:o.label.trim(),description:o.description.trim(),kind:o.kind,...o.kind==="cli"?{command:o.command.trim(),...o.timeoutMs?{timeoutMs:Number(o.timeoutMs)}:{}}:{serverId:o.serverId,toolName:o.toolName,args:e}};d("saving…");const r=await v("/api/actions",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectDir:a,originalId:i?"":n,action:s})});if(r.status!==200){d(r.body&&r.body.error||"HTTP "+r.status);return}T("settings/actions"+f(a)+c)}async function N(){if(!confirm('Delete custom action "'+n+'"?'))return;const e=await v("/api/actions/"+encodeURIComponent(n)+f(a),{method:"DELETE"});e.status===200?T("settings/actions"+f(a)+c):d(e.body&&e.body.error||"HTTP "+e.status)}return t(y,null,t("div",{class:"view-head"},t("a",{href:p,class:"view-back","aria-label":"Back to custom actions"},"←"),t("h2",{class:"view-title"},i?"Add action":o.label||o.id||"Edit action")),t("section",null,t("div",{class:"row"},t("label",{class:"label",for:"action-id"},"Action ID"),t("input",{class:"input",id:"action-id",value:o.id,placeholder:"test",onInput:e=>m("id",e.currentTarget.value),autocapitalize:"off",spellcheck:!1}),t("p",{class:"hint hint--compact"},"Used as ",t("code",null,"@action-id"),". Letters, digits, dot, underscore, and dash only.")),t("div",{class:"row"},t("label",{class:"label",for:"action-label"},"Label"),t("input",{class:"input",id:"action-label",value:o.label,placeholder:"Run tests",onInput:e=>m("label",e.currentTarget.value)})),t("div",{class:"row"},t("label",{class:"label",for:"action-description"},"Description"),t("input",{class:"input",id:"action-description",value:o.description,placeholder:"What this action does",onInput:e=>m("description",e.currentTarget.value)})),t("div",{class:"row"},t("span",{class:"label"},"Runs with"),t("div",{class:"seg",role:"radiogroup","aria-label":"Action type"},[["cli","CLI"],["mcp","MCP"]].map(([e,s])=>t("label",{key:e,class:"seg__item"+(o.kind===e?" seg__item--on":"")},t("input",{type:"radio",name:"action-kind",value:e,checked:o.kind===e,onChange:()=>m("kind",e)}),t("span",{class:"seg__pill"},s))))),o.kind==="cli"?t(y,null,t("div",{class:"row"},t("label",{class:"label",for:"action-command"},"Command"),t("textarea",{class:"input settings-project__mono",id:"action-command",rows:4,value:o.command,placeholder:"npm test",onInput:e=>m("command",e.currentTarget.value),spellcheck:!1})),t("div",{class:"row"},t("label",{class:"label",for:"action-timeout"},"Timeout (ms, optional)"),t("input",{class:"input",id:"action-timeout",type:"number",min:1,max:6e5,inputmode:"numeric",value:o.timeoutMs,placeholder:"30000",onInput:e=>m("timeoutMs",e.currentTarget.value)}))):t(y,null,t("div",{class:"row"},t("label",{class:"label",for:"action-server"},"MCP server"),t("select",{class:"input",id:"action-server",value:o.serverId,onChange:e=>O(e.currentTarget.value)},t("option",{value:""},"Choose a server"),g.map(e=>t("option",{key:e.id,value:e.id},e.name||e.id)))),t("div",{class:"row"},t("label",{class:"label",for:"action-tool"},"MCP tool"),t("select",{class:"input",id:"action-tool",value:o.toolName,onChange:e=>M(e.currentTarget.value)},t("option",{value:""},h.length?"Choose a tool":"Start server to discover tools"),h.map(e=>t("option",{key:e.name,value:e.name},e.name)))),t("div",{class:"row"},t("label",{class:"label",for:"action-args"},"Arguments (JSON)"),t("textarea",{class:"input settings-project__mono",id:"action-args",rows:6,value:o.argsText,onInput:e=>m("argsText",e.currentTarget.value),spellcheck:!1}),t("p",{class:"hint hint--compact"},"Selecting a tool prefills this object from its input schema. Replace placeholder values before saving."))),t("div",{class:"row row--actions"},t("button",{class:"btn btn--primary",type:"button",onClick:x},"Save"),!i&&t("button",{class:"btn btn--danger",type:"button",onClick:N},"Delete"),t("span",{class:"status","aria-live":"polite"},_))))}export{E as SettingsActionEditView,j as SettingsActionsView};
@@ -0,0 +1 @@
1
+ import{A as q,d as v,h as E,f as _,O as Y,k as e,S as P,Q as fe,R as ge,U as he,q as me,b as $,n as X}from"./index-BGvI4n0T.js";import{b as be,a as G,c as ee}from"./agentNavigation-BiiCpFz5.js";function ve({name:l,save:n,onStatus:p,onSaved:c,delay:h=350}){let f=l,r={},g=null,s=null,a=!1;function m(){g!==null&&clearTimeout(g),g=null}async function O(){for(;Object.keys(r).length;){m();const I=r;r={},p({text:"saving…",kind:"busy"});let T;try{T=await n(f,I)}catch(U){return r={...I,...r},m(),a=!0,p({text:U.message||"Could not save",kind:"error"}),!1}f=T.name,Object.keys(r).length||(c(T),p({text:"saved",kind:"success"}))}return!0}function C(){return m(),s||(a=!1,s=O().finally(()=>{s=null}),s)}function R(I,T=!1){if(r={...r,...I},a=!1,m(),p({text:"unsaved changes",kind:"busy"}),T)return C();g=setTimeout(C,h)}return{enqueue:R,flush:C,get name(){return f},get failed(){return a}}}function te(l){return l&&l.projectDir?l.projectDir:$.value&&$.value.dir||""}const ye=[{value:"shell",label:"shell"},{value:"subagent",label:"subagent"},{value:"task",label:"task"},{value:"webpreview",label:"webpreview"},{value:"restart_app",label:"restart_app"},{value:"report_progress",label:"report_progress"},{value:"ask_user",label:"ask_user"},{value:"list_features",label:"list_features"},{value:"read_file",label:"read_file"},{value:"list_files",label:"list_files"},{value:"search_files",label:"search_files"},{value:"write_file",label:"write_file"},{value:"edit_file",label:"edit_file"}];function we(l){return ye.concat((l||[]).filter(n=>n&&n.id).map(n=>({value:"mcp__"+(n.slug||n.id),label:"MCP: "+(n.name||n.id)})))}function K(l,n,p,c){const h=c.map(a=>a.value),f=l!==void 0?l.slice():null;let r;f==null?r=p?h.slice():h.filter(a=>a!==n):r=p?f.concat(n):f.filter(a=>a!==n),r=Array.from(new Set(r));const s=r.length===h.length&&h.every(a=>r.includes(a))?[]:r;return{tools:s.length?s:void 0,send:s}}function ke(l,n,p,c){if(n.id.startsWith("mcp__"))return K(l,n.id,p,c);let h=l,f=l===void 0?[]:l.slice();for(const r of n.tools){const g=K(h,r.id,p,c);h=g.tools,f=g.send}return{tools:h,send:f}}function Ie(l){const n=te(l),p={...l,projectDir:n},[c,h]=v([]),[f,r]=v({text:"",kind:""});async function g(){if(!n){h([]),r({text:"open a chat to pick a project first",kind:"error"});return}r({text:"loading…",kind:"busy"});let s;try{s=await _("/api/agents?projectDir="+encodeURIComponent(n))}catch{r({text:"network error",kind:"error"});return}if(s.status!==200){r({text:"HTTP "+s.status,kind:"error"});return}const a=s.body.agents||[];h(a),r({text:a.length+(a.length===1?" agent":" agents"),kind:"success"})}return E(()=>{g()},[n]),n?e(P,null,e("div",{class:"view-head"},e("a",{href:"#/settings/project?"+ee(p),class:"view-back","aria-label":"Back to project"},"←"),e("h2",{class:"view-title"},"Agents")),e("section",null,e("p",{class:"hint hint--compact"},"Subagent delegation personas, saved in the project's .mouaif.json. The subagent tool and @-mentions can delegate to them."),e("p",{class:"hint hint--compact"},e("code",null,n)),e("ul",{class:"prompts__list","aria-label":"Agents"},c.length===0?e("li",{class:"prompts__empty"},'No agents yet. Tap "Add agent" to create your first one.'):c.map(s=>{const a=[];a.push(Array.isArray(s.tools)&&s.tools.length?s.tools.length+(s.tools.length===1?" tool":" tools"):"all tools"),s.modelId&&a.push(s.modelId);const m=(s.content||"").trim().replace(/\s+/g," ");return m&&a.push(m.length>48?m.slice(0,48)+"…":m),e("li",{key:s.name,class:"prompt-row"},e("a",{class:"prompt-row__main",href:"#/"+G(s.name,p)},e("div",{class:"prompt-row__title"},s.name),e("div",{class:"prompt-row__meta"},a.join(" · ")),e("div",{class:"prompt-row__chev"},"›")))})),e("div",{class:"row row--actions"},e("a",{href:"#/settings/agents/new?"+ee(p),class:"btn btn--primary"},"+ Add agent"),e("span",{class:"status"+(f.kind?" status--"+f.kind:""),"aria-live":"polite"},f.text)))):e(P,null,e("div",{class:"view-head"},e("a",{href:"#/settings",class:"view-back","aria-label":"Back to settings"},"←"),e("h2",{class:"view-title"},"Agents")),e("section",null,e("p",{class:"hint"},"No project selected. Open a chat to pick a project, or use the picker to add a new one."),e("div",{class:"row row--actions"},e("a",{href:"#/projects/new",class:"btn btn--primary"},"Open project picker"))))}function Te(l){const n=l.isNew??l.id==="new",p=n?"":l.id||"",c=te(l),h={...l,projectDir:c},f=be(h),r=l.returnTo==="project"?"Back to project settings":"Back to agents",g=q(!1),s=q(!1),a=q(!1),[m,O]=v([]),[C,R]=v([]),[I,T]=v([]),[U,ne]=v([]),[d,y]=v(null),[x,b]=v({text:"",kind:""}),[D,z]=v(!1),[M,J]=v(!1);E(()=>{let t=!1;async function o(){if(y(null),!!c){if(n){y({name:"",content:""});return}b({text:"loading…",kind:"busy"});try{const i=await _("/api/agents/"+encodeURIComponent(p)+"?projectDir="+encodeURIComponent(c));if(t)return;if(i.status!==200)throw new Error("HTTP "+i.status);y(i.body.agent),b({text:"",kind:""})}catch(i){t||b({text:i.message||"network error",kind:"error"})}}}return o(),()=>{t=!0}},[c,p,n]),E(()=>{let t=!1;async function o(){if(c)try{const[i,k,A,H]=await Promise.all([_("/api/ai/models?projectDir="+encodeURIComponent(c)),_("/api/ai/models/providers"),_("/api/mcp/servers?projectDir="+encodeURIComponent(c)),_("/api/tools/list?projectDir="+encodeURIComponent(c))]),W=i.status===200&&Array.isArray(i.body.models)?i.body.models:[],Z=k.status===200&&Array.isArray(k.body.providers)?k.body.providers.map(u=>u&&u.id).filter(Boolean):[];if(t)return;O(W),R(Z),A.status===200&&Array.isArray(A.body.servers)&&T(A.body.servers),H.status===200&&Array.isArray(H.body.tools)&&ne(H.body.tools);const pe=await Promise.all(Z.map(u=>Promise.all([Y(u).then(j=>({provider:u,models:j.models||[]})).catch(()=>({provider:u,models:[]})),Y(u,{purpose:"image"}).then(j=>({provider:u,models:j.models||[]})).catch(()=>({provider:u,models:[]}))]).then(j=>({provider:u,models:j.flatMap(N=>N.models)})))),S=new Map;for(const u of W)u&&u.id&&u.provider&&S.set(u.provider+"\0"+u.id,u);for(const u of pe)for(const j of u.models){if(!j||!j.id)continue;const N=u.provider+"\0"+j.id;S.has(N)||S.set(N,Object.assign({},j,{provider:u.provider}))}t||O(Array.from(S.values()))}catch{}}return o(),()=>{t=!0}},[c]);const[w]=v(()=>ve({name:p,save:async(t,o)=>{const i=await _("/api/agents/"+encodeURIComponent(t),{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectDir:c,...o})});if(i.status!==200)throw new Error(i.body&&i.body.error||"HTTP "+i.status);return i.body.agent},onStatus:t=>{g.current&&b(t)},onSaved:t=>{g.current&&(y(t),s.current||window.history.replaceState(null,"","#/"+G(t.name,h)))}}));E(()=>(g.current=!0,()=>{g.current=!1,!n&&!w.failed&&w.flush()}),[w,n]);function V(t){n||w.enqueue(t)}function L(t){n||w.enqueue(t,!0)}async function F(t){if(!(t.button>0||t.metaKey||t.ctrlKey||t.shiftKey||t.altKey)&&(t.preventDefault(),!a.current)){if(s.current=!0,!n&&!await w.flush()){s.current=!1;return}g.current&&X(f)}}function se(t){y(o=>Object.assign({},o,{name:t})),V({name:t})}function ae(t){y(o=>Object.assign({},o,{content:t})),V({content:t})}function re(t){const o=t&&t.modelId||"",i=t&&t.providerId||"";y(k=>Object.assign({},k,{modelId:o||void 0,providerId:i||void 0})),L({modelId:o,providerId:i})}function oe(t){y(o=>Object.assign({},o,{thinkingLevel:t||void 0})),L({thinkingLevel:t||""})}function ie(t,o){const i=K(d&&d.tools,t,o,B);y(k=>Object.assign({},k,{tools:i.tools})),L({tools:i.send})}function le(t,o){const i=Q.find(A=>A.id===t);if(!i)return;const k=ke(d&&d.tools,i,o,B);y(A=>Object.assign({},A,{tools:k.tools})),L({tools:k.send})}async function ce(){if(a.current)return;if(!c){b({text:"no project selected",kind:"error"});return}const t=(d&&d.name||"").trim();if(!t){b({text:"name is required",kind:"error"});return}if(!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(t)){b({text:"letters, digits, . _ - only; must start with a letter or digit",kind:"error"});return}a.current=!0,J(!0),b({text:"creating…",kind:"busy"});let o;try{o=await _("/api/agents",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectDir:c,...d,name:t,content:d.content||""})})}catch{b({text:"network error",kind:"error"});return}finally{a.current=!1,J(!1)}if(g.current){if(o.status!==201){b({text:o.body&&o.body.error||"HTTP "+o.status,kind:"error"});return}window.location.replace("#/"+G(o.body.agent.name,h))}}async function de(){if(!(!p||a.current)&&confirm('Delete agent "'+w.name+'"?')){a.current=!0,z(!0);try{if(!await w.flush())return;b({text:"deleting…",kind:"busy"});const t=await _("/api/agents/"+encodeURIComponent(w.name)+"?projectDir="+encodeURIComponent(c),{method:"DELETE"});if(t.status!==200)throw new Error("HTTP "+t.status);g.current&&X(f)}catch(t){b({text:t.message||"network error",kind:"error"})}finally{a.current=!1,z(!1)}}}if(!c)return e(P,null,e("div",{class:"view-head"},e("a",{href:"#/settings/project",class:"view-back","aria-label":"Back"},"←"),e("h2",{class:"view-title"},n?"Add agent":"Edit agent")),e("section",null,e("p",{class:"hint"},"No project selected. Open a chat to pick a project, or use the picker to add a new one."),e("div",{class:"row row--actions"},e("a",{href:"#/projects/new",class:"btn btn--primary"},"Open project picker"))));if(!d)return e(P,null,e("div",{class:"view-head"},e("a",{href:"#/"+f,onClick:F,class:"view-back","aria-label":r},"←"),e("h2",{class:"view-title"},n?"Add agent":"Edit agent")),e("section",null,e("span",{class:"status"+(x.kind?" status--"+x.kind:""),"aria-live":"polite"},x.text||"loading…")));const ue=d.tools!==void 0,B=we(I),Q=fe({choices:B,restricted:ue,selected:t=>d.tools.includes(t),mcpServers:I,catalog:U});return e(P,null,e("div",{class:"view-head"},e("a",{href:"#/"+f,onClick:F,class:"view-back","aria-label":r},"←"),e("h2",{class:"view-title"},n?"Add agent":d.name)),e("section",{class:"agent-editor"},e("p",{class:"hint hint--compact"},e("code",null,c)),e("fieldset",{class:"agent-editor__fields",disabled:M||D},e("div",{class:"row"},e("label",{class:"label",for:"sae-name"},"Name"),e("input",{class:"input",id:"sae-name",type:"text",value:d.name||"",placeholder:"reviewer",maxLength:64,autoCapitalize:"none",spellcheck:!1,onInput:t=>se(t.target.value)}),e("p",{class:"hint hint--compact"},"Letters, digits, . _ - only; must start with a letter or digit.")),e("div",{class:"row"},e("label",{class:"label",for:"sae-content"},"Instructions"),e("textarea",{class:"input prompts__textarea",id:"sae-content",rows:6,value:d.content||"",onInput:t=>ae(t.target.value),placeholder:"You are an assistant who…"}),!n&&e("p",{class:"hint hint--compact"},"Saved automatically as you type.")),e("div",{class:"row"},e("span",{class:"label"},"Model"),e(ge,{models:m,value:d.modelId?{providerId:d.providerId||(m.find(t=>t.id===d.modelId)||{}).provider||"",modelId:d.modelId}:null,variant:"sheet",extraProviders:C,allowClear:!0,clearLabel:"Inherit chat model",placeholder:"Pick a model",disabled:M||D,ariaLabel:"Model for this agent",onChange:re})),e("div",{class:"row"},e("span",{class:"label"},"Thinking"),e(he,{value:d.thinkingLevel||"",descriptor:(m.find(t=>t.id===d.modelId&&(!d.providerId||t.provider===d.providerId))||{}).thinking,inheritLabel:"Inherit chat thinking",disabled:M||D,ariaLabel:"Thinking level for this agent",onChange:oe}),e("p",{class:"hint hint--compact"},`How much reasoning this agent does before answering. "Inherit chat thinking" follows the chat's current model.`)),e("div",{class:"row"},e("span",{class:"label"},"Tools"),e(me,{groups:Q,collapsedByDefault:!0,onToggleGroup:le,onToggleTool:(t,o,i)=>ie(o,i)}),e("p",{class:"hint hint--compact"},"All checked = the agent inherits the chat's full tool surface. Uncheck to build an explicit allowlist.")),e("div",{class:"row row--actions"},n&&e("button",{class:"btn btn--primary",type:"button",onClick:ce,disabled:M},"Create"),!n&&e("button",{class:"btn btn--danger",type:"button",onClick:de,disabled:D},"Delete"),!n&&x.kind==="error"&&e("button",{class:"btn",type:"button",onClick:()=>w.flush()},"Retry save"),e("span",{class:"status"+(x.kind?" status--"+x.kind:""),"aria-live":"polite"},x.text)))))}export{Te as SettingsAgentEditView,Ie as SettingsAgentsView,ke as toggleGroupInList,K as toggleToolInList};
@@ -0,0 +1 @@
1
+ import{d as i,A as j,h as k,l as W,z as q,B as Y,k as t,S as Z,s as J}from"./index-BGvI4n0T.js";const S=[{value:"very-small",label:"Very small — tool names only, no schemas"},{value:"average",label:"Average — full tools, recommended"},{value:"extensive",label:"Extensive — full tools + best-practice guidance"}];function K(r){const n=S.find(c=>c.value===r);return n?n.label.split(" — ")[0].toLowerCase():r||"average"}function U(){const[r,n]=i("average"),[c,h]=i(!0),[d,m]=i(!0),[g,_]=i(!1),[f,b]=i(!0),[v,w]=i(!0),[B,y]=i(""),[C,x]=i(""),[O,T]=i(""),[F,z]=i(""),[A,E]=i(""),[M,R]=i(""),o=j(!0);k(()=>()=>{o.current=!1},[]);const l=j({promptSize:0,enterForNewline:0,autoRetry:0,fileOrbButton:0,dictationButton:0,imageButton:0});k(()=>{(async()=>{try{const e=await W({force:!0});if(!o.current)return;n(e.app&&e.app.promptSize||"average"),h(e.app&&typeof e.app.enterForNewline=="boolean"?e.app.enterForNewline:!0),m(e.app&&typeof e.app.autoRetry=="boolean"?e.app.autoRetry:!0),_(q(e));const s=Y(e);b(s.dictation),w(s.image)}catch(e){o.current&&y("load failed: "+e.message)}})()},[]);async function a(e,s,p,L){const u=(l.current[e]||0)+1;l.current[e]=u,p("saving…");try{if(await J(s),!o.current||l.current[e]!==u)return;p(L)}catch(V){if(!o.current||l.current[e]!==u)return;p("save failed: "+V.message)}}function D(e){const s=e.currentTarget.value;n(s),a("promptSize",{promptSize:s},y,"set to "+K(s))}function N(e){const s=e.currentTarget.checked;h(s),a("enterForNewline",{enterForNewline:s},x,s?"Enter now adds a newline":"Enter now sends")}function P(e){const s=e.currentTarget.checked;m(s),a("autoRetry",{autoRetry:s},T,s?"auto-retry on":"auto-retry off")}function I(e){const s=e.currentTarget.checked;_(s),a("fileOrbButton",{fileOrbButton:s},z,s?"the file button is now a glass orb (open a chat to see it)":"the file button is back to the flat circle")}function H(e){const s=e.currentTarget.checked;b(s),a("dictationButton",{dictationButton:s},E,s?"the composer microphone is shown again":"the composer microphone is hidden (open a chat to see it)")}function G(e){const s=e.currentTarget.checked;w(s),a("imageButton",{imageButton:s},R,s?"the composer image button is shown again":"the composer image button is hidden (open a chat to see it)")}return t(Z,null,t("div",{class:"view-head"},t("a",{href:"#/settings",class:"view-back","aria-label":"Back to settings"},"←"),t("h2",{class:"view-title"},"App defaults")),t("div",{class:"group"},t("div",{class:"group__title"},"Chat defaults",t("span",{class:"group__title-note"},"Apply to every project")),t("p",{class:"hint hint--compact"},"How much tool schema and instruction text the model receives. Smaller = less context used, faster replies."),t("p",{class:"hint hint--compact"},"A project or a single chat can pick a different style for itself."),t("ul",{class:"group__list"},t("li",{class:"settings-project__item"},t("div",{class:"settings-project__item-main"},t("label",{class:"settings-project__item-title",for:"sd-prompt-size"},"Default prompt style"),t("div",{class:"settings-project__item-note"},"How much tool schema and instruction text the model receives. Smaller = less context used, faster replies."),t("div",{class:"settings-project__item-status","aria-live":"polite"},B)),t("select",{class:"input settings-project__select",id:"sd-prompt-size",value:r,onChange:D},S.map(e=>t("option",{value:e.value,key:e.value},e.label)))),t("li",{class:"settings-project__item settings-project__item--col"},t("div",{class:"settings-project__item-row"},t("div",{class:"settings-project__item-main"},t("label",{class:"settings-project__item-title",for:"sd-enter-newline"},"Enter inserts a newline instead of sending"),t("div",{class:"settings-project__item-note"},"When on, Enter adds a new line and you send with the send button or Ctrl/Cmd+Enter. Turn it off to send with Enter (Shift+Enter for a new line)."),t("div",{class:"settings-project__item-status","aria-live":"polite"},C)),t("label",{class:"switch"},t("input",{id:"sd-enter-newline",type:"checkbox",role:"switch","aria-checked":String(c),checked:c,onChange:N}),t("span",{class:"switch__track","aria-hidden":"true"},t("span",{class:"switch__thumb"}))))),t("li",{class:"settings-project__item settings-project__item--col"},t("div",{class:"settings-project__item-row"},t("div",{class:"settings-project__item-main"},t("label",{class:"settings-project__item-title",for:"sd-auto-retry"},"Auto-retry failed sends"),t("div",{class:"settings-project__item-note"},"When a message fails before a response starts (network error or HTTP rejection), automatically resend it once. You can still retry any failed message from its inline error card."),t("div",{class:"settings-project__item-status","aria-live":"polite"},O)),t("label",{class:"switch"},t("input",{id:"sd-auto-retry",type:"checkbox",role:"switch","aria-checked":String(d),checked:d,onChange:P}),t("span",{class:"switch__track","aria-hidden":"true"},t("span",{class:"switch__thumb"}))))),t("li",{class:"settings-project__item settings-project__item--col"},t("div",{class:"settings-project__item-row"},t("div",{class:"settings-project__item-main"},t("label",{class:"settings-project__item-title",for:"sd-file-orb"},"Glass orb file button"),t("div",{class:"settings-project__item-note"},"Draws the button beside the message box as a shaded glass orb: the file and git counts sit on a 3D folder that tilts slowly, and the sphere picks up a moving highlight. It opens the same Files / Preview / Git / Cli menu either way, and the tap target is unchanged. Off keeps the flat circle that matches the other composer buttons."),t("div",{class:"settings-project__item-status","aria-live":"polite"},F)),t("label",{class:"switch"},t("input",{id:"sd-file-orb",type:"checkbox",role:"switch","aria-checked":String(g),checked:g,onChange:I}),t("span",{class:"switch__track","aria-hidden":"true"},t("span",{class:"switch__thumb"}))))),t("li",{class:"settings-project__item settings-project__item--col"},t("div",{class:"settings-project__item-row"},t("div",{class:"settings-project__item-main"},t("label",{class:"settings-project__item-title",for:"sd-dictation-button"},"Dictation microphone in the composer"),t("div",{class:"settings-project__item-note"},"Draws the microphone button beside the message box. Turn it off if you never dictate: the composer keeps the button out of the row. Hiding it disables nothing — the dictation page and every other surface still work, and the dictation model you picked is untouched. On by default."),t("div",{class:"settings-project__item-status","aria-live":"polite"},A)),t("label",{class:"switch"},t("input",{id:"sd-dictation-button",type:"checkbox",role:"switch","aria-checked":String(f),checked:f,onChange:H}),t("span",{class:"switch__track","aria-hidden":"true"},t("span",{class:"switch__thumb"}))))),t("li",{class:"settings-project__item settings-project__item--col"},t("div",{class:"settings-project__item-row"},t("div",{class:"settings-project__item-main"},t("label",{class:"settings-project__item-title",for:"sd-image-button"},"Image button in the composer"),t("div",{class:"settings-project__item-note"},"Draws the picture-attachment button beside the message box. Turn it off if you never attach images. Hiding it disables nothing: pasting an image into the composer still attaches it. On by default."),t("div",{class:"settings-project__item-status","aria-live":"polite"},M)),t("label",{class:"switch"},t("input",{id:"sd-image-button",type:"checkbox",role:"switch","aria-checked":String(v),checked:v,onChange:G}),t("span",{class:"switch__track","aria-hidden":"true"},t("span",{class:"switch__thumb"})))))),t("p",{class:"hint hint--compact"},"Chats and messages are stored in the app database. To keep a chat history you can commit, turn on tracing for that chat in project settings — it writes a project-local trace file you can add to source control.")))}export{U as SettingsDefaultsView};
@@ -0,0 +1 @@
1
+ import{d as S,A as D,h as I,f as K,k as n,S as W,b as _e,n as ee}from"./index-BGvI4n0T.js";import{A as Ce}from"./AgentFilePicker-CcKLJorU.js";import{S as Q,x as te,b as G,D as Y,y as ve,a as ne,o as we,l as Se,f as Le,h as Ae,s as ke,g as xe,G as je,z as Me,A as Ee,B as Ne,C as De,F as Re,v as re}from"./codemirror-Bp6CUUFk.js";function J(s){const e=s.map(({start:t,end:o})=>{const a=Number(t),b=Number(o);if(String(t).trim()===""||String(o).trim()===""||!Number.isSafeInteger(a)||!Number.isSafeInteger(b)||a<1||b<a)throw new Error("Enter whole line numbers: From must be at least 1, and To must not be before From.");return{start:a,end:b}}).sort((t,o)=>t.start-o.start),r=[];for(const t of e){const o=r[r.length-1];o&&t.start<=o.end+1?o.end=Math.max(o.end,t.end):r.push({...t})}return r}function ce(s,e){return s.some(({start:r,end:t})=>e>=r&&e<=t)}function $e(s,e){const r=J(s);return ce(r,e)?r.flatMap(t=>{if(e<t.start||e>t.end)return[t];const o=[];return t.start<e&&o.push({start:t.start,end:e-1}),e<t.end&&o.push({start:e+1,end:t.end}),o}):J([...r,{start:e,end:e}])}function He(s){return s.length?"Lines "+s.map(({start:e,end:r})=>e===r?e:`${e}–${r}`).join(", "):"No lines selected"}function le(s){if(!s||typeof s!="object")return!1;const{startLine:e,endLine:r,startCol:t,endCol:o}=s;return!(!Number.isInteger(e)||!Number.isInteger(r)||e<1||r<e||!Number.isInteger(t)||!Number.isInteger(o)||t<1||o<1||e===r&&o<t)}function P(s){const e=(Array.isArray(s)?s:[]).filter(le);if(!e.length)return[];const r=[];for(const t of e)if(!r.some(a=>a.startLine<=t.startLine&&a.endLine>=t.endLine&&(a.startLine!==a.endLine||a.startCol<=t.startCol&&a.endCol>=t.endCol))){if(t.startLine===t.endLine){const a=r.find(b=>b.startLine===t.startLine&&b.endLine===t.endLine);if(a){a.startCol=Math.min(a.startCol,t.startCol),a.endCol=Math.max(a.endCol,t.endCol);continue}}r.push({...t})}return r}function Oe(s,e){const r=P(s);return r.some(o=>o.startLine===e.startLine&&o.endLine===e.endLine&&o.startCol===e.startCol&&o.endCol===e.endCol)?r.filter(o=>!(o.startLine===e.startLine&&o.endLine===e.endLine&&o.startCol===e.startCol&&o.endCol===e.endCol)):P([...r,e])}function Te({startLine:s,endLine:e,startCol:r,endCol:t}){const o=s===e?`line ${s}`:`lines ${s}–${e}`,a=s===e?`, cols ${r}–${t}`:"";return`${o}${a}`}function de({ranges:s=[],chars:e=[]}={}){const r=Array.isArray(s)?s:[],t=P(e),o=[];return r.length&&o.push(He(r)),t.length&&o.push("Text on "+t.map(Te).join("; ")),o.join(" · ")||"Nothing hidden"}function se({projectDir:s="",from:e="",filePath:r=""}={}){const t=new URLSearchParams({projectDir:s});return e&&t.set("from",e),r&&t.set("file",r),"settings/project/hide?"+t.toString()}const U=new Map;function oe(s){if(!s)return null;const e=s.toLowerCase(),r=e.split("/").pop()||e;if(r==="dockerfile")return null;const t=(r.match(/\.[a-z0-9]+$/)||[""])[0];switch(t){case".js":case".jsx":case".mjs":case".cjs":return re({jsx:t===".jsx"});case".ts":case".tsx":return re({jsx:t===".tsx",typescript:!0});case".html":case".htm":case".svg":case".xml":case".mdx":return Re();case".css":case".scss":case".sass":case".less":return De();case".json":return Ne();case".md":case".markdown":return Ee();case".py":return Me();default:return null}}function ue(s,e,r){const t=Math.max(0,Math.min(e,r)),o=Math.min(s.length,Math.max(e,r));if(o<=t)return null;const a=s.lineAt(t),b=s.lineAt(o);return{startLine:a.number,endLine:b.number,startCol:t-a.from+1,endCol:o-b.from+1}}function ie(s){const e=s.state.selection.main;if(e.empty)return{hasSelection:!1,span:null};const r=ue(s.state.doc,e.from,e.to);return r?{hasSelection:!0,span:r}:{hasSelection:!1,span:null}}function ae(s){if(!s.root||typeof s.root.getSelection!="function")return null;const e=s.root.getSelection();if(!e||e.isCollapsed||!e.rangeCount)return null;const r=e.getRangeAt(0);if(!s.contentDOM.contains(r.startContainer)||!s.contentDOM.contains(r.endContainer))return null;try{return ue(s.state.doc,s.posAtDOM(r.startContainer,r.startOffset),s.posAtDOM(r.endContainer,r.endOffset))}catch{return null}}function Fe(s,e,r,t,o,a,b){const A=Q.define(),M=Q.define(),g=te.define({create:()=>r,update(f,l){for(const d of l.effects)if(d.is(A))return d.value;return f}}),j=te.define({create:()=>t,update(f,l){for(const d of l.effects)if(d.is(M))return d.value;return f}});class m extends je{constructor(l){super(),this.selected=l}eq(l){return l instanceof m&&l.selected===this.selected}toDOM(){const l=document.createElement("span");return l.className="hc__toggle-marker",l.textContent=this.selected?"✓":"+",l.setAttribute("aria-hidden","true"),l}}const z=G.decorations.compute([g],f=>{const l=[],d=f.doc.lines;for(const{start:v,end:w}of f.field(g)){if(!Number.isSafeInteger(v)||!Number.isSafeInteger(w)||v<1||w<v)continue;const L=v,c=Math.min(w,d);for(let h=L;h<=c;h++)l.push(Y.line({class:"hc__line-hidden"}).range(f.doc.line(h).from))}return Y.set(l,!0)}),T=G.decorations.compute([j],f=>{const l=[];for(const d of f.field(j)){if(!le(d))continue;const v=f.doc,w=Math.max(1,d.startLine),L=Math.min(v.lines,d.endLine);if(!(w>L))for(let c=w;c<=L;c++){const h=v.line(c);let p,_;d.startLine===d.endLine?(p=Math.min(d.startCol-1,h.length),_=Math.min(d.endCol,h.length)):c===d.startLine?(p=Math.min(d.startCol-1,h.length),_=h.length):c===d.endLine?(p=0,_=Math.min(d.endCol,h.length)):(p=0,_=h.length),!(_<=p)&&l.push(Y.mark({class:"hc__char-hidden"}).range(h.from+p,h.from+_))}}return Y.set(l,!0)}),F=ve({class:"hc__toggle",lineMarker(f,l){const d=f.state.doc.lineAt(l.from).number;return new m(ce(f.state.field(g),d))},lineMarkerChange(f){return f.docChanged||f.transactions.some(l=>l.effects.some(d=>d.is(A)))},domEventHandlers:{click(f,l){const d=f.state.doc.lineAt(l.from).number,v=$e(f.state.field(g),d);return f.dispatch({effects:A.of(v)}),o(v),!0}}}),k=ne.create({doc:s,extensions:[g,j,Se(),Le(),Ae(),F,z,T,ke(xe),ne.readOnly.of(!0),G.lineWrapping,...oe(e)?[oe(e)]:[],we]}),y=new G({state:k,parent:b});function E(){const f=ae(y)||ie(y).span;a(f?{hasSelection:!0,span:f}:{hasSelection:!1,span:null})}y.dispatch({effects:Q.appendConfig.of(G.updateListener.of(f=>{(f.selectionSet||f.docChanged)&&E()}))});const R=y.dom.ownerDocument;return R.addEventListener("selectionchange",E),requestAnimationFrame(()=>{y.dom.isConnected&&y.requestMeasure()}),E(),{view:y,setHidden:A,setChars:M,hiddenRangesField:g,hiddenCharsField:j,selectionSpan:()=>ae(y)||ie(y).span,destroy(){R.removeEventListener("selectionchange",E),y.destroy()}}}function Ie({projectDir:s,filePath:e,initialRule:r,onSave:t,onClose:o}){const a=JSON.stringify([s,e]),b=r&&Array.isArray(r.ranges)?r.ranges.map(i=>({...i})):[],A=r&&Array.isArray(r.chars)?r.chars.map(i=>({...i})):[],M=U.get(a),[g,j]=S(()=>M?M.ranges||[]:b),[m,z]=S(()=>M?M.chars||[]:A),[T,F]=S({hasSelection:!1,span:null}),[k,y]=S({loading:!0,content:null,error:""}),[E,R]=S(0),[f,l]=S(""),[d,v]=S(!1),w=D(!1),L=D(!0),c=D(null),h=D(null),p=D(null),_=D(null),$=D(0),x=JSON.stringify(g)!==JSON.stringify(b)||JSON.stringify(m)!==JSON.stringify(A);let H=[],B="";try{H=J(g)}catch(i){B=i.message}const V=H.reduce((i,u)=>i+u.end-u.start+1,0),q=P(m).length,fe=(V?`${V} ${V===1?"line":"lines"}`:"")+(V&&q?", ":"")+(q?`${q} ${q===1?"text span":"text spans"}`:"")||"Nothing hidden";I(()=>(L.current=!0,c.current?.focus(),()=>{L.current=!1}),[]),I(()=>{x?U.set(a,{ranges:g,chars:m}):U.delete(a)},[a,g,m,x]),I(()=>{if(!x)return;function i(u){u.preventDefault(),u.returnValue=""}return window.addEventListener("beforeunload",i),()=>window.removeEventListener("beforeunload",i)},[x]),I(()=>{const i=new AbortController;return y({loading:!0,content:null,error:""}),K("/api/file?"+new URLSearchParams({projectDir:s,path:e}),{signal:i.signal}).then(u=>{if(!i.signal.aborted){if(u.status!==200||typeof u.body.content!="string")throw new Error(u.body?.code==="ETOOLARGE"?"File exceeds the 1 MiB preview limit.":"Could not preview this file. It may be missing or unreadable.");y({loading:!1,content:u.body.content,error:""})}}).catch(u=>{i.signal.aborted||y({loading:!1,content:null,error:u.message})}),()=>i.abort()},[s,e,E]),I(()=>{if(!h.current||k.content===null)return;p.current&&(p.current.destroy(),p.current=null);let i=[];try{i=J(g)}catch{i=[]}const u=Fe(k.content,e,i,m,C=>{l(""),j(C)},C=>F(C),h.current);return p.current=u,()=>{p.current&&(p.current.destroy(),p.current=null)}},[k.content]),I(()=>{const i=p.current;if(!i)return;let u=[];try{u=J(g)}catch{u=[]}i.view.dispatch({effects:[i.setHidden.of(u),i.setChars.of(P(m))]})},[g,m]);function X(){return(p.current?p.current.selectionSpan():null)||(T.hasSelection?T.span:null)}function Z(i){return i?(l(""),z(Oe(m,i)),!0):!1}function he(i){if(i&&i.button>0)return;const u=X();_.current=u,Z(u)&&($.current=Date.now())}function pe(){if($.current&&Date.now()-$.current<1e3){$.current=0;return}$.current=0,Z(X()||_.current)}function ge(i,u,C){l(""),j(g.map((O,N)=>N===i?{...O,[u]:C}:O))}function me(){w.current||o()}function be(){w.current||x&&!window.confirm("Discard your unsaved line selection?")||(U.delete(a),o())}async function ye(i){if(i.preventDefault(),w.current)return;let u;try{u=J(g)}catch(N){l(N.message);return}const C=u.length||P(m).length,O=b.length||A.length;if(!(!C&&O&&!window.confirm("Stop hiding content in this file?"))){w.current=!0,v(!0),l("");try{await t(e,{ranges:u,chars:P(m)});const N=U.get(a);N&&N.ranges===g&&N.chars===m&&U.delete(a),L.current&&o()}catch(N){L.current&&l((N.message||"Could not save.")+" Your selection is kept; you can retry.")}finally{w.current=!1,L.current&&v(!1)}}}return n(W,null,n("div",{class:"view-head hidden-content__head"},n("button",{class:"view-back",type:"button",disabled:d,onClick:me,"aria-label":"Back to hidden files"},"←"),n("h2",{class:"view-title",tabIndex:-1,ref:c},"Select hidden content")),n("form",{class:"hidden-content",onSubmit:ye,noValidate:!0},n("p",{class:"hidden-content__path"},e),n("p",{class:"hidden-content__intro"},"Tap a line number in the left gutter to hide the whole line, or drag to select text and hide just that span. Either way, the redacted text is replaced with [hidden] for the agent file tools."),n("p",{class:"hidden-content__scope"},"Only read_file and search_files are filtered—not shell, MCP, or other access. This preview shows the original file to you; saving does not edit it."),n("fieldset",{class:"hidden-content__fields",disabled:d},n("legend",{class:"hidden-content__sr-only"},"Hidden content selection"),k.loading?n("p",{role:"status"},"Loading file preview…"):k.error?n("div",{class:"hidden-content__preview-error"},n("p",{role:"alert"},k.error+" You can still edit ranges manually."),n("button",{class:"btn",type:"button",onClick:()=>R(i=>i+1)},"Retry preview")):n(W,null,n("div",{class:"hidden-content__editor",ref:h,role:"region","aria-label":"Numbered file content","aria-describedby":"hidden-content-editor-help"}),n("div",{class:"hidden-content__toolbar"},n("p",{id:"hidden-content-editor-help",class:"hidden-content__muted"},"Tap a line in the gutter to hide or show it, or drag to select text and tap Hide selected text in the footer. The file is read-only."))),n("details",{class:"hidden-content__manual",open:!!k.error},n("summary",null,"Enter line ranges manually"),n("p",{class:"hidden-content__muted"},"From and To are inclusive. Use the same number to hide one line."),g.map((i,u)=>n("div",{class:"hidden-content__range",key:u},...["start","end"].map(C=>n("label",{key:C},C==="start"?"From":"To",n("input",{class:"input",type:"number",min:1,step:1,"aria-label":`${C==="start"?"From":"To"} line for range ${u+1}`,value:i[C],onInput:O=>ge(u,C,O.target.value)}))),n("button",{class:"btn",type:"button","aria-label":`Remove range ${u+1}`,onClick:()=>{l(""),j(g.filter((C,O)=>O!==u))}},"×"))),n("button",{class:"btn",type:"button",onClick:()=>{const i=H.length?H[H.length-1].end+1:1;j([...g,{start:i,end:i}])}},"+ Add range")),B&&n("p",{class:"hidden-content__error",role:"alert"},B)),n("footer",{class:"hidden-content__footer"},n("div",{class:"hidden-content__selection",role:"status"},n("strong",null,fe),n("span",{class:"hidden-content__muted"},B?"Fix the range values to continue.":V||q?de({ranges:H,chars:m}):"Tap a line number, or select text and tap Hide selected text."),x&&n("span",{class:"hidden-content__muted"},"Unsaved selection")),f&&n("p",{class:"hidden-content__error",role:"alert"},f),n("div",{class:"hidden-content__actions"},n("button",{class:"btn hidden-content__hide-action",type:"button",disabled:d||!T.hasSelection,onPointerDown:he,onClick:pe,"aria-label":"Hide selected text","aria-describedby":"hidden-content-editor-help"},"Hide selected text"),n("button",{class:"btn",type:"button",disabled:d,onClick:be},"Cancel"),n("button",{class:"btn btn--primary",type:"submit",disabled:d||!!B||!x&&!b.length},d?"Saving…":"Save")))))}function ze({projectDir:s="",from:e="",filePath:r=""}){const t=s||_e.value?.dir||"",[o,a]=S(null),[b,A]=S(""),[M,g]=S(""),[j,m]=S(!1),[z,T]=S(0),[F,k]=S(""),[y,E]=S(""),R=D(null),f=D(null);I(()=>{const c=new AbortController;if(a(null),A(""),!t){A("Open this page from a project’s settings.");return}return K("/api/settings/hide-file-content?"+new URLSearchParams({projectDir:t}),{signal:c.signal}).then(h=>{if(!c.signal.aborted){if(h.status!==200||!Array.isArray(h.body.rules))throw new Error("Could not load hidden files.");a(h.body.rules)}}).catch(h=>{c.signal.aborted||A(h.message)}),()=>c.abort()},[t,z]);function l(c){m(!1),g(""),ee(se({projectDir:t,from:e,filePath:c}))}function d(){ee(se({projectDir:t,from:e}))}async function v(c,{ranges:h,chars:p}){const _=o.filter(H=>H.path!==c),$=h.length||p.length;$&&_.push({path:c,ranges:h,chars:p});const x=await K("/api/settings/hide-file-content",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectDir:t,rules:_})});if(x.status!==200||!Array.isArray(x.body.rules))throw new Error(`Could not save (HTTP ${x.status}).`);a(x.body.rules),g($?"Hidden content saved.":"File is no longer hidden.")}async function w(c,h){if(!F&&window.confirm(`Stop hiding content in ${c}?`)){E(""),g(""),k(c);try{const p=await K("/api/settings/hide-file-content",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectDir:t,rules:o.filter(_=>_.path!==c)})});if(p.status!==200||!Array.isArray(p.body.rules))throw new Error(`Could not remove it (HTTP ${p.status}).`);a(p.body.rules),g(`${c} is no longer hidden.`),requestAnimationFrame(()=>{const _=R.current?R.current.querySelectorAll(".hidden-content__remove"):[];(_[Math.min(h,_.length-1)]||f.current)?.focus()})}catch(p){E((p.message||"Could not remove it.")+" Nothing changed; the file is still hidden.")}finally{k("")}}}if(r&&o)return n(Ie,{key:t+"|"+r,projectDir:t,filePath:r,initialRule:o.find(c=>c.path===r),onSave:v,onClose:d});const L=new URLSearchParams({projectDir:t});return e&&L.set("from",e),n(W,null,n("div",{class:"view-head hidden-content__head"},n("a",{class:"view-back",href:"#/settings/project?"+L,"aria-label":"Back to project settings"},"←"),n("h2",{class:"view-title"},"Hide file content")),n("section",{class:"hidden-content","aria-label":"Hidden files"},n("p",{class:"hidden-content__intro"},"Choose a file, then tap the line numbers you want hidden from the agent file tools, or select text and hide just that span. Your file stays unchanged."),n("p",{class:"hidden-content__scope"},n("strong",null,"Not a security boundary. "),"Only ",n("code",null,"read_file")," and ",n("code",null,"search_files")," are filtered. Shell, MCP, and other access can still read the original content."),b?n("div",{role:"alert"},n("p",null,b),t&&n("button",{class:"btn",onClick:()=>T(c=>c+1)},"Retry")):o===null?n("p",{role:"status"},"Loading hidden files…"):n(W,null,o.length?n("ul",{class:"hidden-content__files",ref:R},o.map((c,h)=>n("li",{key:c.path,class:"hidden-content__row"},n("button",{class:"hidden-content__file",type:"button",onClick:()=>l(c.path)},n("span",{class:"hidden-content__file-main"},n("span",{class:"hidden-content__path"},c.path),n("span",{class:"hidden-content__muted"},de(c))),n("span",{"aria-hidden":"true"},"›")),n("button",{class:"hidden-content__remove",type:"button",disabled:!!F,"aria-label":`Stop hiding content in ${c.path}`,onClick:()=>w(c.path,h)},F===c.path?"Removing…":"Remove")))):n("p",{class:"hidden-content__empty"},"Nothing hidden yet. Add a file to hide whole lines or selected text."),n("button",{class:"btn btn--primary",ref:f,onClick:()=>m(!0)},"+ Add file"),y&&n("p",{class:"hidden-content__error",role:"alert"},y),n("p",{role:"status",class:"hidden-content__notice"},M)),j&&n(Ce,{projectDir:t,onPick:l,onClose:()=>m(!1),label:"Choose a file to hide lines",description:"Choose a file to preview its contents and select hidden lines."})))}export{ze as SettingsHiddenContentView};
@@ -0,0 +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};
@@ -0,0 +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=...
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
+ 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};
@@ -0,0 +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};
@@ -0,0 +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};
@@ -0,0 +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};
@@ -0,0 +1,14 @@
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(`
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
+ `);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
+ # Count: `+l.length+`
5
+ # Skipped: 12`,x=ct(s);let S;if(x==="json")S=JSON.stringify({entries:l,skipped:12,truncated:!1,cap:5e3,pattern:"**/*.{js,jsx,ts,tsx,md,css}"});else{const b=[];let V=[];for(const rt of l){const Y=rt.path.split("/"),qs=Y[Y.length-1],Z=Y.slice(0,-1);let $=0;for(;$<Z.length&&$<V.length&&Z[$]===V[$];)$++;for(let St=$;St<Z.length;St++)b.push(" ".repeat(St)+Z[St]+"/");b.push(" ".repeat(Z.length)+qs),V=Z}S=h+`
6
+
7
+ `+b.join(`
8
+ `)}const X=o[Ct(e)];if(X===1/0)return S;const m=Math.max(i,Math.floor(a*X)),A=c(S);if(A<=m)return S;const E=`
9
+
10
+ ...[tool feedback truncated; original `+A+` bytes]...
11
+
12
+ `,N=Math.max(0,m-c(E)),kt=Math.floor(N*.75),Ks=N-kt;return S.slice(0,kt)+E+S.slice(S.length-Ks)}return tt==="output"?t(At,null,t("div",{class:"view-head"},t("a",{href:"#/settings/project?"+k(),class:"view-back","aria-label":"Back to project settings"},"←"),t("h2",{class:"view-title"},"File tool options")),t("section",{class:"settings-project"},t("div",{class:"group settings-project__section"},t("div",{class:"group__title settings-project__section-title"},R("tools"),t("span",null,"Tool output")),t("p",{class:"hint hint--compact"},"How each tool result is structured before it reaches the model. Applies to file tools and all native/MCP tool output.")),t("ul",{class:"group__list"},t("li",{class:"settings-project__item"},t("div",{class:"settings-project__item-main"},t("label",{class:"settings-project__item-title",for:"sp-output-structure"},"Layout"),t("div",{class:"settings-project__item-note"},"How file-listing results (list_files, search_files) are shaped for the model."),t("div",{class:"settings-project__item-status","aria-live":"polite"},Je)),t("select",{class:"input settings-project__select",id:"sp-output-structure",value:ct(lt),onChange:ks},Object.entries(Yt).map(([e,s])=>t("option",{value:e},s.label))))),t("div",{class:"group settings-project__section"},t("div",{class:"group__title"},"Stored value"),t("p",{class:"hint hint--compact"},"This is the exact ",t("code",null,"toolOutput")," object written to ",t("code",null,".mouaif.json")," when you change the profile."),t("pre",{class:"settings__out"},JSON.stringify({toolOutput:{size:Tt,structure:lt}},null,2))),t("div",{class:"group settings-project__section"},t("div",{class:"group__title"},"Example"),t("p",{class:"hint hint--compact"},"What the model would receive for a sample ",t("code",null,"list_files")," result from a realistic monorepo tree, rendered in the selected layout. The sample is sized so the finite size caps show the truncation marker:"),t("pre",{class:"settings__out"},Vs(Tt,lt))))):tt==="preview"?t(At,null,t("div",{class:"view-head"},t("a",{href:"#/settings/project?"+k(),class:"view-back","aria-label":"Back to project settings"},"←"),t("h2",{class:"view-title"},"Web preview")),t("section",{class:"settings-project"},t("div",{class:"group settings-project__section"},t("div",{class:"group__title settings-project__section-title"},R("tools"),t("span",null,"Capture"),t("details",{class:"settings-project__info"},t("summary",{"aria-label":"About web preview capture"},"?"),t("div",{class:"settings-project__info-body"},t("p",null,"Capture a screenshot of a web URL in the Inspector debug Chrome and open it in the full-screen viewer. The same screenshot the AI can refresh with the ",t("code",null,"webpreview")," tool."),t("p",null,"This page respects the project’s ",t("code",null,"webpreview")," authorization gate: in Ask mode the capture is hinted to approve it in the chat.")))),t("ul",{class:"group__list"},t("li",{class:"settings-project__item settings-project__item--col"},t("div",{class:"settings-project__item-row"},t("div",{class:"settings-project__item-main"},t("label",{class:"settings-project__item-title",for:"sp-preview-url"},"URL"),t("div",{class:"settings-project__item-note"},"An http(s) web page to capture."),t("div",{class:"settings-project__item-status","aria-live":"polite"},es)),t("button",{class:"btn btn--primary settings-project__preview-capture",type:"button",onClick:Us,"aria-label":"Capture a web preview"},"Capture…")),O?t("div",{class:"settings-project__preview-shots"},t("button",{class:"settings-project__preview-thumb",type:"button",onClick:()=>Et(!0),"aria-label":"Open full preview of "+(O.title||O.url||"the captured page")},t("img",{src:O.thumbnail,alt:"Preview of "+(O.title||O.url||"the captured page"),draggable:"false",decoding:"async"}))):null)))),Ze?t($s,{onSubmit:Ms,onClose:()=>Ft(!1)}):null,ts&&O?t(Zs,{preview:O,onClose:()=>Et(!1),onRecapture:Rs}):null):tt==="technical"?t(At,null,t("div",{class:"view-head"},t("a",{href:f()?"#/chat/"+encodeURIComponent(f())+"?projectDir="+encodeURIComponent(d()||j||""):"#/settings/project?"+k(),class:"view-back","aria-label":f()?"Back to chat":"Back to project settings"},"←"),t("h2",{class:"view-title"},"Technical details")),t("section",{class:"settings-project"},t("div",{class:"group settings-project__section"},t("div",{class:"group__title settings-project__section-title"},R("files"),t("span",null,"Project settings storage"),t("details",{class:"settings-project__info"},t("summary",{"aria-label":"About project settings storage"},"?"),t("div",{class:"settings-project__info-body"},t("p",null,"Keep this project’s settings in the app database instead of writing a ",t("code",null,".mouaif.json")," file. The project folder stays untouched, so nothing shows up in git.")))),t("ul",{class:"group__list"},t("li",{class:"settings-project__item settings-project__item--col"},t("div",{class:"settings-project__item-row"},t("div",{class:"settings-project__item-main"},t("label",{class:"settings-project__item-title",for:"sp-db-backed"},"Store settings in app DB"),t("div",{class:"settings-project__item-note"},W?"Settings live in the app SQLite store. No .mouaif.json is written to the project.":"Settings live in .mouaif.json and can be committed with the project."),t("div",{class:"settings-project__item-status","aria-live":"polite"},_s)),t("label",{class:"switch"},t("input",{id:"sp-db-backed",type:"checkbox",role:"switch",checked:W,"aria-checked":W?"true":"false",onChange:Ls}),t("span",{class:"switch__track","aria-hidden":"true"},t("span",{class:"switch__thumb"}))))))),t("div",{class:"group settings-project__section"},t("div",{class:"group__title"},"Raw project file"),t("p",{class:"hint hint--compact"},W?"This project is DB-backed — the raw JSON below is shown read-only for reference.":"Hand-edit ",t("code",null,".mouaif.json"),". The main settings page writes the same file."),t("textarea",{class:"input settings-project__code",id:"sp-project-editor",rows:10,spellcheck:!1,value:Se,onInput:e=>H(e.target.value),readOnly:W}),t("div",{class:"row row--actions"},t("button",{class:"btn btn--primary",type:"button",onClick:Bs,disabled:us||W},"Save file"),t("button",{class:"btn",type:"button",onClick:Hs,disabled:ds},"Revert"),t("span",{class:"status","aria-live":"polite"},fs))),t("div",{class:"group settings-project__section"},t("div",{class:"group__title"},"Resolved settings"),t("p",{class:"hint hint--compact"},"Defaults → app → project. Provider keys are redacted."),t("pre",{class:"settings__out"},hs)),t("div",{class:"group settings-project__section",hidden:!He},t("div",{class:"group__title settings-project__section-title"},R("chat"),t("span",null,"Current chat"),t("details",{class:"settings-project__info"},t("summary",{"aria-label":"About tracing"},"?"),t("div",{class:"settings-project__info-body"},t("p",null,"Tracing appends this chat’s events to ",t("code",null,".mouaif/traces/<chatId>.ndjson")," in the project so you can commit it alongside your code. “Export trace” writes the file once, on demand.")))),t("ul",{class:"group__list"},t("li",{class:"settings-project__item settings-project__item--col"},t("div",{class:"settings-project__item-row"},t("div",{class:"settings-project__item-main"},t("label",{class:"settings-project__item-title",for:"sp-chat-trace"},"Trace this chat"),t("div",{class:"settings-project__item-note"},"Append this chat’s events to a trace file in the project."),t("div",{class:"settings-project__item-status","aria-live":"polite"},We)),t("label",{class:"switch"},t("input",{id:"sp-chat-trace",type:"checkbox",role:"switch",checked:Zt,"aria-checked":Zt?"true":"false",onChange:Ss}),t("span",{class:"switch__track","aria-hidden":"true"},t("span",{class:"switch__thumb"})))),t("div",{class:"settings-project__item-actions"},t("span",{class:"settings-project__item-status","aria-live":"polite"},Ve),t("button",{class:"btn",type:"button",onClick:As,disabled:!Ge},"Export trace"),t("button",{class:"btn btn--danger btn--small",type:"button",onClick:Ts},"Delete chat"))))))):t(At,null,t("div",{class:"view-head"},t("a",{href:f()?"#/chat/"+encodeURIComponent(f())+"?projectDir="+encodeURIComponent(d()||j||""):U==="projects"?"#/projects":U==="settings/projects"?"#/settings/projects":"#/settings",class:"view-back","aria-label":f()?"Back to chat":U==="projects"?"Back to projects":U==="settings/projects"?"Back to project list":"Back to settings"},"←"),t("h2",{class:"view-title"},"Project settings")),t("section",{class:"settings-project"},t("div",{class:"settings-project__intro"},t("div",{class:"settings-project__intro-top"},t("p",{class:"settings-project__path"},t("code",null,Ne)),t("span",{class:"status","aria-live":"polite","data-state":nt.state||void 0},nt.text)),t("p",{class:"settings-project__lede"},"Only for this project — everything saves automatically.")),t("div",{class:"group settings-project__section"},t("div",{class:"group__title settings-project__section-title"},R("general"),t("span",null,"General"),t("details",{class:"settings-project__info"},t("summary",{"aria-label":"About these settings"},"?"),t("div",{class:"settings-project__info-body"},t("p",null,"Settings here live in ",t("code",null,".mouaif.json")," and override the app-level defaults for this folder only. Leave a control untouched to inherit the app default.")))),t("ul",{class:"group__list"},t("li",{class:"settings-project__item"},t("div",{class:"settings-project__item-main"},t("label",{class:"settings-project__item-title",for:"sp-prompt-size"},"Prompt style"),t("div",{class:"settings-project__item-note"},"How much tool and instruction detail chats receive."),t("div",{class:"settings-project__item-status","aria-live":"polite"},ze)),t("select",{class:"input settings-project__select",id:"sp-prompt-size",value:Ie,onChange:ws},t("option",{value:""},"Inherit app default"),t("option",{value:"very-small"},"Very small — tool names only, no schemas"),t("option",{value:"average"},"Average — full tools, recommended"),t("option",{value:"extensive"},"Extensive — full tools + best-practice guidance"))),t("li",null,t("a",{class:"group__row settings-project__link-row","aria-label":"Custom prompts",href:"#/settings/prompts?"+k()},t("span",{class:"group__row-body"},t("span",{class:"group__row-label"},"Custom prompts"),t("span",{class:"settings-project__link-sub"},"Reusable system and role prompts")),t("span",{class:"group__row-detail"},ls),t("span",{class:"group__row-chev","aria-hidden":"true"},"›"))))),t("div",{class:"group settings-project__section"},t("div",{class:"group__title settings-project__section-title"},R("tools"),t("span",null,"Tools"),t("details",{class:"settings-project__info"},t("summary",{"aria-label":"About tool permissions"},"?"),t("div",{class:"settings-project__info-body"},t("p",null,"Each tool has three modes:"),t("ul",null,t("li",null,t("strong",null,"Off")," — hidden from the model, zero prompt tokens."),t("li",null,t("strong",null,"Ask")," — you approve every call (the default)."),t("li",null,t("strong",null,"Allow")," — calls run without asking.")),t("p",null,"The checkbox next to a group is a quick Off ↔ Ask toggle. Allowlists (regex patterns that skip the prompt) are still honored if set in the project file, but are edited from the raw JSON in Technical details.")))),t("div",{class:"settings-project__tools-tree"},vt.length?t(to,{groups:Ws(vt),onToggleGroup:Fe,onToggleTool:(e,s,o)=>{e.startsWith("mcp-")?Es(s,o):e==="files"?Cs(s,o?"ask":"off"):Fe(e,o)},collapsedByDefault:!0,onReloadServer:Gs}):t("div",{class:"settings-project__item-note"},"Loading tools…")),t("a",{class:"group__row settings-project__link-row settings-project__options-link","aria-label":"File tool options",href:"#/settings/project/output?"+k()},t("span",{class:"group__row-body"},t("span",{class:"group__row-label"},"File tool options"),t("span",{class:"settings-project__link-sub"},"Output size, structure, and the JSON value")),t("span",{class:"group__row-chev","aria-hidden":"true"},"›"))),t("div",{class:"group settings-project__section"},t("div",{class:"group__title settings-project__section-title"},R("files"),t("span",null,"Agent files"),t("details",{class:"settings-project__info"},t("summary",{"aria-label":"About agent files"},"?"),t("div",{class:"settings-project__info-body"},t("p",null,"Agent files are markdown files at the project root that get injected into the model’s context at the start of every chat. Use them for project conventions, architecture notes, or standing instructions. Turning this off locks them off for every chat; when it is on, a chat can still opt out individually.")))),t("ul",{class:"group__list"},t("li",{class:"settings-project__item settings-project__item--col"},t("div",{class:"settings-project__item-row"},t("div",{class:"settings-project__item-main"},t("label",{class:"settings-project__item-title",for:"sp-agent-files"},"Inject agent files into chats"),t("div",{class:"settings-project__item-note"},"Project-wide gate for instruction files at the project root (e.g. AGENTS.md, CLAUDE.md). Off locks them out of every chat; on lets each chat opt out. ",t("span",{class:"settings-project__item-status","aria-live":"polite"},is))),t("label",{class:"switch"},t("input",{id:"sp-agent-files",type:"checkbox",role:"switch",checked:bt,"aria-checked":bt?"true":"false",onChange:Ns}),t("span",{class:"switch__track","aria-hidden":"true"},t("span",{class:"switch__thumb"}))))),t("li",{class:"settings-project__item settings-project__item--col"},t("div",{class:"settings-project__item-main"},t("label",{class:"settings-project__item-title",for:"sp-agent-file-names"},"File names to look for"),t("div",{class:"settings-project__item-note"},"One file name per line, relative to the project root. Leave empty to use the defaults (AGENTS.md, CLAUDE.md, .github/copilot-instructions.md).")),t("div",{class:"settings-project__afn-row"},t("textarea",{class:"input settings-project__mono settings-project__afn-text",id:"sp-agent-file-names",rows:3,spellcheck:!1,placeholder:`AGENTS.md
13
+ CLAUDE.md
14
+ .github/copilot-instructions.md`,value:Nt,onInput:Js}),t("button",{class:"btn btn--ghost settings-project__afn-pick",type:"button",onClick:()=>jt(!0)},"Pick file…"))))),t("div",{class:"group settings-project__section"},t("div",{class:"group__title settings-project__section-title"},R("agents"),t("span",null,"Agents"),t("details",{class:"settings-project__info"},t("summary",{"aria-label":"About agents"},"?"),t("div",{class:"settings-project__info-body"},t("p",null,"Agents are reusable sub-personas the main chat can delegate to. Each has its own name, instructions, an optional model override, and an optional tool allowlist (no allowlist = all tools). Tap an agent to edit it; edits save automatically.")))),t("div",{class:"settings-project__agents"},ke.length>0&&t("ul",{class:"settings-project__agents-list"},ke.map(e=>t("li",{key:e.name},t("a",{class:"group__row settings-project__agent-link",href:"#/"+lo(e.name,{projectDir:d(),from:U,chatId:f(),returnTo:"project"}),"aria-label":"Configure "+e.name},t("span",{class:"group__row-label"},e.name),t("span",{class:"group__row-chev","aria-hidden":"true"},"›"))))),t("a",{class:"btn btn--primary settings-project__add-agent",href:"#/settings/agents/new?"+k()+"&returnTo=project"},"+ Add agent"))),t("div",{class:"group settings-project__section"},t("div",{class:"group__title settings-project__section-title"},R("more"),t("span",null,"More settings")),t("ul",{class:"group__list"},t("li",null,t("a",{class:"group__row settings-project__link-row","aria-label":"Web preview",href:"#/settings/project/preview?"+k()},t("span",{class:"group__row-body"},t("span",{class:"group__row-label"},"Web preview"),t("span",{class:"settings-project__link-sub"},"Capture and view a web page")),t("span",{class:"group__row-chev","aria-hidden":"true"},"›"))),t("li",null,t("a",{class:"group__row settings-project__link-row","aria-label":"Hide file content",href:"#/settings/project/hide?"+k()},t("span",{class:"group__row-body"},t("span",{class:"group__row-label"},"Hide file content"),t("span",{class:"settings-project__link-sub"},"Redact code from the agent file tools")),t("span",{class:"group__row-detail"},Ht?Ht+" file"+(Ht===1?"":"s"):""),t("span",{class:"group__row-chev","aria-hidden":"true"},"›"))),t("li",null,t("a",{class:"group__row settings-project__link-row","aria-label":"MCP servers",href:"#/settings/mcp?"+k()},t("span",{class:"group__row-body"},t("span",{class:"group__row-label"},"MCP servers"),t("span",{class:"settings-project__link-sub"},"Connect external tool servers")),t("span",{class:"group__row-detail"},cs),t("span",{class:"group__row-chev","aria-hidden":"true"},"›"))),t("li",null,t("a",{class:"group__row settings-project__link-row","aria-label":"Custom actions",href:"#/settings/actions?"+k()},t("span",{class:"group__row-body"},t("span",{class:"group__row-label"},"Custom actions"),t("span",{class:"settings-project__link-sub"},"CLI and MCP shortcuts for the composer")),t("span",{class:"group__row-chev","aria-hidden":"true"},"›"))),t("li",null,t("a",{class:"group__row settings-project__link-row",href:"#/settings/project/technical?"+k()},t("span",{class:"group__row-body"},t("span",{class:"group__row-label"},"Technical details"),t("span",{class:"settings-project__link-sub"},"Raw project file and resolved settings")),t("span",{class:"group__row-chev","aria-hidden":"true"},"›"))))),rs&&t(no,{projectDir:d(),onPick:Is,onClose:()=>jt(!1)})))}export{fo as SettingsProjectView};
@@ -0,0 +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};
@@ -0,0 +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};
@@ -0,0 +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};
@@ -0,0 +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};
@@ -0,0 +1 @@
1
+ function a({projectDir:t="",from:e="",chatId:r="",returnTo:s=""}={}){const n=new URLSearchParams({projectDir:t});return r&&n.set("chatId",r),e&&n.set("from",e),s==="project"&&n.set("returnTo",s),n.toString()}function o(t,e){return"settings/agents/"+encodeURIComponent(t)+"?"+a(e)+(t==="new"?"&edit=1":"")}function i(t){return(t.returnTo==="project"?"settings/project?":"settings/agents?")+a({...t,returnTo:""})}export{o as a,i as b,a as c};