@samanhappy/mcphub 1.0.10 → 1.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/README.md +2 -0
  2. package/dist/cli/commands/tools.js +4 -0
  3. package/dist/cli/commands/tools.js.map +1 -1
  4. package/dist/controllers/contextCostController.js +24 -0
  5. package/dist/controllers/contextCostController.js.map +1 -0
  6. package/dist/controllers/discoveryController.js +203 -0
  7. package/dist/controllers/discoveryController.js.map +1 -0
  8. package/dist/controllers/oauthCallbackController.js +3 -8
  9. package/dist/controllers/oauthCallbackController.js.map +1 -1
  10. package/dist/dao/SystemConfigDaoDbImpl.js +3 -0
  11. package/dist/dao/SystemConfigDaoDbImpl.js.map +1 -1
  12. package/dist/db/entities/SystemConfig.js +4 -0
  13. package/dist/db/entities/SystemConfig.js.map +1 -1
  14. package/dist/db/repositories/SystemConfigRepository.js +1 -0
  15. package/dist/db/repositories/SystemConfigRepository.js.map +1 -1
  16. package/dist/routes/index.js +16 -0
  17. package/dist/routes/index.js.map +1 -1
  18. package/dist/services/contextCostService.js +101 -0
  19. package/dist/services/contextCostService.js.map +1 -0
  20. package/dist/services/groupService.js +1 -1
  21. package/dist/services/groupService.js.map +1 -1
  22. package/dist/services/hostedRuntimeCatalogService.js +2 -1
  23. package/dist/services/hostedRuntimeCatalogService.js.map +1 -1
  24. package/dist/services/mcpService.js +243 -98
  25. package/dist/services/mcpService.js.map +1 -1
  26. package/dist/services/openApiGeneratorService.js +5 -4
  27. package/dist/services/openApiGeneratorService.js.map +1 -1
  28. package/dist/services/smartRoutingService.js +53 -30
  29. package/dist/services/smartRoutingService.js.map +1 -1
  30. package/dist/services/vectorSearchService.js +3 -1
  31. package/dist/services/vectorSearchService.js.map +1 -1
  32. package/dist/utils/mcpApps.js +43 -0
  33. package/dist/utils/mcpApps.js.map +1 -0
  34. package/dist/utils/migration.js +1 -0
  35. package/dist/utils/migration.js.map +1 -1
  36. package/dist/utils/serialization.js +17 -1
  37. package/dist/utils/serialization.js.map +1 -1
  38. package/dist/utils/tokenCost.js +71 -0
  39. package/dist/utils/tokenCost.js.map +1 -0
  40. package/frontend/dist/assets/{ActivityPage-RTpGLfzM.js → ActivityPage-DY-oWiYy.js} +2 -2
  41. package/frontend/dist/assets/{ActivityPage-RTpGLfzM.js.map → ActivityPage-DY-oWiYy.js.map} +1 -1
  42. package/frontend/dist/assets/{ConfirmDialog-CxlizGia.js → ConfirmDialog-Cag_haxr.js} +2 -2
  43. package/frontend/dist/assets/{ConfirmDialog-CxlizGia.js.map → ConfirmDialog-Cag_haxr.js.map} +1 -1
  44. package/frontend/dist/assets/Dashboard-DraIR5Hs.js +2 -0
  45. package/frontend/dist/assets/Dashboard-DraIR5Hs.js.map +1 -0
  46. package/frontend/dist/assets/{DeleteDialog-DRbWonMu.js → DeleteDialog-BBfJpiiD.js} +2 -2
  47. package/frontend/dist/assets/{DeleteDialog-DRbWonMu.js.map → DeleteDialog-BBfJpiiD.js.map} +1 -1
  48. package/frontend/dist/assets/{EndpointCopy-BtPORuga.js → EndpointCopy-Bkkaa0MC.js} +2 -2
  49. package/frontend/dist/assets/{EndpointCopy-BtPORuga.js.map → EndpointCopy-Bkkaa0MC.js.map} +1 -1
  50. package/frontend/dist/assets/GroupsPage-CTFtynaX.js +33 -0
  51. package/frontend/dist/assets/GroupsPage-CTFtynaX.js.map +1 -0
  52. package/frontend/dist/assets/{LoginPage-Nks21Yj0.js → LoginPage-BeZ9wg33.js} +2 -2
  53. package/frontend/dist/assets/{LoginPage-Nks21Yj0.js.map → LoginPage-BeZ9wg33.js.map} +1 -1
  54. package/frontend/dist/assets/{LogsPage-CoupcpK1.js → LogsPage-D5LqNWlN.js} +2 -2
  55. package/frontend/dist/assets/LogsPage-D5LqNWlN.js.map +1 -0
  56. package/frontend/dist/assets/{MarketPage-PwkvXlle.js → MarketPage-DmtOnQVN.js} +2 -2
  57. package/frontend/dist/assets/{MarketPage-PwkvXlle.js.map → MarketPage-DmtOnQVN.js.map} +1 -1
  58. package/frontend/dist/assets/{Pagination-BFi-X7qY.js → Pagination-DBAu79mv.js} +2 -2
  59. package/frontend/dist/assets/{Pagination-BFi-X7qY.js.map → Pagination-DBAu79mv.js.map} +1 -1
  60. package/frontend/dist/assets/{PromptsPage-DQiX0bAK.js → PromptsPage-blyfeDf6.js} +2 -2
  61. package/frontend/dist/assets/{PromptsPage-DQiX0bAK.js.map → PromptsPage-blyfeDf6.js.map} +1 -1
  62. package/frontend/dist/assets/{ResourcesPage-Dltmu_Ze.js → ResourcesPage-3gvaBRAN.js} +2 -2
  63. package/frontend/dist/assets/{ResourcesPage-Dltmu_Ze.js.map → ResourcesPage-3gvaBRAN.js.map} +1 -1
  64. package/frontend/dist/assets/ServersPage-WhPsI-wN.js +37 -0
  65. package/frontend/dist/assets/ServersPage-WhPsI-wN.js.map +1 -0
  66. package/frontend/dist/assets/{SettingsPage-BbRB2fCw.js → SettingsPage-Dg2-dPPZ.js} +2 -2
  67. package/frontend/dist/assets/{SettingsPage-BbRB2fCw.js.map → SettingsPage-Dg2-dPPZ.js.map} +1 -1
  68. package/frontend/dist/assets/{StatusDot-BRb7WUcz.js → StatusDot-D3_AIDzm.js} +2 -2
  69. package/frontend/dist/assets/{StatusDot-BRb7WUcz.js.map → StatusDot-D3_AIDzm.js.map} +1 -1
  70. package/frontend/dist/assets/{ToggleGroup-tIqxkae6.js → ToggleGroup-DU1_5iss.js} +2 -2
  71. package/frontend/dist/assets/{ToggleGroup-tIqxkae6.js.map → ToggleGroup-DU1_5iss.js.map} +1 -1
  72. package/frontend/dist/assets/{UsersPage-DNMaUcUn.js → UsersPage-Cwi4XfTK.js} +2 -2
  73. package/frontend/dist/assets/{UsersPage-DNMaUcUn.js.map → UsersPage-Cwi4XfTK.js.map} +1 -1
  74. package/frontend/dist/assets/contextCost-qVdQkBuO.js +2 -0
  75. package/frontend/dist/assets/contextCost-qVdQkBuO.js.map +1 -0
  76. package/frontend/dist/assets/{framework-vendor-BUhDPOUZ.js → framework-vendor-DeqnZ0v6.js} +5 -5
  77. package/frontend/dist/assets/{framework-vendor-BUhDPOUZ.js.map → framework-vendor-DeqnZ0v6.js.map} +1 -1
  78. package/frontend/dist/assets/i18n-vendor-DP1IRITP.js +10 -0
  79. package/frontend/dist/assets/i18n-vendor-DP1IRITP.js.map +1 -0
  80. package/frontend/dist/assets/{icons-vendor-CKgJB3SC.js → icons-vendor-IYX_ppgH.js} +2 -2
  81. package/frontend/dist/assets/{icons-vendor-CKgJB3SC.js.map → icons-vendor-IYX_ppgH.js.map} +1 -1
  82. package/frontend/dist/assets/index-B-q7kaxJ.js +3 -0
  83. package/frontend/dist/assets/{index-C6LZBP3G.js.map → index-B-q7kaxJ.js.map} +1 -1
  84. package/frontend/dist/assets/{index-B9E3ygVt.css → index-D8V2fxz_.css} +1 -1
  85. package/frontend/dist/assets/{resourceService-BuhqcwTn.js → resourceService-DvnBlx1V.js} +2 -2
  86. package/frontend/dist/assets/{resourceService-BuhqcwTn.js.map → resourceService-DvnBlx1V.js.map} +1 -1
  87. package/frontend/dist/assets/useSettingsData-Dc1fQY2w.js +2 -0
  88. package/frontend/dist/assets/{useSettingsData-iQpBXEjX.js.map → useSettingsData-Dc1fQY2w.js.map} +1 -1
  89. package/frontend/dist/assets/{variableDetection-GTKqOf1F.js → variableDetection-Dnh8qFpO.js} +2 -2
  90. package/frontend/dist/assets/{variableDetection-GTKqOf1F.js.map → variableDetection-Dnh8qFpO.js.map} +1 -1
  91. package/frontend/dist/index.html +5 -5
  92. package/package.json +7 -7
  93. package/frontend/dist/assets/Dashboard-KV-zezVb.js +0 -2
  94. package/frontend/dist/assets/Dashboard-KV-zezVb.js.map +0 -1
  95. package/frontend/dist/assets/GroupsPage-BhNYw74w.js +0 -33
  96. package/frontend/dist/assets/GroupsPage-BhNYw74w.js.map +0 -1
  97. package/frontend/dist/assets/LogsPage-CoupcpK1.js.map +0 -1
  98. package/frontend/dist/assets/ServersPage-CcbF8dGi.js +0 -37
  99. package/frontend/dist/assets/ServersPage-CcbF8dGi.js.map +0 -1
  100. package/frontend/dist/assets/i18n-vendor-Kbr87Ofu.js +0 -2
  101. package/frontend/dist/assets/i18n-vendor-Kbr87Ofu.js.map +0 -1
  102. package/frontend/dist/assets/index-C6LZBP3G.js +0 -3
  103. package/frontend/dist/assets/useServerData-C9R1u5V4.js +0 -2
  104. package/frontend/dist/assets/useServerData-C9R1u5V4.js.map +0 -1
  105. package/frontend/dist/assets/useSettingsData-iQpBXEjX.js +0 -2
