@samanhappy/mcphub 0.12.16 → 0.12.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/index.js +9 -9
- package/dist/config/index.js.map +1 -1
- package/dist/middlewares/userContext.js +17 -2
- package/dist/middlewares/userContext.js.map +1 -1
- package/dist/services/mcpService.js +7 -2
- package/dist/services/mcpService.js.map +1 -1
- package/dist/services/vectorSearchService.js +5 -1
- package/dist/services/vectorSearchService.js.map +1 -1
- package/frontend/dist/assets/ActivityPage-ClgKeihP.js +2 -0
- package/frontend/dist/assets/ActivityPage-ClgKeihP.js.map +1 -0
- package/frontend/dist/assets/Badge-Ck2fhRdl.js +2 -0
- package/frontend/dist/assets/Badge-Ck2fhRdl.js.map +1 -0
- package/frontend/dist/assets/ConfirmDialog-uYjffH4V.js +2 -0
- package/frontend/dist/assets/ConfirmDialog-uYjffH4V.js.map +1 -0
- package/frontend/dist/assets/Dashboard-BIXrLobn.js +2 -0
- package/frontend/dist/assets/Dashboard-BIXrLobn.js.map +1 -0
- package/frontend/dist/assets/DeleteDialog-BAfrV8EB.js +2 -0
- package/frontend/dist/assets/DeleteDialog-BAfrV8EB.js.map +1 -0
- package/frontend/dist/assets/GroupsPage-BjrvyHwu.js +33 -0
- package/frontend/dist/assets/GroupsPage-BjrvyHwu.js.map +1 -0
- package/frontend/dist/assets/LoginPage-BBHt_TfF.js +2 -0
- package/frontend/dist/assets/LoginPage-BBHt_TfF.js.map +1 -0
- package/frontend/dist/assets/LogsPage-D8Znq5NB.js +2 -0
- package/frontend/dist/assets/LogsPage-D8Znq5NB.js.map +1 -0
- package/frontend/dist/assets/MarketPage-haRuzjCw.js +3 -0
- package/frontend/dist/assets/MarketPage-haRuzjCw.js.map +1 -0
- package/frontend/dist/assets/Pagination-y-gVO8ms.js +2 -0
- package/frontend/dist/assets/Pagination-y-gVO8ms.js.map +1 -0
- package/frontend/dist/assets/PromptsPage-ByHWPyGe.js +2 -0
- package/frontend/dist/assets/PromptsPage-ByHWPyGe.js.map +1 -0
- package/frontend/dist/assets/ResourcesPage-Bodw5OY9.js +2 -0
- package/frontend/dist/assets/ResourcesPage-Bodw5OY9.js.map +1 -0
- package/frontend/dist/assets/ServersPage-CtS1I4yS.js +37 -0
- package/frontend/dist/assets/ServersPage-CtS1I4yS.js.map +1 -0
- package/frontend/dist/assets/SettingsPage-DxVigf7p.js +12 -0
- package/frontend/dist/assets/SettingsPage-DxVigf7p.js.map +1 -0
- package/frontend/dist/assets/ToggleGroup-HfxdlkGi.js +2 -0
- package/frontend/dist/assets/ToggleGroup-HfxdlkGi.js.map +1 -0
- package/frontend/dist/assets/UsersPage-Bipw33cS.js +2 -0
- package/frontend/dist/assets/UsersPage-Bipw33cS.js.map +1 -0
- package/frontend/dist/assets/framework-vendor-_OBebcuv.js +61 -0
- package/frontend/dist/assets/framework-vendor-_OBebcuv.js.map +1 -0
- package/frontend/dist/assets/i18n-vendor-MQ921plD.js +2 -0
- package/frontend/dist/assets/i18n-vendor-MQ921plD.js.map +1 -0
- package/frontend/dist/assets/icons-vendor-B67NtVuR.js +172 -0
- package/frontend/dist/assets/icons-vendor-B67NtVuR.js.map +1 -0
- package/frontend/dist/assets/index-CmnA4an8.js +5 -0
- package/frontend/dist/assets/index-CmnA4an8.js.map +1 -0
- package/frontend/dist/assets/index-DfFHVARX.css +1 -0
- package/frontend/dist/assets/resourceService-BfCTSBsr.js +2 -0
- package/frontend/dist/assets/{resourceService-CUz722Nb.js.map → resourceService-BfCTSBsr.js.map} +1 -1
- package/frontend/dist/assets/useGroupData-DLhbP6zd.js +2 -0
- package/frontend/dist/assets/useGroupData-DLhbP6zd.js.map +1 -0
- package/frontend/dist/assets/useServerData-QZqQTYcv.js +2 -0
- package/frontend/dist/assets/useServerData-QZqQTYcv.js.map +1 -0
- package/frontend/dist/assets/useSettingsData-D3VROqS7.js +2 -0
- package/frontend/dist/assets/useSettingsData-D3VROqS7.js.map +1 -0
- package/frontend/dist/assets/variableDetection-C3Xi21av.js +16 -0
- package/frontend/dist/assets/variableDetection-C3Xi21av.js.map +1 -0
- package/frontend/dist/index.html +5 -2
- package/package.json +2 -2
- package/frontend/dist/assets/index-CVDuCISB.js +0 -323
- package/frontend/dist/assets/index-CVDuCISB.js.map +0 -1
- package/frontend/dist/assets/index-PEyR1nSL.css +0 -1
- package/frontend/dist/assets/resourceService-CUz722Nb.js +0 -2
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import{r as b,R as Y,j as e}from"./framework-vendor-_OBebcuv.js";import{u as Z}from"./useGroupData-DLhbP6zd.js";import{u as Q}from"./useServerData-QZqQTYcv.js";import{l as ae,h as xe,d as X}from"./index-CmnA4an8.js";import{u as oe}from"./useSettingsData-D3VROqS7.js";import{u as L}from"./i18n-vendor-MQ921plD.js";import{W as ie,b as ce,F as de,C as ue,g as le,j as ge,l as he,m as be,h as fe,T as ye}from"./icons-vendor-B67NtVuR.js";import{D as je}from"./DeleteDialog-BAfrV8EB.js";const ve={tools:[],prompts:[],resources:[]},ne={tools:"all",prompts:"all",resources:"all"},me=({servers:p,value:I,onChange:s,className:m})=>{const{t:x}=L(),{nameSeparator:z}=oe(),[f,S]=b.useState(new Set),h=Y.useMemo(()=>I.map(t=>typeof t=="string"?{name:t,...ne}:{...t,tools:t.tools||"all",prompts:t.prompts||"all",resources:t.resources||"all"}),[I]),u=Y.useMemo(()=>p.filter(t=>t.enabled!==!1),[p]);Y.useEffect(()=>{const t=new Set(h.map(r=>r.name)),c=new Set(u.map(r=>r.name));S(r=>{const N=new Set;return r.forEach(k=>{(t.has(k)||c.has(k))&&N.add(k)}),N})},[h,u]);const C=t=>{if(h.findIndex(r=>r.name===t)>=0){const r=h.filter(N=>N.name!==t);s(r)}else{const r=[...h,{name:t,...ne}];s(r)}},j=t=>{S(c=>{const r=new Set(c);return r.has(t)?r.delete(t):r.add(t),r})},E=t=>["tools","prompts","resources"].some(c=>{const r=t[c];return r==="all"||Array.isArray(r)&&r.length>0}),w=(t,c,r,N=!1)=>{const k=h.find(d=>d.name===t),a={...k?{...k}:{name:t,...ve},[c]:r};if(!E(a)){const d=h.filter(D=>D.name!==t);s(d),N||S(D=>{const $=new Set(D);return $.delete(t),$});return}if(k){s(h.map(d=>d.name===t?a:d));return}s([...h,a])},R=(t,c)=>{const r=`${t}${z}`;return c.startsWith(r)?c.slice(r.length):c},n=(t,c)=>c==="tools"?(t.tools||[]).filter(r=>r.enabled!==!1).map(r=>({key:r.name,value:R(t.name,r.name),description:r.description})):c==="prompts"?(t.prompts||[]).filter(r=>r.enabled!==!1).map(r=>({key:r.name,value:R(t.name,r.name),description:r.description})):(t.resources||[]).filter(r=>r.enabled!==!1).map(r=>({key:r.uri,value:r.uri,description:r.description})),o=(t,c,r)=>{const N=u.find(d=>d.name===t);if(!N)return;const k=n(N,c).map(d=>d.value),g=h.find(d=>d.name===t);if(!g){w(t,c,[r]);return}const a=g[c];if(a==="all"){const d=k.filter(D=>D!==r);w(t,c,d);return}if(Array.isArray(a)){if(a.includes(r)){w(t,c,a.filter(D=>D!==r));return}const d=[...a,r];w(t,c,d.length===k.length?"all":d);return}w(t,c,[r])},v=t=>{const c=h.find(r=>r.name===t);return!!(c&&E(c))},l=t=>{const c=h.find(r=>r.name===t);return c?["tools","prompts","resources"].some(r=>{const N=c[r];return Array.isArray(N)&&N.length>0}):!1},i=(t,c,r)=>{const N=h.find(g=>g.name===t);if(!N)return!1;const k=N[c];return k==="all"?!0:Array.isArray(k)?k.includes(r):!1},A=(t,c)=>{const r=h.find(g=>g.name===t.name);if(!r)return 0;const N=n(t,c),k=r[c];if(k==="all")return N.length;if(Array.isArray(k)){const g=new Set(N.map(a=>a.value));return k.filter(a=>g.has(a)).length}return 0},T=[{key:"tools",titleKey:"groups.toolSelection",countKey:"groups.toolsSelected",allKey:"groups.allTools"},{key:"prompts",titleKey:"groups.promptSelection",countKey:"groups.promptsSelected",allKey:"groups.allPrompts"},{key:"resources",titleKey:"groups.resourceSelection",countKey:"groups.resourcesSelected",allKey:"groups.allResources"}],P=t=>T.map(({key:c})=>({key:c,count:A(t,c)})).filter(c=>c.count>0);return e.jsxs("div",{className:ae("space-y-4",m),children:[e.jsx("div",{className:"space-y-3",children:u.map(t=>{const c=v(t.name),r=l(t.name),N=f.has(t.name),k=h.find(d=>d.name===t.name),g=P(t),a=T.filter(({key:d})=>n(t,d).length>0);return e.jsxs("div",{className:"border border-gray-200 dark:border-gray-700 rounded-lg hover:border-gray-300 hover:bg-gray-50 dark:bg-gray-800 dark:hover:bg-gray-700 transition-colors",children:[e.jsxs("div",{className:"flex items-center justify-between p-3 cursor-pointer rounded-lg transition-colors",onClick:()=>j(t.name),children:[e.jsxs("div",{className:"flex items-center space-x-3",onClick:d=>{d.stopPropagation(),C(t.name)},children:[e.jsx("input",{type:"checkbox",checked:c||r,onChange:()=>C(t.name),className:"w-4 h-4 text-blue-600 bg-gray-100 dark:bg-gray-800 border-gray-300 rounded focus:ring-blue-500"}),e.jsx("span",{className:"font-medium text-gray-900 cursor-pointer select-none",children:t.name})]}),e.jsxs("div",{className:"flex items-center space-x-3",children:[g.map(({key:d,count:D})=>e.jsxs("span",{className:"text-sm text-green-600 flex items-center gap-1",children:[d==="tools"?e.jsx(ie,{size:14}):d==="prompts"?e.jsx(ce,{size:14}):e.jsx(de,{size:14})," ",D]},d)),a.length>0&&e.jsx("button",{type:"button",className:"p-1 text-gray-400 hover:text-gray-600 transition-colors",children:e.jsx("svg",{className:ae("w-5 h-5 transition-transform",N&&"rotate-180"),fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 9l-7 7-7-7"})})})]})]}),N&&a.length>0&&e.jsx("div",{className:"border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 p-3",children:e.jsx("div",{className:"space-y-4",children:a.map(({key:d,titleKey:D,countKey:$,allKey:q})=>{const O=n(t,d),V=A(t,d),B=(k==null?void 0:k[d])==="all"||V===O.length;return e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-3",children:[e.jsx("span",{className:"text-sm font-medium text-gray-700",children:x(D)}),e.jsxs("div",{className:"flex items-center gap-3",children:[k&&e.jsx("span",{className:"text-xs text-green-600",children:B?`(${x(q)} ${O.length}/${O.length})`:`(${x($)} ${V}/${O.length})`}),e.jsx("button",{type:"button",onClick:()=>{w(t.name,d,B?[]:"all",!0)},className:"text-sm text-blue-600 hover:text-blue-800 transition-colors",children:x(B?"groups.selectNone":"groups.selectAll")})]})]}),e.jsx("div",{className:"grid grid-cols-1 gap-2 max-h-32 overflow-y-auto",children:O.map(G=>{const M=i(t.name,d,G.value);return e.jsxs("label",{className:"flex items-center space-x-2 text-sm",children:[e.jsx("input",{type:"checkbox",checked:M,onChange:()=>o(t.name,d,G.value),className:"w-3 h-3 text-blue-600 bg-gray-100 dark:bg-gray-800 border-gray-300 rounded focus:ring-blue-500"}),e.jsx("span",{className:"text-gray-700 break-all whitespace-nowrap",children:G.value}),G.description&&e.jsx("span",{className:"text-gray-400 text-xs truncate",children:G.description})]},G.key)})})]},d)})})})]},t.name)})}),u.length===0&&e.jsx("p",{className:"text-gray-500 text-sm",children:x("groups.noServerOptions")})]})},Ne=({onAdd:p,onCancel:I})=>{const{t:s}=L(),{createGroup:m}=Z(),{allServers:x}=Q(),[z,f]=b.useState([]),[S,h]=b.useState(null),[u,C]=b.useState(!1),[j,E]=b.useState({name:"",description:"",servers:[]});b.useEffect(()=>{f(x.filter(n=>n.enabled!==!1))},[x]);const w=n=>{const{name:o,value:v}=n.target;E(l=>({...l,[o]:v}))},R=async n=>{n.preventDefault(),C(!0),h(null);try{if(!j.name.trim()){h(s("groups.nameRequired")),C(!1);return}const o=await m(j.name,j.description,j.servers);if(!o||!o.success){h((o==null?void 0:o.message)||s("groups.createError")),C(!1);return}p()}catch(o){h(o instanceof Error?o.message:String(o)),C(!1)}};return e.jsx("div",{className:"fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4",children:e.jsxs("div",{className:"bg-white dark:bg-gray-800 rounded-lg shadow-lg max-w-3xl w-full max-h-[90vh] flex flex-col",children:[e.jsxs("div",{className:"p-6 flex-shrink-0",children:[e.jsx("h2",{className:"text-xl font-semibold text-gray-800 mb-4",children:s("groups.addNew")}),S&&e.jsx("div",{className:"mb-4 p-3 bg-red-100 text-red-700 rounded-md border border-gray-200 dark:border-gray-700",children:S})]}),e.jsxs("form",{onSubmit:R,className:"flex flex-col flex-1 min-h-0",children:[e.jsx("div",{className:"flex-1 overflow-y-auto px-6",children:e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{children:[e.jsxs("label",{className:"block text-gray-700 text-sm font-bold mb-2",htmlFor:"name",children:[s("groups.name")," *"]}),e.jsx("input",{type:"text",id:"name",name:"name",value:j.name,onChange:w,className:"w-full border border-gray-300 rounded-md px-3 py-2 text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent",placeholder:s("groups.namePlaceholder"),required:!0})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-gray-700 text-sm font-bold mb-2",children:s("groups.configureCapabilities")}),e.jsx(me,{servers:z,value:j.servers,onChange:n=>E(o=>({...o,servers:n})),className:"border border-gray-200 dark:border-gray-700 rounded-lg p-4 bg-gray-50 dark:bg-gray-800"})]})]})}),e.jsxs("div",{className:"flex justify-end space-x-3 p-6 pt-4 border-t border-gray-200 dark:border-gray-700 flex-shrink-0",children:[e.jsx("button",{type:"button",onClick:I,className:"px-4 py-2 text-gray-600 hover:text-gray-800 border border-gray-300 rounded-md hover:bg-gray-50 dark:bg-gray-800 dark:hover:bg-gray-700 transition-colors",disabled:u,children:s("common.cancel")}),e.jsx("button",{type:"submit",className:"px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 disabled:opacity-50 transition-colors",disabled:u,children:s(u?"common.submitting":"common.create")})]})]})]})})},we=({group:p,onEdit:I,onCancel:s})=>{const{t:m}=L(),{updateGroup:x}=Z(),{allServers:z}=Q(),[f,S]=b.useState([]),[h,u]=b.useState(null),[C,j]=b.useState(!1),[E,w]=b.useState({name:p.name,description:p.description||"",servers:p.servers||[]});b.useEffect(()=>{S(z.filter(o=>o.enabled!==!1))},[z]);const R=o=>{const{name:v,value:l}=o.target;w(i=>({...i,[v]:l}))},n=async o=>{o.preventDefault(),j(!0),u(null);try{if(!E.name.trim()){u(m("groups.nameRequired")),j(!1);return}const v=await x(p.id,{name:E.name,description:E.description,servers:E.servers});if(!v||!v.success){u((v==null?void 0:v.message)||m("groups.updateError")),j(!1);return}I()}catch(v){u(v instanceof Error?v.message:String(v)),j(!1)}};return e.jsx("div",{className:"fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4",children:e.jsxs("div",{className:"bg-white dark:bg-gray-800 rounded-lg shadow-lg max-w-3xl w-full max-h-[90vh] flex flex-col",children:[e.jsxs("div",{className:"p-6 flex-shrink-0",children:[e.jsx("h2",{className:"text-xl font-semibold text-gray-800 mb-4",children:m("groups.edit")}),h&&e.jsx("div",{className:"mb-4 p-3 bg-red-100 text-red-700 rounded-md border border-gray-200 dark:border-gray-700",children:h})]}),e.jsxs("form",{onSubmit:n,className:"flex flex-col flex-1 min-h-0",children:[e.jsx("div",{className:"flex-1 overflow-y-auto px-6",children:e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{children:[e.jsxs("label",{className:"block text-gray-700 text-sm font-bold mb-2",htmlFor:"name",children:[m("groups.name")," *"]}),e.jsx("input",{type:"text",id:"name",name:"name",value:E.name,onChange:R,className:"w-full border border-gray-300 rounded-md px-3 py-2 text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent",placeholder:m("groups.namePlaceholder"),required:!0})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-gray-700 text-sm font-bold mb-2",children:m("groups.configureCapabilities")}),e.jsx(me,{servers:f,value:E.servers,onChange:o=>w(v=>({...v,servers:o})),className:"border border-gray-200 dark:border-gray-700 rounded-lg p-4 bg-gray-50 dark:bg-gray-800"})]})]})}),e.jsxs("div",{className:"flex justify-end space-x-3 p-6 pt-4 border-t border-gray-200 dark:border-gray-700 flex-shrink-0",children:[e.jsx("button",{type:"button",onClick:s,className:"px-4 py-2 text-gray-600 hover:text-gray-800 border border-gray-300 rounded-md hover:bg-gray-50 dark:bg-gray-800 dark:hover:bg-gray-700 transition-colors",disabled:C,children:m("common.cancel")}),e.jsx("button",{type:"submit",className:"px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 disabled:opacity-50 transition-colors",disabled:C,children:m(C?"common.submitting":"common.save")})]})]})]})})},Se=({group:p,servers:I,onEdit:s,onDelete:m})=>{const{t:x}=L(),{showToast:z}=xe(),{installConfig:f,nameSeparator:S}=oe(),[h,u]=b.useState(!1),[C,j]=b.useState(!1),[E,w]=b.useState(!1),[R,n]=b.useState(null),o=b.useRef(null);b.useEffect(()=>{const g=a=>{o.current&&!o.current.contains(a.target)&&w(!1)};return document.addEventListener("mousedown",g),()=>{document.removeEventListener("mousedown",g)}},[]);const v=()=>{s(p)},l=()=>{u(!0)},i=()=>{m(p.id),u(!1)},A=g=>{if(navigator.clipboard&&window.isSecureContext)navigator.clipboard.writeText(g).then(()=>{j(!0),w(!1),z(x("common.copySuccess"),"success"),setTimeout(()=>j(!1),2e3)});else{const a=document.createElement("textarea");a.value=g,a.style.position="fixed",a.style.left="-9999px",document.body.appendChild(a),a.focus(),a.select();try{document.execCommand("copy"),j(!0),w(!1),z(x("common.copySuccess"),"success"),setTimeout(()=>j(!1),2e3)}catch(d){z(x("common.copyFailed")||"Copy failed","error"),console.error("Copy to clipboard failed:",d)}document.body.removeChild(a)}},T=()=>{A(p.id)},P=()=>{A(`${f.baseUrl}/mcp/${p.id}`)},t=()=>{const g={mcpServers:{mcphub:{url:`${f.baseUrl}/mcp/${p.id}`,headers:{Authorization:"Bearer <your-access-token>"}}}};A(JSON.stringify(g,null,2))},c=g=>g.map(a=>typeof a=="string"?a:a.name),r=g=>{const a=p.servers.find(d=>typeof d=="string"?d===g:d.name===g);return typeof a=="string"?{name:a,tools:"all",prompts:"all",resources:"all"}:a},N=c(p.servers),k=I.filter(g=>N.includes(g.name));return e.jsxs("div",{className:"bg-white dark:bg-gray-800 shadow rounded-lg p-4",children:[e.jsxs("div",{className:"flex justify-between items-center",children:[e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center",children:[e.jsx("h2",{className:"text-xl font-semibold text-gray-800",children:p.name}),e.jsxs("div",{className:"flex items-center ml-3",children:[e.jsx("span",{className:"text-xs text-gray-500 mr-1",children:p.id}),e.jsxs("div",{className:"relative",ref:o,children:[e.jsxs("button",{onClick:()=>w(!E),className:"p-1 text-gray-400 hover:text-gray-600 transition-colors flex items-center",title:x("common.copy"),children:[C?e.jsx(ue,{size:14,className:"text-green-500"}):e.jsx(le,{size:14}),e.jsx(ge,{size:12,className:"ml-1"})]}),E&&e.jsxs("div",{className:"absolute top-full left-0 mt-1 bg-white dark:bg-gray-800 shadow-lg rounded-md border border-gray-200 dark:border-gray-700 py-1 z-10 min-w-[140px]",children:[e.jsxs("button",{onClick:T,className:"w-full px-3 py-2 text-left text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-700 flex items-center",children:[e.jsx(le,{size:12,className:"mr-2"}),x("common.copyId")]}),e.jsxs("button",{onClick:P,className:"w-full px-3 py-2 text-left text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-700 flex items-center",children:[e.jsx(he,{size:12,className:"mr-2"}),x("common.copyUrl")]}),e.jsxs("button",{onClick:t,className:"w-full px-3 py-2 text-left text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-700 flex items-center",children:[e.jsx(be,{size:12,className:"mr-2"}),x("common.copyJson")]})]})]})]})]}),p.description&&e.jsx("p",{className:"text-gray-600 text-sm mt-1",children:p.description})]}),e.jsxs("div",{className:"flex items-center space-x-3",children:[e.jsx("div",{className:"bg-blue-50 text-blue-700 dark:bg-blue-900/40 dark:text-blue-300 px-3 py-1 rounded-full text-sm btn-secondary",children:x("groups.serverCount",{count:p.servers.length})}),e.jsx("button",{onClick:v,className:"text-gray-500 hover:text-gray-700",title:x("groups.edit"),children:e.jsx(fe,{size:18})}),e.jsx("button",{onClick:l,className:"text-gray-500 hover:text-red-600",title:x("groups.delete"),children:e.jsx(ye,{size:18})})]})]}),e.jsx("div",{className:"",children:k.length===0?e.jsx("p",{className:"text-gray-500 italic",children:x("groups.noServers")}):e.jsx("div",{className:"flex flex-wrap gap-2",children:k.map(g=>{const a=r(g.name),d=a&&a.tools!=="all"&&Array.isArray(a.tools),D=a&&a.prompts!=="all"&&Array.isArray(a.prompts),$=a&&a.resources!=="all"&&Array.isArray(a.resources),q=(g.tools||[]).filter(y=>y.enabled!==!1),O=(g.prompts||[]).filter(y=>y.enabled!==!1),V=(g.resources||[]).filter(y=>y.enabled!==!1),B=y=>{const F=`${g.name}${S}`;return y.startsWith(F)?y.slice(F.length):y},G=y=>{const F=`${g.name}${S}`;return y.startsWith(F)?y.slice(F.length):y},M=q.map(y=>B(y.name)),U=O.map(y=>G(y.name)),J=V.map(y=>y.uri),ee=new Set(M),se=new Set(U),te=new Set(J),K=d&&Array.isArray(a==null?void 0:a.tools)?a.tools.filter(y=>ee.has(y)).length:M.length,H=D&&Array.isArray(a==null?void 0:a.prompts)?a.prompts.filter(y=>se.has(y)).length:U.length,W=$&&Array.isArray(a==null?void 0:a.resources)?a.resources.filter(y=>te.has(y)).length:J.length,re=R===g.name,_=y=>y==="tools"?d&&Array.isArray(a==null?void 0:a.tools)?a.tools.filter(F=>ee.has(F)):M:y==="prompts"?D&&Array.isArray(a==null?void 0:a.prompts)?a.prompts.filter(F=>se.has(F)):U:$&&Array.isArray(a==null?void 0:a.resources)?a.resources.filter(F=>te.has(F)):J,pe=()=>{n(re?null:g.name)};return e.jsxs("div",{className:"relative",children:[e.jsxs("div",{className:"flex items-center space-x-2 bg-gray-50 dark:bg-gray-800 rounded-lg px-3 py-2 cursor-pointer hover:bg-gray-100 transition-colors",onClick:pe,children:[e.jsx("span",{className:"font-medium text-gray-700 text-sm",children:g.name}),e.jsx("span",{className:`inline-block h-2 w-2 rounded-full ${g.status==="connected"?"bg-green-500":g.status==="connecting"?"bg-yellow-500":"bg-red-500"}`}),K>0&&e.jsxs("span",{className:"text-xs text-blue-600 bg-blue-100 dark:bg-blue-900/40 dark:text-blue-300 px-2 py-0.5 rounded flex items-center gap-1",children:[e.jsx(ie,{size:12}),K]}),H>0&&e.jsxs("span",{className:"text-xs text-purple-600 bg-purple-100 dark:bg-purple-900/40 dark:text-purple-300 px-2 py-0.5 rounded flex items-center gap-1",children:[e.jsx(ce,{size:12}),H]}),W>0&&e.jsxs("span",{className:"text-xs text-emerald-600 bg-emerald-100 dark:bg-emerald-900/40 dark:text-emerald-300 px-2 py-0.5 rounded flex items-center gap-1",children:[e.jsx(de,{size:12}),W]})]}),re&&e.jsx("div",{className:"absolute top-full left-0 mt-1 bg-white dark:bg-gray-800 shadow-lg rounded-md border border-gray-200 dark:border-gray-700 p-3 z-10 min-w-[300px] max-w-[400px]",children:e.jsxs("div",{className:"space-y-3",children:[K>0&&e.jsxs("div",{children:[e.jsxs("div",{className:"text-gray-600 text-xs mb-2",children:[x(d?"groups.selectedTools":"groups.allTools"),":"]}),e.jsx("div",{className:"flex flex-wrap gap-1",children:_("tools").map((y,F)=>e.jsx("span",{className:"inline-block bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 px-2 py-1 rounded text-xs",children:y},`tool-${F}`))})]}),H>0&&e.jsxs("div",{children:[e.jsxs("div",{className:"text-gray-600 text-xs mb-2",children:[x(D?"groups.selectedPrompts":"groups.allPrompts"),":"]}),e.jsx("div",{className:"flex flex-wrap gap-1",children:_("prompts").map((y,F)=>e.jsx("span",{className:"inline-block bg-purple-50 text-purple-700 dark:bg-purple-900/40 dark:text-purple-300 px-2 py-1 rounded text-xs",children:y},`prompt-${F}`))})]}),W>0&&e.jsxs("div",{children:[e.jsxs("div",{className:"text-gray-600 text-xs mb-2",children:[x($?"groups.selectedResources":"groups.allResources"),":"]}),e.jsx("div",{className:"flex flex-wrap gap-1",children:_("resources").map((y,F)=>e.jsx("span",{className:"inline-block bg-emerald-50 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-300 px-2 py-1 rounded text-xs break-all",children:y},`resource-${F}`))})]})]})})]},g.name)})})}),e.jsx(je,{isOpen:h,onClose:()=>u(!1),onConfirm:i,serverName:p.name,isGroup:!0})]})},ke=({onSuccess:p,onCancel:I})=>{const{t:s}=L(),[m,x]=b.useState(""),[z,f]=b.useState(null),[S,h]=b.useState(!1),[u,C]=b.useState(null),j=`{
|
|
2
|
+
"groups": [
|
|
3
|
+
{
|
|
4
|
+
"name": "AI Assistants",
|
|
5
|
+
"servers": ["openai-server", "anthropic-server"]
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"name": "Development Tools",
|
|
9
|
+
"servers": [
|
|
10
|
+
{
|
|
11
|
+
"name": "github-server",
|
|
12
|
+
"tools": ["create_issue", "list_repos"],
|
|
13
|
+
"prompts": ["triage_prompt"],
|
|
14
|
+
"resources": ["resource://docs/repo-guide"]
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"name": "gitlab-server",
|
|
18
|
+
"tools": "all",
|
|
19
|
+
"prompts": "all",
|
|
20
|
+
"resources": "all"
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
Supports:
|
|
28
|
+
- Simple server list: ["server1", "server2"]
|
|
29
|
+
- Advanced server config: [{"name": "server1", "tools": ["tool1"], "prompts": ["prompt1"], "resources": ["resource://docs/guide"]}]
|
|
30
|
+
- All groups will be imported in a single efficient batch operation.`,E=l=>{try{const i=JSON.parse(l.trim());if(!i.groups||!Array.isArray(i.groups))return f(s("groupImport.invalidFormat")),null;for(const A of i.groups)if(!A.name||typeof A.name!="string")return f(s("groupImport.missingName")),null;return i}catch{return f(s("groupImport.parseError")),null}},w=()=>{f(null);const l=E(m);l&&C(l.groups)},R=async()=>{if(u){h(!0),f(null);try{const l=await X("/groups/batch",{groups:u});if(l.success){const{successCount:i,failureCount:A,results:T}=l;if(A>0){const P=T.filter(t=>!t.success).map(t=>`${t.name}: ${t.message||s("groupImport.addFailed")}`);f(s("groupImport.partialSuccess",{count:i,total:u.length})+`
|
|
31
|
+
`+P.join(`
|
|
32
|
+
`))}i>0&&p()}else f(l.message||s("groupImport.importFailed"))}catch(l){console.error("Import error:",l),f(s("groupImport.importFailed"))}finally{h(!1)}}},n=l=>e.jsx("span",{className:"text-gray-500 ml-2",children:s(`groups.${l}`)}),o=(l,i)=>{if(!i||i==="all")return null;const A=Array.isArray(i)?i.join(", "):i;return e.jsx("span",{className:"text-gray-500 ml-2",children:s(`groups.${l}`,{items:A})})},v=l=>!l||l.length===0?e.jsx("span",{className:"text-gray-500",children:s("groups.noServers")}):e.jsx("div",{className:"space-y-1",children:l.map((i,A)=>typeof i=="string"?e.jsxs("div",{className:"text-sm",children:["• ",i]},A):e.jsxs("div",{className:"text-sm",children:["• ",i.name,i.tools&&i.tools!=="all"&&e.jsxs("span",{className:"text-gray-500 ml-2",children:["(",Array.isArray(i.tools)?i.tools.join(", "):i.tools,")"]}),i.tools==="all"&&n("previewAllTools"),o("previewPrompts",i.prompts),i.prompts==="all"&&n("previewAllPrompts"),o("previewResources",i.resources),i.resources==="all"&&n("previewAllResources")]},A))});return e.jsx("div",{className:"fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4",children:e.jsxs("div",{className:"bg-white dark:bg-gray-800 shadow rounded-lg p-6 w-full max-w-4xl max-h-[90vh] overflow-y-auto",children:[e.jsxs("div",{className:"flex justify-between items-center mb-6",children:[e.jsx("h2",{className:"text-xl font-semibold text-gray-900",children:s("groupImport.title")}),e.jsx("button",{onClick:I,className:"text-gray-500 hover:text-gray-700",children:"✕"})]}),z&&e.jsx("div",{className:"mb-4 bg-red-50 border-l-4 border-red-500 p-4 rounded",children:e.jsx("p",{className:"text-red-700 whitespace-pre-wrap",children:z})}),u?e.jsxs("div",{children:[e.jsxs("div",{className:"mb-4",children:[e.jsx("h3",{className:"text-lg font-medium text-gray-900 mb-3",children:s("groupImport.previewTitle")}),e.jsx("div",{className:"space-y-3",children:u.map((l,i)=>e.jsx("div",{className:"bg-gray-50 dark:bg-gray-800 p-4 rounded-lg border border-gray-200 dark:border-gray-700",children:e.jsx("div",{className:"flex items-start justify-between",children:e.jsxs("div",{className:"flex-1",children:[e.jsx("h4",{className:"font-medium text-gray-900",children:l.name}),l.description&&e.jsx("p",{className:"text-sm text-gray-600 mt-1",children:l.description}),e.jsxs("div",{className:"mt-2 text-sm text-gray-600",children:[e.jsxs("strong",{children:[s("groups.servers"),":"]}),e.jsx("div",{className:"mt-1",children:v(l.servers)})]})]})})},i))})]}),e.jsxs("div",{className:"flex justify-end space-x-4",children:[e.jsx("button",{onClick:()=>C(null),disabled:S,className:"px-4 py-2 text-gray-700 bg-gray-200 rounded hover:bg-gray-300 disabled:opacity-50 btn-secondary",children:s("common.back")}),e.jsx("button",{onClick:R,disabled:S,className:"px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50 flex items-center btn-primary",children:S?e.jsxs(e.Fragment,{children:[e.jsxs("svg",{className:"animate-spin h-4 w-4 mr-2",xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",children:[e.jsx("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),e.jsx("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"})]}),s("groupImport.importing")]}):s("groupImport.import")})]})]}):e.jsxs("div",{children:[e.jsxs("div",{className:"mb-4",children:[e.jsx("label",{className:"block text-sm font-medium text-gray-700 mb-2",children:s("groupImport.inputLabel")}),e.jsx("textarea",{value:m,onChange:l=>x(l.target.value),className:"w-full h-96 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono text-sm",placeholder:j}),e.jsx("p",{className:"text-xs text-gray-500 mt-2",children:s("groupImport.inputHelp")})]}),e.jsxs("div",{className:"flex justify-end space-x-4",children:[e.jsx("button",{onClick:I,className:"px-4 py-2 text-gray-700 bg-gray-200 rounded hover:bg-gray-300 btn-secondary",children:s("common.cancel")}),e.jsx("button",{onClick:w,disabled:!m.trim(),className:"px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50 btn-primary",children:s("groupImport.preview")})]})]})]})})},Ce=({groups:p,onCancel:I})=>{const{t:s}=L(),[m,x]=b.useState(""),[z,f]=b.useState(""),[S,h]=b.useState([]),[u,C]=b.useState(!1),[j,E]=b.useState(!1),[w,R]=b.useState(null),n=l=>{h(i=>i.includes(l)?i.filter(A=>A!==l):[...i,l])},o=()=>{S.length===p.length?h([]):h(p.map(l=>l.id))},v=async()=>{if(!m.trim()){R(s("template.nameRequired"));return}E(!0),R(null);try{const l=await X("/templates/export",{name:m.trim(),description:z.trim()||void 0,groupIds:S.length>0?S:void 0,includeDisabledServers:u});if(l.success&&l.data){const i=l.data,A=new Blob([JSON.stringify(i,null,2)],{type:"application/json"}),T=URL.createObjectURL(A),P=document.createElement("a");P.href=T,P.download=`${i.name.replace(/[^a-zA-Z0-9-_]/g,"_")}.mcphub-template.json`,document.body.appendChild(P),P.click(),document.body.removeChild(P),URL.revokeObjectURL(T),I()}else R(l.message||s("template.exportFailed"))}catch(l){console.error("Export error:",l),R(s("template.exportFailed"))}finally{E(!1)}};return e.jsx("div",{className:"fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4",children:e.jsxs("div",{className:"bg-white dark:bg-gray-800 shadow rounded-lg p-6 w-full max-w-3xl max-h-[90vh] overflow-y-auto",children:[e.jsxs("div",{className:"flex justify-between items-center mb-6",children:[e.jsx("h2",{className:"text-xl font-semibold text-gray-900",children:s("template.exportTitle")}),e.jsx("button",{onClick:I,className:"text-gray-500 hover:text-gray-700",children:"✕"})]}),w&&e.jsx("div",{className:"mb-4 bg-red-50 border-l-4 border-red-500 p-4 rounded",children:e.jsx("p",{className:"text-red-700",children:w})}),e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{children:[e.jsxs("label",{className:"block text-sm font-medium text-gray-700 mb-1",children:[s("template.name")," *"]}),e.jsx("input",{type:"text",value:m,onChange:l=>x(l.target.value),className:"w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500",placeholder:s("template.namePlaceholder")})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-sm font-medium text-gray-700 mb-1",children:s("template.description")}),e.jsx("input",{type:"text",value:z,onChange:l=>f(l.target.value),className:"w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500",placeholder:s("template.descriptionPlaceholder")})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex justify-between items-center mb-2",children:[e.jsx("label",{className:"block text-sm font-medium text-gray-700",children:s("template.selectGroups")}),e.jsx("button",{onClick:o,className:"text-sm text-blue-600 hover:text-blue-800",children:S.length===p.length?s("template.deselectAll"):s("template.selectAll")})]}),e.jsx("p",{className:"text-xs text-gray-500 mb-2",children:s("template.selectGroupsHelp")}),e.jsx("div",{className:"border border-gray-200 dark:border-gray-700 rounded-md max-h-48 overflow-y-auto",children:p.map(l=>e.jsxs("label",{className:"flex items-center px-3 py-2 hover:bg-gray-50 dark:bg-gray-800 dark:hover:bg-gray-700 cursor-pointer",children:[e.jsx("input",{type:"checkbox",checked:S.includes(l.id),onChange:()=>n(l.id),className:"mr-3 h-4 w-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"}),e.jsxs("div",{children:[e.jsx("span",{className:"text-sm font-medium text-gray-900",children:l.name}),l.description&&e.jsx("span",{className:"text-xs text-gray-500 ml-2",children:l.description})]})]},l.id))})]}),e.jsxs("label",{className:"flex items-center space-x-2 cursor-pointer",children:[e.jsx("input",{type:"checkbox",checked:u,onChange:l=>C(l.target.checked),className:"h-4 w-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"}),e.jsx("span",{className:"text-sm text-gray-700",children:s("template.includeDisabled")})]})]}),e.jsx("div",{className:"mt-4 p-3 bg-blue-50 border border-blue-200 rounded-md",children:e.jsx("p",{className:"text-sm text-blue-700",children:s("template.exportNote")})}),e.jsxs("div",{className:"flex justify-end space-x-4 mt-6",children:[e.jsx("button",{onClick:I,className:"px-4 py-2 text-gray-700 bg-gray-200 rounded hover:bg-gray-300 btn-secondary",children:s("common.cancel")}),e.jsx("button",{onClick:v,disabled:j||!m.trim(),className:"px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed btn-primary",children:s(j?"template.exporting":"template.export")})]})]})})},Ae=({onSuccess:p,onCancel:I})=>{const{t:s}=L(),[m,x]=b.useState(null),[z,f]=b.useState(null),[S,h]=b.useState(!1),[u,C]=b.useState(null),j=b.useRef(null),E=n=>{var l;f(null),x(null),C(null);const o=(l=n.target.files)==null?void 0:l[0];if(!o)return;const v=new FileReader;v.onload=i=>{var A;try{const T=JSON.parse((A=i.target)==null?void 0:A.result);if(!T.version||!T.name||!T.servers||!T.groups){f(s("template.invalidFormat"));return}x(T)}catch{f(s("template.parseError"))}},v.readAsText(o)},w=n=>{if(f(null),x(null),C(null),!!n.trim())try{const o=JSON.parse(n.trim());if(!o.version||!o.name||!o.servers||!o.groups){f(s("template.invalidFormat"));return}x(o)}catch{f(s("template.parseError"))}},R=async()=>{if(m){h(!0),f(null);try{const n=await X("/templates/import",m);n.data?(C(n.data),n.data.success&&p()):f(n.message||s("template.importFailed"))}catch(n){console.error("Import error:",n),f(s("template.importFailed"))}finally{h(!1)}}};return e.jsx("div",{className:"fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4",children:e.jsxs("div",{className:"bg-white dark:bg-gray-800 shadow rounded-lg p-6 w-full max-w-4xl max-h-[90vh] overflow-y-auto",children:[e.jsxs("div",{className:"flex justify-between items-center mb-6",children:[e.jsx("h2",{className:"text-xl font-semibold text-gray-900",children:s("template.importTitle")}),e.jsx("button",{onClick:I,className:"text-gray-500 hover:text-gray-700",children:"✕"})]}),z&&e.jsx("div",{className:"mb-4 bg-red-50 border-l-4 border-red-500 p-4 rounded",children:e.jsx("p",{className:"text-red-700",children:z})}),u&&e.jsxs("div",{className:`mb-4 p-4 rounded border-l-4 ${u.success?"bg-green-50 border-green-500":"bg-yellow-50 border-yellow-500"}`,children:[e.jsx("p",{className:u.success?"text-green-700":"text-yellow-700",children:s("template.importResult",{serversCreated:u.serversCreated,serversSkipped:u.serversSkipped,groupsCreated:u.groupsCreated,groupsSkipped:u.groupsSkipped})}),u.requiredEnvVars.length>0&&e.jsxs("div",{className:"mt-2 p-2 bg-orange-50 border border-orange-200 rounded",children:[e.jsx("p",{className:"text-sm font-medium text-orange-800",children:s("template.envVarsNeeded")}),e.jsx("ul",{className:"mt-1 text-sm text-orange-700",children:u.requiredEnvVars.map(n=>e.jsx("li",{className:"font-mono",children:n},n))})]})]}),m?e.jsxs("div",{children:[e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"p-3 bg-gray-50 dark:bg-gray-800 rounded-md",children:[e.jsx("h3",{className:"font-medium text-gray-900",children:m.name}),m.description&&e.jsx("p",{className:"text-sm text-gray-600 mt-1",children:m.description}),e.jsxs("p",{className:"text-xs text-gray-500 mt-1",children:[s("template.version"),": ",m.version," | ",s("template.createdAt"),":"," ",new Date(m.createdAt).toLocaleDateString()]})]}),e.jsxs("div",{children:[e.jsxs("h4",{className:"text-sm font-medium text-gray-700 mb-2",children:[s("template.servers")," (",Object.keys(m.servers).length,")"]}),e.jsx("div",{className:"border border-gray-200 dark:border-gray-700 rounded-md divide-y divide-gray-200 dark:divide-gray-700 max-h-40 overflow-y-auto",children:Object.entries(m.servers).map(([n,o])=>e.jsxs("div",{className:"px-3 py-2",children:[e.jsx("span",{className:"text-sm font-medium text-gray-900",children:n}),e.jsx("span",{className:"text-xs text-gray-500 ml-2",children:o.type||"stdio"})]},n))})]}),e.jsxs("div",{children:[e.jsxs("h4",{className:"text-sm font-medium text-gray-700 mb-2",children:[s("template.groups")," (",m.groups.length,")"]}),e.jsx("div",{className:"border border-gray-200 dark:border-gray-700 rounded-md divide-y divide-gray-200 dark:divide-gray-700 max-h-40 overflow-y-auto",children:m.groups.map((n,o)=>e.jsxs("div",{className:"px-3 py-2",children:[e.jsx("span",{className:"text-sm font-medium text-gray-900",children:n.name}),n.description&&e.jsx("span",{className:"text-xs text-gray-500 ml-2",children:n.description}),e.jsxs("div",{className:"text-xs text-gray-500 mt-1",children:[n.servers.length," ",s("template.serversInGroup")]})]},o))})]}),m.requiredEnvVars.length>0&&e.jsxs("div",{className:"p-3 bg-orange-50 border border-orange-200 rounded-md",children:[e.jsx("h4",{className:"text-sm font-medium text-orange-800",children:s("template.envVarsNeeded")}),e.jsx("ul",{className:"mt-1 text-sm text-orange-700 font-mono",children:m.requiredEnvVars.map(n=>e.jsx("li",{children:n},n))})]})]}),e.jsxs("div",{className:"flex justify-end space-x-4 mt-6",children:[e.jsx("button",{onClick:()=>{x(null),C(null),j.current&&(j.current.value="")},className:"px-4 py-2 text-gray-700 bg-gray-200 rounded hover:bg-gray-300 btn-secondary",children:s("common.back")}),e.jsx("button",{onClick:R,disabled:S,className:"px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed btn-primary",children:s(S?"template.importing":"template.import")})]})]}):e.jsxs("div",{children:[e.jsxs("div",{className:"mb-4",children:[e.jsx("div",{className:"flex items-center justify-between mb-2",children:e.jsx("label",{className:"block text-sm font-medium text-gray-700",children:s("template.uploadFile")})}),e.jsx("div",{className:"flex items-center space-x-4",children:e.jsx("input",{ref:j,type:"file",accept:".json",onChange:E,className:"block text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100"})})]}),e.jsxs("div",{className:"relative my-4",children:[e.jsx("div",{className:"absolute inset-0 flex items-center",children:e.jsx("div",{className:"w-full border-t border-gray-300"})}),e.jsx("div",{className:"relative flex justify-center text-sm",children:e.jsx("span",{className:"bg-white dark:bg-gray-800 px-2 text-gray-500",children:s("template.or")})})]}),e.jsxs("div",{className:"mb-4",children:[e.jsx("label",{className:"block text-sm font-medium text-gray-700 mb-2",children:s("template.pasteJson")}),e.jsx("textarea",{className:"w-full h-64 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono text-sm",placeholder:s("template.pastePlaceholder"),onChange:n=>w(n.target.value)})]}),e.jsx("div",{className:"flex justify-end space-x-4",children:e.jsx("button",{onClick:I,className:"px-4 py-2 text-gray-700 bg-gray-200 rounded hover:bg-gray-300 btn-secondary",children:s("common.cancel")})})]})]})})},$e=()=>{const{t:p}=L(),{groups:I,loading:s,error:m,setError:x,deleteGroup:z,triggerRefresh:f}=Z(),{allServers:S}=Q({refreshOnMount:!0}),[h,u]=b.useState(null),[C,j]=b.useState(!1),[E,w]=b.useState(!1),[R,n]=b.useState(!1),[o,v]=b.useState(!1),l=r=>{u(r)},i=()=>{u(null),f()},A=async r=>{const N=await z(r);(!N||!N.success)&&x((N==null?void 0:N.message)||p("groups.deleteError"))},T=()=>{j(!0)},P=()=>{j(!1),f()},t=()=>{w(!1),f()},c=()=>{v(!1),f()};return e.jsxs("div",{children:[e.jsxs("div",{className:"flex justify-between items-center mb-8",children:[e.jsx("h1",{className:"text-2xl font-bold text-gray-900",children:p("pages.groups.title")}),e.jsxs("div",{className:"flex space-x-4",children:[e.jsxs("button",{onClick:T,className:"px-4 py-2 bg-blue-100 text-blue-800 rounded hover:bg-blue-200 flex items-center btn-primary transition-all duration-200",children:[e.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",className:"h-4 w-4 mr-2",viewBox:"0 0 20 20",fill:"currentColor",children:e.jsx("path",{fillRule:"evenodd",d:"M10 3a1 1 0 00-1 1v5H4a1 1 0 100 2h5v5a1 1 0 102 0v-5h5a1 1 0 100-2h-5V4a1 1 0 00-1-1z",clipRule:"evenodd"})}),p("groups.add")]}),e.jsxs("button",{onClick:()=>w(!0),className:"px-4 py-2 bg-blue-100 text-blue-800 rounded hover:bg-blue-200 flex items-center btn-primary transition-all duration-200",children:[e.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",className:"h-4 w-4 mr-2",viewBox:"0 0 20 20",fill:"currentColor",children:e.jsx("path",{fillRule:"evenodd",d:"M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z",clipRule:"evenodd"})}),p("groupImport.button")]}),e.jsxs("button",{onClick:()=>n(!0),className:"px-4 py-2 bg-green-100 text-green-800 rounded hover:bg-green-200 flex items-center btn-primary transition-all duration-200",children:[e.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",className:"h-4 w-4 mr-2",viewBox:"0 0 20 20",fill:"currentColor",children:e.jsx("path",{fillRule:"evenodd",d:"M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM6.293 6.707a1 1 0 010-1.414l3-3a1 1 0 011.414 0l3 3a1 1 0 01-1.414 1.414L11 5.414V13a1 1 0 11-2 0V5.414L7.707 6.707a1 1 0 01-1.414 0z",clipRule:"evenodd"})}),p("template.exportButton")]}),e.jsxs("button",{onClick:()=>v(!0),className:"px-4 py-2 bg-green-100 text-green-800 rounded hover:bg-green-200 flex items-center btn-primary transition-all duration-200",children:[e.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",className:"h-4 w-4 mr-2",viewBox:"0 0 20 20",fill:"currentColor",children:e.jsx("path",{fillRule:"evenodd",d:"M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z",clipRule:"evenodd"})}),p("template.importButton")]})]})]}),m&&e.jsx("div",{className:"bg-red-100 border-l-4 border-red-500 text-red-700 p-4 mb-6 error-box rounded-lg",children:e.jsx("p",{children:m})}),s?e.jsx("div",{className:"bg-white dark:bg-gray-800 shadow rounded-lg p-6 loading-container",children:e.jsxs("div",{className:"flex flex-col items-center justify-center",children:[e.jsxs("svg",{className:"animate-spin h-10 w-10 text-blue-500 mb-4",xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",children:[e.jsx("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),e.jsx("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"})]}),e.jsx("p",{className:"text-gray-600",children:p("app.loading")})]})}):I.length===0?e.jsx("div",{className:"bg-white dark:bg-gray-800 shadow rounded-lg p-6 empty-state",children:e.jsx("p",{className:"text-gray-600",children:p("groups.noGroups")})}):e.jsx("div",{className:"space-y-6",children:I.map(r=>e.jsx(Se,{group:r,servers:S,onEdit:l,onDelete:A},r.id))}),C&&e.jsx(Ne,{onAdd:P,onCancel:P}),E&&e.jsx(ke,{onSuccess:t,onCancel:()=>w(!1)}),h&&e.jsx(we,{group:h,onEdit:i,onCancel:()=>u(null)}),R&&e.jsx(Ce,{groups:I,onCancel:()=>n(!1)}),o&&e.jsx(Ae,{onSuccess:c,onCancel:()=>v(!1)})]})};export{$e as default};
|
|
33
|
+
//# sourceMappingURL=GroupsPage-BjrvyHwu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GroupsPage-BjrvyHwu.js","sources":["../../src/components/ServerToolConfig.tsx","../../src/components/AddGroupForm.tsx","../../src/components/EditGroupForm.tsx","../../src/components/GroupCard.tsx","../../src/components/GroupImportForm.tsx","../../src/components/TemplateExportForm.tsx","../../src/components/TemplateImportForm.tsx","../../src/pages/GroupsPage.tsx"],"sourcesContent":["import React, { useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { IGroupServerConfig, Prompt, Resource, Server, Tool } from '@/types';\nimport { Wrench, MessageSquare, FileText } from 'lucide-react';\nimport { cn } from '@/utils/cn';\nimport { useSettingsData } from '@/hooks/useSettingsData';\n\ntype CapabilityKey = 'tools' | 'prompts' | 'resources';\n\nconst EMPTY_SELECTIONS: Pick<IGroupServerConfig, CapabilityKey> = {\n tools: [],\n prompts: [],\n resources: [],\n};\n\nconst FULL_SELECTIONS: Pick<IGroupServerConfig, CapabilityKey> = {\n tools: 'all',\n prompts: 'all',\n resources: 'all',\n};\n\ninterface ServerToolConfigProps {\n servers: Server[];\n value: string[] | IGroupServerConfig[];\n onChange: (value: IGroupServerConfig[]) => void;\n className?: string;\n}\n\nexport const ServerToolConfig: React.FC<ServerToolConfigProps> = ({\n servers,\n value,\n onChange,\n className\n}) => {\n const { t } = useTranslation();\n const { nameSeparator } = useSettingsData();\n const [expandedServers, setExpandedServers] = useState<Set<string>>(new Set());\n\n // Normalize current value to IGroupServerConfig[] format\n const normalizedValue: IGroupServerConfig[] = React.useMemo(() => {\n return value.map(item => {\n if (typeof item === 'string') {\n return { name: item, ...FULL_SELECTIONS };\n }\n return {\n ...item,\n tools: item.tools || 'all',\n prompts: item.prompts || 'all',\n resources: item.resources || 'all',\n };\n });\n }, [value]);\n\n // Get available servers (enabled only)\n const availableServers = React.useMemo(() =>\n servers.filter(server => server.enabled !== false),\n [servers]\n );\n\n // Clean up expanded servers when servers are removed from configuration\n // But keep servers that were explicitly expanded even if they have no configuration\n React.useEffect(() => {\n const configuredServerNames = new Set(normalizedValue.map(config => config.name));\n const availableServerNames = new Set(availableServers.map(server => server.name));\n\n setExpandedServers(prev => {\n const newSet = new Set<string>();\n prev.forEach(serverName => {\n // Keep expanded if server is configured OR if server exists and user manually expanded it\n if (configuredServerNames.has(serverName) || availableServerNames.has(serverName)) {\n newSet.add(serverName);\n }\n });\n return newSet;\n });\n }, [normalizedValue, availableServers]);\n\n const toggleServer = (serverName: string) => {\n const existingIndex = normalizedValue.findIndex(config => config.name === serverName);\n\n if (existingIndex >= 0) {\n // Remove server - this also removes all capability selections\n const newValue = normalizedValue.filter(config => config.name !== serverName);\n onChange(newValue);\n } else {\n // Add server with all capabilities by default\n const newValue = [...normalizedValue, { name: serverName, ...FULL_SELECTIONS }];\n onChange(newValue);\n }\n };\n\n const toggleServerExpanded = (serverName: string) => {\n setExpandedServers(prev => {\n const newSet = new Set(prev);\n if (newSet.has(serverName)) {\n newSet.delete(serverName);\n } else {\n newSet.add(serverName);\n }\n return newSet;\n });\n };\n\n const hasAnyCapabilitySelection = (config: IGroupServerConfig) => {\n return (['tools', 'prompts', 'resources'] as CapabilityKey[]).some((capability) => {\n const selection = config[capability];\n return selection === 'all' || (Array.isArray(selection) && selection.length > 0);\n });\n };\n\n const updateServerCapability = (\n serverName: string,\n capability: CapabilityKey,\n selection: string[] | 'all',\n keepExpanded = false,\n ) => {\n const existingServer = normalizedValue.find(config => config.name === serverName);\n const baseConfig: IGroupServerConfig = existingServer\n ? { ...existingServer }\n : { name: serverName, ...EMPTY_SELECTIONS };\n const nextConfig: IGroupServerConfig = {\n ...baseConfig,\n [capability]: selection,\n };\n\n if (!hasAnyCapabilitySelection(nextConfig)) {\n const newValue = normalizedValue.filter(config => config.name !== serverName);\n onChange(newValue);\n if (!keepExpanded) {\n setExpandedServers(prev => {\n const newSet = new Set(prev);\n newSet.delete(serverName);\n return newSet;\n });\n }\n return;\n }\n\n if (existingServer) {\n onChange(normalizedValue.map(config => (config.name === serverName ? nextConfig : config)));\n return;\n }\n\n onChange([...normalizedValue, nextConfig]);\n };\n\n const normalizeNamedCapability = (serverName: string, name: string) => {\n const prefix = `${serverName}${nameSeparator}`;\n return name.startsWith(prefix) ? name.slice(prefix.length) : name;\n };\n\n const getCapabilityItems = (server: Server, capability: CapabilityKey) => {\n if (capability === 'tools') {\n return (server.tools || []).filter(tool => tool.enabled !== false).map((tool: Tool) => ({\n key: tool.name,\n value: normalizeNamedCapability(server.name, tool.name),\n description: tool.description,\n }));\n }\n\n if (capability === 'prompts') {\n return (server.prompts || []).filter(prompt => prompt.enabled !== false).map((prompt: Prompt) => ({\n key: prompt.name,\n value: normalizeNamedCapability(server.name, prompt.name),\n description: prompt.description,\n }));\n }\n\n return (server.resources || []).filter(resource => resource.enabled !== false).map((resource: Resource) => ({\n key: resource.uri,\n value: resource.uri,\n description: resource.description,\n }));\n };\n\n const toggleCapabilityItem = (serverName: string, capability: CapabilityKey, itemValue: string) => {\n const server = availableServers.find(s => s.name === serverName);\n if (!server) return;\n\n const allItems = getCapabilityItems(server, capability).map(item => item.value);\n const serverConfig = normalizedValue.find(config => config.name === serverName);\n\n if (!serverConfig) {\n updateServerCapability(serverName, capability, [itemValue]);\n return;\n }\n\n const currentSelection = serverConfig[capability];\n if (currentSelection === 'all') {\n const nextSelection = allItems.filter(value => value !== itemValue);\n updateServerCapability(serverName, capability, nextSelection);\n return;\n }\n\n if (Array.isArray(currentSelection)) {\n if (currentSelection.includes(itemValue)) {\n updateServerCapability(\n serverName,\n capability,\n currentSelection.filter(value => value !== itemValue),\n );\n return;\n }\n\n const nextSelection = [...currentSelection, itemValue];\n updateServerCapability(\n serverName,\n capability,\n nextSelection.length === allItems.length ? 'all' : nextSelection,\n );\n return;\n }\n\n updateServerCapability(serverName, capability, [itemValue]);\n };\n\n const isServerSelected = (serverName: string) => {\n const serverConfig = normalizedValue.find(config => config.name === serverName);\n return Boolean(serverConfig && hasAnyCapabilitySelection(serverConfig));\n };\n\n const isServerPartiallySelected = (serverName: string) => {\n const serverConfig = normalizedValue.find(config => config.name === serverName);\n if (!serverConfig) return false;\n\n return (['tools', 'prompts', 'resources'] as CapabilityKey[]).some((capability) => {\n const selection = serverConfig[capability];\n return Array.isArray(selection) && selection.length > 0;\n });\n };\n\n const isCapabilityItemSelected = (serverName: string, capability: CapabilityKey, itemValue: string) => {\n const serverConfig = normalizedValue.find(config => config.name === serverName);\n if (!serverConfig) return false;\n\n const selection = serverConfig[capability];\n if (selection === 'all') return true;\n return Array.isArray(selection) ? selection.includes(itemValue) : false;\n };\n\n const getSelectedCapabilityCount = (server: Server, capability: CapabilityKey) => {\n const serverConfig = normalizedValue.find(config => config.name === server.name);\n if (!serverConfig) return 0;\n\n const items = getCapabilityItems(server, capability);\n const selection = serverConfig[capability];\n if (selection === 'all') return items.length;\n if (Array.isArray(selection)) {\n const itemSet = new Set(items.map(item => item.value));\n return selection.filter(item => itemSet.has(item)).length;\n }\n return 0;\n };\n\n const capabilityConfigs: Array<{ key: CapabilityKey; titleKey: string; countKey: string; allKey: string }> = [\n { key: 'tools', titleKey: 'groups.toolSelection', countKey: 'groups.toolsSelected', allKey: 'groups.allTools' },\n { key: 'prompts', titleKey: 'groups.promptSelection', countKey: 'groups.promptsSelected', allKey: 'groups.allPrompts' },\n { key: 'resources', titleKey: 'groups.resourceSelection', countKey: 'groups.resourcesSelected', allKey: 'groups.allResources' },\n ];\n\n const getServerSummaryBadges = (server: Server) => {\n return capabilityConfigs\n .map(({ key }) => ({ key, count: getSelectedCapabilityCount(server, key) }))\n .filter((entry) => entry.count > 0);\n };\n\n return (\n <div className={cn(\"space-y-4\", className)}>\n <div className=\"space-y-3\">\n {availableServers.map(server => {\n const isSelected = isServerSelected(server.name);\n const isPartiallySelected = isServerPartiallySelected(server.name);\n const isExpanded = expandedServers.has(server.name);\n const serverConfig = normalizedValue.find(config => config.name === server.name);\n const summaryBadges = getServerSummaryBadges(server);\n const serverCapabilities = capabilityConfigs.filter(({ key }) => getCapabilityItems(server, key).length > 0);\n\n return (\n <div key={server.name} className=\"border border-gray-200 dark:border-gray-700 rounded-lg hover:border-gray-300 hover:bg-gray-50 dark:bg-gray-800 dark:hover:bg-gray-700 transition-colors\">\n <div\n className=\"flex items-center justify-between p-3 cursor-pointer rounded-lg transition-colors\"\n onClick={() => toggleServerExpanded(server.name)}\n >\n <div\n className=\"flex items-center space-x-3\"\n onClick={(e) => {\n e.stopPropagation();\n toggleServer(server.name);\n }}\n >\n <input\n type=\"checkbox\"\n checked={isSelected || isPartiallySelected}\n onChange={() => toggleServer(server.name)}\n className=\"w-4 h-4 text-blue-600 bg-gray-100 dark:bg-gray-800 border-gray-300 rounded focus:ring-blue-500\"\n />\n <span className=\"font-medium text-gray-900 cursor-pointer select-none\">\n {server.name}\n </span>\n </div>\n\n <div className=\"flex items-center space-x-3\">\n {summaryBadges.map(({ key, count }) => (\n <span key={key} className=\"text-sm text-green-600 flex items-center gap-1\">\n {key === 'tools' ? <Wrench size={14} /> : key === 'prompts' ? <MessageSquare size={14} /> : <FileText size={14} />} {count}\n </span>\n ))}\n\n {serverCapabilities.length > 0 && (\n <button\n type=\"button\"\n className=\"p-1 text-gray-400 hover:text-gray-600 transition-colors\"\n >\n <svg\n className={cn(\"w-5 h-5 transition-transform\", isExpanded && \"rotate-180\")}\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M19 9l-7 7-7-7\" />\n </svg>\n </button>\n )}\n </div>\n </div>\n\n {isExpanded && serverCapabilities.length > 0 && (\n <div className=\"border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 p-3\">\n <div className=\"space-y-4\">\n {serverCapabilities.map(({ key, titleKey, countKey, allKey }) => {\n const items = getCapabilityItems(server, key);\n const selectedCount = getSelectedCapabilityCount(server, key);\n const allSelected = serverConfig?.[key] === 'all' || selectedCount === items.length;\n\n return (\n <div key={key}>\n <div className=\"flex items-center justify-between mb-3\">\n <span className=\"text-sm font-medium text-gray-700\">\n {t(titleKey)}\n </span>\n <div className=\"flex items-center gap-3\">\n {serverConfig && (\n <span className=\"text-xs text-green-600\">\n {allSelected\n ? `(${t(allKey)} ${items.length}/${items.length})`\n : `(${t(countKey)} ${selectedCount}/${items.length})`}\n </span>\n )}\n <button\n type=\"button\"\n onClick={() => {\n updateServerCapability(\n server.name,\n key,\n allSelected ? [] : 'all',\n true,\n );\n }}\n className=\"text-sm text-blue-600 hover:text-blue-800 transition-colors\"\n >\n {allSelected ? t('groups.selectNone') : t('groups.selectAll')}\n </button>\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-2 max-h-32 overflow-y-auto\">\n {items.map(item => {\n const isChecked = isCapabilityItemSelected(server.name, key, item.value);\n\n return (\n <label key={item.key} className=\"flex items-center space-x-2 text-sm\">\n <input\n type=\"checkbox\"\n checked={isChecked}\n onChange={() => toggleCapabilityItem(server.name, key, item.value)}\n className=\"w-3 h-3 text-blue-600 bg-gray-100 dark:bg-gray-800 border-gray-300 rounded focus:ring-blue-500\"\n />\n <span className=\"text-gray-700 break-all whitespace-nowrap\">\n {item.value}\n </span>\n {item.description && (\n <span className=\"text-gray-400 text-xs truncate\">\n {item.description}\n </span>\n )}\n </label>\n );\n })}\n </div>\n </div>\n );\n })}\n </div>\n </div>\n )}\n </div>\n );\n })}\n </div>\n\n {availableServers.length === 0 && (\n <p className=\"text-gray-500 text-sm\">{t('groups.noServerOptions')}</p>\n )}\n </div>\n );\n};\n","import { useState, useEffect } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { useGroupData } from '@/hooks/useGroupData';\nimport { useServerData } from '@/hooks/useServerData';\nimport { GroupFormData, Server, IGroupServerConfig } from '@/types';\nimport { ServerToolConfig } from './ServerToolConfig';\n\ninterface AddGroupFormProps {\n onAdd: () => void;\n onCancel: () => void;\n}\n\nconst AddGroupForm = ({ onAdd, onCancel }: AddGroupFormProps) => {\n const { t } = useTranslation();\n const { createGroup } = useGroupData();\n const { allServers } = useServerData();\n const [availableServers, setAvailableServers] = useState<Server[]>([]);\n const [error, setError] = useState<string | null>(null);\n const [isSubmitting, setIsSubmitting] = useState(false);\n\n const [formData, setFormData] = useState<GroupFormData>({\n name: '',\n description: '',\n servers: [] as IGroupServerConfig[],\n });\n\n useEffect(() => {\n // Filter available servers (enabled only)\n setAvailableServers(allServers.filter((server) => server.enabled !== false));\n }, [allServers]);\n\n const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {\n const { name, value } = e.target;\n setFormData((prev) => ({\n ...prev,\n [name]: value,\n }));\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setIsSubmitting(true);\n setError(null);\n\n try {\n if (!formData.name.trim()) {\n setError(t('groups.nameRequired'));\n setIsSubmitting(false);\n return;\n }\n\n const result = await createGroup(formData.name, formData.description, formData.servers);\n if (!result || !result.success) {\n setError(result?.message || t('groups.createError'));\n setIsSubmitting(false);\n return;\n }\n\n onAdd();\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n setIsSubmitting(false);\n }\n };\n\n return (\n <div className=\"fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4\">\n <div className=\"bg-white dark:bg-gray-800 rounded-lg shadow-lg max-w-3xl w-full max-h-[90vh] flex flex-col\">\n <div className=\"p-6 flex-shrink-0\">\n <h2 className=\"text-xl font-semibold text-gray-800 mb-4\">{t('groups.addNew')}</h2>\n\n {error && (\n <div className=\"mb-4 p-3 bg-red-100 text-red-700 rounded-md border border-gray-200 dark:border-gray-700\">\n {error}\n </div>\n )}\n </div>\n\n <form onSubmit={handleSubmit} className=\"flex flex-col flex-1 min-h-0\">\n <div className=\"flex-1 overflow-y-auto px-6\">\n <div className=\"space-y-4\">\n <div>\n <label className=\"block text-gray-700 text-sm font-bold mb-2\" htmlFor=\"name\">\n {t('groups.name')} *\n </label>\n <input\n type=\"text\"\n id=\"name\"\n name=\"name\"\n value={formData.name}\n onChange={handleChange}\n className=\"w-full border border-gray-300 rounded-md px-3 py-2 text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent\"\n placeholder={t('groups.namePlaceholder')}\n required\n />\n </div>\n\n <div>\n <label className=\"block text-gray-700 text-sm font-bold mb-2\">\n {t('groups.configureCapabilities')}\n </label>\n <ServerToolConfig\n servers={availableServers}\n value={formData.servers as IGroupServerConfig[]}\n onChange={(servers) => setFormData((prev) => ({ ...prev, servers }))}\n className=\"border border-gray-200 dark:border-gray-700 rounded-lg p-4 bg-gray-50 dark:bg-gray-800\"\n />\n </div>\n </div>\n </div>\n\n <div className=\"flex justify-end space-x-3 p-6 pt-4 border-t border-gray-200 dark:border-gray-700 flex-shrink-0\">\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"px-4 py-2 text-gray-600 hover:text-gray-800 border border-gray-300 rounded-md hover:bg-gray-50 dark:bg-gray-800 dark:hover:bg-gray-700 transition-colors\"\n disabled={isSubmitting}\n >\n {t('common.cancel')}\n </button>\n <button\n type=\"submit\"\n className=\"px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 disabled:opacity-50 transition-colors\"\n disabled={isSubmitting}\n >\n {isSubmitting ? t('common.submitting') : t('common.create')}\n </button>\n </div>\n </form>\n </div>\n </div>\n );\n};\n\nexport default AddGroupForm;\n","import { useState, useEffect } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { Group, GroupFormData, Server, IGroupServerConfig } from '@/types';\nimport { useGroupData } from '@/hooks/useGroupData';\nimport { useServerData } from '@/hooks/useServerData';\nimport { ServerToolConfig } from './ServerToolConfig';\n\ninterface EditGroupFormProps {\n group: Group;\n onEdit: () => void;\n onCancel: () => void;\n}\n\nconst EditGroupForm = ({ group, onEdit, onCancel }: EditGroupFormProps) => {\n const { t } = useTranslation();\n const { updateGroup } = useGroupData();\n const { allServers } = useServerData();\n const [availableServers, setAvailableServers] = useState<Server[]>([]);\n const [error, setError] = useState<string | null>(null);\n const [isSubmitting, setIsSubmitting] = useState(false);\n\n const [formData, setFormData] = useState<GroupFormData>({\n name: group.name,\n description: group.description || '',\n servers: group.servers || [],\n });\n\n useEffect(() => {\n // Filter available servers (enabled only)\n setAvailableServers(allServers.filter((server) => server.enabled !== false));\n }, [allServers]);\n\n const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {\n const { name, value } = e.target;\n setFormData((prev) => ({\n ...prev,\n [name]: value,\n }));\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setIsSubmitting(true);\n setError(null);\n\n try {\n if (!formData.name.trim()) {\n setError(t('groups.nameRequired'));\n setIsSubmitting(false);\n return;\n }\n\n const result = await updateGroup(group.id, {\n name: formData.name,\n description: formData.description,\n servers: formData.servers,\n });\n\n if (!result || !result.success) {\n setError(result?.message || t('groups.updateError'));\n setIsSubmitting(false);\n return;\n }\n\n onEdit();\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n setIsSubmitting(false);\n }\n };\n\n return (\n <div className=\"fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4\">\n <div className=\"bg-white dark:bg-gray-800 rounded-lg shadow-lg max-w-3xl w-full max-h-[90vh] flex flex-col\">\n <div className=\"p-6 flex-shrink-0\">\n <h2 className=\"text-xl font-semibold text-gray-800 mb-4\">{t('groups.edit')}</h2>\n\n {error && (\n <div className=\"mb-4 p-3 bg-red-100 text-red-700 rounded-md border border-gray-200 dark:border-gray-700\">\n {error}\n </div>\n )}\n </div>\n\n <form onSubmit={handleSubmit} className=\"flex flex-col flex-1 min-h-0\">\n <div className=\"flex-1 overflow-y-auto px-6\">\n <div className=\"space-y-4\">\n <div>\n <label className=\"block text-gray-700 text-sm font-bold mb-2\" htmlFor=\"name\">\n {t('groups.name')} *\n </label>\n <input\n type=\"text\"\n id=\"name\"\n name=\"name\"\n value={formData.name}\n onChange={handleChange}\n className=\"w-full border border-gray-300 rounded-md px-3 py-2 text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent\"\n placeholder={t('groups.namePlaceholder')}\n required\n />\n </div>\n\n <div>\n <label className=\"block text-gray-700 text-sm font-bold mb-2\">\n {t('groups.configureCapabilities')}\n </label>\n <ServerToolConfig\n servers={availableServers}\n value={formData.servers as IGroupServerConfig[]}\n onChange={(servers) => setFormData((prev) => ({ ...prev, servers }))}\n className=\"border border-gray-200 dark:border-gray-700 rounded-lg p-4 bg-gray-50 dark:bg-gray-800\"\n />\n </div>\n </div>\n </div>\n\n <div className=\"flex justify-end space-x-3 p-6 pt-4 border-t border-gray-200 dark:border-gray-700 flex-shrink-0\">\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"px-4 py-2 text-gray-600 hover:text-gray-800 border border-gray-300 rounded-md hover:bg-gray-50 dark:bg-gray-800 dark:hover:bg-gray-700 transition-colors\"\n disabled={isSubmitting}\n >\n {t('common.cancel')}\n </button>\n <button\n type=\"submit\"\n className=\"px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 disabled:opacity-50 transition-colors\"\n disabled={isSubmitting}\n >\n {isSubmitting ? t('common.submitting') : t('common.save')}\n </button>\n </div>\n </form>\n </div>\n </div>\n );\n};\n\nexport default EditGroupForm;\n","import { useState, useRef, useEffect } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { Group, Server, IGroupServerConfig } from '@/types';\nimport {\n Edit,\n Trash,\n Copy,\n Check,\n Link,\n FileCode,\n DropdownIcon,\n Wrench,\n MessageSquare,\n FileText,\n} from '@/components/icons/LucideIcons';\nimport DeleteDialog from '@/components/ui/DeleteDialog';\nimport { useToast } from '@/contexts/ToastContext';\nimport { useSettingsData } from '@/hooks/useSettingsData';\n\ninterface GroupCardProps {\n group: Group;\n servers: Server[];\n onEdit: (group: Group) => void;\n onDelete: (groupId: string) => void;\n}\n\nconst GroupCard = ({ group, servers, onEdit, onDelete }: GroupCardProps) => {\n const { t } = useTranslation();\n const { showToast } = useToast();\n const { installConfig, nameSeparator } = useSettingsData();\n const [showDeleteDialog, setShowDeleteDialog] = useState(false);\n const [copied, setCopied] = useState(false);\n const [showCopyDropdown, setShowCopyDropdown] = useState(false);\n const [expandedServer, setExpandedServer] = useState<string | null>(null);\n const dropdownRef = useRef<HTMLDivElement>(null);\n\n // Close dropdown when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\n setShowCopyDropdown(false);\n }\n };\n\n document.addEventListener('mousedown', handleClickOutside);\n return () => {\n document.removeEventListener('mousedown', handleClickOutside);\n };\n }, []);\n\n const handleEdit = () => {\n onEdit(group);\n };\n\n const handleDelete = () => {\n setShowDeleteDialog(true);\n };\n\n const handleConfirmDelete = () => {\n onDelete(group.id);\n setShowDeleteDialog(false);\n };\n\n const copyToClipboard = (text: string) => {\n if (navigator.clipboard && window.isSecureContext) {\n navigator.clipboard.writeText(text).then(() => {\n setCopied(true);\n setShowCopyDropdown(false);\n showToast(t('common.copySuccess'), 'success');\n setTimeout(() => setCopied(false), 2000);\n });\n } else {\n // Fallback for HTTP or unsupported clipboard API\n const textArea = document.createElement('textarea');\n textArea.value = text;\n // Avoid scrolling to bottom\n textArea.style.position = 'fixed';\n textArea.style.left = '-9999px';\n document.body.appendChild(textArea);\n textArea.focus();\n textArea.select();\n try {\n document.execCommand('copy');\n setCopied(true);\n setShowCopyDropdown(false);\n showToast(t('common.copySuccess'), 'success');\n setTimeout(() => setCopied(false), 2000);\n } catch (err) {\n showToast(t('common.copyFailed') || 'Copy failed', 'error');\n console.error('Copy to clipboard failed:', err);\n }\n document.body.removeChild(textArea);\n }\n };\n\n const handleCopyId = () => {\n copyToClipboard(group.id);\n };\n\n const handleCopyUrl = () => {\n copyToClipboard(`${installConfig.baseUrl}/mcp/${group.id}`);\n };\n\n const handleCopyJson = () => {\n const jsonConfig = {\n mcpServers: {\n mcphub: {\n url: `${installConfig.baseUrl}/mcp/${group.id}`,\n headers: {\n Authorization: 'Bearer <your-access-token>',\n },\n },\n },\n };\n copyToClipboard(JSON.stringify(jsonConfig, null, 2));\n };\n\n // Helper function to normalize group servers to get server names\n const getServerNames = (servers: string[] | IGroupServerConfig[]): string[] => {\n return servers.map((server) => (typeof server === 'string' ? server : server.name));\n };\n\n // Helper function to get server configuration\n const getServerConfig = (serverName: string): IGroupServerConfig | undefined => {\n const server = group.servers.find((s) =>\n typeof s === 'string' ? s === serverName : s.name === serverName,\n );\n if (typeof server === 'string') {\n return { name: server, tools: 'all', prompts: 'all', resources: 'all' };\n }\n return server;\n };\n\n // Get servers that belong to this group\n const serverNames = getServerNames(group.servers);\n const groupServers = servers.filter((server) => serverNames.includes(server.name));\n\n return (\n <div className=\"bg-white dark:bg-gray-800 shadow rounded-lg p-4\">\n <div className=\"flex justify-between items-center\">\n <div>\n <div className=\"flex items-center\">\n <h2 className=\"text-xl font-semibold text-gray-800\">{group.name}</h2>\n <div className=\"flex items-center ml-3\">\n <span className=\"text-xs text-gray-500 mr-1\">{group.id}</span>\n <div className=\"relative\" ref={dropdownRef}>\n <button\n onClick={() => setShowCopyDropdown(!showCopyDropdown)}\n className=\"p-1 text-gray-400 hover:text-gray-600 transition-colors flex items-center\"\n title={t('common.copy')}\n >\n {copied ? <Check size={14} className=\"text-green-500\" /> : <Copy size={14} />}\n <DropdownIcon size={12} className=\"ml-1\" />\n </button>\n\n {showCopyDropdown && (\n <div className=\"absolute top-full left-0 mt-1 bg-white dark:bg-gray-800 shadow-lg rounded-md border border-gray-200 dark:border-gray-700 py-1 z-10 min-w-[140px]\">\n <button\n onClick={handleCopyId}\n className=\"w-full px-3 py-2 text-left text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-700 flex items-center\"\n >\n <Copy size={12} className=\"mr-2\" />\n {t('common.copyId')}\n </button>\n <button\n onClick={handleCopyUrl}\n className=\"w-full px-3 py-2 text-left text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-700 flex items-center\"\n >\n <Link size={12} className=\"mr-2\" />\n {t('common.copyUrl')}\n </button>\n <button\n onClick={handleCopyJson}\n className=\"w-full px-3 py-2 text-left text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-700 flex items-center\"\n >\n <FileCode size={12} className=\"mr-2\" />\n {t('common.copyJson')}\n </button>\n </div>\n )}\n </div>\n </div>\n </div>\n {group.description && <p className=\"text-gray-600 text-sm mt-1\">{group.description}</p>}\n </div>\n <div className=\"flex items-center space-x-3\">\n <div className=\"bg-blue-50 text-blue-700 dark:bg-blue-900/40 dark:text-blue-300 px-3 py-1 rounded-full text-sm btn-secondary\">\n {t('groups.serverCount', { count: group.servers.length })}\n </div>\n <button\n onClick={handleEdit}\n className=\"text-gray-500 hover:text-gray-700\"\n title={t('groups.edit')}\n >\n <Edit size={18} />\n </button>\n <button\n onClick={handleDelete}\n className=\"text-gray-500 hover:text-red-600\"\n title={t('groups.delete')}\n >\n <Trash size={18} />\n </button>\n </div>\n </div>\n\n <div className=\"\">\n {groupServers.length === 0 ? (\n <p className=\"text-gray-500 italic\">{t('groups.noServers')}</p>\n ) : (\n <div className=\"flex flex-wrap gap-2\">\n {groupServers.map((server) => {\n const serverConfig = getServerConfig(server.name);\n const hasToolRestrictions =\n serverConfig && serverConfig.tools !== 'all' && Array.isArray(serverConfig.tools);\n const hasPromptRestrictions =\n serverConfig && serverConfig.prompts !== 'all' && Array.isArray(serverConfig.prompts);\n const hasResourceRestrictions =\n serverConfig && serverConfig.resources !== 'all' && Array.isArray(serverConfig.resources);\n\n const enabledServerTools = (server.tools || []).filter((tool) => tool.enabled !== false);\n const enabledServerPrompts = (server.prompts || []).filter(\n (prompt) => prompt.enabled !== false,\n );\n const enabledServerResources = (server.resources || []).filter(\n (resource) => resource.enabled !== false,\n );\n const normalizeToolName = (toolName: string): string => {\n const prefix = `${server.name}${nameSeparator}`;\n return toolName.startsWith(prefix) ? toolName.slice(prefix.length) : toolName;\n };\n const normalizePromptName = (promptName: string): string => {\n const prefix = `${server.name}${nameSeparator}`;\n return promptName.startsWith(prefix) ? promptName.slice(prefix.length) : promptName;\n };\n const enabledToolNames = enabledServerTools.map((tool) => normalizeToolName(tool.name));\n const enabledPromptNames = enabledServerPrompts.map((prompt) =>\n normalizePromptName(prompt.name),\n );\n const enabledResourceUris = enabledServerResources.map((resource) => resource.uri);\n const enabledToolNameSet = new Set(enabledToolNames);\n const enabledPromptNameSet = new Set(enabledPromptNames);\n const enabledResourceUriSet = new Set(enabledResourceUris);\n\n const toolCount =\n hasToolRestrictions && Array.isArray(serverConfig?.tools)\n ? serverConfig.tools.filter((toolName) => enabledToolNameSet.has(toolName)).length\n : enabledToolNames.length;\n const promptCount =\n hasPromptRestrictions && Array.isArray(serverConfig?.prompts)\n ? serverConfig.prompts.filter((promptName) => enabledPromptNameSet.has(promptName))\n .length\n : enabledPromptNames.length;\n const resourceCount =\n hasResourceRestrictions && Array.isArray(serverConfig?.resources)\n ? serverConfig.resources.filter((resourceUri) => enabledResourceUriSet.has(resourceUri))\n .length\n : enabledResourceUris.length;\n\n const isExpanded = expandedServer === server.name;\n\n const getCapabilityList = (\n capability: 'tools' | 'prompts' | 'resources',\n ): string[] => {\n if (capability === 'tools') {\n if (hasToolRestrictions && Array.isArray(serverConfig?.tools)) {\n return serverConfig.tools.filter((toolName) => enabledToolNameSet.has(toolName));\n }\n return enabledToolNames;\n }\n\n if (capability === 'prompts') {\n if (hasPromptRestrictions && Array.isArray(serverConfig?.prompts)) {\n return serverConfig.prompts.filter((promptName) => enabledPromptNameSet.has(promptName));\n }\n return enabledPromptNames;\n }\n\n if (hasResourceRestrictions && Array.isArray(serverConfig?.resources)) {\n return serverConfig.resources.filter((resourceUri) => enabledResourceUriSet.has(resourceUri));\n }\n return enabledResourceUris;\n };\n\n const handleServerClick = () => {\n setExpandedServer(isExpanded ? null : server.name);\n };\n\n return (\n <div key={server.name} className=\"relative\">\n <div\n className=\"flex items-center space-x-2 bg-gray-50 dark:bg-gray-800 rounded-lg px-3 py-2 cursor-pointer hover:bg-gray-100 transition-colors\"\n onClick={handleServerClick}\n >\n <span className=\"font-medium text-gray-700 text-sm\">{server.name}</span>\n <span\n className={`inline-block h-2 w-2 rounded-full ${\n server.status === 'connected'\n ? 'bg-green-500'\n : server.status === 'connecting'\n ? 'bg-yellow-500'\n : 'bg-red-500'\n }`}\n ></span>\n {toolCount > 0 && (\n <span className=\"text-xs text-blue-600 bg-blue-100 dark:bg-blue-900/40 dark:text-blue-300 px-2 py-0.5 rounded flex items-center gap-1\">\n <Wrench size={12} />\n {toolCount}\n </span>\n )}\n {promptCount > 0 && (\n <span className=\"text-xs text-purple-600 bg-purple-100 dark:bg-purple-900/40 dark:text-purple-300 px-2 py-0.5 rounded flex items-center gap-1\">\n <MessageSquare size={12} />\n {promptCount}\n </span>\n )}\n {resourceCount > 0 && (\n <span className=\"text-xs text-emerald-600 bg-emerald-100 dark:bg-emerald-900/40 dark:text-emerald-300 px-2 py-0.5 rounded flex items-center gap-1\">\n <FileText size={12} />\n {resourceCount}\n </span>\n )}\n </div>\n\n {isExpanded && (\n <div className=\"absolute top-full left-0 mt-1 bg-white dark:bg-gray-800 shadow-lg rounded-md border border-gray-200 dark:border-gray-700 p-3 z-10 min-w-[300px] max-w-[400px]\">\n <div className=\"space-y-3\">\n {toolCount > 0 && (\n <div>\n <div className=\"text-gray-600 text-xs mb-2\">\n {hasToolRestrictions ? t('groups.selectedTools') : t('groups.allTools')}:\n </div>\n <div className=\"flex flex-wrap gap-1\">\n {getCapabilityList('tools').map((toolName, index) => (\n <span\n key={`tool-${index}`}\n className=\"inline-block bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 px-2 py-1 rounded text-xs\"\n >\n {toolName}\n </span>\n ))}\n </div>\n </div>\n )}\n\n {promptCount > 0 && (\n <div>\n <div className=\"text-gray-600 text-xs mb-2\">\n {hasPromptRestrictions ? t('groups.selectedPrompts') : t('groups.allPrompts')}:\n </div>\n <div className=\"flex flex-wrap gap-1\">\n {getCapabilityList('prompts').map((promptName, index) => (\n <span\n key={`prompt-${index}`}\n className=\"inline-block bg-purple-50 text-purple-700 dark:bg-purple-900/40 dark:text-purple-300 px-2 py-1 rounded text-xs\"\n >\n {promptName}\n </span>\n ))}\n </div>\n </div>\n )}\n\n {resourceCount > 0 && (\n <div>\n <div className=\"text-gray-600 text-xs mb-2\">\n {hasResourceRestrictions ? t('groups.selectedResources') : t('groups.allResources')}:\n </div>\n <div className=\"flex flex-wrap gap-1\">\n {getCapabilityList('resources').map((resourceUri, index) => (\n <span\n key={`resource-${index}`}\n className=\"inline-block bg-emerald-50 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-300 px-2 py-1 rounded text-xs break-all\"\n >\n {resourceUri}\n </span>\n ))}\n </div>\n </div>\n )}\n </div>\n </div>\n )}\n </div>\n );\n })}\n </div>\n )}\n </div>\n\n <DeleteDialog\n isOpen={showDeleteDialog}\n onClose={() => setShowDeleteDialog(false)}\n onConfirm={handleConfirmDelete}\n serverName={group.name}\n isGroup={true}\n />\n </div>\n );\n};\n\nexport default GroupCard;\n","import React, { useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { apiPost } from '@/utils/fetchInterceptor';\n\ninterface GroupImportFormProps {\n onSuccess: () => void;\n onCancel: () => void;\n}\n\ninterface ImportGroupConfig {\n name: string;\n description?: string;\n servers?: string[] | Array<{\n name: string;\n tools?: string[] | 'all';\n prompts?: string[] | 'all';\n resources?: string[] | 'all';\n }>;\n}\n\ninterface ImportJsonFormat {\n groups: ImportGroupConfig[];\n}\n\nconst GroupImportForm: React.FC<GroupImportFormProps> = ({ onSuccess, onCancel }) => {\n const { t } = useTranslation();\n const [jsonInput, setJsonInput] = useState('');\n const [error, setError] = useState<string | null>(null);\n const [isImporting, setIsImporting] = useState(false);\n const [previewGroups, setPreviewGroups] = useState<ImportGroupConfig[] | null>(null);\n\n const examplePlaceholder = `{\n \"groups\": [\n {\n \"name\": \"AI Assistants\",\n \"servers\": [\"openai-server\", \"anthropic-server\"]\n },\n {\n \"name\": \"Development Tools\",\n \"servers\": [\n {\n \"name\": \"github-server\",\n \"tools\": [\"create_issue\", \"list_repos\"],\n \"prompts\": [\"triage_prompt\"],\n \"resources\": [\"resource://docs/repo-guide\"]\n },\n {\n \"name\": \"gitlab-server\",\n \"tools\": \"all\",\n \"prompts\": \"all\",\n \"resources\": \"all\"\n }\n ]\n }\n ]\n}\n\nSupports:\n- Simple server list: [\"server1\", \"server2\"]\n- Advanced server config: [{\"name\": \"server1\", \"tools\": [\"tool1\"], \"prompts\": [\"prompt1\"], \"resources\": [\"resource://docs/guide\"]}]\n- All groups will be imported in a single efficient batch operation.`;\n\n const parseAndValidateJson = (input: string): ImportJsonFormat | null => {\n try {\n const parsed = JSON.parse(input.trim());\n\n // Validate structure\n if (!parsed.groups || !Array.isArray(parsed.groups)) {\n setError(t('groupImport.invalidFormat'));\n return null;\n }\n\n // Validate each group\n for (const group of parsed.groups) {\n if (!group.name || typeof group.name !== 'string') {\n setError(t('groupImport.missingName'));\n return null;\n }\n }\n\n return parsed as ImportJsonFormat;\n } catch (e) {\n setError(t('groupImport.parseError'));\n return null;\n }\n };\n\n const handlePreview = () => {\n setError(null);\n const parsed = parseAndValidateJson(jsonInput);\n if (!parsed) return;\n\n setPreviewGroups(parsed.groups);\n };\n\n const handleImport = async () => {\n if (!previewGroups) return;\n\n setIsImporting(true);\n setError(null);\n\n try {\n // Use batch import API for better performance\n const result = await apiPost('/groups/batch', {\n groups: previewGroups,\n });\n\n if (result.success) {\n const { successCount, failureCount, results } = result;\n\n if (failureCount > 0) {\n const errors = results\n .filter((r: any) => !r.success)\n .map((r: any) => `${r.name}: ${r.message || t('groupImport.addFailed')}`);\n\n setError(\n t('groupImport.partialSuccess', { count: successCount, total: previewGroups.length }) +\n '\\n' +\n errors.join('\\n'),\n );\n }\n\n if (successCount > 0) {\n onSuccess();\n }\n } else {\n setError(result.message || t('groupImport.importFailed'));\n }\n } catch (err) {\n console.error('Import error:', err);\n setError(t('groupImport.importFailed'));\n } finally {\n setIsImporting(false);\n }\n };\n\n const renderAllCapabilitiesLabel = (\n key: 'previewAllTools' | 'previewAllPrompts' | 'previewAllResources',\n ) => <span className=\"text-gray-500 ml-2\">{t(`groups.${key}`)}</span>;\n\n const renderCapabilityPreview = (\n key: 'previewPrompts' | 'previewResources',\n value: string[] | 'all' | undefined,\n ) => {\n if (!value || value === 'all') {\n return null;\n }\n\n const items = Array.isArray(value) ? value.join(', ') : value;\n return <span className=\"text-gray-500 ml-2\">{t(`groups.${key}`, { items })}</span>;\n };\n\n const renderServerList = (\n servers?: string[] | Array<{\n name: string;\n tools?: string[] | 'all';\n prompts?: string[] | 'all';\n resources?: string[] | 'all';\n }>,\n ) => {\n if (!servers || servers.length === 0) {\n return <span className=\"text-gray-500\">{t('groups.noServers')}</span>;\n }\n\n return (\n <div className=\"space-y-1\">\n {servers.map((server, idx) => {\n if (typeof server === 'string') {\n return (\n <div key={idx} className=\"text-sm\">\n • {server}\n </div>\n );\n } else {\n return (\n <div key={idx} className=\"text-sm\">\n • {server.name}\n {server.tools && server.tools !== 'all' && (\n <span className=\"text-gray-500 ml-2\">\n ({Array.isArray(server.tools) ? server.tools.join(', ') : server.tools})\n </span>\n )}\n {server.tools === 'all' && renderAllCapabilitiesLabel('previewAllTools')}\n {renderCapabilityPreview('previewPrompts', server.prompts)}\n {server.prompts === 'all' && renderAllCapabilitiesLabel('previewAllPrompts')}\n {renderCapabilityPreview('previewResources', server.resources)}\n {server.resources === 'all' && renderAllCapabilitiesLabel('previewAllResources')}\n </div>\n );\n }\n })}\n </div>\n );\n };\n\n return (\n <div className=\"fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4\">\n <div className=\"bg-white dark:bg-gray-800 shadow rounded-lg p-6 w-full max-w-4xl max-h-[90vh] overflow-y-auto\">\n <div className=\"flex justify-between items-center mb-6\">\n <h2 className=\"text-xl font-semibold text-gray-900\">{t('groupImport.title')}</h2>\n <button onClick={onCancel} className=\"text-gray-500 hover:text-gray-700\">\n ✕\n </button>\n </div>\n\n {error && (\n <div className=\"mb-4 bg-red-50 border-l-4 border-red-500 p-4 rounded\">\n <p className=\"text-red-700 whitespace-pre-wrap\">{error}</p>\n </div>\n )}\n\n {!previewGroups ? (\n <div>\n <div className=\"mb-4\">\n <label className=\"block text-sm font-medium text-gray-700 mb-2\">\n {t('groupImport.inputLabel')}\n </label>\n <textarea\n value={jsonInput}\n onChange={(e) => setJsonInput(e.target.value)}\n className=\"w-full h-96 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono text-sm\"\n placeholder={examplePlaceholder}\n />\n <p className=\"text-xs text-gray-500 mt-2\">{t('groupImport.inputHelp')}</p>\n </div>\n\n <div className=\"flex justify-end space-x-4\">\n <button\n onClick={onCancel}\n className=\"px-4 py-2 text-gray-700 bg-gray-200 rounded hover:bg-gray-300 btn-secondary\"\n >\n {t('common.cancel')}\n </button>\n <button\n onClick={handlePreview}\n disabled={!jsonInput.trim()}\n className=\"px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50 btn-primary\"\n >\n {t('groupImport.preview')}\n </button>\n </div>\n </div>\n ) : (\n <div>\n <div className=\"mb-4\">\n <h3 className=\"text-lg font-medium text-gray-900 mb-3\">\n {t('groupImport.previewTitle')}\n </h3>\n <div className=\"space-y-3\">\n {previewGroups.map((group, index) => (\n <div key={index} className=\"bg-gray-50 dark:bg-gray-800 p-4 rounded-lg border border-gray-200 dark:border-gray-700\">\n <div className=\"flex items-start justify-between\">\n <div className=\"flex-1\">\n <h4 className=\"font-medium text-gray-900\">{group.name}</h4>\n {group.description && (\n <p className=\"text-sm text-gray-600 mt-1\">{group.description}</p>\n )}\n <div className=\"mt-2 text-sm text-gray-600\">\n <strong>{t('groups.servers')}:</strong>\n <div className=\"mt-1\">{renderServerList(group.servers)}</div>\n </div>\n </div>\n </div>\n </div>\n ))}\n </div>\n </div>\n\n <div className=\"flex justify-end space-x-4\">\n <button\n onClick={() => setPreviewGroups(null)}\n disabled={isImporting}\n className=\"px-4 py-2 text-gray-700 bg-gray-200 rounded hover:bg-gray-300 disabled:opacity-50 btn-secondary\"\n >\n {t('common.back')}\n </button>\n <button\n onClick={handleImport}\n disabled={isImporting}\n className=\"px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50 flex items-center btn-primary\"\n >\n {isImporting ? (\n <>\n <svg\n className=\"animate-spin h-4 w-4 mr-2\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n className=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n strokeWidth=\"4\"\n ></circle>\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n ></path>\n </svg>\n {t('groupImport.importing')}\n </>\n ) : (\n t('groupImport.import')\n )}\n </button>\n </div>\n </div>\n )}\n </div>\n </div>\n );\n};\n\nexport default GroupImportForm;\n","import React, { useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { apiPost } from '@/utils/fetchInterceptor';\nimport { Group, ConfigTemplate } from '@/types';\n\ninterface TemplateExportFormProps {\n groups: Group[];\n onCancel: () => void;\n}\n\nconst TemplateExportForm: React.FC<TemplateExportFormProps> = ({ groups, onCancel }) => {\n const { t } = useTranslation();\n const [name, setName] = useState('');\n const [description, setDescription] = useState('');\n const [selectedGroupIds, setSelectedGroupIds] = useState<string[]>([]);\n const [includeDisabled, setIncludeDisabled] = useState(false);\n const [isExporting, setIsExporting] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const handleToggleGroup = (groupId: string) => {\n setSelectedGroupIds((prev) =>\n prev.includes(groupId) ? prev.filter((id) => id !== groupId) : [...prev, groupId],\n );\n };\n\n const handleSelectAll = () => {\n if (selectedGroupIds.length === groups.length) {\n setSelectedGroupIds([]);\n } else {\n setSelectedGroupIds(groups.map((g) => g.id));\n }\n };\n\n const handleExport = async () => {\n if (!name.trim()) {\n setError(t('template.nameRequired'));\n return;\n }\n\n setIsExporting(true);\n setError(null);\n\n try {\n const result = await apiPost('/templates/export', {\n name: name.trim(),\n description: description.trim() || undefined,\n groupIds: selectedGroupIds.length > 0 ? selectedGroupIds : undefined,\n includeDisabledServers: includeDisabled,\n });\n\n if (result.success && result.data) {\n const template: ConfigTemplate = result.data;\n const blob = new Blob([JSON.stringify(template, null, 2)], { type: 'application/json' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = `${template.name.replace(/[^a-zA-Z0-9-_]/g, '_')}.mcphub-template.json`;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n URL.revokeObjectURL(url);\n onCancel();\n } else {\n setError(result.message || t('template.exportFailed'));\n }\n } catch (err) {\n console.error('Export error:', err);\n setError(t('template.exportFailed'));\n } finally {\n setIsExporting(false);\n }\n };\n\n return (\n <div className=\"fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4\">\n <div className=\"bg-white dark:bg-gray-800 shadow rounded-lg p-6 w-full max-w-3xl max-h-[90vh] overflow-y-auto\">\n <div className=\"flex justify-between items-center mb-6\">\n <h2 className=\"text-xl font-semibold text-gray-900\">{t('template.exportTitle')}</h2>\n <button onClick={onCancel} className=\"text-gray-500 hover:text-gray-700\">\n ✕\n </button>\n </div>\n\n {error && (\n <div className=\"mb-4 bg-red-50 border-l-4 border-red-500 p-4 rounded\">\n <p className=\"text-red-700\">{error}</p>\n </div>\n )}\n\n <div className=\"space-y-4\">\n <div>\n <label className=\"block text-sm font-medium text-gray-700 mb-1\">\n {t('template.name')} *\n </label>\n <input\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n className=\"w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500\"\n placeholder={t('template.namePlaceholder')}\n />\n </div>\n\n <div>\n <label className=\"block text-sm font-medium text-gray-700 mb-1\">\n {t('template.description')}\n </label>\n <input\n type=\"text\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n className=\"w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500\"\n placeholder={t('template.descriptionPlaceholder')}\n />\n </div>\n\n <div>\n <div className=\"flex justify-between items-center mb-2\">\n <label className=\"block text-sm font-medium text-gray-700\">\n {t('template.selectGroups')}\n </label>\n <button\n onClick={handleSelectAll}\n className=\"text-sm text-blue-600 hover:text-blue-800\"\n >\n {selectedGroupIds.length === groups.length\n ? t('template.deselectAll')\n : t('template.selectAll')}\n </button>\n </div>\n <p className=\"text-xs text-gray-500 mb-2\">{t('template.selectGroupsHelp')}</p>\n <div className=\"border border-gray-200 dark:border-gray-700 rounded-md max-h-48 overflow-y-auto\">\n {groups.map((group) => (\n <label\n key={group.id}\n className=\"flex items-center px-3 py-2 hover:bg-gray-50 dark:bg-gray-800 dark:hover:bg-gray-700 cursor-pointer\"\n >\n <input\n type=\"checkbox\"\n checked={selectedGroupIds.includes(group.id)}\n onChange={() => handleToggleGroup(group.id)}\n className=\"mr-3 h-4 w-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500\"\n />\n <div>\n <span className=\"text-sm font-medium text-gray-900\">{group.name}</span>\n {group.description && (\n <span className=\"text-xs text-gray-500 ml-2\">{group.description}</span>\n )}\n </div>\n </label>\n ))}\n </div>\n </div>\n\n <label className=\"flex items-center space-x-2 cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={includeDisabled}\n onChange={(e) => setIncludeDisabled(e.target.checked)}\n className=\"h-4 w-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500\"\n />\n <span className=\"text-sm text-gray-700\">{t('template.includeDisabled')}</span>\n </label>\n </div>\n\n <div className=\"mt-4 p-3 bg-blue-50 border border-blue-200 rounded-md\">\n <p className=\"text-sm text-blue-700\">{t('template.exportNote')}</p>\n </div>\n\n <div className=\"flex justify-end space-x-4 mt-6\">\n <button\n onClick={onCancel}\n className=\"px-4 py-2 text-gray-700 bg-gray-200 rounded hover:bg-gray-300 btn-secondary\"\n >\n {t('common.cancel')}\n </button>\n <button\n onClick={handleExport}\n disabled={isExporting || !name.trim()}\n className=\"px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed btn-primary\"\n >\n {isExporting ? t('template.exporting') : t('template.export')}\n </button>\n </div>\n </div>\n </div>\n );\n};\n\nexport default TemplateExportForm;\n","import React, { useState, useRef } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { apiPost } from '@/utils/fetchInterceptor';\nimport { ConfigTemplate, TemplateImportResult } from '@/types';\n\ninterface TemplateImportFormProps {\n onSuccess: () => void;\n onCancel: () => void;\n}\n\nconst TemplateImportForm: React.FC<TemplateImportFormProps> = ({ onSuccess, onCancel }) => {\n const { t } = useTranslation();\n const [template, setTemplate] = useState<ConfigTemplate | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [isImporting, setIsImporting] = useState(false);\n const [result, setResult] = useState<TemplateImportResult | null>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {\n setError(null);\n setTemplate(null);\n setResult(null);\n\n const file = e.target.files?.[0];\n if (!file) return;\n\n const reader = new FileReader();\n reader.onload = (event) => {\n try {\n const parsed = JSON.parse(event.target?.result as string);\n if (!parsed.version || !parsed.name || !parsed.servers || !parsed.groups) {\n setError(t('template.invalidFormat'));\n return;\n }\n setTemplate(parsed as ConfigTemplate);\n } catch {\n setError(t('template.parseError'));\n }\n };\n reader.readAsText(file);\n };\n\n const handlePaste = (input: string) => {\n setError(null);\n setTemplate(null);\n setResult(null);\n\n if (!input.trim()) return;\n\n try {\n const parsed = JSON.parse(input.trim());\n if (!parsed.version || !parsed.name || !parsed.servers || !parsed.groups) {\n setError(t('template.invalidFormat'));\n return;\n }\n setTemplate(parsed as ConfigTemplate);\n } catch {\n setError(t('template.parseError'));\n }\n };\n\n const handleImport = async () => {\n if (!template) return;\n\n setIsImporting(true);\n setError(null);\n\n try {\n const response = await apiPost('/templates/import', template);\n\n if (response.data) {\n setResult(response.data as TemplateImportResult);\n if (response.data.success) {\n onSuccess();\n }\n } else {\n setError(response.message || t('template.importFailed'));\n }\n } catch (err) {\n console.error('Import error:', err);\n setError(t('template.importFailed'));\n } finally {\n setIsImporting(false);\n }\n };\n\n return (\n <div className=\"fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4\">\n <div className=\"bg-white dark:bg-gray-800 shadow rounded-lg p-6 w-full max-w-4xl max-h-[90vh] overflow-y-auto\">\n <div className=\"flex justify-between items-center mb-6\">\n <h2 className=\"text-xl font-semibold text-gray-900\">{t('template.importTitle')}</h2>\n <button onClick={onCancel} className=\"text-gray-500 hover:text-gray-700\">\n ✕\n </button>\n </div>\n\n {error && (\n <div className=\"mb-4 bg-red-50 border-l-4 border-red-500 p-4 rounded\">\n <p className=\"text-red-700\">{error}</p>\n </div>\n )}\n\n {result && (\n <div\n className={`mb-4 p-4 rounded border-l-4 ${result.success ? 'bg-green-50 border-green-500' : 'bg-yellow-50 border-yellow-500'}`}\n >\n <p className={result.success ? 'text-green-700' : 'text-yellow-700'}>\n {t('template.importResult', {\n serversCreated: result.serversCreated,\n serversSkipped: result.serversSkipped,\n groupsCreated: result.groupsCreated,\n groupsSkipped: result.groupsSkipped,\n })}\n </p>\n {result.requiredEnvVars.length > 0 && (\n <div className=\"mt-2 p-2 bg-orange-50 border border-orange-200 rounded\">\n <p className=\"text-sm font-medium text-orange-800\">\n {t('template.envVarsNeeded')}\n </p>\n <ul className=\"mt-1 text-sm text-orange-700\">\n {result.requiredEnvVars.map((v) => (\n <li key={v} className=\"font-mono\">\n {v}\n </li>\n ))}\n </ul>\n </div>\n )}\n </div>\n )}\n\n {!template ? (\n <div>\n <div className=\"mb-4\">\n <div className=\"flex items-center justify-between mb-2\">\n <label className=\"block text-sm font-medium text-gray-700\">\n {t('template.uploadFile')}\n </label>\n </div>\n <div className=\"flex items-center space-x-4\">\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\".json\"\n onChange={handleFileSelect}\n className=\"block text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100\"\n />\n </div>\n </div>\n\n <div className=\"relative my-4\">\n <div className=\"absolute inset-0 flex items-center\">\n <div className=\"w-full border-t border-gray-300\" />\n </div>\n <div className=\"relative flex justify-center text-sm\">\n <span className=\"bg-white dark:bg-gray-800 px-2 text-gray-500\">{t('template.or')}</span>\n </div>\n </div>\n\n <div className=\"mb-4\">\n <label className=\"block text-sm font-medium text-gray-700 mb-2\">\n {t('template.pasteJson')}\n </label>\n <textarea\n className=\"w-full h-64 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono text-sm\"\n placeholder={t('template.pastePlaceholder')}\n onChange={(e) => handlePaste(e.target.value)}\n />\n </div>\n\n <div className=\"flex justify-end space-x-4\">\n <button\n onClick={onCancel}\n className=\"px-4 py-2 text-gray-700 bg-gray-200 rounded hover:bg-gray-300 btn-secondary\"\n >\n {t('common.cancel')}\n </button>\n </div>\n </div>\n ) : (\n <div>\n {/* Template preview */}\n <div className=\"space-y-4\">\n <div className=\"p-3 bg-gray-50 dark:bg-gray-800 rounded-md\">\n <h3 className=\"font-medium text-gray-900\">{template.name}</h3>\n {template.description && (\n <p className=\"text-sm text-gray-600 mt-1\">{template.description}</p>\n )}\n <p className=\"text-xs text-gray-500 mt-1\">\n {t('template.version')}: {template.version} | {t('template.createdAt')}:{' '}\n {new Date(template.createdAt).toLocaleDateString()}\n </p>\n </div>\n\n <div>\n <h4 className=\"text-sm font-medium text-gray-700 mb-2\">\n {t('template.servers')} ({Object.keys(template.servers).length})\n </h4>\n <div className=\"border border-gray-200 dark:border-gray-700 rounded-md divide-y divide-gray-200 dark:divide-gray-700 max-h-40 overflow-y-auto\">\n {Object.entries(template.servers).map(([name, config]) => (\n <div key={name} className=\"px-3 py-2\">\n <span className=\"text-sm font-medium text-gray-900\">{name}</span>\n <span className=\"text-xs text-gray-500 ml-2\">\n {config.type || 'stdio'}\n </span>\n </div>\n ))}\n </div>\n </div>\n\n <div>\n <h4 className=\"text-sm font-medium text-gray-700 mb-2\">\n {t('template.groups')} ({template.groups.length})\n </h4>\n <div className=\"border border-gray-200 dark:border-gray-700 rounded-md divide-y divide-gray-200 dark:divide-gray-700 max-h-40 overflow-y-auto\">\n {template.groups.map((group, idx) => (\n <div key={idx} className=\"px-3 py-2\">\n <span className=\"text-sm font-medium text-gray-900\">{group.name}</span>\n {group.description && (\n <span className=\"text-xs text-gray-500 ml-2\">{group.description}</span>\n )}\n <div className=\"text-xs text-gray-500 mt-1\">\n {group.servers.length} {t('template.serversInGroup')}\n </div>\n </div>\n ))}\n </div>\n </div>\n\n {template.requiredEnvVars.length > 0 && (\n <div className=\"p-3 bg-orange-50 border border-orange-200 rounded-md\">\n <h4 className=\"text-sm font-medium text-orange-800\">\n {t('template.envVarsNeeded')}\n </h4>\n <ul className=\"mt-1 text-sm text-orange-700 font-mono\">\n {template.requiredEnvVars.map((v) => (\n <li key={v}>{v}</li>\n ))}\n </ul>\n </div>\n )}\n </div>\n\n <div className=\"flex justify-end space-x-4 mt-6\">\n <button\n onClick={() => {\n setTemplate(null);\n setResult(null);\n if (fileInputRef.current) fileInputRef.current.value = '';\n }}\n className=\"px-4 py-2 text-gray-700 bg-gray-200 rounded hover:bg-gray-300 btn-secondary\"\n >\n {t('common.back')}\n </button>\n <button\n onClick={handleImport}\n disabled={isImporting}\n className=\"px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed btn-primary\"\n >\n {isImporting ? t('template.importing') : t('template.import')}\n </button>\n </div>\n </div>\n )}\n </div>\n </div>\n );\n};\n\nexport default TemplateImportForm;\n","import React, { useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { Group } from '@/types';\nimport { useGroupData } from '@/hooks/useGroupData';\nimport { useServerData } from '@/hooks/useServerData';\nimport AddGroupForm from '@/components/AddGroupForm';\nimport EditGroupForm from '@/components/EditGroupForm';\nimport GroupCard from '@/components/GroupCard';\nimport GroupImportForm from '@/components/GroupImportForm';\nimport TemplateExportForm from '@/components/TemplateExportForm';\nimport TemplateImportForm from '@/components/TemplateImportForm';\n\nconst GroupsPage: React.FC = () => {\n const { t } = useTranslation();\n const {\n groups,\n loading: groupsLoading,\n error: groupError,\n setError: setGroupError,\n deleteGroup,\n triggerRefresh,\n } = useGroupData();\n const { allServers } = useServerData({ refreshOnMount: true });\n\n const [editingGroup, setEditingGroup] = useState<Group | null>(null);\n const [showAddForm, setShowAddForm] = useState(false);\n const [showImportForm, setShowImportForm] = useState(false);\n const [showTemplateExport, setShowTemplateExport] = useState(false);\n const [showTemplateImport, setShowTemplateImport] = useState(false);\n\n const handleEditClick = (group: Group) => {\n setEditingGroup(group);\n };\n\n const handleEditComplete = () => {\n setEditingGroup(null);\n triggerRefresh(); // Refresh the groups list after editing\n };\n\n const handleDeleteGroup = async (groupId: string) => {\n const result = await deleteGroup(groupId);\n if (!result || !result.success) {\n setGroupError(result?.message || t('groups.deleteError'));\n }\n };\n\n const handleAddGroup = () => {\n setShowAddForm(true);\n };\n\n const handleAddComplete = () => {\n setShowAddForm(false);\n triggerRefresh(); // Refresh the groups list after adding\n };\n\n const handleImportSuccess = () => {\n setShowImportForm(false);\n triggerRefresh(); // Refresh the groups list after import\n };\n\n const handleTemplateImportSuccess = () => {\n setShowTemplateImport(false);\n triggerRefresh();\n };\n\n return (\n <div>\n <div className=\"flex justify-between items-center mb-8\">\n <h1 className=\"text-2xl font-bold text-gray-900\">{t('pages.groups.title')}</h1>\n <div className=\"flex space-x-4\">\n <button\n onClick={handleAddGroup}\n className=\"px-4 py-2 bg-blue-100 text-blue-800 rounded hover:bg-blue-200 flex items-center btn-primary transition-all duration-200\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"h-4 w-4 mr-2\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M10 3a1 1 0 00-1 1v5H4a1 1 0 100 2h5v5a1 1 0 102 0v-5h5a1 1 0 100-2h-5V4a1 1 0 00-1-1z\"\n clipRule=\"evenodd\"\n />\n </svg>\n {t('groups.add')}\n </button>\n <button\n onClick={() => setShowImportForm(true)}\n className=\"px-4 py-2 bg-blue-100 text-blue-800 rounded hover:bg-blue-200 flex items-center btn-primary transition-all duration-200\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"h-4 w-4 mr-2\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z\"\n clipRule=\"evenodd\"\n />\n </svg>\n {t('groupImport.button')}\n </button>\n <button\n onClick={() => setShowTemplateExport(true)}\n className=\"px-4 py-2 bg-green-100 text-green-800 rounded hover:bg-green-200 flex items-center btn-primary transition-all duration-200\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"h-4 w-4 mr-2\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM6.293 6.707a1 1 0 010-1.414l3-3a1 1 0 011.414 0l3 3a1 1 0 01-1.414 1.414L11 5.414V13a1 1 0 11-2 0V5.414L7.707 6.707a1 1 0 01-1.414 0z\"\n clipRule=\"evenodd\"\n />\n </svg>\n {t('template.exportButton')}\n </button>\n <button\n onClick={() => setShowTemplateImport(true)}\n className=\"px-4 py-2 bg-green-100 text-green-800 rounded hover:bg-green-200 flex items-center btn-primary transition-all duration-200\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"h-4 w-4 mr-2\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z\"\n clipRule=\"evenodd\"\n />\n </svg>\n {t('template.importButton')}\n </button>\n </div>\n </div>\n\n {groupError && (\n <div className=\"bg-red-100 border-l-4 border-red-500 text-red-700 p-4 mb-6 error-box rounded-lg\">\n <p>{groupError}</p>\n </div>\n )}\n\n {groupsLoading ? (\n <div className=\"bg-white dark:bg-gray-800 shadow rounded-lg p-6 loading-container\">\n <div className=\"flex flex-col items-center justify-center\">\n <svg\n className=\"animate-spin h-10 w-10 text-blue-500 mb-4\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n className=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n strokeWidth=\"4\"\n ></circle>\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n ></path>\n </svg>\n <p className=\"text-gray-600\">{t('app.loading')}</p>\n </div>\n </div>\n ) : groups.length === 0 ? (\n <div className=\"bg-white dark:bg-gray-800 shadow rounded-lg p-6 empty-state\">\n <p className=\"text-gray-600\">{t('groups.noGroups')}</p>\n </div>\n ) : (\n <div className=\"space-y-6\">\n {groups.map((group) => (\n <GroupCard\n key={group.id}\n group={group}\n servers={allServers}\n onEdit={handleEditClick}\n onDelete={handleDeleteGroup}\n />\n ))}\n </div>\n )}\n\n {showAddForm && <AddGroupForm onAdd={handleAddComplete} onCancel={handleAddComplete} />}\n\n {showImportForm && (\n <GroupImportForm\n onSuccess={handleImportSuccess}\n onCancel={() => setShowImportForm(false)}\n />\n )}\n\n {editingGroup && (\n <EditGroupForm\n group={editingGroup}\n onEdit={handleEditComplete}\n onCancel={() => setEditingGroup(null)}\n />\n )}\n\n {showTemplateExport && (\n <TemplateExportForm\n groups={groups}\n onCancel={() => setShowTemplateExport(false)}\n />\n )}\n\n {showTemplateImport && (\n <TemplateImportForm\n onSuccess={handleTemplateImportSuccess}\n onCancel={() => setShowTemplateImport(false)}\n />\n )}\n </div>\n );\n};\n\nexport default GroupsPage;\n"],"names":["EMPTY_SELECTIONS","FULL_SELECTIONS","ServerToolConfig","servers","value","onChange","className","t","useTranslation","nameSeparator","useSettingsData","expandedServers","setExpandedServers","useState","normalizedValue","React","item","availableServers","server","configuredServerNames","config","availableServerNames","prev","newSet","serverName","toggleServer","newValue","toggleServerExpanded","hasAnyCapabilitySelection","capability","selection","updateServerCapability","keepExpanded","existingServer","nextConfig","normalizeNamedCapability","name","prefix","getCapabilityItems","tool","prompt","resource","toggleCapabilityItem","itemValue","s","allItems","serverConfig","currentSelection","nextSelection","isServerSelected","isServerPartiallySelected","isCapabilityItemSelected","getSelectedCapabilityCount","items","itemSet","capabilityConfigs","getServerSummaryBadges","key","entry","cn","jsx","isSelected","isPartiallySelected","isExpanded","summaryBadges","serverCapabilities","jsxs","e","count","Wrench","MessageSquare","FileText","titleKey","countKey","allKey","selectedCount","allSelected","isChecked","AddGroupForm","onAdd","onCancel","createGroup","useGroupData","allServers","useServerData","setAvailableServers","error","setError","isSubmitting","setIsSubmitting","formData","setFormData","useEffect","handleChange","handleSubmit","result","err","EditGroupForm","group","onEdit","updateGroup","GroupCard","onDelete","showToast","useToast","installConfig","showDeleteDialog","setShowDeleteDialog","copied","setCopied","showCopyDropdown","setShowCopyDropdown","expandedServer","setExpandedServer","dropdownRef","useRef","handleClickOutside","event","handleEdit","handleDelete","handleConfirmDelete","copyToClipboard","text","textArea","handleCopyId","handleCopyUrl","handleCopyJson","jsonConfig","getServerNames","getServerConfig","serverNames","groupServers","Check","Copy","DropdownIcon","Link","FileCode","Edit","Trash","hasToolRestrictions","hasPromptRestrictions","hasResourceRestrictions","enabledServerTools","enabledServerPrompts","enabledServerResources","normalizeToolName","toolName","normalizePromptName","promptName","enabledToolNames","enabledPromptNames","enabledResourceUris","enabledToolNameSet","enabledPromptNameSet","enabledResourceUriSet","toolCount","promptCount","resourceCount","resourceUri","getCapabilityList","handleServerClick","index","DeleteDialog","GroupImportForm","onSuccess","jsonInput","setJsonInput","isImporting","setIsImporting","previewGroups","setPreviewGroups","examplePlaceholder","parseAndValidateJson","input","parsed","handlePreview","handleImport","apiPost","successCount","failureCount","results","errors","r","renderAllCapabilitiesLabel","renderCapabilityPreview","renderServerList","idx","Fragment","TemplateExportForm","groups","setName","description","setDescription","selectedGroupIds","setSelectedGroupIds","includeDisabled","setIncludeDisabled","isExporting","setIsExporting","handleToggleGroup","groupId","id","handleSelectAll","g","handleExport","template","blob","url","a","TemplateImportForm","setTemplate","setResult","fileInputRef","handleFileSelect","file","_a","reader","handlePaste","response","v","GroupsPage","groupsLoading","groupError","setGroupError","deleteGroup","triggerRefresh","editingGroup","setEditingGroup","showAddForm","setShowAddForm","showImportForm","setShowImportForm","showTemplateExport","setShowTemplateExport","showTemplateImport","setShowTemplateImport","handleEditClick","handleEditComplete","handleDeleteGroup","handleAddGroup","handleAddComplete","handleImportSuccess","handleTemplateImportSuccess"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{u as $,j as e,r as n,g as q}from"./framework-vendor-_OBebcuv.js";import{u as M,g as G,T as O,L as K,a as V,c as _,b as H}from"./index-CmnA4an8.js";import{u as A}from"./i18n-vendor-MQ921plD.js";import{B as J}from"./icons-vendor-B67NtVuR.js";const Q=({isOpen:r,onClose:i})=>{const{t:l}=A(),u=$();if(!r)return null;const f=()=>{i(),u("/settings"),setTimeout(()=>{const o=document.querySelector('[data-section="password"]');if(o){o.scrollIntoView({behavior:"smooth",block:"start"});const g=o.querySelector('[role="button"]');g&&!o.querySelector(".mt-4")&&g.click()}},100)},h=o=>{o.target===o.currentTarget&&i()},c=o=>{o.key==="Escape"&&i()};return e.jsx("div",{className:"fixed inset-0 bg-black/50 z-[100] flex items-center justify-center p-4",onClick:h,onKeyDown:c,tabIndex:-1,children:e.jsx("div",{className:"bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-md w-full transform transition-all duration-200 ease-out",role:"dialog","aria-modal":"true","aria-labelledby":"password-warning-title","aria-describedby":"password-warning-message",children:e.jsxs("div",{className:"p-6",children:[e.jsxs("div",{className:"flex items-start space-x-3",children:[e.jsx("div",{className:"flex-shrink-0",children:e.jsx("svg",{className:"w-6 h-6 text-yellow-600 dark:text-yellow-400",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"})})}),e.jsxs("div",{className:"flex-1",children:[e.jsx("h3",{id:"password-warning-title",className:"text-lg font-medium text-gray-900 dark:text-white mb-2",children:l("auth.defaultPasswordWarning")}),e.jsx("p",{id:"password-warning-message",className:"text-gray-600 dark:text-gray-300 leading-relaxed",children:l("auth.defaultPasswordMessage")})]})]}),e.jsxs("div",{className:"flex justify-end space-x-3 mt-6",children:[e.jsx("button",{onClick:i,className:"px-4 py-2 text-gray-600 dark:text-gray-300 hover:text-gray-800 dark:hover:text-white hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 rounded-md transition-colors duration-150 btn-secondary",children:l("common.cancel")}),e.jsx("button",{onClick:f,className:"px-4 py-2 bg-yellow-600 hover:bg-yellow-700 text-white rounded-md transition-colors duration-150 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-yellow-500 btn-warning",autoFocus:!0,children:l("auth.goToSettings")})]})]})})})},X=r=>{if(!r)return null;try{const i=typeof window<"u"?window.location.origin:"http://localhost",l=new URL(r,i);return l.origin!==i?null:`${l.pathname}${l.search}${l.hash}`||"/"}catch{return r.startsWith("/")&&!r.startsWith("//")?r:null}},te=()=>{const{t:r}=A(),[i,l]=n.useState(""),[u,f]=n.useState(""),[h,c]=n.useState(null),[o,g]=n.useState(!1),[x,w]=n.useState(null),[v,y]=n.useState(null),[B,R]=n.useState(void 0),[m,k]=n.useState({google:!1,github:!1}),[z,j]=n.useState(!1),{login:D,auth:b}=M(),N=q(),S=$(),d=n.useMemo(()=>{const a=new URLSearchParams(N.search);return X(a.get("returnUrl"))},[N.search]),C=n.useCallback(a=>{if(!a)return!1;const t=a.toLowerCase();return t.includes("failed to fetch")||t.includes("networkerror")||t.includes("network error")||t.includes("connection refused")||t.includes("unable to connect")||t.includes("fetch error")||t.includes("econnrefused")||t.includes("http 500")||t.includes("internal server error")||t.includes("proxy error")},[]),L=n.useCallback(()=>{if(!d)return"/";if(!d.startsWith("/oauth/authorize"))return d;const a=G();if(!a)return d;try{const t=window.location.origin,s=new URL(d,t);return s.searchParams.set("token",a),`${s.pathname}${s.search}${s.hash}`}catch{const t=d.includes("?")?"&":"?";return`${d}${t}token=${encodeURIComponent(a)}`}},[d]),p=n.useCallback(()=>{d?window.location.assign(L()):S("/")},[L,S,d]);n.useEffect(()=>{!b.loading&&b.isAuthenticated&&p()},[b.isAuthenticated,b.loading,p]),n.useEffect(()=>{(async()=>{var U,W,E,T;const s=(await V()).betterAuth;if(!(s!=null&&s.enabled)){k({google:!1,github:!1});return}R(s.basePath),k({google:((W=(U=s.providers)==null?void 0:U.google)==null?void 0:W.enabled)===!0,github:((T=(E=s.providers)==null?void 0:E.github)==null?void 0:T.enabled)===!0})})()},[]);const I=async a=>{a.preventDefault(),c(null),y(null),g(!0);try{if(!i||!u){c(r("auth.emptyFields")),g(!1);return}const t=await D(i,u);if(t.success)t.isUsingDefaultPassword?j(!0):p();else{const s=t.message;C(s)?c(r("auth.serverUnavailable")):c(r("auth.loginFailed"))}}catch(t){const s=t instanceof Error?t.message:void 0;C(s)?c(r("auth.serverUnavailable")):c(r("auth.loginError"))}finally{g(!1)}},P=async a=>{y(null),w(a);try{await _(B).signIn.social({provider:a,callbackURL:d||"/",errorCallbackURL:`${H()}/login`})}catch(t){console.error("Social login error:",t),y(r("auth.socialLoginFailed")),w(null)}},F=()=>{j(!1),p()};return e.jsxs("div",{className:"relative min-h-screen w-full overflow-hidden bg-gray-50 dark:bg-gray-950",children:[e.jsxs("div",{className:"absolute top-4 right-4 z-20 flex items-center gap-2",children:[e.jsx("a",{href:"https://docs.mcphub.app",target:"_blank",rel:"noopener noreferrer",className:"rounded-md p-2 text-gray-500 hover:bg-gray-100 dark:bg-gray-800 hover:text-gray-900 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-200","aria-label":"Documentation",children:e.jsx(J,{className:"h-5 w-5"})}),e.jsx(O,{}),e.jsx(K,{})]}),e.jsx("div",{className:"pointer-events-none absolute inset-0 -z-10 opacity-60 dark:opacity-70",style:{backgroundImage:"radial-gradient(60rem 60rem at 20% -10%, rgba(99,102,241,0.25), transparent), radial-gradient(50rem 50rem at 120% 10%, rgba(168,85,247,0.15), transparent)"}}),e.jsx("div",{className:"pointer-events-none absolute inset-0 -z-10",children:e.jsxs("svg",{className:"h-full w-full opacity-[0.08] dark:opacity-[0.12]",xmlns:"http://www.w3.org/2000/svg",children:[e.jsx("defs",{children:e.jsx("pattern",{id:"grid",width:"32",height:"32",patternUnits:"userSpaceOnUse",children:e.jsx("path",{d:"M 32 0 L 0 0 0 32",fill:"none",stroke:"currentColor",strokeWidth:"0.5"})})}),e.jsx("rect",{width:"100%",height:"100%",fill:"url(#grid)",className:"text-gray-400 dark:text-gray-300"})]})}),e.jsx("div",{className:"relative mx-auto flex min-h-screen w-full max-w-md items-center justify-center px-6 py-16",children:e.jsxs("div",{className:"w-full space-y-16",children:[e.jsx("div",{className:"flex justify-center w-full",children:e.jsx("h1",{className:"text-5xl sm:text-5xl font-extrabold leading-tight tracking-tight text-gray-900 dark:text-white whitespace-nowrap",children:e.jsx("span",{className:"bg-gradient-to-r from-indigo-400 via-cyan-400 to-emerald-400 bg-clip-text text-transparent",children:r("auth.slogan")})})}),e.jsxs("div",{className:"login-card relative w-full rounded-2xl border border-white/10 bg-white/60 p-8 shadow-xl backdrop-blur-md transition dark:border-white/10 dark:bg-gray-900/60",children:[e.jsx("div",{className:"absolute -top-24 right-12 h-40 w-40 -translate-y-6 rounded-full bg-indigo-500/30 blur-3xl"}),e.jsx("div",{className:"absolute -bottom-24 -left-12 h-40 w-40 translate-y-6 rounded-full bg-cyan-500/20 blur-3xl"}),e.jsxs("form",{className:"mt-4 space-y-4",onSubmit:I,children:[e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{children:[e.jsx("label",{htmlFor:"username",className:"sr-only",children:r("auth.username")}),e.jsx("input",{id:"username",name:"username",type:"text",autoComplete:"username",required:!0,className:"login-input appearance-none relative block w-full rounded-md border border-gray-300/60 bg-white/70 px-3 py-3 text-gray-900 shadow-sm outline-none ring-0 transition-all placeholder:text-gray-500 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 dark:border-gray-700/60 dark:bg-gray-800/70 dark:text-white dark:placeholder:text-gray-400",placeholder:r("auth.username"),value:i,onChange:a=>l(a.target.value)})]}),e.jsxs("div",{children:[e.jsx("label",{htmlFor:"password",className:"sr-only",children:r("auth.password")}),e.jsx("input",{id:"password",name:"password",type:"password",autoComplete:"current-password",required:!0,className:"login-input appearance-none relative block w-full rounded-md border border-gray-300/60 bg-white/70 px-3 py-3 text-gray-900 shadow-sm outline-none ring-0 transition-all placeholder:text-gray-500 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 dark:border-gray-700/60 dark:bg-gray-800/70 dark:text-white dark:placeholder:text-gray-400",placeholder:r("auth.password"),value:u,onChange:a=>f(a.target.value)})]})]}),h&&e.jsx("div",{className:"error-box rounded border border-red-500/20 bg-red-500/10 p-2 text-center text-sm text-red-600 dark:text-red-400",children:h}),e.jsx("div",{children:e.jsx("button",{type:"submit",disabled:o,className:"login-button btn-primary group relative flex w-full items-center justify-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-sm font-medium text-white transition-all hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-70",children:r(o?"auth.loggingIn":"auth.login")})})]}),(m.google||m.github)&&e.jsxs("div",{className:"mt-6 space-y-4",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("div",{className:"h-px flex-1 bg-gray-200/80 dark:bg-gray-700/80"}),e.jsx("span",{className:"text-xs uppercase tracking-widest text-gray-500 dark:text-gray-400",children:r("auth.orContinue")}),e.jsx("div",{className:"h-px flex-1 bg-gray-200/80 dark:bg-gray-700/80"})]}),v&&e.jsx("div",{className:"error-box rounded border border-red-500/20 bg-red-500/10 p-2 text-center text-sm text-red-600 dark:text-red-400",children:v}),e.jsxs("div",{className:"space-y-3",children:[m.google&&e.jsx("button",{type:"button",onClick:()=>P("google"),disabled:x!==null,className:"flex w-full items-center justify-center gap-2 rounded-md border border-gray-200 bg-white/80 px-4 py-2 text-sm font-medium text-gray-700 shadow-sm transition hover:bg-gray-50 dark:hover:bg-gray-700 disabled:cursor-not-allowed disabled:opacity-70 dark:border-gray-700 dark:bg-gray-900/70 dark:text-gray-200",children:r(x==="google"?"auth.loggingIn":"auth.loginWithGoogle")}),m.github&&e.jsx("button",{type:"button",onClick:()=>P("github"),disabled:x!==null,className:"flex w-full items-center justify-center gap-2 rounded-md border border-gray-200 bg-gray-900 px-4 py-2 text-sm font-medium text-white shadow-sm transition hover:bg-gray-800 disabled:cursor-not-allowed disabled:opacity-70 dark:border-gray-700",children:r(x==="github"?"auth.loggingIn":"auth.loginWithGithub")})]})]})]})]})}),e.jsx(Q,{isOpen:z,onClose:F})]})};export{te as default};
|
|
2
|
+
//# sourceMappingURL=LoginPage-BBHt_TfF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LoginPage-BBHt_TfF.js","sources":["../../src/components/ui/DefaultPasswordWarningModal.tsx","../../src/pages/LoginPage.tsx"],"sourcesContent":["import React from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { useNavigate } from 'react-router-dom';\n\ninterface DefaultPasswordWarningModalProps {\n isOpen: boolean;\n onClose: () => void;\n}\n\nconst DefaultPasswordWarningModal: React.FC<DefaultPasswordWarningModalProps> = ({\n isOpen,\n onClose,\n}) => {\n const { t } = useTranslation();\n const navigate = useNavigate();\n\n if (!isOpen) return null;\n\n const handleGoToSettings = () => {\n onClose();\n navigate('/settings');\n // Auto-scroll to password section after a small delay to ensure page is loaded\n setTimeout(() => {\n const passwordSection = document.querySelector('[data-section=\"password\"]');\n if (passwordSection) {\n passwordSection.scrollIntoView({ behavior: 'smooth', block: 'start' });\n // If the section is collapsed, expand it\n const clickTarget = passwordSection.querySelector('[role=\"button\"]');\n if (clickTarget && !passwordSection.querySelector('.mt-4')) {\n (clickTarget as HTMLElement).click();\n }\n }\n }, 100);\n };\n\n const handleBackdropClick = (e: React.MouseEvent) => {\n if (e.target === e.currentTarget) {\n onClose();\n }\n };\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Escape') {\n onClose();\n }\n };\n\n return (\n <div\n className=\"fixed inset-0 bg-black/50 z-[100] flex items-center justify-center p-4\"\n onClick={handleBackdropClick}\n onKeyDown={handleKeyDown}\n tabIndex={-1}\n >\n <div\n className=\"bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-md w-full transform transition-all duration-200 ease-out\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"password-warning-title\"\n aria-describedby=\"password-warning-message\"\n >\n <div className=\"p-6\">\n <div className=\"flex items-start space-x-3\">\n <div className=\"flex-shrink-0\">\n <svg\n className=\"w-6 h-6 text-yellow-600 dark:text-yellow-400\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z\"\n />\n </svg>\n </div>\n <div className=\"flex-1\">\n <h3\n id=\"password-warning-title\"\n className=\"text-lg font-medium text-gray-900 dark:text-white mb-2\"\n >\n {t('auth.defaultPasswordWarning')}\n </h3>\n <p\n id=\"password-warning-message\"\n className=\"text-gray-600 dark:text-gray-300 leading-relaxed\"\n >\n {t('auth.defaultPasswordMessage')}\n </p>\n </div>\n </div>\n\n <div className=\"flex justify-end space-x-3 mt-6\">\n <button\n onClick={onClose}\n className=\"px-4 py-2 text-gray-600 dark:text-gray-300 hover:text-gray-800 dark:hover:text-white hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 rounded-md transition-colors duration-150 btn-secondary\"\n >\n {t('common.cancel')}\n </button>\n <button\n onClick={handleGoToSettings}\n className=\"px-4 py-2 bg-yellow-600 hover:bg-yellow-700 text-white rounded-md transition-colors duration-150 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-yellow-500 btn-warning\"\n autoFocus\n >\n {t('auth.goToSettings')}\n </button>\n </div>\n </div>\n </div>\n </div>\n );\n};\n\nexport default DefaultPasswordWarningModal;\n","import React, { useState, useMemo, useCallback, useEffect } from 'react';\nimport { useLocation, useNavigate } from 'react-router-dom';\nimport { useTranslation } from 'react-i18next';\nimport { BookOpen } from 'lucide-react';\nimport { useAuth } from '../contexts/AuthContext';\nimport { getToken } from '../services/authService';\nimport { getPublicConfig } from '../services/configService';\nimport { createBetterAuthClient } from '../services/betterAuthClient';\nimport { getBasePath } from '../utils/runtime';\nimport ThemeSwitch from '@/components/ui/ThemeSwitch';\nimport LanguageSwitch from '@/components/ui/LanguageSwitch';\nimport DefaultPasswordWarningModal from '@/components/ui/DefaultPasswordWarningModal';\n\nconst sanitizeReturnUrl = (value: string | null): string | null => {\n if (!value) {\n return null;\n }\n\n try {\n // Support both relative paths and absolute URLs on the same origin\n const origin = typeof window !== 'undefined' ? window.location.origin : 'http://localhost';\n const url = new URL(value, origin);\n if (url.origin !== origin) {\n return null;\n }\n const relativePath = `${url.pathname}${url.search}${url.hash}`;\n return relativePath || '/';\n } catch {\n if (value.startsWith('/') && !value.startsWith('//')) {\n return value;\n }\n return null;\n }\n};\n\nconst LoginPage: React.FC = () => {\n const { t } = useTranslation();\n const [username, setUsername] = useState('');\n const [password, setPassword] = useState('');\n const [error, setError] = useState<string | null>(null);\n const [loading, setLoading] = useState(false);\n const [socialLoading, setSocialLoading] = useState<'google' | 'github' | null>(null);\n const [socialError, setSocialError] = useState<string | null>(null);\n const [betterAuthBasePath, setBetterAuthBasePath] = useState<string | undefined>(undefined);\n const [socialProviders, setSocialProviders] = useState({\n google: false,\n github: false,\n });\n const [showDefaultPasswordWarning, setShowDefaultPasswordWarning] = useState(false);\n const { login, auth } = useAuth();\n const location = useLocation();\n const navigate = useNavigate();\n const returnUrl = useMemo(() => {\n const params = new URLSearchParams(location.search);\n return sanitizeReturnUrl(params.get('returnUrl'));\n }, [location.search]);\n\n const isServerUnavailableError = useCallback((message?: string) => {\n if (!message) return false;\n const normalized = message.toLowerCase();\n\n return (\n normalized.includes('failed to fetch') ||\n normalized.includes('networkerror') ||\n normalized.includes('network error') ||\n normalized.includes('connection refused') ||\n normalized.includes('unable to connect') ||\n normalized.includes('fetch error') ||\n normalized.includes('econnrefused') ||\n normalized.includes('http 500') ||\n normalized.includes('internal server error') ||\n normalized.includes('proxy error')\n );\n }, []);\n\n const buildRedirectTarget = useCallback(() => {\n if (!returnUrl) {\n return '/';\n }\n\n // Only attach JWT when returning to the OAuth authorize endpoint\n if (!returnUrl.startsWith('/oauth/authorize')) {\n return returnUrl;\n }\n\n const token = getToken();\n if (!token) {\n return returnUrl;\n }\n\n try {\n const origin = window.location.origin;\n const url = new URL(returnUrl, origin);\n url.searchParams.set('token', token);\n return `${url.pathname}${url.search}${url.hash}`;\n } catch {\n const separator = returnUrl.includes('?') ? '&' : '?';\n return `${returnUrl}${separator}token=${encodeURIComponent(token)}`;\n }\n }, [returnUrl]);\n\n const redirectAfterLogin = useCallback(() => {\n if (returnUrl) {\n window.location.assign(buildRedirectTarget());\n } else {\n navigate('/');\n }\n }, [buildRedirectTarget, navigate, returnUrl]);\n\n useEffect(() => {\n if (!auth.loading && auth.isAuthenticated) {\n redirectAfterLogin();\n }\n }, [auth.isAuthenticated, auth.loading, redirectAfterLogin]);\n\n useEffect(() => {\n const loadAuthProviders = async () => {\n const publicConfig = await getPublicConfig();\n const betterAuth = publicConfig.betterAuth;\n if (!betterAuth?.enabled) {\n setSocialProviders({ google: false, github: false });\n return;\n }\n\n setBetterAuthBasePath(betterAuth.basePath);\n setSocialProviders({\n google: betterAuth.providers?.google?.enabled === true,\n github: betterAuth.providers?.github?.enabled === true,\n });\n };\n\n loadAuthProviders();\n }, []);\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n setSocialError(null);\n setLoading(true);\n\n try {\n if (!username || !password) {\n setError(t('auth.emptyFields'));\n setLoading(false);\n return;\n }\n\n const result = await login(username, password);\n\n if (result.success) {\n if (result.isUsingDefaultPassword) {\n // Show warning modal instead of navigating immediately\n setShowDefaultPasswordWarning(true);\n } else {\n redirectAfterLogin();\n }\n } else {\n const message = result.message;\n if (isServerUnavailableError(message)) {\n setError(t('auth.serverUnavailable'));\n } else {\n setError(t('auth.loginFailed'));\n }\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : undefined;\n if (isServerUnavailableError(message)) {\n setError(t('auth.serverUnavailable'));\n } else {\n setError(t('auth.loginError'));\n }\n } finally {\n setLoading(false);\n }\n };\n\n const handleSocialLogin = async (provider: 'google' | 'github') => {\n setSocialError(null);\n setSocialLoading(provider);\n try {\n const client = createBetterAuthClient(betterAuthBasePath);\n await client.signIn.social({\n provider,\n callbackURL: returnUrl || '/',\n errorCallbackURL: `${getBasePath()}/login`,\n });\n } catch (err) {\n console.error('Social login error:', err);\n setSocialError(t('auth.socialLoginFailed'));\n setSocialLoading(null);\n }\n };\n\n const handleCloseWarning = () => {\n setShowDefaultPasswordWarning(false);\n redirectAfterLogin();\n };\n\n return (\n <div className=\"relative min-h-screen w-full overflow-hidden bg-gray-50 dark:bg-gray-950\">\n {/* Top-right controls */}\n <div className=\"absolute top-4 right-4 z-20 flex items-center gap-2\">\n <a\n href=\"https://docs.mcphub.app\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"rounded-md p-2 text-gray-500 hover:bg-gray-100 dark:bg-gray-800 hover:text-gray-900 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-200\"\n aria-label=\"Documentation\"\n >\n <BookOpen className=\"h-5 w-5\" />\n </a>\n <ThemeSwitch />\n <LanguageSwitch />\n </div>\n\n {/* Tech background layer */}\n <div\n className=\"pointer-events-none absolute inset-0 -z-10 opacity-60 dark:opacity-70\"\n style={{\n backgroundImage:\n 'radial-gradient(60rem 60rem at 20% -10%, rgba(99,102,241,0.25), transparent), radial-gradient(50rem 50rem at 120% 10%, rgba(168,85,247,0.15), transparent)',\n }}\n />\n <div className=\"pointer-events-none absolute inset-0 -z-10\">\n <svg\n className=\"h-full w-full opacity-[0.08] dark:opacity-[0.12]\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <defs>\n <pattern id=\"grid\" width=\"32\" height=\"32\" patternUnits=\"userSpaceOnUse\">\n <path d=\"M 32 0 L 0 0 0 32\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"0.5\" />\n </pattern>\n </defs>\n <rect\n width=\"100%\"\n height=\"100%\"\n fill=\"url(#grid)\"\n className=\"text-gray-400 dark:text-gray-300\"\n />\n </svg>\n </div>\n\n {/* Main content */}\n <div className=\"relative mx-auto flex min-h-screen w-full max-w-md items-center justify-center px-6 py-16\">\n <div className=\"w-full space-y-16\">\n {/* Centered slogan */}\n <div className=\"flex justify-center w-full\">\n <h1 className=\"text-5xl sm:text-5xl font-extrabold leading-tight tracking-tight text-gray-900 dark:text-white whitespace-nowrap\">\n <span className=\"bg-gradient-to-r from-indigo-400 via-cyan-400 to-emerald-400 bg-clip-text text-transparent\">\n {t('auth.slogan')}\n </span>\n </h1>\n </div>\n\n {/* Centered login card */}\n <div className=\"login-card relative w-full rounded-2xl border border-white/10 bg-white/60 p-8 shadow-xl backdrop-blur-md transition dark:border-white/10 dark:bg-gray-900/60\">\n <div className=\"absolute -top-24 right-12 h-40 w-40 -translate-y-6 rounded-full bg-indigo-500/30 blur-3xl\" />\n <div className=\"absolute -bottom-24 -left-12 h-40 w-40 translate-y-6 rounded-full bg-cyan-500/20 blur-3xl\" />\n <form className=\"mt-4 space-y-4\" onSubmit={handleSubmit}>\n <div className=\"space-y-4\">\n <div>\n <label htmlFor=\"username\" className=\"sr-only\">\n {t('auth.username')}\n </label>\n <input\n id=\"username\"\n name=\"username\"\n type=\"text\"\n autoComplete=\"username\"\n required\n className=\"login-input appearance-none relative block w-full rounded-md border border-gray-300/60 bg-white/70 px-3 py-3 text-gray-900 shadow-sm outline-none ring-0 transition-all placeholder:text-gray-500 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 dark:border-gray-700/60 dark:bg-gray-800/70 dark:text-white dark:placeholder:text-gray-400\"\n placeholder={t('auth.username')}\n value={username}\n onChange={(e) => setUsername(e.target.value)}\n />\n </div>\n <div>\n <label htmlFor=\"password\" className=\"sr-only\">\n {t('auth.password')}\n </label>\n <input\n id=\"password\"\n name=\"password\"\n type=\"password\"\n autoComplete=\"current-password\"\n required\n className=\"login-input appearance-none relative block w-full rounded-md border border-gray-300/60 bg-white/70 px-3 py-3 text-gray-900 shadow-sm outline-none ring-0 transition-all placeholder:text-gray-500 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 dark:border-gray-700/60 dark:bg-gray-800/70 dark:text-white dark:placeholder:text-gray-400\"\n placeholder={t('auth.password')}\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n />\n </div>\n </div>\n\n {error && (\n <div className=\"error-box rounded border border-red-500/20 bg-red-500/10 p-2 text-center text-sm text-red-600 dark:text-red-400\">\n {error}\n </div>\n )}\n\n <div>\n <button\n type=\"submit\"\n disabled={loading}\n className=\"login-button btn-primary group relative flex w-full items-center justify-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-sm font-medium text-white transition-all hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-70\"\n >\n {loading ? t('auth.loggingIn') : t('auth.login')}\n </button>\n </div>\n </form>\n\n {(socialProviders.google || socialProviders.github) && (\n <div className=\"mt-6 space-y-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"h-px flex-1 bg-gray-200/80 dark:bg-gray-700/80\" />\n <span className=\"text-xs uppercase tracking-widest text-gray-500 dark:text-gray-400\">\n {t('auth.orContinue')}\n </span>\n <div className=\"h-px flex-1 bg-gray-200/80 dark:bg-gray-700/80\" />\n </div>\n\n {socialError && (\n <div className=\"error-box rounded border border-red-500/20 bg-red-500/10 p-2 text-center text-sm text-red-600 dark:text-red-400\">\n {socialError}\n </div>\n )}\n\n <div className=\"space-y-3\">\n {socialProviders.google && (\n <button\n type=\"button\"\n onClick={() => handleSocialLogin('google')}\n disabled={socialLoading !== null}\n className=\"flex w-full items-center justify-center gap-2 rounded-md border border-gray-200 bg-white/80 px-4 py-2 text-sm font-medium text-gray-700 shadow-sm transition hover:bg-gray-50 dark:hover:bg-gray-700 disabled:cursor-not-allowed disabled:opacity-70 dark:border-gray-700 dark:bg-gray-900/70 dark:text-gray-200\"\n >\n {socialLoading === 'google' ? t('auth.loggingIn') : t('auth.loginWithGoogle')}\n </button>\n )}\n {socialProviders.github && (\n <button\n type=\"button\"\n onClick={() => handleSocialLogin('github')}\n disabled={socialLoading !== null}\n className=\"flex w-full items-center justify-center gap-2 rounded-md border border-gray-200 bg-gray-900 px-4 py-2 text-sm font-medium text-white shadow-sm transition hover:bg-gray-800 disabled:cursor-not-allowed disabled:opacity-70 dark:border-gray-700\"\n >\n {socialLoading === 'github' ? t('auth.loggingIn') : t('auth.loginWithGithub')}\n </button>\n )}\n </div>\n </div>\n )}\n </div>\n </div>\n </div>\n\n {/* Default Password Warning Modal */}\n <DefaultPasswordWarningModal\n isOpen={showDefaultPasswordWarning}\n onClose={handleCloseWarning}\n />\n </div>\n );\n};\n\nexport default LoginPage;\n"],"names":["DefaultPasswordWarningModal","isOpen","onClose","t","useTranslation","navigate","useNavigate","handleGoToSettings","passwordSection","clickTarget","handleBackdropClick","e","handleKeyDown","jsx","jsxs","sanitizeReturnUrl","value","origin","url","LoginPage","username","setUsername","useState","password","setPassword","error","setError","loading","setLoading","socialLoading","setSocialLoading","socialError","setSocialError","betterAuthBasePath","setBetterAuthBasePath","socialProviders","setSocialProviders","showDefaultPasswordWarning","setShowDefaultPasswordWarning","login","auth","useAuth","location","useLocation","returnUrl","useMemo","params","isServerUnavailableError","useCallback","message","normalized","buildRedirectTarget","token","getToken","separator","redirectAfterLogin","useEffect","betterAuth","getPublicConfig","_b","_a","_d","_c","handleSubmit","result","err","handleSocialLogin","provider","createBetterAuthClient","getBasePath","handleCloseWarning","BookOpen","ThemeSwitch","LanguageSwitch"],"mappings":"uPASA,MAAMA,EAA0E,CAAC,CAC/E,OAAAC,EACA,QAAAC,CACF,IAAM,CACJ,KAAM,CAAE,EAAAC,CAAA,EAAMC,EAAA,EACRC,EAAWC,EAAA,EAEjB,GAAI,CAACL,EAAQ,OAAO,KAEpB,MAAMM,EAAqB,IAAM,CAC/BL,EAAA,EACAG,EAAS,WAAW,EAEpB,WAAW,IAAM,CACf,MAAMG,EAAkB,SAAS,cAAc,2BAA2B,EAC1E,GAAIA,EAAiB,CACnBA,EAAgB,eAAe,CAAE,SAAU,SAAU,MAAO,QAAS,EAErE,MAAMC,EAAcD,EAAgB,cAAc,iBAAiB,EAC/DC,GAAe,CAACD,EAAgB,cAAc,OAAO,GACtDC,EAA4B,MAAA,CAEjC,CACF,EAAG,GAAG,CACR,EAEMC,EAAuBC,GAAwB,CAC/CA,EAAE,SAAWA,EAAE,eACjBT,EAAA,CAEJ,EAEMU,EAAiBD,GAA2B,CAC5CA,EAAE,MAAQ,UACZT,EAAA,CAEJ,EAEA,OACEW,EAAAA,IAAC,MAAA,CACC,UAAU,yEACV,QAASH,EACT,UAAWE,EACX,SAAU,GAEV,SAAAC,EAAAA,IAAC,MAAA,CACC,UAAU,gHACV,KAAK,SACL,aAAW,OACX,kBAAgB,yBAChB,mBAAiB,2BAEjB,SAAAC,EAAAA,KAAC,MAAA,CAAI,UAAU,MACb,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,6BACb,SAAA,CAAAD,EAAAA,IAAC,MAAA,CAAI,UAAU,gBACb,SAAAA,EAAAA,IAAC,MAAA,CACC,UAAU,+CACV,KAAK,OACL,QAAQ,YACR,OAAO,eAEP,SAAAA,EAAAA,IAAC,OAAA,CACC,cAAc,QACd,eAAe,QACf,YAAa,EACb,EAAE,2IAAA,CAAA,CACJ,CAAA,EAEJ,EACAC,EAAAA,KAAC,MAAA,CAAI,UAAU,SACb,SAAA,CAAAD,EAAAA,IAAC,KAAA,CACC,GAAG,yBACH,UAAU,yDAET,WAAE,6BAA6B,CAAA,CAAA,EAElCA,EAAAA,IAAC,IAAA,CACC,GAAG,2BACH,UAAU,mDAET,WAAE,6BAA6B,CAAA,CAAA,CAClC,CAAA,CACF,CAAA,EACF,EAEAC,EAAAA,KAAC,MAAA,CAAI,UAAU,kCACb,SAAA,CAAAD,EAAAA,IAAC,SAAA,CACC,QAASX,EACT,UAAU,yMAET,WAAE,eAAe,CAAA,CAAA,EAEpBW,EAAAA,IAAC,SAAA,CACC,QAASN,EACT,UAAU,yLACV,UAAS,GAER,WAAE,mBAAmB,CAAA,CAAA,CACxB,CAAA,CACF,CAAA,CAAA,CACF,CAAA,CAAA,CACF,CAAA,CAGN,ECpGMQ,EAAqBC,GAAwC,CACjE,GAAI,CAACA,EACH,OAAO,KAGT,GAAI,CAEF,MAAMC,EAAS,OAAO,OAAW,IAAc,OAAO,SAAS,OAAS,mBAClEC,EAAM,IAAI,IAAIF,EAAOC,CAAM,EACjC,OAAIC,EAAI,SAAWD,EACV,KAEY,GAAGC,EAAI,QAAQ,GAAGA,EAAI,MAAM,GAAGA,EAAI,IAAI,IACrC,GACzB,MAAQ,CACN,OAAIF,EAAM,WAAW,GAAG,GAAK,CAACA,EAAM,WAAW,IAAI,EAC1CA,EAEF,IACT,CACF,EAEMG,GAAsB,IAAM,CAChC,KAAM,CAAE,EAAAhB,CAAA,EAAMC,EAAA,EACR,CAACgB,EAAUC,CAAW,EAAIC,EAAAA,SAAS,EAAE,EACrC,CAACC,EAAUC,CAAW,EAAIF,EAAAA,SAAS,EAAE,EACrC,CAACG,EAAOC,CAAQ,EAAIJ,EAAAA,SAAwB,IAAI,EAChD,CAACK,EAASC,CAAU,EAAIN,EAAAA,SAAS,EAAK,EACtC,CAACO,EAAeC,CAAgB,EAAIR,EAAAA,SAAqC,IAAI,EAC7E,CAACS,EAAaC,CAAc,EAAIV,EAAAA,SAAwB,IAAI,EAC5D,CAACW,EAAoBC,CAAqB,EAAIZ,EAAAA,SAA6B,MAAS,EACpF,CAACa,EAAiBC,CAAkB,EAAId,WAAS,CACrD,OAAQ,GACR,OAAQ,EAAA,CACT,EACK,CAACe,EAA4BC,CAA6B,EAAIhB,EAAAA,SAAS,EAAK,EAC5E,CAAE,MAAAiB,EAAO,KAAAC,CAAA,EAASC,EAAA,EAClBC,EAAWC,EAAA,EACXtC,EAAWC,EAAA,EACXsC,EAAYC,EAAAA,QAAQ,IAAM,CAC9B,MAAMC,EAAS,IAAI,gBAAgBJ,EAAS,MAAM,EAClD,OAAO3B,EAAkB+B,EAAO,IAAI,WAAW,CAAC,CAClD,EAAG,CAACJ,EAAS,MAAM,CAAC,EAEdK,EAA2BC,cAAaC,GAAqB,CACjE,GAAI,CAACA,EAAS,MAAO,GACrB,MAAMC,EAAaD,EAAQ,YAAA,EAE3B,OACEC,EAAW,SAAS,iBAAiB,GACrCA,EAAW,SAAS,cAAc,GAClCA,EAAW,SAAS,eAAe,GACnCA,EAAW,SAAS,oBAAoB,GACxCA,EAAW,SAAS,mBAAmB,GACvCA,EAAW,SAAS,aAAa,GACjCA,EAAW,SAAS,cAAc,GAClCA,EAAW,SAAS,UAAU,GAC9BA,EAAW,SAAS,uBAAuB,GAC3CA,EAAW,SAAS,aAAa,CAErC,EAAG,CAAA,CAAE,EAECC,EAAsBH,EAAAA,YAAY,IAAM,CAC5C,GAAI,CAACJ,EACH,MAAO,IAIT,GAAI,CAACA,EAAU,WAAW,kBAAkB,EAC1C,OAAOA,EAGT,MAAMQ,EAAQC,EAAA,EACd,GAAI,CAACD,EACH,OAAOR,EAGT,GAAI,CACF,MAAM3B,EAAS,OAAO,SAAS,OACzBC,EAAM,IAAI,IAAI0B,EAAW3B,CAAM,EACrC,OAAAC,EAAI,aAAa,IAAI,QAASkC,CAAK,EAC5B,GAAGlC,EAAI,QAAQ,GAAGA,EAAI,MAAM,GAAGA,EAAI,IAAI,EAChD,MAAQ,CACN,MAAMoC,EAAYV,EAAU,SAAS,GAAG,EAAI,IAAM,IAClD,MAAO,GAAGA,CAAS,GAAGU,CAAS,SAAS,mBAAmBF,CAAK,CAAC,EACnE,CACF,EAAG,CAACR,CAAS,CAAC,EAERW,EAAqBP,EAAAA,YAAY,IAAM,CACvCJ,EACF,OAAO,SAAS,OAAOO,GAAqB,EAE5C9C,EAAS,GAAG,CAEhB,EAAG,CAAC8C,EAAqB9C,EAAUuC,CAAS,CAAC,EAE7CY,EAAAA,UAAU,IAAM,CACV,CAAChB,EAAK,SAAWA,EAAK,iBACxBe,EAAA,CAEJ,EAAG,CAACf,EAAK,gBAAiBA,EAAK,QAASe,CAAkB,CAAC,EAE3DC,EAAAA,UAAU,IAAM,EACY,SAAY,aAEpC,MAAMC,GADe,MAAMC,EAAA,GACK,WAChC,GAAI,EAACD,GAAA,MAAAA,EAAY,SAAS,CACxBrB,EAAmB,CAAE,OAAQ,GAAO,OAAQ,GAAO,EACnD,MACF,CAEAF,EAAsBuB,EAAW,QAAQ,EACzCrB,EAAmB,CACjB,SAAQuB,GAAAC,EAAAH,EAAW,YAAX,YAAAG,EAAsB,SAAtB,YAAAD,EAA8B,WAAY,GAClD,SAAQE,GAAAC,EAAAL,EAAW,YAAX,YAAAK,EAAsB,SAAtB,YAAAD,EAA8B,WAAY,EAAA,CACnD,CACH,GAEA,CACF,EAAG,CAAA,CAAE,EAEL,MAAME,EAAe,MAAOpD,GAAuB,CACjDA,EAAE,eAAA,EACFe,EAAS,IAAI,EACbM,EAAe,IAAI,EACnBJ,EAAW,EAAI,EAEf,GAAI,CACF,GAAI,CAACR,GAAY,CAACG,EAAU,CAC1BG,EAASvB,EAAE,kBAAkB,CAAC,EAC9ByB,EAAW,EAAK,EAChB,MACF,CAEA,MAAMoC,EAAS,MAAMzB,EAAMnB,EAAUG,CAAQ,EAE7C,GAAIyC,EAAO,QACLA,EAAO,uBAET1B,EAA8B,EAAI,EAElCiB,EAAA,MAEG,CACL,MAAMN,EAAUe,EAAO,QACnBjB,EAAyBE,CAAO,EAClCvB,EAASvB,EAAE,wBAAwB,CAAC,EAEpCuB,EAASvB,EAAE,kBAAkB,CAAC,CAElC,CACF,OAAS8D,EAAK,CACZ,MAAMhB,EAAUgB,aAAe,MAAQA,EAAI,QAAU,OACjDlB,EAAyBE,CAAO,EAClCvB,EAASvB,EAAE,wBAAwB,CAAC,EAEpCuB,EAASvB,EAAE,iBAAiB,CAAC,CAEjC,QAAA,CACEyB,EAAW,EAAK,CAClB,CACF,EAEMsC,EAAoB,MAAOC,GAAkC,CACjEnC,EAAe,IAAI,EACnBF,EAAiBqC,CAAQ,EACzB,GAAI,CAEF,MADeC,EAAuBnC,CAAkB,EAC3C,OAAO,OAAO,CACzB,SAAAkC,EACA,YAAavB,GAAa,IAC1B,iBAAkB,GAAGyB,EAAA,CAAa,QAAA,CACnC,CACH,OAASJ,EAAK,CACZ,QAAQ,MAAM,sBAAuBA,CAAG,EACxCjC,EAAe7B,EAAE,wBAAwB,CAAC,EAC1C2B,EAAiB,IAAI,CACvB,CACF,EAEMwC,EAAqB,IAAM,CAC/BhC,EAA8B,EAAK,EACnCiB,EAAA,CACF,EAEA,OACEzC,EAAAA,KAAC,MAAA,CAAI,UAAU,2EAEb,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,sDACb,SAAA,CAAAD,EAAAA,IAAC,IAAA,CACC,KAAK,0BACL,OAAO,SACP,IAAI,sBACJ,UAAU,yJACV,aAAW,gBAEX,SAAAA,EAAAA,IAAC0D,EAAA,CAAS,UAAU,SAAA,CAAU,CAAA,CAAA,QAE/BC,EAAA,EAAY,QACZC,EAAA,CAAA,CAAe,CAAA,EAClB,EAGA5D,EAAAA,IAAC,MAAA,CACC,UAAU,wEACV,MAAO,CACL,gBACE,4JAAA,CACJ,CAAA,EAEFA,EAAAA,IAAC,MAAA,CAAI,UAAU,6CACb,SAAAC,EAAAA,KAAC,MAAA,CACC,UAAU,mDACV,MAAM,6BAEN,SAAA,CAAAD,EAAAA,IAAC,OAAA,CACC,eAAC,UAAA,CAAQ,GAAG,OAAO,MAAM,KAAK,OAAO,KAAK,aAAa,iBACrD,eAAC,OAAA,CAAK,EAAE,oBAAoB,KAAK,OAAO,OAAO,eAAe,YAAY,KAAA,CAAM,CAAA,CAClF,CAAA,CACF,EACAA,EAAAA,IAAC,OAAA,CACC,MAAM,OACN,OAAO,OACP,KAAK,aACL,UAAU,kCAAA,CAAA,CACZ,CAAA,CAAA,EAEJ,QAGC,MAAA,CAAI,UAAU,4FACb,SAAAC,EAAAA,KAAC,MAAA,CAAI,UAAU,oBAEb,SAAA,CAAAD,MAAC,MAAA,CAAI,UAAU,6BACb,SAAAA,EAAAA,IAAC,MAAG,UAAU,mHACZ,SAAAA,EAAAA,IAAC,OAAA,CAAK,UAAU,6FACb,SAAAV,EAAE,aAAa,CAAA,CAClB,EACF,EACF,EAGAW,EAAAA,KAAC,MAAA,CAAI,UAAU,+JACb,SAAA,CAAAD,EAAAA,IAAC,MAAA,CAAI,UAAU,2FAAA,CAA4F,EAC3GA,EAAAA,IAAC,MAAA,CAAI,UAAU,2FAAA,CAA4F,EAC3GC,EAAAA,KAAC,OAAA,CAAK,UAAU,iBAAiB,SAAUiD,EACzC,SAAA,CAAAjD,EAAAA,KAAC,MAAA,CAAI,UAAU,YACb,SAAA,CAAAA,OAAC,MAAA,CACC,SAAA,CAAAD,EAAAA,IAAC,SAAM,QAAQ,WAAW,UAAU,UACjC,SAAAV,EAAE,eAAe,EACpB,EACAU,EAAAA,IAAC,QAAA,CACC,GAAG,WACH,KAAK,WACL,KAAK,OACL,aAAa,WACb,SAAQ,GACR,UAAU,0VACV,YAAaV,EAAE,eAAe,EAC9B,MAAOiB,EACP,SAAWT,GAAMU,EAAYV,EAAE,OAAO,KAAK,CAAA,CAAA,CAC7C,EACF,SACC,MAAA,CACC,SAAA,CAAAE,EAAAA,IAAC,SAAM,QAAQ,WAAW,UAAU,UACjC,SAAAV,EAAE,eAAe,EACpB,EACAU,EAAAA,IAAC,QAAA,CACC,GAAG,WACH,KAAK,WACL,KAAK,WACL,aAAa,mBACb,SAAQ,GACR,UAAU,0VACV,YAAaV,EAAE,eAAe,EAC9B,MAAOoB,EACP,SAAWZ,GAAMa,EAAYb,EAAE,OAAO,KAAK,CAAA,CAAA,CAC7C,CAAA,CACF,CAAA,EACF,EAECc,GACCZ,EAAAA,IAAC,MAAA,CAAI,UAAU,kHACZ,SAAAY,EACH,QAGD,MAAA,CACC,SAAAZ,EAAAA,IAAC,SAAA,CACC,KAAK,SACL,SAAUc,EACV,UAAU,2UAET,SAAUxB,EAAVwB,EAAY,iBAAsB,YAAN,CAAkB,CAAA,CACjD,CACF,CAAA,EACF,GAEEQ,EAAgB,QAAUA,EAAgB,SAC1CrB,OAAC,MAAA,CAAI,UAAU,iBACb,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,0BACb,SAAA,CAAAD,EAAAA,IAAC,MAAA,CAAI,UAAU,gDAAA,CAAiD,QAC/D,OAAA,CAAK,UAAU,qEACb,SAAAV,EAAE,iBAAiB,EACtB,EACAU,EAAAA,IAAC,MAAA,CAAI,UAAU,gDAAA,CAAiD,CAAA,EAClE,EAECkB,GACClB,EAAAA,IAAC,MAAA,CAAI,UAAU,kHACZ,SAAAkB,EACH,EAGFjB,EAAAA,KAAC,MAAA,CAAI,UAAU,YACZ,SAAA,CAAAqB,EAAgB,QACftB,EAAAA,IAAC,SAAA,CACC,KAAK,SACL,QAAS,IAAMqD,EAAkB,QAAQ,EACzC,SAAUrC,IAAkB,KAC5B,UAAU,mTAET,SAA6B1B,MAAX,SAAa,iBAAsB,sBAAN,CAA4B,CAAA,EAG/EgC,EAAgB,QACftB,EAAAA,IAAC,SAAA,CACC,KAAK,SACL,QAAS,IAAMqD,EAAkB,QAAQ,EACzC,SAAUrC,IAAkB,KAC5B,UAAU,mPAET,SAA6B1B,MAAX,SAAa,iBAAsB,sBAAN,CAA4B,CAAA,CAC9E,CAAA,CAEJ,CAAA,CAAA,CACF,CAAA,CAAA,CAEJ,CAAA,CAAA,CACF,CAAA,CACF,EAGAU,EAAAA,IAACb,EAAA,CACC,OAAQqC,EACR,QAASiC,CAAA,CAAA,CACX,EACF,CAEJ"}
|