@@ -1,2 +0,0 @@
1
- import{b as $,R as u,r as S,j as e}from"./framework-vendor-BUhDPOUZ.js";import{u as D}from"./useServerData-C9R1u5V4.js";import{d as L}from"./index-C6LZBP3G.js";import{u as T}from"./useSettingsData-iQpBXEjX.js";import{E as h}from"./EndpointCopy-BtPORuga.js";import{S as M}from"./StatusDot-BRb7WUcz.js";import{u as P}from"./i18n-vendor-Kbr87Ofu.js";import{R as A,P as U,j as y,k as H}from"./icons-vendor-CKgJB3SC.js";const c=({label:s,value:l,tone:a="default"})=>{const t=a==="ok"?"oklch(0.4 0.13 145)":a==="warn"?"oklch(0.45 0.13 80)":a==="err"?"oklch(0.45 0.18 25)":a==="muted"?"var(--hub-ink-3)":"var(--hub-ink)";return e.jsxs("div",{className:"hub-card",style:{padding:"14px 16px"},children:[e.jsx("div",{className:"text-[12px]",style:{color:"var(--hub-ink-3)"},children:s}),e.jsx("div",{className:"hub-num",style:{fontSize:26,fontWeight:500,letterSpacing:"-0.02em",lineHeight:1.1,marginTop:8,color:t},children:l})]})},w=(s,l)=>l?l==="stdio"?s("server.typeStdio")||"stdio":l==="sse"?s("server.typeSse")||"sse":l==="streamable-http"?s("server.typeStreamableHttp")||"http":l==="openapi"?s("server.typeOpenapi")||"openapi":l:null,I=()=>{var j;const{t:s}=P(),l=$(),{allServers:a,error:t,setError:C,isLoading:p,triggerRefresh:R}=D({refreshOnMount:!0}),{groups:b}=L(),{installConfig:m}=T(),[z,x]=u.useState(!1),v=u.useRef(!1);u.useEffect(()=>{if(p){v.current=!0;return}if(v.current){x(!0);return}(a.length>0||t)&&x(!0)},[p,a.length,t]);const n=S.useMemo(()=>({total:a.length,online:a.filter(r=>r.status==="connected").length,disabled:a.filter(r=>r.enabled===!1).length,offline:a.filter(r=>r.status==="disconnected"&&r.enabled!==!1).length,connecting:a.filter(r=>(r.status==="connecting"||r.status==="oauth_required")&&r.enabled!==!1).length,tools:a.reduce((r,i)=>{var o;return r+(((o=i.tools)==null?void 0:o.length)||0)},0)}),[a]),E=S.useMemo(()=>a.slice(0,6),[a]),d=((j=m==null?void 0:m.baseUrl)==null?void 0:j.replace(/\/+$/,""))||"",g="minmax(220px,1.9fr) minmax(110px,0.95fr) minmax(120px,0.95fr) 80px 80px 90px 72px",f=!z;return e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-end justify-between gap-4 mb-6",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"hub-h1",children:s("pages.dashboard.title")}),e.jsxs("p",{className:"hub-sub",children:[s("pages.dashboard.totalServers")," · ",e.jsx("span",{className:"hub-num",children:n.total})," · ",s("pages.dashboard.onlineServers")," · ",e.jsx("span",{className:"hub-num",children:n.online})]})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs("button",{className:"hub-btn",onClick:()=>R(),children:[e.jsx(A,{size:13})," ",s("common.refresh")]}),e.jsxs("button",{className:"hub-btn primary",onClick:()=>l("/servers"),children:[e.jsx(U,{size:13})," ",s("server.add")]})]})]}),t&&e.jsxs("div",{className:"hub-card flex items-center justify-between gap-3 mb-5",style:{padding:"10px 14px",borderColor:"oklch(0.85 0.1 25)",background:"oklch(0.97 0.03 25)",color:"oklch(0.4 0.18 25)"},children:[e.jsxs("div",{className:"flex items-center gap-2 min-w-0",children:[e.jsx(y,{size:14,className:"flex-shrink-0"}),e.jsx("span",{className:"truncate text-[13px]",children:t})]}),e.jsx("button",{className:"hub-icon-btn sm",onClick:()=>C(null),"aria-label":s("app.closeButton"),children:"✕"})]}),f?e.jsx("div",{className:"grid grid-cols-2 md:grid-cols-5 gap-3 mb-6",children:Array.from({length:5}).map((r,i)=>e.jsx("div",{className:"hub-card animate-pulse",style:{padding:"14px 16px",height:78}},i))}):e.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-5 gap-3 mb-6",children:[e.jsx(c,{label:s("pages.dashboard.totalServers"),value:n.total}),e.jsx(c,{label:s("pages.dashboard.onlineServers"),value:n.online,tone:"ok"}),e.jsx(c,{label:s("pages.dashboard.connectingServers"),value:n.connecting,tone:"warn"}),e.jsx(c,{label:s("pages.dashboard.offlineServers"),value:n.offline,tone:"err"}),e.jsx(c,{label:s("pages.dashboard.disabledServers"),value:n.disabled,tone:"muted"})]}),a.length>0&&!f&&e.jsxs("div",{className:"hub-card overflow-hidden mb-6",children:[e.jsxs("div",{className:"flex items-center justify-between px-4 py-3",style:{borderBottom:"1px solid var(--hub-line-2)"},children:[e.jsx("h3",{className:"hub-card-title",children:s("pages.dashboard.recentServers")}),e.jsxs("button",{className:"hub-btn ghost sm",style:{color:"var(--hub-ink-3)"},onClick:()=>l("/servers"),children:[s("common.viewAll")||"View all",e.jsx(H,{size:12})]})]}),e.jsxs("div",{className:"hub-row head hub-mono",style:{gridTemplateColumns:g},children:[e.jsx("div",{children:s("server.name")}),e.jsx("div",{children:s("server.status")}),e.jsx("div",{children:s("common.type")||"Transport"}),e.jsx("div",{children:s("server.tools")}),e.jsx("div",{children:s("server.prompts")}),e.jsx("div",{children:s("nav.resources")}),e.jsx("div",{children:s("server.enabled")})]}),E.map(r=>{var i,o,N,k;return e.jsxs("div",{className:"hub-row hover cursor-pointer",style:{gridTemplateColumns:g},onClick:()=>l("/servers"),children:[e.jsxs("div",{className:"flex items-center gap-2 min-w-0",children:[e.jsx("span",{className:"hub-mono truncate",style:{fontSize:13,color:r.enabled===!1?"var(--hub-ink-3)":"var(--hub-ink)"},children:r.name}),r.error&&e.jsx(y,{size:13,className:"text-[var(--hub-err)] flex-shrink-0"})]}),e.jsx("div",{className:"min-w-0",children:e.jsx(M,{status:r.status,enabled:r.enabled})}),e.jsx("div",{className:"min-w-0",children:(i=r.config)!=null&&i.type?e.jsx("span",{className:"hub-tag",title:w(s,r.config.type)??void 0,children:w(s,r.config.type)}):e.jsx("span",{style:{color:"var(--hub-ink-3)",fontSize:12},children:"—"})}),e.jsx("div",{className:"hub-num hub-mono",style:{fontSize:12.5},children:((o=r.tools)==null?void 0:o.length)||0}),e.jsx("div",{className:"hub-num hub-mono",style:{fontSize:12.5,color:"var(--hub-ink-2)"},children:((N=r.prompts)==null?void 0:N.length)||0}),e.jsx("div",{className:"hub-num hub-mono",style:{fontSize:12.5,color:"var(--hub-ink-2)"},children:((k=r.resources)==null?void 0:k.length)||0}),e.jsx("div",{className:"text-[12px]",style:{color:r.enabled!==!1?"var(--hub-ok)":"var(--hub-ink-3)"},children:r.enabled!==!1?"✓":"—"})]},r.name)})]}),e.jsxs("div",{className:"hub-card mb-5",style:{padding:16},children:[e.jsxs("div",{className:"flex justify-between items-start gap-3 mb-3",children:[e.jsxs("div",{children:[e.jsx("h3",{className:"hub-card-title",children:s("pages.dashboard.endpoints")||"MCP Endpoints"}),e.jsx("p",{className:"hub-sub",style:{marginTop:2},children:s("pages.dashboard.endpointsHint")||"Use these URLs in Claude Desktop, Cursor, or any MCP client"})]}),e.jsxs("a",{className:"hub-btn ghost",href:"https://docs.mcphub.app",target:"_blank",rel:"noopener noreferrer",style:{color:"var(--hub-ink-3)"},children:[s("common.docs")||"Docs"," →"]})]}),e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-2.5",children:[e.jsx(h,{label:"ALL",url:`${d}/mcp`}),e.jsx(h,{label:"SMART",url:`${d}/mcp/$smart`}),b.slice(0,2).map(r=>e.jsx(h,{label:"GROUP",url:`${d}/mcp/${r.name}`},r.id)),b.length<2&&a[0]&&e.jsx(h,{label:"SERVER",url:`${d}/mcp/${a[0].name}`})]})]})]})};export{I as default};
2
- //# sourceMappingURL=Dashboard-KV-zezVb.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Dashboard-KV-zezVb.js","sources":["../../src/pages/Dashboard.tsx"],"sourcesContent":["import React, { useMemo } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { useNavigate } from 'react-router-dom';\nimport { RefreshCw, Plus, ChevronRight, AlertCircle } from 'lucide-react';\nimport { useServerData } from '@/hooks/useServerData';\nimport { useGroupData } from '@/hooks/useGroupData';\nimport { useSettingsData } from '@/hooks/useSettingsData';\nimport { Server } from '@/types';\nimport { EndpointCopy } from '@/components/ui/EndpointCopy';\nimport { ServerStatusDot } from '@/components/ui/StatusDot';\n\nconst Stat: React.FC<{ label: string; value: React.ReactNode; tone?: 'ok' | 'warn' | 'err' | 'muted' | 'default' }> = ({\n label,\n value,\n tone = 'default',\n}) => {\n const toneColor =\n tone === 'ok'\n ? 'oklch(0.4 0.13 145)'\n : tone === 'warn'\n ? 'oklch(0.45 0.13 80)'\n : tone === 'err'\n ? 'oklch(0.45 0.18 25)'\n : tone === 'muted'\n ? 'var(--hub-ink-3)'\n : 'var(--hub-ink)';\n return (\n <div className=\"hub-card\" style={{ padding: '14px 16px' }}>\n <div className=\"text-[12px]\" style={{ color: 'var(--hub-ink-3)' }}>\n {label}\n </div>\n <div\n className=\"hub-num\"\n style={{\n fontSize: 26,\n fontWeight: 500,\n letterSpacing: '-0.02em',\n lineHeight: 1.1,\n marginTop: 8,\n color: toneColor,\n }}\n >\n {value}\n </div>\n </div>\n );\n};\n\nconst transportLabel = (t: any, type?: string) => {\n if (!type) return null;\n if (type === 'stdio') return t('server.typeStdio') || 'stdio';\n if (type === 'sse') return t('server.typeSse') || 'sse';\n if (type === 'streamable-http') return t('server.typeStreamableHttp') || 'http';\n if (type === 'openapi') return t('server.typeOpenapi') || 'openapi';\n return type;\n};\n\nconst DashboardPage: React.FC = () => {\n const { t } = useTranslation();\n const navigate = useNavigate();\n const { allServers, error, setError, isLoading, triggerRefresh } = useServerData({\n refreshOnMount: true,\n });\n const { groups } = useGroupData();\n const { installConfig } = useSettingsData();\n\n const [hasLoaded, setHasLoaded] = React.useState(false);\n const loadingStartedRef = React.useRef(false);\n React.useEffect(() => {\n if (isLoading) {\n loadingStartedRef.current = true;\n return;\n }\n if (loadingStartedRef.current) {\n setHasLoaded(true);\n return;\n }\n if (allServers.length > 0 || error) setHasLoaded(true);\n }, [isLoading, allServers.length, error]);\n\n const stats = useMemo(\n () => ({\n total: allServers.length,\n online: allServers.filter((s: Server) => s.status === 'connected').length,\n disabled: allServers.filter((s: Server) => s.enabled === false).length,\n offline: allServers.filter(\n (s: Server) => s.status === 'disconnected' && s.enabled !== false,\n ).length,\n connecting: allServers.filter(\n (s: Server) =>\n (s.status === 'connecting' || s.status === 'oauth_required') && s.enabled !== false,\n ).length,\n tools: allServers.reduce((acc, s) => acc + (s.tools?.length || 0), 0),\n }),\n [allServers],\n );\n\n const recentServers = useMemo(() => allServers.slice(0, 6), [allServers]);\n const baseUrl = installConfig?.baseUrl?.replace(/\\/+$/, '') || '';\n const recentServerColumns =\n 'minmax(220px,1.9fr) minmax(110px,0.95fr) minmax(120px,0.95fr) 80px 80px 90px 72px';\n\n const showSkeleton = !hasLoaded;\n\n return (\n <div>\n {/* Header */}\n <div className=\"flex items-end justify-between gap-4 mb-6\">\n <div>\n <h1 className=\"hub-h1\">{t('pages.dashboard.title')}</h1>\n <p className=\"hub-sub\">\n {t('pages.dashboard.totalServers')} · <span className=\"hub-num\">{stats.total}</span>\n {' · '}\n {t('pages.dashboard.onlineServers')} · <span className=\"hub-num\">{stats.online}</span>\n </p>\n </div>\n <div className=\"flex gap-2\">\n <button className=\"hub-btn\" onClick={() => triggerRefresh()}>\n <RefreshCw size={13} /> {t('common.refresh')}\n </button>\n <button className=\"hub-btn primary\" onClick={() => navigate('/servers')}>\n <Plus size={13} /> {t('server.add')}\n </button>\n </div>\n </div>\n\n {error && (\n <div\n className=\"hub-card flex items-center justify-between gap-3 mb-5\"\n style={{\n padding: '10px 14px',\n borderColor: 'oklch(0.85 0.1 25)',\n background: 'oklch(0.97 0.03 25)',\n color: 'oklch(0.4 0.18 25)',\n }}\n >\n <div className=\"flex items-center gap-2 min-w-0\">\n <AlertCircle size={14} className=\"flex-shrink-0\" />\n <span className=\"truncate text-[13px]\">{error}</span>\n </div>\n <button\n className=\"hub-icon-btn sm\"\n onClick={() => setError(null)}\n aria-label={t('app.closeButton')}\n >\n ✕\n </button>\n </div>\n )}\n\n {/* Stat row */}\n {showSkeleton ? (\n <div className=\"grid grid-cols-2 md:grid-cols-5 gap-3 mb-6\">\n {Array.from({ length: 5 }).map((_, i) => (\n <div\n key={i}\n className=\"hub-card animate-pulse\"\n style={{ padding: '14px 16px', height: 78 }}\n />\n ))}\n </div>\n ) : (\n <div className=\"grid grid-cols-2 md:grid-cols-5 gap-3 mb-6\">\n <Stat label={t('pages.dashboard.totalServers')} value={stats.total} />\n <Stat label={t('pages.dashboard.onlineServers')} value={stats.online} tone=\"ok\" />\n <Stat label={t('pages.dashboard.connectingServers')} value={stats.connecting} tone=\"warn\" />\n <Stat label={t('pages.dashboard.offlineServers')} value={stats.offline} tone=\"err\" />\n <Stat label={t('pages.dashboard.disabledServers')} value={stats.disabled} tone=\"muted\" />\n </div>\n )}\n\n {/* Recent servers */}\n {allServers.length > 0 && !showSkeleton && (\n <div className=\"hub-card overflow-hidden mb-6\">\n <div\n className=\"flex items-center justify-between px-4 py-3\"\n style={{ borderBottom: '1px solid var(--hub-line-2)' }}\n >\n <h3 className=\"hub-card-title\">{t('pages.dashboard.recentServers')}</h3>\n <button\n className=\"hub-btn ghost sm\"\n style={{ color: 'var(--hub-ink-3)' }}\n onClick={() => navigate('/servers')}\n >\n {t('common.viewAll') || 'View all'}\n <ChevronRight size={12} />\n </button>\n </div>\n <div\n className=\"hub-row head hub-mono\"\n style={{ gridTemplateColumns: recentServerColumns }}\n >\n <div>{t('server.name')}</div>\n <div>{t('server.status')}</div>\n <div>{t('common.type') || 'Transport'}</div>\n <div>{t('server.tools')}</div>\n <div>{t('server.prompts')}</div>\n <div>{t('nav.resources')}</div>\n <div>{t('server.enabled')}</div>\n </div>\n {recentServers.map((s) => (\n <div\n key={s.name}\n className=\"hub-row hover cursor-pointer\"\n style={{ gridTemplateColumns: recentServerColumns }}\n onClick={() => navigate('/servers')}\n >\n <div className=\"flex items-center gap-2 min-w-0\">\n <span\n className=\"hub-mono truncate\"\n style={{ fontSize: 13, color: s.enabled === false ? 'var(--hub-ink-3)' : 'var(--hub-ink)' }}\n >\n {s.name}\n </span>\n {s.error && <AlertCircle size={13} className=\"text-[var(--hub-err)] flex-shrink-0\" />}\n </div>\n <div className=\"min-w-0\">\n <ServerStatusDot status={s.status} enabled={s.enabled} />\n </div>\n <div className=\"min-w-0\">\n {s.config?.type ? (\n <span className=\"hub-tag\" title={transportLabel(t, s.config.type) ?? undefined}>\n {transportLabel(t, s.config.type)}\n </span>\n ) : (\n <span style={{ color: 'var(--hub-ink-3)', fontSize: 12 }}>—</span>\n )}\n </div>\n <div className=\"hub-num hub-mono\" style={{ fontSize: 12.5 }}>\n {s.tools?.length || 0}\n </div>\n <div className=\"hub-num hub-mono\" style={{ fontSize: 12.5, color: 'var(--hub-ink-2)' }}>\n {s.prompts?.length || 0}\n </div>\n <div className=\"hub-num hub-mono\" style={{ fontSize: 12.5, color: 'var(--hub-ink-2)' }}>\n {s.resources?.length || 0}\n </div>\n <div className=\"text-[12px]\" style={{ color: s.enabled !== false ? 'var(--hub-ok)' : 'var(--hub-ink-3)' }}>\n {s.enabled !== false ? '✓' : '—'}\n </div>\n </div>\n ))}\n </div>\n )}\n\n {/* Endpoint quick-access */}\n <div className=\"hub-card mb-5\" style={{ padding: 16 }}>\n <div className=\"flex justify-between items-start gap-3 mb-3\">\n <div>\n <h3 className=\"hub-card-title\">{t('pages.dashboard.endpoints') || 'MCP Endpoints'}</h3>\n <p className=\"hub-sub\" style={{ marginTop: 2 }}>\n {t('pages.dashboard.endpointsHint') ||\n 'Use these URLs in Claude Desktop, Cursor, or any MCP client'}\n </p>\n </div>\n <a\n className=\"hub-btn ghost\"\n href=\"https://docs.mcphub.app\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n style={{ color: 'var(--hub-ink-3)' }}\n >\n {t('common.docs') || 'Docs'} →\n </a>\n </div>\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-2.5\">\n <EndpointCopy label=\"ALL\" url={`${baseUrl}/mcp`} />\n <EndpointCopy label=\"SMART\" url={`${baseUrl}/mcp/$smart`} />\n {groups.slice(0, 2).map((g) => (\n <EndpointCopy key={g.id} label=\"GROUP\" url={`${baseUrl}/mcp/${g.name}`} />\n ))}\n {/* Pad with first server endpoint if there's space */}\n {groups.length < 2 && allServers[0] && (\n <EndpointCopy label=\"SERVER\" url={`${baseUrl}/mcp/${allServers[0].name}`} />\n )}\n </div>\n </div>\n </div>\n );\n};\n\nexport default DashboardPage;\n"],"names":["Stat","label","value","tone","toneColor","jsxs","jsx","transportLabel","t","type","DashboardPage","useTranslation","navigate","useNavigate","allServers","error","setError","isLoading","triggerRefresh","useServerData","groups","useGroupData","installConfig","useSettingsData","hasLoaded","setHasLoaded","React","loadingStartedRef","stats","useMemo","s","acc","_a","recentServers","baseUrl","recentServerColumns","showSkeleton","RefreshCw","Plus","AlertCircle","_","ChevronRight","ServerStatusDot","_b","_c","_d","EndpointCopy","g"],"mappings":"+ZAWA,MAAMA,EAAgH,CAAC,CACrH,MAAAC,EACA,MAAAC,EACA,KAAAC,EAAO,SACT,IAAM,CACJ,MAAMC,EACJD,IAAS,KACL,sBACAA,IAAS,OACP,sBACAA,IAAS,MACP,sBACAA,IAAS,QACP,mBACA,iBACZ,OACEE,EAAAA,KAAC,OAAI,UAAU,WAAW,MAAO,CAAE,QAAS,aAC1C,SAAA,CAAAC,EAAAA,IAAC,MAAA,CAAI,UAAU,cAAc,MAAO,CAAE,MAAO,kBAAA,EAC1C,SAAAL,CAAA,CACH,EACAK,EAAAA,IAAC,MAAA,CACC,UAAU,UACV,MAAO,CACL,SAAU,GACV,WAAY,IACZ,cAAe,UACf,WAAY,IACZ,UAAW,EACX,MAAOF,CAAA,EAGR,SAAAF,CAAA,CAAA,CACH,EACF,CAEJ,EAEMK,EAAiB,CAACC,EAAQC,IACzBA,EACDA,IAAS,QAAgBD,EAAE,kBAAkB,GAAK,QAClDC,IAAS,MAAcD,EAAE,gBAAgB,GAAK,MAC9CC,IAAS,kBAA0BD,EAAE,2BAA2B,GAAK,OACrEC,IAAS,UAAkBD,EAAE,oBAAoB,GAAK,UACnDC,EALW,KAQdC,EAA0B,IAAM,OACpC,KAAM,CAAE,EAAAF,CAAA,EAAMG,EAAA,EACRC,EAAWC,EAAA,EACX,CAAE,WAAAC,EAAY,MAAAC,EAAO,SAAAC,EAAU,UAAAC,EAAW,eAAAC,CAAA,EAAmBC,EAAc,CAC/E,eAAgB,EAAA,CACjB,EACK,CAAE,OAAAC,CAAA,EAAWC,EAAA,EACb,CAAE,cAAAC,CAAA,EAAkBC,EAAA,EAEpB,CAACC,EAAWC,CAAY,EAAIC,EAAM,SAAS,EAAK,EAChDC,EAAoBD,EAAM,OAAO,EAAK,EAC5CA,EAAM,UAAU,IAAM,CACpB,GAAIT,EAAW,CACbU,EAAkB,QAAU,GAC5B,MACF,CACA,GAAIA,EAAkB,QAAS,CAC7BF,EAAa,EAAI,EACjB,MACF,EACIX,EAAW,OAAS,GAAKC,MAAoB,EAAI,CACvD,EAAG,CAACE,EAAWH,EAAW,OAAQC,CAAK,CAAC,EAExC,MAAMa,EAAQC,EAAAA,QACZ,KAAO,CACL,MAAOf,EAAW,OAClB,OAAQA,EAAW,OAAQgB,GAAcA,EAAE,SAAW,WAAW,EAAE,OACnE,SAAUhB,EAAW,OAAQgB,GAAcA,EAAE,UAAY,EAAK,EAAE,OAChE,QAAShB,EAAW,OACjBgB,GAAcA,EAAE,SAAW,gBAAkBA,EAAE,UAAY,EAAA,EAC5D,OACF,WAAYhB,EAAW,OACpBgB,IACEA,EAAE,SAAW,cAAgBA,EAAE,SAAW,mBAAqBA,EAAE,UAAY,EAAA,EAChF,OACF,MAAOhB,EAAW,OAAO,CAACiB,EAAKD,IAAA,OAAM,OAAAC,KAAOC,EAAAF,EAAE,QAAF,YAAAE,EAAS,SAAU,IAAI,CAAC,CAAA,GAEtE,CAAClB,CAAU,CAAA,EAGPmB,EAAgBJ,UAAQ,IAAMf,EAAW,MAAM,EAAG,CAAC,EAAG,CAACA,CAAU,CAAC,EAClEoB,IAAUF,EAAAV,GAAA,YAAAA,EAAe,UAAf,YAAAU,EAAwB,QAAQ,OAAQ,MAAO,GACzDG,EACJ,oFAEIC,EAAe,CAACZ,EAEtB,cACG,MAAA,CAEC,SAAA,CAAAnB,EAAAA,KAAC,MAAA,CAAI,UAAU,4CACb,SAAA,CAAAA,OAAC,MAAA,CACC,SAAA,CAAAC,MAAC,KAAA,CAAG,UAAU,SAAU,SAAAE,EAAE,uBAAuB,EAAE,EACnDH,EAAAA,KAAC,IAAA,CAAE,UAAU,UACV,SAAA,CAAAG,EAAE,8BAA8B,EAAE,MAAGF,EAAAA,IAAC,OAAA,CAAK,UAAU,UAAW,WAAM,MAAM,EAC5E,QACAE,EAAE,+BAA+B,EAAE,MAAGF,EAAAA,IAAC,OAAA,CAAK,UAAU,UAAW,WAAM,MAAA,CAAO,CAAA,CAAA,CACjF,CAAA,EACF,EACAD,EAAAA,KAAC,MAAA,CAAI,UAAU,aACb,SAAA,CAAAA,OAAC,UAAO,UAAU,UAAU,QAAS,IAAMa,IACzC,SAAA,CAAAZ,EAAAA,IAAC+B,EAAA,CAAU,KAAM,EAAA,CAAI,EAAE,IAAE7B,EAAE,gBAAgB,CAAA,EAC7C,EACAH,OAAC,UAAO,UAAU,kBAAkB,QAAS,IAAMO,EAAS,UAAU,EACpE,SAAA,CAAAN,EAAAA,IAACgC,EAAA,CAAK,KAAM,EAAA,CAAI,EAAE,IAAE9B,EAAE,YAAY,CAAA,CAAA,CACpC,CAAA,CAAA,CACF,CAAA,EACF,EAECO,GACCV,EAAAA,KAAC,MAAA,CACC,UAAU,wDACV,MAAO,CACL,QAAS,YACT,YAAa,qBACb,WAAY,sBACZ,MAAO,oBAAA,EAGT,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,kCACb,SAAA,CAAAC,EAAAA,IAACiC,EAAA,CAAY,KAAM,GAAI,UAAU,gBAAgB,EACjDjC,EAAAA,IAAC,OAAA,CAAK,UAAU,uBAAwB,SAAAS,CAAA,CAAM,CAAA,EAChD,EACAT,EAAAA,IAAC,SAAA,CACC,UAAU,kBACV,QAAS,IAAMU,EAAS,IAAI,EAC5B,aAAYR,EAAE,iBAAiB,EAChC,SAAA,GAAA,CAAA,CAED,CAAA,CAAA,EAKH4B,EACC9B,EAAAA,IAAC,MAAA,CAAI,UAAU,6CACZ,SAAA,MAAM,KAAK,CAAE,OAAQ,EAAG,EAAE,IAAI,CAACkC,EAAG,IACjClC,EAAAA,IAAC,MAAA,CAEC,UAAU,yBACV,MAAO,CAAE,QAAS,YAAa,OAAQ,EAAA,CAAG,EAFrC,CAAA,CAIR,CAAA,CACH,EAEAD,OAAC,MAAA,CAAI,UAAU,6CACb,SAAA,CAAAC,MAACN,GAAK,MAAOQ,EAAE,8BAA8B,EAAG,MAAOoB,EAAM,MAAO,EACpEtB,EAAAA,IAACN,EAAA,CAAK,MAAOQ,EAAE,+BAA+B,EAAG,MAAOoB,EAAM,OAAQ,KAAK,IAAA,CAAK,EAChFtB,EAAAA,IAACN,EAAA,CAAK,MAAOQ,EAAE,mCAAmC,EAAG,MAAOoB,EAAM,WAAY,KAAK,MAAA,CAAO,EAC1FtB,EAAAA,IAACN,EAAA,CAAK,MAAOQ,EAAE,gCAAgC,EAAG,MAAOoB,EAAM,QAAS,KAAK,KAAA,CAAM,EACnFtB,EAAAA,IAACN,EAAA,CAAK,MAAOQ,EAAE,iCAAiC,EAAG,MAAOoB,EAAM,SAAU,KAAK,OAAA,CAAQ,CAAA,EACzF,EAIDd,EAAW,OAAS,GAAK,CAACsB,GACzB/B,OAAC,MAAA,CAAI,UAAU,gCACb,SAAA,CAAAA,EAAAA,KAAC,MAAA,CACC,UAAU,8CACV,MAAO,CAAE,aAAc,6BAAA,EAEvB,SAAA,CAAAC,MAAC,KAAA,CAAG,UAAU,iBAAkB,SAAAE,EAAE,+BAA+B,EAAE,EACnEH,EAAAA,KAAC,SAAA,CACC,UAAU,mBACV,MAAO,CAAE,MAAO,kBAAA,EAChB,QAAS,IAAMO,EAAS,UAAU,EAEjC,SAAA,CAAAJ,EAAE,gBAAgB,GAAK,WACxBF,EAAAA,IAACmC,EAAA,CAAa,KAAM,EAAA,CAAI,CAAA,CAAA,CAAA,CAC1B,CAAA,CAAA,EAEFpC,EAAAA,KAAC,MAAA,CACC,UAAU,wBACV,MAAO,CAAE,oBAAqB8B,CAAA,EAE9B,SAAA,CAAA7B,EAAAA,IAAC,MAAA,CAAK,SAAAE,EAAE,aAAa,CAAA,CAAE,EACvBF,EAAAA,IAAC,MAAA,CAAK,SAAAE,EAAE,eAAe,CAAA,CAAE,EACzBF,EAAAA,IAAC,MAAA,CAAK,SAAAE,EAAE,aAAa,GAAK,YAAY,EACtCF,EAAAA,IAAC,MAAA,CAAK,SAAAE,EAAE,cAAc,CAAA,CAAE,EACxBF,EAAAA,IAAC,MAAA,CAAK,SAAAE,EAAE,gBAAgB,CAAA,CAAE,EAC1BF,EAAAA,IAAC,MAAA,CAAK,SAAAE,EAAE,eAAe,CAAA,CAAE,EACzBF,EAAAA,IAAC,MAAA,CAAK,SAAAE,EAAE,gBAAgB,CAAA,CAAE,CAAA,CAAA,CAAA,EAE3ByB,EAAc,IAAKH,gBAClBzB,OAAAA,EAAAA,KAAC,MAAA,CAEC,UAAU,+BACV,MAAO,CAAE,oBAAqB8B,CAAA,EAC9B,QAAS,IAAMvB,EAAS,UAAU,EAElC,SAAA,CAAAP,EAAAA,KAAC,MAAA,CAAI,UAAU,kCACb,SAAA,CAAAC,EAAAA,IAAC,OAAA,CACC,UAAU,oBACV,MAAO,CAAE,SAAU,GAAI,MAAOwB,EAAE,UAAY,GAAQ,mBAAqB,gBAAA,EAExE,SAAAA,EAAE,IAAA,CAAA,EAEJA,EAAE,OAASxB,EAAAA,IAACiC,GAAY,KAAM,GAAI,UAAU,qCAAA,CAAsC,CAAA,EACrF,EACAjC,EAAAA,IAAC,MAAA,CAAI,UAAU,UACb,SAAAA,EAAAA,IAACoC,EAAA,CAAgB,OAAQZ,EAAE,OAAQ,QAASA,EAAE,OAAA,CAAS,EACzD,QACC,MAAA,CAAI,UAAU,UACZ,UAAAE,EAAAF,EAAE,SAAF,MAAAE,EAAU,KACT1B,EAAAA,IAAC,OAAA,CAAK,UAAU,UAAU,MAAOC,EAAeC,EAAGsB,EAAE,OAAO,IAAI,GAAK,OAClE,SAAAvB,EAAeC,EAAGsB,EAAE,OAAO,IAAI,CAAA,CAClC,EAEAxB,EAAAA,IAAC,OAAA,CAAK,MAAO,CAAE,MAAO,mBAAoB,SAAU,EAAA,EAAM,aAAC,EAE/D,EACAA,EAAAA,IAAC,MAAA,CAAI,UAAU,mBAAmB,MAAO,CAAE,SAAU,IAAA,EAClD,WAAAqC,EAAAb,EAAE,QAAF,YAAAa,EAAS,SAAU,CAAA,CACtB,EACArC,EAAAA,IAAC,MAAA,CAAI,UAAU,mBAAmB,MAAO,CAAE,SAAU,KAAM,MAAO,kBAAA,EAC/D,WAAAsC,EAAAd,EAAE,UAAF,YAAAc,EAAW,SAAU,EACxB,EACAtC,EAAAA,IAAC,MAAA,CAAI,UAAU,mBAAmB,MAAO,CAAE,SAAU,KAAM,MAAO,kBAAA,EAC/D,WAAAuC,EAAAf,EAAE,YAAF,YAAAe,EAAa,SAAU,EAC1B,QACC,MAAA,CAAI,UAAU,cAAc,MAAO,CAAE,MAAOf,EAAE,UAAY,GAAQ,gBAAkB,kBAAA,EAClF,WAAE,UAAY,GAAQ,IAAM,GAAA,CAC/B,CAAA,CAAA,EArCKA,EAAE,IAAA,EAuCV,CAAA,EACH,EAIFzB,OAAC,OAAI,UAAU,gBAAgB,MAAO,CAAE,QAAS,IAC/C,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,8CACb,SAAA,CAAAA,OAAC,MAAA,CACC,SAAA,CAAAC,MAAC,MAAG,UAAU,iBAAkB,SAAAE,EAAE,2BAA2B,GAAK,gBAAgB,EAClFF,EAAAA,IAAC,IAAA,CAAE,UAAU,UAAU,MAAO,CAAE,UAAW,CAAA,EACxC,SAAAE,EAAE,+BAA+B,GAChC,6DAAA,CACJ,CAAA,EACF,EACAH,EAAAA,KAAC,IAAA,CACC,UAAU,gBACV,KAAK,0BACL,OAAO,SACP,IAAI,sBACJ,MAAO,CAAE,MAAO,kBAAA,EAEf,SAAA,CAAAG,EAAE,aAAa,GAAK,OAAO,IAAA,CAAA,CAAA,CAC9B,EACF,EACAH,EAAAA,KAAC,MAAA,CAAI,UAAU,0CACb,SAAA,CAAAC,MAACwC,GAAa,MAAM,MAAM,IAAK,GAAGZ,CAAO,OAAQ,QAChDY,EAAA,CAAa,MAAM,QAAQ,IAAK,GAAGZ,CAAO,cAAe,EACzDd,EAAO,MAAM,EAAG,CAAC,EAAE,IAAK2B,GACvBzC,EAAAA,IAACwC,EAAA,CAAwB,MAAM,QAAQ,IAAK,GAAGZ,CAAO,QAAQa,EAAE,IAAI,EAAA,EAAjDA,EAAE,EAAmD,CACzE,EAEA3B,EAAO,OAAS,GAAKN,EAAW,CAAC,SAC/BgC,EAAA,CAAa,MAAM,SAAS,IAAK,GAAGZ,CAAO,QAAQpB,EAAW,CAAC,EAAE,IAAI,EAAA,CAAI,CAAA,CAAA,CAE9E,CAAA,CAAA,CACF,CAAA,EACF,CAEJ"}
@@ -1,33 +0,0 @@
1
- import{r as b,R as W,j as e}from"./framework-vendor-BUhDPOUZ.js";import{m as U,d as q,i as Q,e as M}from"./index-C6LZBP3G.js";import{u as J}from"./useServerData-C9R1u5V4.js";import{u as X}from"./useSettingsData-iQpBXEjX.js";import{u as P}from"./i18n-vendor-Kbr87Ofu.js";import{W as ee,f as se,F as te,C as re,o as B,x as ae,u as le,t as ne,T as oe,s as ie,v as V,D as ce,P as _,j as de,X as me}from"./icons-vendor-CKgJB3SC.js";import{D as ue}from"./DeleteDialog-DRbWonMu.js";const pe={tools:[],prompts:[],resources:[]},H={tools:"all",prompts:"all",resources:"all"},Y=({servers:i,value:m,onChange:t,className:d})=>{const{t:u}=P(),{nameSeparator:A}=X(),[g,k]=b.useState(new Set),x=W.useMemo(()=>m.map(s=>typeof s=="string"?{name:s,...H}:{...s,tools:s.tools||"all",prompts:s.prompts||"all",resources:s.resources||"all"}),[m]),p=W.useMemo(()=>i.filter(s=>s.enabled!==!1),[i]);W.useEffect(()=>{const s=new Set(x.map(r=>r.name)),l=new Set(p.map(r=>r.name));k(r=>{const S=new Set;return r.forEach(N=>{(s.has(N)||l.has(N))&&S.add(N)}),S})},[x,p]);const v=s=>{if(x.findIndex(r=>r.name===s)>=0){const r=x.filter(S=>S.name!==s);t(r)}else{const r=[...x,{name:s,...H}];t(r)}},y=s=>{k(l=>{const r=new Set(l);return r.has(s)?r.delete(s):r.add(s),r})},w=s=>["tools","prompts","resources"].some(l=>{const r=s[l];return r==="all"||Array.isArray(r)&&r.length>0}),C=(s,l,r,S=!1)=>{const N=x.find(h=>h.name===s),z={...N?{...N}:{name:s,...pe},[l]:r};if(!w(z)){const h=x.filter(F=>F.name!==s);t(h),S||k(F=>{const I=new Set(F);return I.delete(s),I});return}if(N){t(x.map(h=>h.name===s?z:h));return}t([...x,z])},E=(s,l)=>{const r=`${s}${A}`;return l.startsWith(r)?l.slice(r.length):l},o=(s,l)=>l==="tools"?(s.tools||[]).filter(r=>r.enabled!==!1).map(r=>({key:r.name,value:E(s.name,r.name),description:r.description})):l==="prompts"?(s.prompts||[]).filter(r=>r.enabled!==!1).map(r=>({key:r.name,value:E(s.name,r.name),description:r.description})):(s.resources||[]).filter(r=>r.enabled!==!1).map(r=>({key:r.uri,value:r.uri,description:r.description})),c=(s,l,r)=>{const S=p.find(h=>h.name===s);if(!S)return;const N=o(S,l).map(h=>h.value),D=x.find(h=>h.name===s);if(!D){C(s,l,[r]);return}const z=D[l];if(z==="all"){const h=N.filter(F=>F!==r);C(s,l,h);return}if(Array.isArray(z)){if(z.includes(r)){C(s,l,z.filter(F=>F!==r));return}const h=[...z,r];C(s,l,h.length===N.length?"all":h);return}C(s,l,[r])},f=s=>{const l=x.find(r=>r.name===s);return!!(l&&w(l))},a=s=>{const l=x.find(r=>r.name===s);return l?["tools","prompts","resources"].some(r=>{const S=l[r];return Array.isArray(S)&&S.length>0}):!1},n=(s,l,r)=>{const S=x.find(D=>D.name===s);if(!S)return!1;const N=S[l];return N==="all"?!0:Array.isArray(N)?N.includes(r):!1},j=(s,l)=>{const r=x.find(D=>D.name===s.name);if(!r)return 0;const S=o(s,l),N=r[l];if(N==="all")return S.length;if(Array.isArray(N)){const D=new Set(S.map(z=>z.value));return N.filter(z=>D.has(z)).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"}],R=s=>T.map(({key:l})=>({key:l,count:j(s,l)})).filter(l=>l.count>0);return e.jsxs("div",{className:U("space-y-4",d),children:[e.jsx("div",{className:"space-y-3",children:p.map(s=>{const l=f(s.name),r=a(s.name),S=g.has(s.name),N=x.find(h=>h.name===s.name),D=R(s),z=T.filter(({key:h})=>o(s,h).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:()=>y(s.name),children:[e.jsxs("div",{className:"flex items-center space-x-3",onClick:h=>{h.stopPropagation(),v(s.name)},children:[e.jsx("input",{type:"checkbox",checked:l||r,onChange:()=>v(s.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:s.name})]}),e.jsxs("div",{className:"flex items-center space-x-3",children:[D.map(({key:h,count:F})=>e.jsxs("span",{className:"text-sm text-green-600 flex items-center gap-1",children:[h==="tools"?e.jsx(ee,{size:14}):h==="prompts"?e.jsx(se,{size:14}):e.jsx(te,{size:14})," ",F]},h)),z.length>0&&e.jsx("button",{type:"button",className:"p-1 text-gray-400 hover:text-gray-600 transition-colors",children:e.jsx("svg",{className:U("w-5 h-5 transition-transform",S&&"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"})})})]})]}),S&&z.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:z.map(({key:h,titleKey:F,countKey:I,allKey:L})=>{const $=o(s,h),K=j(s,h),O=(N==null?void 0:N[h])==="all"||K===$.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:u(F)}),e.jsxs("div",{className:"flex items-center gap-3",children:[N&&e.jsx("span",{className:"text-xs text-green-600",children:O?`(${u(L)} ${$.length}/${$.length})`:`(${u(I)} ${K}/${$.length})`}),e.jsx("button",{type:"button",onClick:()=>{C(s.name,h,O?[]:"all",!0)},className:"text-sm text-blue-600 hover:text-blue-800 transition-colors",children:u(O?"groups.selectNone":"groups.selectAll")})]})]}),e.jsx("div",{className:"grid grid-cols-1 gap-2 max-h-32 overflow-y-auto",children:$.map(G=>{const Z=n(s.name,h,G.value);return e.jsxs("label",{className:"flex items-center space-x-2 text-sm",children:[e.jsx("input",{type:"checkbox",checked:Z,onChange:()=>c(s.name,h,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)})})]},h)})})})]},s.name)})}),p.length===0&&e.jsx("p",{className:"text-gray-500 text-sm",children:u("groups.noServerOptions")})]})},xe=({onAdd:i,onCancel:m})=>{const{t}=P(),{createGroup:d}=q(),{allServers:u}=J(),[A,g]=b.useState([]),[k,x]=b.useState(null),[p,v]=b.useState(!1),[y,w]=b.useState({name:"",description:"",servers:[]});b.useEffect(()=>{g(u.filter(o=>o.enabled!==!1))},[u]);const C=o=>{const{name:c,value:f}=o.target;w(a=>({...a,[c]:f}))},E=async o=>{o.preventDefault(),v(!0),x(null);try{if(!y.name.trim()){x(t("groups.nameRequired")),v(!1);return}const c=await d(y.name,y.description,y.servers);if(!c||!c.success){x((c==null?void 0:c.message)||t("groups.createError")),v(!1);return}i()}catch(c){x(c instanceof Error?c.message:String(c)),v(!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:t("groups.addNew")}),k&&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:k})]}),e.jsxs("form",{onSubmit:E,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:[t("groups.name")," *"]}),e.jsx("input",{type:"text",id:"name",name:"name",value:y.name,onChange:C,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:t("groups.namePlaceholder"),required:!0})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-gray-700 text-sm font-bold mb-2",children:t("groups.configureCapabilities")}),e.jsx(Y,{servers:A,value:y.servers,onChange:o=>w(c=>({...c,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-2 p-5 pt-3 border-t border-[var(--hub-line-2)] flex-shrink-0",children:[e.jsx("button",{type:"button",onClick:m,className:"hub-btn",disabled:p,children:t("common.cancel")}),e.jsx("button",{type:"submit",className:"hub-btn primary",disabled:p,children:t(p?"common.submitting":"common.create")})]})]})]})})},he=({group:i,onEdit:m,onCancel:t})=>{const{t:d}=P(),{updateGroup:u}=q(),{allServers:A}=J(),[g,k]=b.useState([]),[x,p]=b.useState(null),[v,y]=b.useState(!1),[w,C]=b.useState({name:i.name,description:i.description||"",servers:i.servers||[]});b.useEffect(()=>{k(A.filter(c=>c.enabled!==!1))},[A]);const E=c=>{const{name:f,value:a}=c.target;C(n=>({...n,[f]:a}))},o=async c=>{c.preventDefault(),y(!0),p(null);try{if(!w.name.trim()){p(d("groups.nameRequired")),y(!1);return}const f=await u(i.id,{name:w.name,description:w.description,servers:w.servers});if(!f||!f.success){p((f==null?void 0:f.message)||d("groups.updateError")),y(!1);return}m()}catch(f){p(f instanceof Error?f.message:String(f)),y(!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:d("groups.edit")}),x&&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:x})]}),e.jsxs("form",{onSubmit:o,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:[d("groups.name")," *"]}),e.jsx("input",{type:"text",id:"name",name:"name",value:w.name,onChange:E,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:d("groups.namePlaceholder"),required:!0})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-gray-700 text-sm font-bold mb-2",children:d("groups.configureCapabilities")}),e.jsx(Y,{servers:g,value:w.servers,onChange:c=>C(f=>({...f,servers:c})),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-2 p-5 pt-3 border-t border-[var(--hub-line-2)] flex-shrink-0",children:[e.jsx("button",{type:"button",onClick:t,className:"hub-btn",disabled:v,children:d("common.cancel")}),e.jsx("button",{type:"submit",className:"hub-btn primary",disabled:v,children:d(v?"common.submitting":"common.save")})]})]})]})})},be=i=>i.map(m=>typeof m=="string"?m:m.name),ge=(i,m)=>{const t=i.servers.find(d=>typeof d=="string"?d===m:d.name===m);return t?typeof t=="string"?{name:t,tools:"all",prompts:"all",resources:"all"}:t:{name:m,tools:"all",prompts:"all",resources:"all"}},fe=async i=>{try{if(navigator.clipboard&&window.isSecureContext)return await navigator.clipboard.writeText(i),!0}catch{}try{const m=document.createElement("textarea");m.value=i,m.style.position="fixed",m.style.left="-9999px",document.body.appendChild(m),m.focus(),m.select();const t=document.execCommand("copy");return document.body.removeChild(m),t}catch{return!1}},ye=({group:i,servers:m,onEdit:t,onDelete:d})=>{var R;const{t:u}=P(),{showToast:A}=Q(),{installConfig:g,nameSeparator:k}=X(),x=((R=g==null?void 0:g.baseUrl)==null?void 0:R.replace(/\/+$/,""))||"",[p,v]=b.useState(!1),[y,w]=b.useState(!1),[C,E]=b.useState(!1),o=b.useRef(null);b.useEffect(()=>{const s=l=>{o.current&&!o.current.contains(l.target)&&E(!1)};return document.addEventListener("mousedown",s),()=>document.removeEventListener("mousedown",s)},[]);const c=async s=>{await fe(s)?(w(!0),E(!1),A(u("common.copySuccess")||"Copied","success"),setTimeout(()=>w(!1),1500)):A(u("common.copyFailed")||"Copy failed","error")},f=`${x}/mcp/${i.name}`,a=be(i.servers),n=m.filter(s=>a.includes(s.name)),j=s=>{const l=ge(i,s.name),r=`${s.name}${k}`,S=s.tools||[],N=s.prompts||[],D=s.resources||[],z=Array.isArray(l.tools)?S.filter(I=>{if(I.enabled===!1)return!1;const L=I.name.startsWith(r)?I.name.slice(r.length):I.name;return l.tools.includes(L)}).length:S.filter(I=>I.enabled!==!1).length,h=Array.isArray(l.prompts)?N.filter(I=>{if(I.enabled===!1)return!1;const L=I.name.startsWith(r)?I.name.slice(r.length):I.name;return l.prompts.includes(L)}).length:N.filter(I=>I.enabled!==!1).length,F=Array.isArray(l.resources)?D.filter(I=>I.enabled!==!1&&l.resources.includes(I.uri)).length:D.filter(I=>I.enabled!==!1).length;return{visibleTools:z,totalTools:S.length,visiblePrompts:h,totalPrompts:N.length,visibleResources:F,totalResources:D.length}},T=n.reduce((s,l)=>s+j(l).visibleTools,0);return e.jsxs("div",{className:"hub-card overflow-visible",children:[e.jsxs("div",{className:"flex items-start gap-3 px-4 py-3",style:{borderBottom:"1px solid var(--hub-line-2)"},children:[e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{style:{fontSize:15,fontWeight:600,letterSpacing:"-0.015em"},children:i.name}),e.jsx("span",{className:"hub-mono",style:{fontSize:11,color:"var(--hub-ink-3)",padding:"0 6px",border:"1px solid var(--hub-line)",borderRadius:4,height:18,display:"inline-flex",alignItems:"center"},title:i.id,children:i.id})]}),i.description&&e.jsx("div",{style:{fontSize:12.5,color:"var(--hub-ink-3)",marginTop:2},children:i.description})]}),e.jsxs("div",{className:"flex items-center gap-1",ref:o,children:[e.jsxs("div",{className:"relative",children:[e.jsx("button",{onClick:()=>E(s=>!s),className:"hub-icon-btn sm",title:u("common.copy"),children:y?e.jsx(re,{size:13,className:"text-[var(--hub-ok)]"}):e.jsx(B,{size:13})}),C&&e.jsxs("div",{className:"absolute top-full right-0 mt-1 z-20 hub-card",style:{minWidth:160,padding:4},children:[e.jsxs("button",{onClick:()=>c(i.id),className:"flex items-center gap-2 w-full px-2.5 py-1.5 text-[13px] rounded-md hover:bg-[var(--hub-surface-hover)] text-left",children:[e.jsx(B,{size:12})," ",u("common.copyId")]}),e.jsxs("button",{onClick:()=>c(f),className:"flex items-center gap-2 w-full px-2.5 py-1.5 text-[13px] rounded-md hover:bg-[var(--hub-surface-hover)] text-left",children:[e.jsx(ae,{size:12})," ",u("common.copyUrl")]}),e.jsxs("button",{onClick:()=>c(JSON.stringify({mcpServers:{mcphub:{url:f,headers:{Authorization:"Bearer <your-access-token>"}}}},null,2)),className:"flex items-center gap-2 w-full px-2.5 py-1.5 text-[13px] rounded-md hover:bg-[var(--hub-surface-hover)] text-left",children:[e.jsx(le,{size:12})," ",u("common.copyJson")]})]})]}),e.jsx("button",{onClick:()=>t(i),className:"hub-icon-btn sm",title:u("groups.edit"),children:e.jsx(ne,{size:13})}),e.jsx("button",{onClick:()=>v(!0),className:"hub-icon-btn sm",title:u("groups.delete"),style:{color:"var(--hub-ink-3)"},children:e.jsx(oe,{size:13})})]})]}),e.jsxs("div",{className:"grid items-center gap-3 px-4 py-3",style:{gridTemplateColumns:"1fr 80px 1fr"},children:[e.jsx("div",{className:"flex flex-col gap-1.5",children:n.length===0?e.jsx("div",{style:{fontSize:12,color:"var(--hub-ink-3)"},children:u("groups.noServers")}):n.map(s=>{const l=j(s);return e.jsxs("div",{className:"flex items-center gap-2.5 px-2.5 py-1.5 rounded-md",style:{background:"var(--hub-bg-2)",border:"1px solid var(--hub-line-2)"},children:[e.jsx("span",{className:"inline-block flex-shrink-0",style:{width:6,height:6,borderRadius:50,background:s.status==="connected"?"var(--hub-ok)":s.status==="connecting"?"var(--hub-warn)":"var(--hub-err)"}}),e.jsx("span",{className:"hub-mono truncate flex-1",style:{fontSize:12.5},children:s.name}),e.jsxs("span",{className:"hub-mono hub-num flex-shrink-0",style:{fontSize:11,color:"var(--hub-ink-3)"},children:[l.visibleTools,"/",l.totalTools," tools"]})]},s.name)})}),e.jsxs("svg",{width:"80",height:"80",viewBox:"0 0 80 80",className:"self-center",children:[n.length===0?e.jsx("path",{d:"M0,40 C30,40 50,40 80,40",stroke:"var(--hub-line)",strokeWidth:"1",fill:"none",strokeDasharray:"3 3"}):n.map((s,l)=>{const r=12+60/Math.max(n.length,1)*(l+.5);return e.jsx("path",{d:`M0,${r} C 30,${r} 50,40 80,40`,stroke:"var(--hub-line)",strokeWidth:"1",fill:"none",strokeDasharray:"3 3"},l)}),e.jsx("circle",{cx:"80",cy:"40",r:"4",fill:"var(--hub-ink)"})]}),e.jsxs("div",{className:"px-3 py-2.5 rounded-md",style:{border:"1px solid var(--hub-line)",background:"var(--hub-bg-2)"},children:[e.jsx("div",{className:"hub-sect",style:{marginBottom:4},children:"endpoint"}),e.jsxs("div",{className:"hub-mono break-all",style:{fontSize:12,color:"var(--hub-ink-2)",lineHeight:1.4},children:[e.jsx("span",{style:{color:"var(--hub-ink-3)"},children:"/mcp/"}),e.jsx("b",{style:{color:"var(--hub-ink)",fontWeight:600},children:i.name})]}),e.jsx("div",{className:"flex gap-1.5 mt-2",children:e.jsxs("button",{className:"hub-btn sm flex-1 justify-center",onClick:()=>c(f),children:[e.jsx(B,{size:11})," ",u("common.copy")]})})]})]}),e.jsxs("div",{className:"flex justify-between items-center px-4 py-2",style:{borderTop:"1px solid var(--hub-line-2)",background:"var(--hub-bg-2)",fontSize:12,color:"var(--hub-ink-3)"},children:[e.jsxs("div",{className:"hub-mono",children:[e.jsx("span",{style:{color:"var(--hub-ink-2)"},children:n.length})," ",u("nav.servers").toLowerCase()," ·"," ",e.jsx("span",{style:{color:"var(--hub-ink-2)"},children:T})," ",u("server.tools").toLowerCase()]}),e.jsxs("button",{className:"hub-btn ghost sm",style:{color:"var(--hub-ink-3)"},onClick:()=>t(i),children:[u("groups.configureTools")||u("groups.edit"),e.jsx(ie,{size:11,style:{transform:"rotate(-90deg)"}})]})]}),e.jsx(ue,{isOpen:p,onClose:()=>v(!1),onConfirm:()=>{d(i.id),v(!1)},serverName:i.name,isGroup:!0})]})},je=({onSuccess:i,onCancel:m})=>{const{t}=P(),[d,u]=b.useState(""),[A,g]=b.useState(null),[k,x]=b.useState(!1),[p,v]=b.useState(null),y=`{
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.`,w=a=>{try{const n=JSON.parse(a.trim());if(!n.groups||!Array.isArray(n.groups))return g(t("groupImport.invalidFormat")),null;for(const j of n.groups)if(!j.name||typeof j.name!="string")return g(t("groupImport.missingName")),null;return n}catch{return g(t("groupImport.parseError")),null}},C=()=>{g(null);const a=w(d);a&&v(a.groups)},E=async()=>{if(p){x(!0),g(null);try{const a=await M("/groups/batch",{groups:p});if(a.success){const{successCount:n,failureCount:j,results:T}=a;if(j>0){const R=T.filter(s=>!s.success).map(s=>`${s.name}: ${s.message||t("groupImport.addFailed")}`);g(t("groupImport.partialSuccess",{count:n,total:p.length})+`
31
- `+R.join(`
32
- `))}n>0&&i()}else g(a.message||t("groupImport.importFailed"))}catch(a){console.error("Import error:",a),g(t("groupImport.importFailed"))}finally{x(!1)}}},o=a=>e.jsx("span",{className:"text-gray-500 ml-2",children:t(`groups.${a}`)}),c=(a,n)=>{if(!n||n==="all")return null;const j=Array.isArray(n)?n.join(", "):n;return e.jsx("span",{className:"text-gray-500 ml-2",children:t(`groups.${a}`,{items:j})})},f=a=>!a||a.length===0?e.jsx("span",{className:"text-gray-500",children:t("groups.noServers")}):e.jsx("div",{className:"space-y-1",children:a.map((n,j)=>typeof n=="string"?e.jsxs("div",{className:"text-sm",children:["• ",n]},j):e.jsxs("div",{className:"text-sm",children:["• ",n.name,n.tools&&n.tools!=="all"&&e.jsxs("span",{className:"text-gray-500 ml-2",children:["(",Array.isArray(n.tools)?n.tools.join(", "):n.tools,")"]}),n.tools==="all"&&o("previewAllTools"),c("previewPrompts",n.prompts),n.prompts==="all"&&o("previewAllPrompts"),c("previewResources",n.resources),n.resources==="all"&&o("previewAllResources")]},j))});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:t("groupImport.title")}),e.jsx("button",{onClick:m,className:"text-gray-500 hover:text-gray-700",children:"✕"})]}),A&&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:A})}),p?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:t("groupImport.previewTitle")}),e.jsx("div",{className:"space-y-3",children:p.map((a,n)=>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:a.name}),a.description&&e.jsx("p",{className:"text-sm text-gray-600 mt-1",children:a.description}),e.jsxs("div",{className:"mt-2 text-sm text-gray-600",children:[e.jsxs("strong",{children:[t("groups.servers"),":"]}),e.jsx("div",{className:"mt-1",children:f(a.servers)})]})]})})},n))})]}),e.jsxs("div",{className:"flex justify-end space-x-4",children:[e.jsx("button",{onClick:()=>v(null),disabled:k,className:"hub-btn",children:t("common.back")}),e.jsx("button",{onClick:E,disabled:k,className:"hub-btn primary",children:k?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"})]}),t("groupImport.importing")]}):t("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:t("groupImport.inputLabel")}),e.jsx("textarea",{value:d,onChange:a=>u(a.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:y}),e.jsx("p",{className:"text-xs text-gray-500 mt-2",children:t("groupImport.inputHelp")})]}),e.jsxs("div",{className:"flex justify-end space-x-4",children:[e.jsx("button",{onClick:m,className:"hub-btn",children:t("common.cancel")}),e.jsx("button",{onClick:C,disabled:!d.trim(),className:"hub-btn primary",children:t("groupImport.preview")})]})]})]})})},ve=({groups:i,onCancel:m})=>{const{t}=P(),[d,u]=b.useState(""),[A,g]=b.useState(""),[k,x]=b.useState([]),[p,v]=b.useState(!1),[y,w]=b.useState(!1),[C,E]=b.useState(null),o=a=>{x(n=>n.includes(a)?n.filter(j=>j!==a):[...n,a])},c=()=>{k.length===i.length?x([]):x(i.map(a=>a.id))},f=async()=>{if(!d.trim()){E(t("template.nameRequired"));return}w(!0),E(null);try{const a=await M("/templates/export",{name:d.trim(),description:A.trim()||void 0,groupIds:k.length>0?k:void 0,includeDisabledServers:p});if(a.success&&a.data){const n=a.data,j=new Blob([JSON.stringify(n,null,2)],{type:"application/json"}),T=URL.createObjectURL(j),R=document.createElement("a");R.href=T,R.download=`${n.name.replace(/[^a-zA-Z0-9-_]/g,"_")}.mcphub-template.json`,document.body.appendChild(R),R.click(),document.body.removeChild(R),URL.revokeObjectURL(T),m()}else E(a.message||t("template.exportFailed"))}catch(a){console.error("Export error:",a),E(t("template.exportFailed"))}finally{w(!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:t("template.exportTitle")}),e.jsx("button",{onClick:m,className:"text-gray-500 hover:text-gray-700",children:"✕"})]}),C&&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:C})}),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:[t("template.name")," *"]}),e.jsx("input",{type:"text",value:d,onChange:a=>u(a.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:t("template.namePlaceholder")})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-sm font-medium text-gray-700 mb-1",children:t("template.description")}),e.jsx("input",{type:"text",value:A,onChange:a=>g(a.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:t("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:t("template.selectGroups")}),e.jsx("button",{onClick:c,className:"text-sm text-blue-600 hover:text-blue-800",children:k.length===i.length?t("template.deselectAll"):t("template.selectAll")})]}),e.jsx("p",{className:"text-xs text-gray-500 mb-2",children:t("template.selectGroupsHelp")}),e.jsx("div",{className:"border border-gray-200 dark:border-gray-700 rounded-md max-h-48 overflow-y-auto",children:i.map(a=>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:k.includes(a.id),onChange:()=>o(a.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:a.name}),a.description&&e.jsx("span",{className:"text-xs text-gray-500 ml-2",children:a.description})]})]},a.id))})]}),e.jsxs("label",{className:"flex items-center space-x-2 cursor-pointer",children:[e.jsx("input",{type:"checkbox",checked:p,onChange:a=>v(a.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:t("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:t("template.exportNote")})}),e.jsxs("div",{className:"flex justify-end space-x-4 mt-6",children:[e.jsx("button",{onClick:m,className:"hub-btn",children:t("common.cancel")}),e.jsx("button",{onClick:f,disabled:y||!d.trim(),className:"hub-btn primary",children:t(y?"template.exporting":"template.export")})]})]})})},Ne=({onSuccess:i,onCancel:m})=>{const{t}=P(),[d,u]=b.useState(null),[A,g]=b.useState(null),[k,x]=b.useState(!1),[p,v]=b.useState(null),y=b.useRef(null),w=o=>{var a;g(null),u(null),v(null);const c=(a=o.target.files)==null?void 0:a[0];if(!c)return;const f=new FileReader;f.onload=n=>{var j;try{const T=JSON.parse((j=n.target)==null?void 0:j.result);if(!T.version||!T.name||!T.servers||!T.groups){g(t("template.invalidFormat"));return}u(T)}catch{g(t("template.parseError"))}},f.readAsText(c)},C=o=>{if(g(null),u(null),v(null),!!o.trim())try{const c=JSON.parse(o.trim());if(!c.version||!c.name||!c.servers||!c.groups){g(t("template.invalidFormat"));return}u(c)}catch{g(t("template.parseError"))}},E=async()=>{if(d){x(!0),g(null);try{const o=await M("/templates/import",d);o.data?(v(o.data),o.data.success&&i()):g(o.message||t("template.importFailed"))}catch(o){console.error("Import error:",o),g(t("template.importFailed"))}finally{x(!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:t("template.importTitle")}),e.jsx("button",{onClick:m,className:"text-gray-500 hover:text-gray-700",children:"✕"})]}),A&&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:A})}),p&&e.jsxs("div",{className:`mb-4 p-4 rounded border-l-4 ${p.success?"bg-green-50 border-green-500":"bg-yellow-50 border-yellow-500"}`,children:[e.jsx("p",{className:p.success?"text-green-700":"text-yellow-700",children:t("template.importResult",{serversCreated:p.serversCreated,serversSkipped:p.serversSkipped,groupsCreated:p.groupsCreated,groupsSkipped:p.groupsSkipped})}),p.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:t("template.envVarsNeeded")}),e.jsx("ul",{className:"mt-1 text-sm text-orange-700",children:p.requiredEnvVars.map(o=>e.jsx("li",{className:"font-mono",children:o},o))})]})]}),d?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:d.name}),d.description&&e.jsx("p",{className:"text-sm text-gray-600 mt-1",children:d.description}),e.jsxs("p",{className:"text-xs text-gray-500 mt-1",children:[t("template.version"),": ",d.version," | ",t("template.createdAt"),":"," ",new Date(d.createdAt).toLocaleDateString()]})]}),e.jsxs("div",{children:[e.jsxs("h4",{className:"text-sm font-medium text-gray-700 mb-2",children:[t("template.servers")," (",Object.keys(d.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(d.servers).map(([o,c])=>e.jsxs("div",{className:"px-3 py-2",children:[e.jsx("span",{className:"text-sm font-medium text-gray-900",children:o}),e.jsx("span",{className:"text-xs text-gray-500 ml-2",children:c.type||"stdio"})]},o))})]}),e.jsxs("div",{children:[e.jsxs("h4",{className:"text-sm font-medium text-gray-700 mb-2",children:[t("template.groups")," (",d.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:d.groups.map((o,c)=>e.jsxs("div",{className:"px-3 py-2",children:[e.jsx("span",{className:"text-sm font-medium text-gray-900",children:o.name}),o.description&&e.jsx("span",{className:"text-xs text-gray-500 ml-2",children:o.description}),e.jsxs("div",{className:"text-xs text-gray-500 mt-1",children:[o.servers.length," ",t("template.serversInGroup")]})]},c))})]}),d.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:t("template.envVarsNeeded")}),e.jsx("ul",{className:"mt-1 text-sm text-orange-700 font-mono",children:d.requiredEnvVars.map(o=>e.jsx("li",{children:o},o))})]})]}),e.jsxs("div",{className:"flex justify-end space-x-4 mt-6",children:[e.jsx("button",{onClick:()=>{u(null),v(null),y.current&&(y.current.value="")},className:"hub-btn",children:t("common.back")}),e.jsx("button",{onClick:E,disabled:k,className:"hub-btn primary",children:t(k?"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:t("template.uploadFile")})}),e.jsx("div",{className:"flex items-center space-x-4",children:e.jsx("input",{ref:y,type:"file",accept:".json",onChange:w,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:t("template.or")})})]}),e.jsxs("div",{className:"mb-4",children:[e.jsx("label",{className:"block text-sm font-medium text-gray-700 mb-2",children:t("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:t("template.pastePlaceholder"),onChange:o=>C(o.target.value)})]}),e.jsx("div",{className:"flex justify-end space-x-4",children:e.jsx("button",{onClick:m,className:"hub-btn",children:t("common.cancel")})})]})]})})},ze=()=>{const{t:i}=P(),{groups:m,loading:t,error:d,setError:u,deleteGroup:A,triggerRefresh:g}=q(),{allServers:k}=J({refreshOnMount:!0}),[x,p]=b.useState(null),[v,y]=b.useState(!1),[w,C]=b.useState(!1),[E,o]=b.useState(!1),[c,f]=b.useState(!1),a=async n=>{const j=await A(n);(!j||!j.success)&&u((j==null?void 0:j.message)||i("groups.deleteError"))};return e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-end justify-between gap-4 mb-6",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"hub-h1",children:i("pages.groups.title")}),e.jsxs("p",{className:"hub-sub",children:[e.jsx("span",{className:"hub-num",children:m.length})," ",i("nav.groups").toLowerCase()]})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs("button",{className:"hub-btn",onClick:()=>C(!0),children:[e.jsx(V,{size:13})," ",i("groupImport.button")]}),e.jsxs("button",{className:"hub-btn",onClick:()=>o(!0),children:[e.jsx(ce,{size:13})," ",i("template.exportButton")]}),e.jsxs("button",{className:"hub-btn",onClick:()=>f(!0),children:[e.jsx(V,{size:13})," ",i("template.importButton")]}),e.jsxs("button",{className:"hub-btn primary",onClick:()=>y(!0),children:[e.jsx(_,{size:13})," ",i("groups.add")]})]})]}),d&&e.jsxs("div",{className:"hub-card flex items-center justify-between gap-3 mb-4",style:{padding:"10px 14px",borderColor:"oklch(0.85 0.1 25)",background:"oklch(0.97 0.03 25)",color:"oklch(0.4 0.18 25)"},children:[e.jsxs("div",{className:"flex items-center gap-2 min-w-0",children:[e.jsx(de,{size:14,className:"flex-shrink-0"}),e.jsx("span",{className:"truncate text-[13px]",children:d})]}),e.jsx("button",{className:"hub-icon-btn sm",onClick:()=>u(null),children:e.jsx(me,{size:13})})]}),t?e.jsx("div",{className:"hub-card p-6 text-center",style:{color:"var(--hub-ink-3)"},children:i("app.loading")}):m.length===0?e.jsx("div",{className:"hub-card p-10 text-center",style:{color:"var(--hub-ink-3)"},children:i("groups.noGroups")}):e.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-2 gap-3.5",children:[m.map(n=>e.jsx(ye,{group:n,servers:k,onEdit:p,onDelete:a},n.id)),e.jsx("button",{onClick:()=>y(!0),className:"flex items-center justify-center text-[var(--hub-ink-3)] hover:text-[var(--hub-ink-2)] transition-colors",style:{border:"1px dashed var(--hub-line)",borderRadius:10,minHeight:200,background:"transparent",cursor:"pointer"},children:e.jsxs("div",{className:"text-center",children:[e.jsx("div",{className:"grid place-items-center mx-auto mb-2",style:{width:36,height:36,borderRadius:10,border:"1px solid var(--hub-line)"},children:e.jsx(_,{size:16})}),e.jsx("div",{style:{fontSize:13,fontWeight:500,color:"var(--hub-ink-2)"},children:i("groups.add")}),e.jsx("div",{style:{fontSize:12,marginTop:2},children:i("groups.addNew")})]})})]}),v&&e.jsx(xe,{onAdd:()=>{y(!1),g()},onCancel:()=>y(!1)}),w&&e.jsx(je,{onSuccess:()=>{C(!1),g()},onCancel:()=>C(!1)}),x&&e.jsx(he,{group:x,onEdit:()=>{p(null),g()},onCancel:()=>p(null)}),E&&e.jsx(ve,{groups:m,onCancel:()=>o(!1)}),c&&e.jsx(Ne,{onSuccess:()=>{f(!1),g()},onCancel:()=>f(!1)})]})};export{ze as default};
33
- //# sourceMappingURL=GroupsPage-BhNYw74w.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"GroupsPage-BhNYw74w.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-2 p-5 pt-3 border-t border-[var(--hub-line-2)] flex-shrink-0\">\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"hub-btn\"\n disabled={isSubmitting}\n >\n {t('common.cancel')}\n </button>\n <button\n type=\"submit\"\n className=\"hub-btn primary\"\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-2 p-5 pt-3 border-t border-[var(--hub-line-2)] flex-shrink-0\">\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"hub-btn\"\n disabled={isSubmitting}\n >\n {t('common.cancel')}\n </button>\n <button\n type=\"submit\"\n className=\"hub-btn primary\"\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 { Edit3, Trash2, Copy, Check, Link as LinkIcon, FileCode, ChevronDown } from 'lucide-react';\nimport { Group, Server, IGroupServerConfig } from '@/types';\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 getServerNames = (servers: string[] | IGroupServerConfig[]): string[] =>\n servers.map((server) => (typeof server === 'string' ? server : server.name));\n\nconst getServerConfig = (\n group: Group,\n serverName: string,\n): IGroupServerConfig => {\n const server = group.servers.find((s) =>\n typeof s === 'string' ? s === serverName : s.name === serverName,\n );\n if (!server) return { name: serverName, tools: 'all', prompts: 'all', resources: 'all' };\n if (typeof server === 'string') {\n return { name: server, tools: 'all', prompts: 'all', resources: 'all' };\n }\n return server;\n};\n\nconst copyText = async (value: string): Promise<boolean> => {\n try {\n if (navigator.clipboard && window.isSecureContext) {\n await navigator.clipboard.writeText(value);\n return true;\n }\n } catch {\n /* noop */\n }\n try {\n const el = document.createElement('textarea');\n el.value = value;\n el.style.position = 'fixed';\n el.style.left = '-9999px';\n document.body.appendChild(el);\n el.focus();\n el.select();\n const ok = document.execCommand('copy');\n document.body.removeChild(el);\n return ok;\n } catch {\n return false;\n }\n};\n\nconst GroupCard = ({ group, servers, onEdit, onDelete }: GroupCardProps) => {\n const { t } = useTranslation();\n const { showToast } = useToast();\n const { installConfig, nameSeparator } = useSettingsData();\n const baseUrl = installConfig?.baseUrl?.replace(/\\/+$/, '') || '';\n\n const [showDeleteDialog, setShowDeleteDialog] = useState(false);\n const [copied, setCopied] = useState(false);\n const [showCopyDropdown, setShowCopyDropdown] = useState(false);\n const dropdownRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const handle = (e: MouseEvent) => {\n if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {\n setShowCopyDropdown(false);\n }\n };\n document.addEventListener('mousedown', handle);\n return () => document.removeEventListener('mousedown', handle);\n }, []);\n\n const doCopy = async (text: string) => {\n const ok = await copyText(text);\n if (ok) {\n setCopied(true);\n setShowCopyDropdown(false);\n showToast(t('common.copySuccess') || 'Copied', 'success');\n setTimeout(() => setCopied(false), 1500);\n } else {\n showToast(t('common.copyFailed') || 'Copy failed', 'error');\n }\n };\n\n const groupEndpoint = `${baseUrl}/mcp/${group.name}`;\n\n const serverNames = getServerNames(group.servers);\n const groupServers = servers.filter((s) => serverNames.includes(s.name));\n\n const tally = (server: Server) => {\n const cfg = getServerConfig(group, server.name);\n const prefix = `${server.name}${nameSeparator}`;\n const allTools = server.tools || [];\n const allPrompts = server.prompts || [];\n const allResources = server.resources || [];\n\n const visibleTools =\n Array.isArray(cfg.tools)\n ? allTools.filter((t) => {\n if (t.enabled === false) return false;\n const short = t.name.startsWith(prefix) ? t.name.slice(prefix.length) : t.name;\n return cfg.tools!.includes(short);\n }).length\n : allTools.filter((t) => t.enabled !== false).length;\n const visiblePrompts =\n Array.isArray(cfg.prompts)\n ? allPrompts.filter((p) => {\n if (p.enabled === false) return false;\n const short = p.name.startsWith(prefix) ? p.name.slice(prefix.length) : p.name;\n return cfg.prompts!.includes(short);\n }).length\n : allPrompts.filter((p) => p.enabled !== false).length;\n const visibleResources =\n Array.isArray(cfg.resources)\n ? allResources.filter((r) => r.enabled !== false && cfg.resources!.includes(r.uri)).length\n : allResources.filter((r) => r.enabled !== false).length;\n\n return {\n visibleTools,\n totalTools: allTools.length,\n visiblePrompts,\n totalPrompts: allPrompts.length,\n visibleResources,\n totalResources: allResources.length,\n };\n };\n\n const totalVisibleTools = groupServers.reduce(\n (acc, s) => acc + tally(s).visibleTools,\n 0,\n );\n\n return (\n <div className=\"hub-card overflow-visible\">\n {/* Header */}\n <div\n className=\"flex items-start gap-3 px-4 py-3\"\n style={{ borderBottom: '1px solid var(--hub-line-2)' }}\n >\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2\">\n <span style={{ fontSize: 15, fontWeight: 600, letterSpacing: '-0.015em' }}>\n {group.name}\n </span>\n <span\n className=\"hub-mono\"\n style={{\n fontSize: 11,\n color: 'var(--hub-ink-3)',\n padding: '0 6px',\n border: '1px solid var(--hub-line)',\n borderRadius: 4,\n height: 18,\n display: 'inline-flex',\n alignItems: 'center',\n }}\n title={group.id}\n >\n {group.id}\n </span>\n </div>\n {group.description && (\n <div style={{ fontSize: 12.5, color: 'var(--hub-ink-3)', marginTop: 2 }}>\n {group.description}\n </div>\n )}\n </div>\n <div className=\"flex items-center gap-1\" ref={dropdownRef}>\n <div className=\"relative\">\n <button\n onClick={() => setShowCopyDropdown((v) => !v)}\n className=\"hub-icon-btn sm\"\n title={t('common.copy')}\n >\n {copied ? (\n <Check size={13} className=\"text-[var(--hub-ok)]\" />\n ) : (\n <Copy size={13} />\n )}\n </button>\n {showCopyDropdown && (\n <div\n className=\"absolute top-full right-0 mt-1 z-20 hub-card\"\n style={{ minWidth: 160, padding: 4 }}\n >\n <button\n onClick={() => doCopy(group.id)}\n className=\"flex items-center gap-2 w-full px-2.5 py-1.5 text-[13px] rounded-md hover:bg-[var(--hub-surface-hover)] text-left\"\n >\n <Copy size={12} /> {t('common.copyId')}\n </button>\n <button\n onClick={() => doCopy(groupEndpoint)}\n className=\"flex items-center gap-2 w-full px-2.5 py-1.5 text-[13px] rounded-md hover:bg-[var(--hub-surface-hover)] text-left\"\n >\n <LinkIcon size={12} /> {t('common.copyUrl')}\n </button>\n <button\n onClick={() =>\n doCopy(\n JSON.stringify(\n {\n mcpServers: {\n mcphub: {\n url: groupEndpoint,\n headers: { Authorization: 'Bearer <your-access-token>' },\n },\n },\n },\n null,\n 2,\n ),\n )\n }\n className=\"flex items-center gap-2 w-full px-2.5 py-1.5 text-[13px] rounded-md hover:bg-[var(--hub-surface-hover)] text-left\"\n >\n <FileCode size={12} /> {t('common.copyJson')}\n </button>\n </div>\n )}\n </div>\n <button\n onClick={() => onEdit(group)}\n className=\"hub-icon-btn sm\"\n title={t('groups.edit')}\n >\n <Edit3 size={13} />\n </button>\n <button\n onClick={() => setShowDeleteDialog(true)}\n className=\"hub-icon-btn sm\"\n title={t('groups.delete')}\n style={{ color: 'var(--hub-ink-3)' }}\n >\n <Trash2 size={13} />\n </button>\n </div>\n </div>\n\n {/* Routing diagram */}\n <div\n className=\"grid items-center gap-3 px-4 py-3\"\n style={{ gridTemplateColumns: '1fr 80px 1fr' }}\n >\n {/* Servers */}\n <div className=\"flex flex-col gap-1.5\">\n {groupServers.length === 0 ? (\n <div style={{ fontSize: 12, color: 'var(--hub-ink-3)' }}>{t('groups.noServers')}</div>\n ) : (\n groupServers.map((s) => {\n const tn = tally(s);\n return (\n <div\n key={s.name}\n className=\"flex items-center gap-2.5 px-2.5 py-1.5 rounded-md\"\n style={{\n background: 'var(--hub-bg-2)',\n border: '1px solid var(--hub-line-2)',\n }}\n >\n <span\n className=\"inline-block flex-shrink-0\"\n style={{\n width: 6,\n height: 6,\n borderRadius: 50,\n background:\n s.status === 'connected'\n ? 'var(--hub-ok)'\n : s.status === 'connecting'\n ? 'var(--hub-warn)'\n : 'var(--hub-err)',\n }}\n />\n <span className=\"hub-mono truncate flex-1\" style={{ fontSize: 12.5 }}>\n {s.name}\n </span>\n <span\n className=\"hub-mono hub-num flex-shrink-0\"\n style={{ fontSize: 11, color: 'var(--hub-ink-3)' }}\n >\n {tn.visibleTools}/{tn.totalTools} tools\n </span>\n </div>\n );\n })\n )}\n </div>\n\n {/* Flow */}\n <svg width=\"80\" height=\"80\" viewBox=\"0 0 80 80\" className=\"self-center\">\n {groupServers.length === 0 ? (\n <path d=\"M0,40 C30,40 50,40 80,40\" stroke=\"var(--hub-line)\" strokeWidth=\"1\" fill=\"none\" strokeDasharray=\"3 3\" />\n ) : (\n groupServers.map((_, i) => {\n const y1 = 12 + (60 / Math.max(groupServers.length, 1)) * (i + 0.5);\n return (\n <path\n key={i}\n d={`M0,${y1} C 30,${y1} 50,40 80,40`}\n stroke=\"var(--hub-line)\"\n strokeWidth=\"1\"\n fill=\"none\"\n strokeDasharray=\"3 3\"\n />\n );\n })\n )}\n <circle cx=\"80\" cy=\"40\" r=\"4\" fill=\"var(--hub-ink)\" />\n </svg>\n\n {/* Endpoint */}\n <div\n className=\"px-3 py-2.5 rounded-md\"\n style={{\n border: '1px solid var(--hub-line)',\n background: 'var(--hub-bg-2)',\n }}\n >\n <div\n className=\"hub-sect\"\n style={{ marginBottom: 4 }}\n >\n endpoint\n </div>\n <div\n className=\"hub-mono break-all\"\n style={{ fontSize: 12, color: 'var(--hub-ink-2)', lineHeight: 1.4 }}\n >\n <span style={{ color: 'var(--hub-ink-3)' }}>/mcp/</span>\n <b style={{ color: 'var(--hub-ink)', fontWeight: 600 }}>{group.name}</b>\n </div>\n <div className=\"flex gap-1.5 mt-2\">\n <button\n className=\"hub-btn sm flex-1 justify-center\"\n onClick={() => doCopy(groupEndpoint)}\n >\n <Copy size={11} /> {t('common.copy')}\n </button>\n </div>\n </div>\n </div>\n\n {/* Footer */}\n <div\n className=\"flex justify-between items-center px-4 py-2\"\n style={{\n borderTop: '1px solid var(--hub-line-2)',\n background: 'var(--hub-bg-2)',\n fontSize: 12,\n color: 'var(--hub-ink-3)',\n }}\n >\n <div className=\"hub-mono\">\n <span style={{ color: 'var(--hub-ink-2)' }}>{groupServers.length}</span>{' '}\n {t('nav.servers').toLowerCase()} ·{' '}\n <span style={{ color: 'var(--hub-ink-2)' }}>{totalVisibleTools}</span>{' '}\n {t('server.tools').toLowerCase()}\n </div>\n <button\n className=\"hub-btn ghost sm\"\n style={{ color: 'var(--hub-ink-3)' }}\n onClick={() => onEdit(group)}\n >\n {t('groups.configureTools') || t('groups.edit')}\n <ChevronDown size={11} style={{ transform: 'rotate(-90deg)' }} />\n </button>\n </div>\n\n <DeleteDialog\n isOpen={showDeleteDialog}\n onClose={() => setShowDeleteDialog(false)}\n onConfirm={() => {\n onDelete(group.id);\n setShowDeleteDialog(false);\n }}\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=\"hub-btn\"\n >\n {t('common.cancel')}\n </button>\n <button\n onClick={handlePreview}\n disabled={!jsonInput.trim()}\n className=\"hub-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=\"hub-btn\"\n >\n {t('common.back')}\n </button>\n <button\n onClick={handleImport}\n disabled={isImporting}\n className=\"hub-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=\"hub-btn\"\n >\n {t('common.cancel')}\n </button>\n <button\n onClick={handleExport}\n disabled={isExporting || !name.trim()}\n className=\"hub-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=\"hub-btn\"\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=\"hub-btn\"\n >\n {t('common.back')}\n </button>\n <button\n onClick={handleImport}\n disabled={isImporting}\n className=\"hub-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 { Plus, Download, Upload, AlertCircle, X } from 'lucide-react';\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 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 return (\n <div>\n <div className=\"flex items-end justify-between gap-4 mb-6\">\n <div>\n <h1 className=\"hub-h1\">{t('pages.groups.title')}</h1>\n <p className=\"hub-sub\">\n <span className=\"hub-num\">{groups.length}</span> {t('nav.groups').toLowerCase()}\n </p>\n </div>\n <div className=\"flex gap-2\">\n <button className=\"hub-btn\" onClick={() => setShowImportForm(true)}>\n <Upload size={13} /> {t('groupImport.button')}\n </button>\n <button className=\"hub-btn\" onClick={() => setShowTemplateExport(true)}>\n <Download size={13} /> {t('template.exportButton')}\n </button>\n <button className=\"hub-btn\" onClick={() => setShowTemplateImport(true)}>\n <Upload size={13} /> {t('template.importButton')}\n </button>\n <button className=\"hub-btn primary\" onClick={() => setShowAddForm(true)}>\n <Plus size={13} /> {t('groups.add')}\n </button>\n </div>\n </div>\n\n {groupError && (\n <div\n className=\"hub-card flex items-center justify-between gap-3 mb-4\"\n style={{\n padding: '10px 14px',\n borderColor: 'oklch(0.85 0.1 25)',\n background: 'oklch(0.97 0.03 25)',\n color: 'oklch(0.4 0.18 25)',\n }}\n >\n <div className=\"flex items-center gap-2 min-w-0\">\n <AlertCircle size={14} className=\"flex-shrink-0\" />\n <span className=\"truncate text-[13px]\">{groupError}</span>\n </div>\n <button className=\"hub-icon-btn sm\" onClick={() => setGroupError(null)}>\n <X size={13} />\n </button>\n </div>\n )}\n\n {groupsLoading ? (\n <div className=\"hub-card p-6 text-center\" style={{ color: 'var(--hub-ink-3)' }}>\n {t('app.loading')}\n </div>\n ) : groups.length === 0 ? (\n <div className=\"hub-card p-10 text-center\" style={{ color: 'var(--hub-ink-3)' }}>\n {t('groups.noGroups')}\n </div>\n ) : (\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-3.5\">\n {groups.map((group) => (\n <GroupCard\n key={group.id}\n group={group}\n servers={allServers}\n onEdit={setEditingGroup}\n onDelete={handleDeleteGroup}\n />\n ))}\n <button\n onClick={() => setShowAddForm(true)}\n className=\"flex items-center justify-center text-[var(--hub-ink-3)] hover:text-[var(--hub-ink-2)] transition-colors\"\n style={{\n border: '1px dashed var(--hub-line)',\n borderRadius: 10,\n minHeight: 200,\n background: 'transparent',\n cursor: 'pointer',\n }}\n >\n <div className=\"text-center\">\n <div\n className=\"grid place-items-center mx-auto mb-2\"\n style={{\n width: 36,\n height: 36,\n borderRadius: 10,\n border: '1px solid var(--hub-line)',\n }}\n >\n <Plus size={16} />\n </div>\n <div style={{ fontSize: 13, fontWeight: 500, color: 'var(--hub-ink-2)' }}>\n {t('groups.add')}\n </div>\n <div style={{ fontSize: 12, marginTop: 2 }}>{t('groups.addNew')}</div>\n </div>\n </button>\n </div>\n )}\n\n {showAddForm && (\n <AddGroupForm\n onAdd={() => {\n setShowAddForm(false);\n triggerRefresh();\n }}\n onCancel={() => setShowAddForm(false)}\n />\n )}\n\n {showImportForm && (\n <GroupImportForm\n onSuccess={() => {\n setShowImportForm(false);\n triggerRefresh();\n }}\n onCancel={() => setShowImportForm(false)}\n />\n )}\n\n {editingGroup && (\n <EditGroupForm\n group={editingGroup}\n onEdit={() => {\n setEditingGroup(null);\n triggerRefresh();\n }}\n onCancel={() => setEditingGroup(null)}\n />\n )}\n\n {showTemplateExport && (\n <TemplateExportForm groups={groups} onCancel={() => setShowTemplateExport(false)} />\n )}\n\n {showTemplateImport && (\n <TemplateImportForm\n onSuccess={() => {\n setShowTemplateImport(false);\n triggerRefresh();\n }}\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","getServerNames","getServerConfig","copyText","el","ok","GroupCard","onDelete","showToast","useToast","installConfig","baseUrl","_a","showDeleteDialog","setShowDeleteDialog","copied","setCopied","showCopyDropdown","setShowCopyDropdown","dropdownRef","useRef","handle","doCopy","text","groupEndpoint","serverNames","groupServers","tally","cfg","allTools","allPrompts","allResources","visibleTools","short","visiblePrompts","p","visibleResources","r","totalVisibleTools","acc","v","Check","Copy","LinkIcon","FileCode","Edit3","Trash2","tn","_","i","y1","ChevronDown","DeleteDialog","GroupImportForm","onSuccess","jsonInput","setJsonInput","isImporting","setIsImporting","previewGroups","setPreviewGroups","examplePlaceholder","parseAndValidateJson","input","parsed","handlePreview","handleImport","apiPost","successCount","failureCount","results","errors","renderAllCapabilitiesLabel","renderCapabilityPreview","renderServerList","idx","index","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","reader","event","handlePaste","response","GroupsPage","groupsLoading","groupError","setGroupError","deleteGroup","triggerRefresh","editingGroup","setEditingGroup","showAddForm","setShowAddForm","showImportForm","setShowImportForm","showTemplateExport","setShowTemplateExport","showTemplateImport","setShowTemplateImport","handleDeleteGroup","Upload","Download","Plus","AlertCircle","X"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"LogsPage-CoupcpK1.js","sources":["../../../node_modules/.pnpm/@radix-ui+react-compose-refs@1.1.2_@types+react@19.2.7_react@19.2.1/node_modules/@radix-ui/react-compose-refs/dist/index.mjs","../../../node_modules/.pnpm/@radix-ui+react-slot@1.2.3_@types+react@19.2.7_react@19.2.1/node_modules/@radix-ui/react-slot/dist/index.mjs","../../src/components/ui/Button.tsx","../../src/components/ui/Badge.tsx","../../src/components/LogViewer.tsx","../../src/pages/LogsPage.tsx"],"sourcesContent":["// packages/react/compose-refs/src/compose-refs.tsx\nimport * as React from \"react\";\nfunction setRef(ref, value) {\n if (typeof ref === \"function\") {\n return ref(value);\n } else if (ref !== null && ref !== void 0) {\n ref.current = value;\n }\n}\nfunction composeRefs(...refs) {\n return (node) => {\n let hasCleanup = false;\n const cleanups = refs.map((ref) => {\n const cleanup = setRef(ref, node);\n if (!hasCleanup && typeof cleanup == \"function\") {\n hasCleanup = true;\n }\n return cleanup;\n });\n if (hasCleanup) {\n return () => {\n for (let i = 0; i < cleanups.length; i++) {\n const cleanup = cleanups[i];\n if (typeof cleanup == \"function\") {\n cleanup();\n } else {\n setRef(refs[i], null);\n }\n }\n };\n }\n };\n}\nfunction useComposedRefs(...refs) {\n return React.useCallback(composeRefs(...refs), refs);\n}\nexport {\n composeRefs,\n useComposedRefs\n};\n//# sourceMappingURL=index.mjs.map\n","// src/slot.tsx\nimport * as React from \"react\";\nimport { composeRefs } from \"@radix-ui/react-compose-refs\";\nimport { Fragment as Fragment2, jsx } from \"react/jsx-runtime\";\n// @__NO_SIDE_EFFECTS__\nfunction createSlot(ownerName) {\n const SlotClone = /* @__PURE__ */ createSlotClone(ownerName);\n const Slot2 = React.forwardRef((props, forwardedRef) => {\n const { children, ...slotProps } = props;\n const childrenArray = React.Children.toArray(children);\n const slottable = childrenArray.find(isSlottable);\n if (slottable) {\n const newElement = slottable.props.children;\n const newChildren = childrenArray.map((child) => {\n if (child === slottable) {\n if (React.Children.count(newElement) > 1) return React.Children.only(null);\n return React.isValidElement(newElement) ? newElement.props.children : null;\n } else {\n return child;\n }\n });\n return /* @__PURE__ */ jsx(SlotClone, { ...slotProps, ref: forwardedRef, children: React.isValidElement(newElement) ? React.cloneElement(newElement, void 0, newChildren) : null });\n }\n return /* @__PURE__ */ jsx(SlotClone, { ...slotProps, ref: forwardedRef, children });\n });\n Slot2.displayName = `${ownerName}.Slot`;\n return Slot2;\n}\nvar Slot = /* @__PURE__ */ createSlot(\"Slot\");\n// @__NO_SIDE_EFFECTS__\nfunction createSlotClone(ownerName) {\n const SlotClone = React.forwardRef((props, forwardedRef) => {\n const { children, ...slotProps } = props;\n if (React.isValidElement(children)) {\n const childrenRef = getElementRef(children);\n const props2 = mergeProps(slotProps, children.props);\n if (children.type !== React.Fragment) {\n props2.ref = forwardedRef ? composeRefs(forwardedRef, childrenRef) : childrenRef;\n }\n return React.cloneElement(children, props2);\n }\n return React.Children.count(children) > 1 ? React.Children.only(null) : null;\n });\n SlotClone.displayName = `${ownerName}.SlotClone`;\n return SlotClone;\n}\nvar SLOTTABLE_IDENTIFIER = Symbol(\"radix.slottable\");\n// @__NO_SIDE_EFFECTS__\nfunction createSlottable(ownerName) {\n const Slottable2 = ({ children }) => {\n return /* @__PURE__ */ jsx(Fragment2, { children });\n };\n Slottable2.displayName = `${ownerName}.Slottable`;\n Slottable2.__radixId = SLOTTABLE_IDENTIFIER;\n return Slottable2;\n}\nvar Slottable = /* @__PURE__ */ createSlottable(\"Slottable\");\nfunction isSlottable(child) {\n return React.isValidElement(child) && typeof child.type === \"function\" && \"__radixId\" in child.type && child.type.__radixId === SLOTTABLE_IDENTIFIER;\n}\nfunction mergeProps(slotProps, childProps) {\n const overrideProps = { ...childProps };\n for (const propName in childProps) {\n const slotPropValue = slotProps[propName];\n const childPropValue = childProps[propName];\n const isHandler = /^on[A-Z]/.test(propName);\n if (isHandler) {\n if (slotPropValue && childPropValue) {\n overrideProps[propName] = (...args) => {\n const result = childPropValue(...args);\n slotPropValue(...args);\n return result;\n };\n } else if (slotPropValue) {\n overrideProps[propName] = slotPropValue;\n }\n } else if (propName === \"style\") {\n overrideProps[propName] = { ...slotPropValue, ...childPropValue };\n } else if (propName === \"className\") {\n overrideProps[propName] = [slotPropValue, childPropValue].filter(Boolean).join(\" \");\n }\n }\n return { ...slotProps, ...overrideProps };\n}\nfunction getElementRef(element) {\n let getter = Object.getOwnPropertyDescriptor(element.props, \"ref\")?.get;\n let mayWarn = getter && \"isReactWarning\" in getter && getter.isReactWarning;\n if (mayWarn) {\n return element.ref;\n }\n getter = Object.getOwnPropertyDescriptor(element, \"ref\")?.get;\n mayWarn = getter && \"isReactWarning\" in getter && getter.isReactWarning;\n if (mayWarn) {\n return element.props.ref;\n }\n return element.props.ref || element.ref;\n}\nexport {\n Slot as Root,\n Slot,\n Slottable,\n createSlot,\n createSlottable\n};\n//# sourceMappingURL=index.mjs.map\n","import React, { useState } from 'react';\nimport { Slot } from '@radix-ui/react-slot';\nimport { cn } from '../../utils/cn';\n\ntype ButtonVariant = 'default' | 'outline' | 'ghost' | 'link' | 'destructive';\ntype ButtonSize = 'default' | 'sm' | 'lg' | 'icon';\n\ninterface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: ButtonVariant;\n size?: ButtonSize;\n asChild?: boolean;\n loading?: boolean;\n children: React.ReactNode;\n}\n\nconst variantStyles: Record<ButtonVariant, string> = {\n default: 'bg-blue-500 text-white hover:bg-blue-600 focus:ring-blue-500',\n outline: 'border border-gray-300 dark:border-gray-700 bg-transparent hover:bg-gray-100 dark:hover:bg-gray-800 text-gray-700 dark:text-gray-300',\n ghost: 'bg-transparent hover:bg-gray-100 dark:hover:bg-gray-800 text-gray-700 dark:text-gray-300',\n link: 'bg-transparent underline-offset-4 hover:underline text-blue-500 hover:text-blue-600',\n destructive: 'bg-red-500 text-white hover:bg-red-600 focus:ring-red-500',\n};\n\nconst sizeStyles: Record<ButtonSize, string> = {\n default: 'h-10 py-2 px-4',\n sm: 'h-8 px-3 text-sm',\n lg: 'h-12 px-6',\n icon: 'h-10 w-10 p-0 text-center',\n};\n\nexport function Button({\n variant = 'default',\n size = 'default',\n className,\n disabled,\n loading: externalLoading = false,\n asChild = false,\n children,\n onClick,\n ...props\n}: ButtonProps) {\n const [internalLoading, setInternalLoading] = useState(false);\n const isLoading = externalLoading || internalLoading;\n\n const handleClick = async (e: React.MouseEvent<HTMLButtonElement>) => {\n if (!onClick) return;\n \n try {\n const result = onClick(e);\n if (result && typeof (result as any).then === 'function') {\n setInternalLoading(true);\n await result;\n }\n } finally {\n if (internalLoading) {\n // Need to check if component unmounted?\n // It's a standard pattern, state update on unmounted is no longer an error in React 18+\n }\n setInternalLoading(false);\n }\n };\n\n const Comp = asChild ? Slot : 'button';\n\n return (\n <Comp\n className={cn(\n 'rounded-md inline-flex items-center justify-center font-medium transition-all duration-200 active:scale-95 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none relative',\n variantStyles[variant],\n sizeStyles[size],\n className\n )}\n disabled={disabled || isLoading}\n onClick={handleClick}\n {...props}\n >\n {asChild ? (\n children\n ) : (\n <>\n <span className={cn('flex items-center justify-center', isLoading && 'invisible')}>\n {children}\n </span>\n {isLoading && (\n <span className=\"absolute inset-0 flex items-center justify-center\">\n <svg className=\"animate-spin h-5 w-5\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle className=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"4\"></circle>\n <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\"></path>\n </svg>\n </span>\n )}\n </>\n )}\n </Comp>\n );\n}","import React from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { cn } from '../../utils/cn';\n\ntype BadgeVariant = 'default' | 'secondary' | 'outline' | 'destructive';\n\ntype BadgeProps = {\n children: React.ReactNode;\n variant?: BadgeVariant;\n className?: string;\n onClick?: () => void;\n};\n\nconst badgeVariants = {\n default: 'bg-blue-500 text-white hover:bg-blue-600',\n secondary:\n 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600',\n outline:\n 'bg-transparent border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800',\n destructive: 'bg-red-500 text-white hover:bg-red-600',\n};\n\nexport function Badge({ children, variant = 'default', className, onClick }: BadgeProps) {\n return (\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-semibold transition-colors',\n badgeVariants[variant],\n onClick ? 'cursor-pointer' : '',\n className,\n )}\n onClick={onClick}\n >\n {children}\n </span>\n );\n}\n\n// For backward compatibility with existing code\nexport const StatusBadge = ({\n status,\n onAuthClick,\n}: {\n status: 'connected' | 'disconnected' | 'connecting' | 'oauth_required';\n onAuthClick?: (e: React.MouseEvent) => void;\n}) => {\n const { t } = useTranslation();\n\n const colors = {\n connecting: 'status-badge-connecting',\n connected: 'status-badge-online',\n disconnected: 'status-badge-offline',\n oauth_required: 'status-badge-oauth-required',\n };\n\n // Map status to translation keys\n const statusTranslations = {\n connected: 'status.online',\n disconnected: 'status.offline',\n connecting: 'status.connecting',\n oauth_required: 'status.oauthRequired',\n };\n\n const isOAuthRequired = status === 'oauth_required';\n\n return (\n <span\n className={`px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${colors[status]} ${isOAuthRequired && onAuthClick ? 'cursor-pointer hover:opacity-80' : ''}`}\n onClick={isOAuthRequired && onAuthClick ? (e) => onAuthClick(e) : undefined}\n title={isOAuthRequired ? t('status.clickToAuthorize') : undefined}\n >\n {isOAuthRequired && '🔐 '}\n {t(statusTranslations[status] || status)}\n </span>\n );\n};\n","import React, { useEffect, useRef, useState } from 'react';\nimport { LogEntry } from '../services/logService';\nimport { Button } from './ui/Button';\nimport { Badge } from './ui/Badge';\nimport { useTranslation } from 'react-i18next';\n\ninterface LogViewerProps {\n logs: LogEntry[];\n isLoading?: boolean;\n error?: Error | null;\n onClear?: () => void;\n}\n\nconst LogViewer: React.FC<LogViewerProps> = ({ logs, isLoading = false, error = null, onClear }) => {\n const { t } = useTranslation();\n const logContainerRef = useRef<HTMLDivElement>(null);\n const [autoScroll, setAutoScroll] = useState(true);\n const [filter, setFilter] = useState<string>('');\n const [typeFilter, setTypeFilter] = useState<Array<'info' | 'error' | 'warn' | 'debug'>>(['info', 'error', 'warn', 'debug']);\n const [sourceFilter, setSourceFilter] = useState<Array<'main' | 'child'>>(['main', 'child']);\n\n // Auto scroll to bottom when new logs come in if autoScroll is enabled\n useEffect(() => {\n if (autoScroll && logContainerRef.current) {\n logContainerRef.current.scrollTop = logContainerRef.current.scrollHeight;\n }\n }, [logs, autoScroll]);\n\n // Filter logs based on current filter settings\n const filteredLogs = logs.filter(log => {\n const matchesText = filter ? log.message.toLowerCase().includes(filter.toLowerCase()) : true;\n const matchesType = typeFilter.includes(log.type);\n const matchesSource = sourceFilter.includes(log.source as 'main' | 'child');\n return matchesText && matchesType && matchesSource;\n });\n\n // Format timestamp to readable format\n const formatTimestamp = (timestamp: number) => {\n const date = new Date(timestamp);\n return date.toLocaleTimeString([], {\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false\n });\n };\n\n // Get badge color based on log type\n const getLogTypeColor = (type: string) => {\n switch (type) {\n case 'error': return 'bg-red-400/80 text-white';\n case 'warn': return 'bg-yellow-400/80 text-gray-900';\n case 'debug': return 'bg-purple-400/80 text-white';\n case 'info': return 'bg-blue-400/80 text-white';\n default: return 'bg-blue-400/80 text-white';\n }\n };\n\n // Get badge color based on log source\n const getSourceColor = (source: string) => {\n switch (source) {\n case 'main': return 'bg-green-400/80 text-white';\n case 'child': return 'bg-orange-400/80 text-white';\n default: return 'bg-gray-400/80 text-white';\n }\n };\n\n return (\n <div className=\"flex flex-col h-full\">\n <div className=\"bg-card p-3 rounded-t-md flex flex-wrap items-center justify-between gap-2\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"font-semibold text-sm\">{t('logs.filters')}:</span>\n\n {/* Text search filter */}\n <input\n type=\"text\"\n placeholder={t('logs.search')}\n className=\"shadow appearance-none border border-gray-200 dark:border-gray-700 rounded py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline form-input\"\n value={filter}\n onChange={(e) => setFilter(e.target.value)}\n />\n\n {/* Log type filters */}\n <div className=\"flex gap-1 items-center\">\n {(['debug', 'info', 'error', 'warn'] as const).map(type => (\n <Badge\n key={type}\n variant={typeFilter.includes(type) ? 'default' : 'outline'}\n className={`cursor-pointer ${typeFilter.includes(type) ? getLogTypeColor(type) : ''}`}\n onClick={() => {\n if (typeFilter.includes(type)) {\n setTypeFilter(prev => prev.filter(t => t !== type));\n } else {\n setTypeFilter(prev => [...prev, type]);\n }\n }}\n >\n {type}\n </Badge>\n ))}\n </div>\n\n {/* Log source filters */}\n <div className=\"flex gap-1 items-center ml-2\">\n {(['main', 'child'] as const).map(source => (\n <Badge\n key={source}\n variant={sourceFilter.includes(source) ? 'default' : 'outline'}\n className={`cursor-pointer ${sourceFilter.includes(source) ? getSourceColor(source) : ''}`}\n onClick={() => {\n if (sourceFilter.includes(source)) {\n setSourceFilter(prev => prev.filter(s => s !== source));\n } else {\n setSourceFilter(prev => [...prev, source]);\n }\n }}\n >\n {source === 'main' ? t('logs.mainProcess') : t('logs.childProcess')}\n </Badge>\n ))}\n </div>\n </div>\n\n <div className=\"flex items-center gap-2\">\n <label className=\"flex items-center gap-1 text-sm\">\n <input\n type=\"checkbox\"\n checked={autoScroll}\n onChange={() => setAutoScroll(!autoScroll)}\n className=\"form-checkbox h-4 w-4\"\n />\n {t('logs.autoScroll')}\n </label>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={onClear}\n className='btn-secondary'\n disabled={isLoading || logs.length === 0}\n >\n {t('logs.clearLogs')}\n </Button>\n </div>\n </div>\n\n <div\n ref={logContainerRef}\n className=\"flex-grow p-2 overflow-auto bg-card rounded-b-md font-mono text-sm\"\n style={{ maxHeight: 'calc(100vh - 300px)' }}\n >\n {isLoading ? (\n <div className=\"flex justify-center items-center h-full\">\n <span>{t('logs.loading')}</span>\n </div>\n ) : error ? (\n <div className=\"text-red-500 p-2\">\n {error.message}\n </div>\n ) : filteredLogs.length === 0 ? (\n <div className=\"text-center text-muted-foreground p-8\">\n {filter || typeFilter.length < 4 || sourceFilter.length < 2\n ? t('logs.noMatch')\n : t('logs.noLogs')}\n </div>\n ) : (\n filteredLogs.map((log, index) => (\n <div\n key={`${log.timestamp}-${index}`}\n className={`py-1 ${log.type === 'error' ? 'text-red-500' :\n log.type === 'warn' ? 'text-yellow-500' : ''\n }`}\n >\n <span className=\"text-gray-400\">[{formatTimestamp(log.timestamp)}]</span>\n <Badge className={`ml-2 mr-1 ${getLogTypeColor(log.type)}`}>\n {log.type}\n </Badge>\n <Badge\n variant=\"default\"\n className={`mr-2 ${getSourceColor(log.source)}`}\n >\n {log.source === 'main' ? t('logs.main') : t('logs.child')}\n {log.processId ? ` (${log.processId})` : ''}\n </Badge>\n <span className=\"whitespace-pre-wrap\">{log.message}</span>\n </div>\n ))\n )}\n </div>\n </div>\n );\n};\n\nexport default LogViewer;","import React from 'react';\nimport { useTranslation } from 'react-i18next';\nimport LogViewer from '../components/LogViewer';\nimport { useLogs } from '../services/logService';\n\nconst LogsPage: React.FC = () => {\n const { t } = useTranslation();\n const { logs, loading, error, clearLogs } = useLogs();\n\n return (\n <div>\n <div className=\"mb-6\">\n <h1 className=\"hub-h1\">{t('pages.logs.title')}</h1>\n <p className=\"hub-sub\">\n <span className=\"hub-num\">{logs.length}</span> entries\n </p>\n </div>\n <div className=\"hub-card overflow-hidden\">\n <LogViewer logs={logs} isLoading={loading} error={error} onClear={clearLogs} />\n </div>\n </div>\n );\n};\n\nexport default LogsPage;\n"],"names":["setRef","ref","value","composeRefs","refs","node","hasCleanup","cleanups","cleanup","i","createSlot","ownerName","SlotClone","createSlotClone","Slot2","React.forwardRef","props","forwardedRef","children","slotProps","childrenArray","React.Children","slottable","isSlottable","newElement","newChildren","child","React.isValidElement","jsx","React.cloneElement","Slot","childrenRef","getElementRef","props2","mergeProps","React.Fragment","SLOTTABLE_IDENTIFIER","childProps","overrideProps","propName","slotPropValue","childPropValue","args","result","element","getter","_a","mayWarn","_b","variantStyles","sizeStyles","Button","variant","size","className","disabled","externalLoading","asChild","onClick","internalLoading","setInternalLoading","useState","isLoading","handleClick","e","Comp","cn","jsxs","Fragment","badgeVariants","Badge","LogViewer","logs","error","onClear","t","useTranslation","logContainerRef","useRef","autoScroll","setAutoScroll","filter","setFilter","typeFilter","setTypeFilter","sourceFilter","setSourceFilter","useEffect","filteredLogs","log","matchesText","matchesType","matchesSource","formatTimestamp","timestamp","getLogTypeColor","type","getSourceColor","source","prev","s","index","LogsPage","loading","clearLogs","useLogs"],"mappings":"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","x_google_ignoreList":[0,1]}