@samanhappy/mcphub 1.0.16 → 1.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/betterAuth.js +2 -2
- package/dist/betterAuth.js.map +1 -1
- package/dist/cli/commands/cache.js +39 -0
- package/dist/cli/commands/cache.js.map +1 -0
- package/dist/cli/commands/servers.js +14 -0
- package/dist/cli/commands/servers.js.map +1 -1
- package/dist/cli/help.js +11 -2
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/main.js +1 -0
- package/dist/cli/main.js.map +1 -1
- package/dist/controllers/groupController.js +66 -2
- package/dist/controllers/groupController.js.map +1 -1
- package/dist/controllers/serverController.js +62 -1
- package/dist/controllers/serverController.js.map +1 -1
- package/dist/db/entities/Group.js.map +1 -1
- package/dist/middlewares/auth.js +20 -7
- package/dist/middlewares/auth.js.map +1 -1
- package/dist/routes/index.js +3 -1
- package/dist/routes/index.js.map +1 -1
- package/dist/services/groupService.js +25 -0
- package/dist/services/groupService.js.map +1 -1
- package/dist/services/mcpService.js +270 -25
- package/dist/services/mcpService.js.map +1 -1
- package/dist/services/smartRoutingService.js +63 -12
- package/dist/services/smartRoutingService.js.map +1 -1
- package/dist/utils/cacheUtils.js +110 -0
- package/dist/utils/cacheUtils.js.map +1 -0
- package/frontend/dist/assets/{ActivityPage-BYPkr9n2.js → ActivityPage-C36IBbnj.js} +2 -2
- package/frontend/dist/assets/{ActivityPage-BYPkr9n2.js.map → ActivityPage-C36IBbnj.js.map} +1 -1
- package/frontend/dist/assets/{ConfirmDialog-Cag_haxr.js → ConfirmDialog-BKNVI65J.js} +2 -2
- package/frontend/dist/assets/{ConfirmDialog-Cag_haxr.js.map → ConfirmDialog-BKNVI65J.js.map} +1 -1
- package/frontend/dist/assets/{Dashboard-BbGvQMHv.js → Dashboard-BsUNNsqb.js} +2 -2
- package/frontend/dist/assets/{Dashboard-BbGvQMHv.js.map → Dashboard-BsUNNsqb.js.map} +1 -1
- package/frontend/dist/assets/{DeleteDialog-BBfJpiiD.js → DeleteDialog-7NPk3Hro.js} +2 -2
- package/frontend/dist/assets/{DeleteDialog-BBfJpiiD.js.map → DeleteDialog-7NPk3Hro.js.map} +1 -1
- package/frontend/dist/assets/{EndpointCopy-DhPgdjmi.js → EndpointCopy-eF3xFbaZ.js} +2 -2
- package/frontend/dist/assets/{EndpointCopy-DhPgdjmi.js.map → EndpointCopy-eF3xFbaZ.js.map} +1 -1
- package/frontend/dist/assets/GroupsPage-Dp-NU5wZ.js +33 -0
- package/frontend/dist/assets/GroupsPage-Dp-NU5wZ.js.map +1 -0
- package/frontend/dist/assets/{LoginPage-2PH92kBp.js → LoginPage-9PIMlvGe.js} +2 -2
- package/frontend/dist/assets/{LoginPage-2PH92kBp.js.map → LoginPage-9PIMlvGe.js.map} +1 -1
- package/frontend/dist/assets/{LogsPage-D8ws1iFm.js → LogsPage-C8WS43Dw.js} +2 -2
- package/frontend/dist/assets/{LogsPage-D8ws1iFm.js.map → LogsPage-C8WS43Dw.js.map} +1 -1
- package/frontend/dist/assets/{MarketPage-BL9qUEMR.js → MarketPage-Cyl5VvwI.js} +2 -2
- package/frontend/dist/assets/{MarketPage-BL9qUEMR.js.map → MarketPage-Cyl5VvwI.js.map} +1 -1
- package/frontend/dist/assets/{Pagination-DBAu79mv.js → Pagination-DAcOi8eq.js} +2 -2
- package/frontend/dist/assets/{Pagination-DBAu79mv.js.map → Pagination-DAcOi8eq.js.map} +1 -1
- package/frontend/dist/assets/{PromptsPage-CtXO4diZ.js → PromptsPage-CqxKWAR9.js} +2 -2
- package/frontend/dist/assets/{PromptsPage-CtXO4diZ.js.map → PromptsPage-CqxKWAR9.js.map} +1 -1
- package/frontend/dist/assets/{ResourcesPage-GD-T8LpP.js → ResourcesPage-Dq7Grza0.js} +2 -2
- package/frontend/dist/assets/{ResourcesPage-GD-T8LpP.js.map → ResourcesPage-Dq7Grza0.js.map} +1 -1
- package/frontend/dist/assets/ServersPage-xt7QkwjZ.js +37 -0
- package/frontend/dist/assets/ServersPage-xt7QkwjZ.js.map +1 -0
- package/frontend/dist/assets/SettingsPage-Cn5Krvgb.js +12 -0
- package/frontend/dist/assets/SettingsPage-Cn5Krvgb.js.map +1 -0
- package/frontend/dist/assets/{StatusDot-Bp40buM9.js → StatusDot-DAStUyI3.js} +2 -2
- package/frontend/dist/assets/{StatusDot-Bp40buM9.js.map → StatusDot-DAStUyI3.js.map} +1 -1
- package/frontend/dist/assets/{ToggleGroup-B15lxnw6.js → ToggleGroup-CQvqGQLA.js} +2 -2
- package/frontend/dist/assets/{ToggleGroup-B15lxnw6.js.map → ToggleGroup-CQvqGQLA.js.map} +1 -1
- package/frontend/dist/assets/{UsersPage-Cv80MtZ4.js → UsersPage-Bsa2NGcJ.js} +2 -2
- package/frontend/dist/assets/{UsersPage-Cv80MtZ4.js.map → UsersPage-Bsa2NGcJ.js.map} +1 -1
- package/frontend/dist/assets/{contextCost-DldRDO4O.js → contextCost-DrQqHXcP.js} +2 -2
- package/frontend/dist/assets/{contextCost-DldRDO4O.js.map → contextCost-DrQqHXcP.js.map} +1 -1
- package/frontend/dist/assets/framework-vendor-X-WP1v0m.js +61 -0
- package/frontend/dist/assets/framework-vendor-X-WP1v0m.js.map +1 -0
- package/frontend/dist/assets/{i18n-vendor-DP1IRITP.js → i18n-vendor-BLr2MLKp.js} +2 -2
- package/frontend/dist/assets/{i18n-vendor-DP1IRITP.js.map → i18n-vendor-BLr2MLKp.js.map} +1 -1
- package/frontend/dist/assets/{icons-vendor-BTEm6PQs.js → icons-vendor-DMtsx1SI.js} +63 -58
- package/frontend/dist/assets/icons-vendor-DMtsx1SI.js.map +1 -0
- package/frontend/dist/assets/{index-D1-Bdpe1.css → index-BlTPJflb.css} +1 -1
- package/frontend/dist/assets/index-BsgjLwhT.js +3 -0
- package/frontend/dist/assets/index-BsgjLwhT.js.map +1 -0
- package/frontend/dist/assets/{resourceService-CN0gM37U.js → resourceService-Bg941cnv.js} +2 -2
- package/frontend/dist/assets/{resourceService-CN0gM37U.js.map → resourceService-Bg941cnv.js.map} +1 -1
- package/frontend/dist/assets/useSettingsData-BwKohDXD.js +2 -0
- package/frontend/dist/assets/{useSettingsData-DewLOhzu.js.map → useSettingsData-BwKohDXD.js.map} +1 -1
- package/frontend/dist/assets/{variableDetection-hIevXYOZ.js → variableDetection-Bp_stsiy.js} +2 -2
- package/frontend/dist/assets/{variableDetection-hIevXYOZ.js.map → variableDetection-Bp_stsiy.js.map} +1 -1
- package/frontend/dist/index.html +5 -5
- package/package.json +13 -12
- package/frontend/dist/assets/GroupsPage-BC9FlhX4.js +0 -33
- package/frontend/dist/assets/GroupsPage-BC9FlhX4.js.map +0 -1
- package/frontend/dist/assets/ServersPage-CXUBWjQO.js +0 -37
- package/frontend/dist/assets/ServersPage-CXUBWjQO.js.map +0 -1
- package/frontend/dist/assets/SettingsPage-C5tAS-rd.js +0 -12
- package/frontend/dist/assets/SettingsPage-C5tAS-rd.js.map +0 -1
- package/frontend/dist/assets/framework-vendor-DeqnZ0v6.js +0 -61
- package/frontend/dist/assets/framework-vendor-DeqnZ0v6.js.map +0 -1
- package/frontend/dist/assets/icons-vendor-BTEm6PQs.js.map +0 -1
- package/frontend/dist/assets/index-OwXusZPZ.js +0 -3
- package/frontend/dist/assets/index-OwXusZPZ.js.map +0 -1
- package/frontend/dist/assets/useSettingsData-DewLOhzu.js +0 -2
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{d as $,R as u,r as p,j as e}from"./framework-vendor-
|
|
2
|
-
//# sourceMappingURL=Dashboard-
|
|
1
|
+
import{d as $,R as u,r as p,j as e}from"./framework-vendor-X-WP1v0m.js";import{u as L,a as M,f as P}from"./contextCost-DrQqHXcP.js";import{d as A}from"./index-BsgjLwhT.js";import{u as U}from"./useSettingsData-BwKohDXD.js";import{E as h}from"./EndpointCopy-eF3xFbaZ.js";import{S as H}from"./StatusDot-DAStUyI3.js";import{u as O}from"./i18n-vendor-BLr2MLKp.js";import{R as _,P as B,l as C,m as G}from"./icons-vendor-DMtsx1SI.js";const o=({label:r,value:l,tone:a="default"})=>{const i=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:r}),e.jsx("div",{className:"hub-num",style:{fontSize:26,fontWeight:500,letterSpacing:"-0.02em",lineHeight:1.1,marginTop:8,color:i},children:l})]})},w=(r,l)=>l?l==="stdio"?r("server.typeStdio")||"stdio":l==="sse"?r("server.typeSse")||"sse":l==="streamable-http"?r("server.typeStreamableHttp")||"http":l==="openapi"?r("server.typeOpenapi")||"openapi":l:null,X=()=>{var k;const{t:r}=O(),l=$(),{allServers:a,error:i,setError:R,isLoading:b,triggerRefresh:z}=L({refreshOnMount:!0}),{groups:x}=A(),{installConfig:m}=U(),{serverCosts:v}=M(),[E,f]=u.useState(!1),g=u.useRef(!1);u.useEffect(()=>{if(b){g.current=!0;return}if(g.current){f(!0);return}(a.length>0||i)&&f(!0)},[b,a.length,i]);const t=p.useMemo(()=>({total:a.length,online:a.filter(s=>s.status==="connected").length,disabled:a.filter(s=>s.enabled===!1).length,offline:a.filter(s=>s.status==="disconnected"&&s.enabled!==!1).length,connecting:a.filter(s=>(s.status==="connecting"||s.status==="oauth_required")&&s.enabled!==!1).length,tools:a.reduce((s,n)=>{var c;return s+(((c=n.tools)==null?void 0:c.length)||0)},0)}),[a]),D=p.useMemo(()=>v.filter(s=>s.connected).reduce((s,n)=>s+n.exposed,0),[v]),T=p.useMemo(()=>a.slice(0,6),[a]),d=((k=m==null?void 0:m.baseUrl)==null?void 0:k.replace(/\/+$/,""))||"",j="minmax(220px,1.9fr) minmax(110px,0.95fr) minmax(120px,0.95fr) 80px 80px 90px 72px",N=!E;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:r("pages.dashboard.title")}),e.jsxs("p",{className:"hub-sub",children:[r("pages.dashboard.totalServers")," · ",e.jsx("span",{className:"hub-num",children:t.total})," · ",r("pages.dashboard.onlineServers")," · ",e.jsx("span",{className:"hub-num",children:t.online})]})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs("button",{className:"hub-btn",onClick:()=>z(),children:[e.jsx(_,{size:13})," ",r("common.refresh")]}),e.jsxs("button",{className:"hub-btn primary",onClick:()=>l("/servers"),children:[e.jsx(B,{size:13})," ",r("server.add")]})]})]}),i&&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(C,{size:14,className:"flex-shrink-0"}),e.jsx("span",{className:"truncate text-[13px]",children:i})]}),e.jsx("button",{className:"hub-icon-btn sm",onClick:()=>R(null),"aria-label":r("app.closeButton"),children:"✕"})]}),N?e.jsx("div",{className:"grid grid-cols-2 md:grid-cols-6 gap-3 mb-6",children:Array.from({length:6}).map((s,n)=>e.jsx("div",{className:"hub-card animate-pulse",style:{padding:"14px 16px",height:78}},n))}):e.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-6 gap-3 mb-6",children:[e.jsx(o,{label:r("pages.dashboard.totalServers"),value:t.total}),e.jsx(o,{label:r("pages.dashboard.onlineServers"),value:t.online,tone:"ok"}),e.jsx(o,{label:r("pages.dashboard.connectingServers"),value:t.connecting,tone:"warn"}),e.jsx(o,{label:r("pages.dashboard.offlineServers"),value:t.offline,tone:"err"}),e.jsx(o,{label:r("pages.dashboard.disabledServers"),value:t.disabled,tone:"muted"}),e.jsx(o,{label:r("cost.totalFootprint"),value:P(D)})]}),a.length>0&&!N&&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:r("pages.dashboard.recentServers")}),e.jsxs("button",{className:"hub-btn ghost sm",style:{color:"var(--hub-ink-3)"},onClick:()=>l("/servers"),children:[r("common.viewAll")||"View all",e.jsx(G,{size:12})]})]}),e.jsxs("div",{className:"hub-row head hub-mono",style:{gridTemplateColumns:j},children:[e.jsx("div",{children:r("server.name")}),e.jsx("div",{children:r("server.status")}),e.jsx("div",{children:r("common.type")||"Transport"}),e.jsx("div",{children:r("server.tools")}),e.jsx("div",{children:r("server.prompts")}),e.jsx("div",{children:r("nav.resources")}),e.jsx("div",{children:r("server.enabled")})]}),T.map(s=>{var n,c,S,y;return e.jsxs("div",{className:"hub-row hover cursor-pointer",style:{gridTemplateColumns:j},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:s.enabled===!1?"var(--hub-ink-3)":"var(--hub-ink)"},children:s.name}),s.error&&e.jsx(C,{size:13,className:"text-[var(--hub-err)] flex-shrink-0"})]}),e.jsx("div",{className:"min-w-0",children:e.jsx(H,{status:s.status,enabled:s.enabled})}),e.jsx("div",{className:"min-w-0",children:(n=s.config)!=null&&n.type?e.jsx("span",{className:"hub-tag",title:w(r,s.config.type)??void 0,children:w(r,s.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:((c=s.tools)==null?void 0:c.length)||0}),e.jsx("div",{className:"hub-num hub-mono",style:{fontSize:12.5,color:"var(--hub-ink-2)"},children:((S=s.prompts)==null?void 0:S.length)||0}),e.jsx("div",{className:"hub-num hub-mono",style:{fontSize:12.5,color:"var(--hub-ink-2)"},children:((y=s.resources)==null?void 0:y.length)||0}),e.jsx("div",{className:"text-[12px]",style:{color:s.enabled!==!1?"var(--hub-ok)":"var(--hub-ink-3)"},children:s.enabled!==!1?"✓":"—"})]},s.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:r("pages.dashboard.endpoints")||"MCP Endpoints"}),e.jsx("p",{className:"hub-sub",style:{marginTop:2},children:r("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:[r("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`}),x.slice(0,2).map(s=>e.jsx(h,{label:"GROUP",url:`${d}/mcp/${s.name}`},s.id)),x.length<2&&a[0]&&e.jsx(h,{label:"SERVER",url:`${d}/mcp/${a[0].name}`})]})]})]})};export{X as default};
|
|
2
|
+
//# sourceMappingURL=Dashboard-BsUNNsqb.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Dashboard-BbGvQMHv.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 { useCostData } from '@/hooks/useCostData';\nimport { formatTokens } from '@/utils/contextCost';\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 const { serverCosts } = useCostData();\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 footprint = useMemo(\n () => serverCosts.filter((c) => c.connected).reduce((acc, c) => acc + c.exposed, 0),\n [serverCosts],\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-6 gap-3 mb-6\">\n {Array.from({ length: 6 }).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-6 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 <Stat label={t('cost.totalFootprint')} value={formatTokens(footprint)} />\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","serverCosts","useCostData","hasLoaded","setHasLoaded","React","loadingStartedRef","stats","useMemo","acc","s","_a","footprint","c","recentServers","baseUrl","recentServerColumns","showSkeleton","RefreshCw","Plus","AlertCircle","_","i","formatTokens","ChevronRight","ServerStatusDot","_b","_c","_d","EndpointCopy","g"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Dashboard-BsUNNsqb.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 { useCostData } from '@/hooks/useCostData';\nimport { formatTokens } from '@/utils/contextCost';\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 const { serverCosts } = useCostData();\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 footprint = useMemo(\n () => serverCosts.filter((c) => c.connected).reduce((acc, c) => acc + c.exposed, 0),\n [serverCosts],\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-6 gap-3 mb-6\">\n {Array.from({ length: 6 }).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-6 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 <Stat label={t('cost.totalFootprint')} value={formatTokens(footprint)} />\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","serverCosts","useCostData","hasLoaded","setHasLoaded","React","loadingStartedRef","stats","useMemo","acc","s","_a","footprint","c","recentServers","baseUrl","recentServerColumns","showSkeleton","RefreshCw","Plus","AlertCircle","_","i","formatTokens","ChevronRight","ServerStatusDot","_b","_c","_d","EndpointCopy","g"],"mappings":"2aAaA,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,EACpB,CAAE,YAAAC,CAAA,EAAgBC,EAAA,EAElB,CAACC,EAAWC,CAAY,EAAIC,EAAM,SAAS,EAAK,EAChDC,EAAoBD,EAAM,OAAO,EAAK,EAC5CA,EAAM,UAAU,IAAM,CACpB,GAAIX,EAAW,CACbY,EAAkB,QAAU,GAC5B,MACF,CACA,GAAIA,EAAkB,QAAS,CAC7BF,EAAa,EAAI,EACjB,MACF,EACIb,EAAW,OAAS,GAAKC,MAAoB,EAAI,CACvD,EAAG,CAACE,EAAWH,EAAW,OAAQC,CAAK,CAAC,EAExC,MAAMe,EAAQC,EAAAA,QACZ,KAAO,CACL,MAAOjB,EAAW,OAClB,OAAQA,EAAW,OAAQ,GAAc,EAAE,SAAW,WAAW,EAAE,OACnE,SAAUA,EAAW,OAAQ,GAAc,EAAE,UAAY,EAAK,EAAE,OAChE,QAASA,EAAW,OACjB,GAAc,EAAE,SAAW,gBAAkB,EAAE,UAAY,EAAA,EAC5D,OACF,WAAYA,EAAW,OACpB,IACE,EAAE,SAAW,cAAgB,EAAE,SAAW,mBAAqB,EAAE,UAAY,EAAA,EAChF,OACF,MAAOA,EAAW,OAAO,CAACkB,EAAKC,IAAA,OAAM,OAAAD,KAAOE,EAAAD,EAAE,QAAF,YAAAC,EAAS,SAAU,IAAI,CAAC,CAAA,GAEtE,CAACpB,CAAU,CAAA,EAGPqB,EAAYJ,EAAAA,QAChB,IAAMP,EAAY,OAAQY,GAAMA,EAAE,SAAS,EAAE,OAAO,CAACJ,EAAKI,IAAMJ,EAAMI,EAAE,QAAS,CAAC,EAClF,CAACZ,CAAW,CAAA,EAGRa,EAAgBN,UAAQ,IAAMjB,EAAW,MAAM,EAAG,CAAC,EAAG,CAACA,CAAU,CAAC,EAClEwB,IAAUJ,EAAAZ,GAAA,YAAAA,EAAe,UAAf,YAAAY,EAAwB,QAAQ,OAAQ,MAAO,GACzDK,EACJ,oFAEIC,EAAe,CAACd,EAEtB,cACG,MAAA,CAEC,SAAA,CAAArB,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,IAACmC,EAAA,CAAU,KAAM,EAAA,CAAI,EAAE,IAAEjC,EAAE,gBAAgB,CAAA,EAC7C,EACAH,OAAC,UAAO,UAAU,kBAAkB,QAAS,IAAMO,EAAS,UAAU,EACpE,SAAA,CAAAN,EAAAA,IAACoC,EAAA,CAAK,KAAM,EAAA,CAAI,EAAE,IAAElC,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,IAACqC,EAAA,CAAY,KAAM,GAAI,UAAU,gBAAgB,EACjDrC,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,EAKHgC,EACClC,EAAAA,IAAC,MAAA,CAAI,UAAU,6CACZ,SAAA,MAAM,KAAK,CAAE,OAAQ,EAAG,EAAE,IAAI,CAACsC,EAAGC,IACjCvC,EAAAA,IAAC,MAAA,CAEC,UAAU,yBACV,MAAO,CAAE,QAAS,YAAa,OAAQ,EAAA,CAAG,EAFrCuC,CAAA,CAIR,CAAA,CACH,EAEAxC,OAAC,MAAA,CAAI,UAAU,6CACb,SAAA,CAAAC,MAACN,GAAK,MAAOQ,EAAE,8BAA8B,EAAG,MAAOsB,EAAM,MAAO,EACpExB,EAAAA,IAACN,EAAA,CAAK,MAAOQ,EAAE,+BAA+B,EAAG,MAAOsB,EAAM,OAAQ,KAAK,IAAA,CAAK,EAChFxB,EAAAA,IAACN,EAAA,CAAK,MAAOQ,EAAE,mCAAmC,EAAG,MAAOsB,EAAM,WAAY,KAAK,MAAA,CAAO,EAC1FxB,EAAAA,IAACN,EAAA,CAAK,MAAOQ,EAAE,gCAAgC,EAAG,MAAOsB,EAAM,QAAS,KAAK,KAAA,CAAM,EACnFxB,EAAAA,IAACN,EAAA,CAAK,MAAOQ,EAAE,iCAAiC,EAAG,MAAOsB,EAAM,SAAU,KAAK,OAAA,CAAQ,EACvFxB,MAACN,GAAK,MAAOQ,EAAE,qBAAqB,EAAG,MAAOsC,EAAaX,CAAS,CAAA,CAAG,CAAA,EACzE,EAIDrB,EAAW,OAAS,GAAK,CAAC0B,GACzBnC,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,IAACyC,EAAA,CAAa,KAAM,EAAA,CAAI,CAAA,CAAA,CAAA,CAC1B,CAAA,CAAA,EAEF1C,EAAAA,KAAC,MAAA,CACC,UAAU,wBACV,MAAO,CAAE,oBAAqBkC,CAAA,EAE9B,SAAA,CAAAjC,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,EAE3B6B,EAAc,IAAK,gBAClBhC,OAAAA,EAAAA,KAAC,MAAA,CAEC,UAAU,+BACV,MAAO,CAAE,oBAAqBkC,CAAA,EAC9B,QAAS,IAAM3B,EAAS,UAAU,EAElC,SAAA,CAAAP,EAAAA,KAAC,MAAA,CAAI,UAAU,kCACb,SAAA,CAAAC,EAAAA,IAAC,OAAA,CACC,UAAU,oBACV,MAAO,CAAE,SAAU,GAAI,MAAO,EAAE,UAAY,GAAQ,mBAAqB,gBAAA,EAExE,SAAA,EAAE,IAAA,CAAA,EAEJ,EAAE,OAASA,EAAAA,IAACqC,GAAY,KAAM,GAAI,UAAU,qCAAA,CAAsC,CAAA,EACrF,EACArC,EAAAA,IAAC,MAAA,CAAI,UAAU,UACb,SAAAA,EAAAA,IAAC0C,EAAA,CAAgB,OAAQ,EAAE,OAAQ,QAAS,EAAE,OAAA,CAAS,EACzD,QACC,MAAA,CAAI,UAAU,UACZ,UAAAd,EAAA,EAAE,SAAF,MAAAA,EAAU,KACT5B,EAAAA,IAAC,OAAA,CAAK,UAAU,UAAU,MAAOC,EAAeC,EAAG,EAAE,OAAO,IAAI,GAAK,OAClE,SAAAD,EAAeC,EAAG,EAAE,OAAO,IAAI,CAAA,CAClC,EAEAF,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,WAAA2C,EAAA,EAAE,QAAF,YAAAA,EAAS,SAAU,CAAA,CACtB,EACA3C,EAAAA,IAAC,MAAA,CAAI,UAAU,mBAAmB,MAAO,CAAE,SAAU,KAAM,MAAO,kBAAA,EAC/D,WAAA4C,EAAA,EAAE,UAAF,YAAAA,EAAW,SAAU,EACxB,EACA5C,EAAAA,IAAC,MAAA,CAAI,UAAU,mBAAmB,MAAO,CAAE,SAAU,KAAM,MAAO,kBAAA,EAC/D,WAAA6C,EAAA,EAAE,YAAF,YAAAA,EAAa,SAAU,EAC1B,QACC,MAAA,CAAI,UAAU,cAAc,MAAO,CAAE,MAAO,EAAE,UAAY,GAAQ,gBAAkB,kBAAA,EAClF,WAAE,UAAY,GAAQ,IAAM,GAAA,CAC/B,CAAA,CAAA,EArCK,EAAE,IAAA,EAuCV,CAAA,EACH,EAIF9C,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,MAAC8C,GAAa,MAAM,MAAM,IAAK,GAAGd,CAAO,OAAQ,QAChDc,EAAA,CAAa,MAAM,QAAQ,IAAK,GAAGd,CAAO,cAAe,EACzDlB,EAAO,MAAM,EAAG,CAAC,EAAE,IAAKiC,GACvB/C,EAAAA,IAAC8C,EAAA,CAAwB,MAAM,QAAQ,IAAK,GAAGd,CAAO,QAAQe,EAAE,IAAI,EAAA,EAAjDA,EAAE,EAAmD,CACzE,EAEAjC,EAAO,OAAS,GAAKN,EAAW,CAAC,SAC/BsC,EAAA,CAAa,MAAM,SAAS,IAAK,GAAGd,CAAO,QAAQxB,EAAW,CAAC,EAAE,IAAI,EAAA,CAAI,CAAA,CAAA,CAE9E,CAAA,CAAA,CACF,CAAA,EACF,CAEJ"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{j as s}from"./framework-vendor-
|
|
2
|
-
//# sourceMappingURL=DeleteDialog-
|
|
1
|
+
import{j as s}from"./framework-vendor-X-WP1v0m.js";import{u as i}from"./i18n-vendor-BLr2MLKp.js";const d=({isOpen:a,onClose:r,onConfirm:c,serverName:n,isGroup:l=!1,isUser:t=!1})=>{const{t:e}=i();return a?s.jsx("div",{className:"fixed inset-0 bg-black/50 bg-opacity-30 z-50 flex items-center justify-center p-4",children:s.jsx("div",{className:"hub-card max-w-md w-full",children:s.jsxs("div",{className:"p-6",children:[s.jsx("h3",{className:"text-lg font-medium text-gray-900 mb-3",children:e(t?"users.confirmDelete":l?"groups.confirmDelete":"server.confirmDelete")}),s.jsx("p",{className:"text-gray-500 mb-6",children:t?e("users.deleteWarning",{username:n}):l?e("groups.deleteWarning",{name:n}):e("server.deleteWarning",{name:n})}),s.jsxs("div",{className:"flex justify-end space-x-3",children:[s.jsx("button",{onClick:r,className:"hub-btn",children:e("common.cancel")}),s.jsx("button",{onClick:c,className:"hub-btn danger",children:e("common.delete")})]})]})})}):null};export{d as D};
|
|
2
|
+
//# sourceMappingURL=DeleteDialog-7NPk3Hro.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeleteDialog-
|
|
1
|
+
{"version":3,"file":"DeleteDialog-7NPk3Hro.js","sources":["../../src/components/ui/DeleteDialog.tsx"],"sourcesContent":["import { useTranslation } from 'react-i18next'\n\ninterface DeleteDialogProps {\n isOpen: boolean\n onClose: () => void\n onConfirm: () => void\n serverName: string\n isGroup?: boolean\n isUser?: boolean\n}\n\nconst DeleteDialog = ({ isOpen, onClose, onConfirm, serverName, isGroup = false, isUser = false }: DeleteDialogProps) => {\n const { t } = useTranslation()\n\n if (!isOpen) return null\n\n return (\n <div className=\"fixed inset-0 bg-black/50 bg-opacity-30 z-50 flex items-center justify-center p-4\">\n <div className=\"hub-card max-w-md w-full\">\n <div className=\"p-6\">\n <h3 className=\"text-lg font-medium text-gray-900 mb-3\">\n {isUser\n ? t('users.confirmDelete')\n : isGroup\n ? t('groups.confirmDelete')\n : t('server.confirmDelete')}\n </h3>\n <p className=\"text-gray-500 mb-6\">\n {isUser\n ? t('users.deleteWarning', { username: serverName })\n : isGroup\n ? t('groups.deleteWarning', { name: serverName })\n : t('server.deleteWarning', { name: serverName })}\n </p>\n <div className=\"flex justify-end space-x-3\">\n <button\n onClick={onClose}\n className=\"hub-btn\"\n >\n {t('common.cancel')}\n </button>\n <button\n onClick={onConfirm}\n className=\"hub-btn danger\"\n >\n {t('common.delete')}\n </button>\n </div>\n </div>\n </div>\n </div>\n )\n}\n\nexport default DeleteDialog"],"names":["DeleteDialog","isOpen","onClose","onConfirm","serverName","isGroup","isUser","t","useTranslation","jsx","jsxs"],"mappings":"iGAWA,MAAMA,EAAe,CAAC,CAAE,OAAAC,EAAQ,QAAAC,EAAS,UAAAC,EAAW,WAAAC,EAAY,QAAAC,EAAU,GAAO,OAAAC,EAAS,MAA+B,CACvH,KAAM,CAAE,EAAAC,CAAA,EAAMC,EAAA,EAEd,OAAKP,EAGHQ,EAAAA,IAAC,MAAA,CAAI,UAAU,oFACb,SAAAA,EAAAA,IAAC,MAAA,CAAI,UAAU,2BACb,SAAAC,EAAAA,KAAC,MAAA,CAAI,UAAU,MACb,SAAA,CAAAD,EAAAA,IAAC,KAAA,CAAG,UAAU,yCACX,SACGF,EADHD,EACK,sBACFD,EACI,uBACA,sBAHmB,EAI7B,EACAI,EAAAA,IAAC,IAAA,CAAE,UAAU,qBACV,SAAAH,EACGC,EAAE,sBAAuB,CAAE,SAAUH,CAAA,CAAY,EACjDC,EACEE,EAAE,uBAAwB,CAAE,KAAMH,CAAA,CAAY,EAC9CG,EAAE,uBAAwB,CAAE,KAAMH,CAAA,CAAY,CAAA,CACtD,EACAM,EAAAA,KAAC,MAAA,CAAI,UAAU,6BACb,SAAA,CAAAD,EAAAA,IAAC,SAAA,CACC,QAASP,EACT,UAAU,UAET,WAAE,eAAe,CAAA,CAAA,EAEpBO,EAAAA,IAAC,SAAA,CACC,QAASN,EACT,UAAU,iBAET,WAAE,eAAe,CAAA,CAAA,CACpB,CAAA,CACF,CAAA,CAAA,CACF,EACF,EACF,EApCkB,IAsCtB"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{r as y,j as t}from"./framework-vendor-
|
|
2
|
-
//# sourceMappingURL=EndpointCopy-
|
|
1
|
+
import{r as y,j as t}from"./framework-vendor-X-WP1v0m.js";import{i as x,m as r}from"./index-BsgjLwhT.js";import{u as h}from"./i18n-vendor-BLr2MLKp.js";import{C,q as b}from"./icons-vendor-DMtsx1SI.js";const f=async e=>{try{if(navigator.clipboard&&window.isSecureContext)return await navigator.clipboard.writeText(e),!0}catch{}try{const o=document.createElement("textarea");o.value=e,o.style.position="fixed",o.style.left="-9999px",document.body.appendChild(o),o.focus(),o.select();const s=document.execCommand("copy");return document.body.removeChild(o),s}catch{return!1}},E=({url:e,label:o,prefix:s,className:p,copyValue:d,ariaLabel:l})=>{const{t:c}=h(),{showToast:n}=x(),[a,i]=y.useState(!1),m=async u=>{if(u.stopPropagation(),!await f(d??e)){n(c("common.copyFailed")||"Copy failed","error");return}i(!0),n(c("common.copySuccess")||"Copied to clipboard","success"),setTimeout(()=>i(!1),1200)};return t.jsxs("div",{className:r("hub-endpoint",p),role:"group","aria-label":l||e,children:[o&&t.jsx("div",{className:"hub-endpoint-label",children:o}),t.jsxs("div",{className:"hub-endpoint-url",title:e,children:[s&&t.jsx("span",{style:{color:"var(--hub-ink-3)"},children:s}),e]}),t.jsx("button",{type:"button",onClick:m,className:r("hub-endpoint-copy",a?"copied":""),title:c("common.copy")||"Copy","aria-label":c("common.copy")||"Copy",children:a?t.jsx(C,{size:13}):t.jsx(b,{size:13})})]})};export{E};
|
|
2
|
+
//# sourceMappingURL=EndpointCopy-eF3xFbaZ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EndpointCopy-
|
|
1
|
+
{"version":3,"file":"EndpointCopy-eF3xFbaZ.js","sources":["../../src/components/ui/EndpointCopy.tsx"],"sourcesContent":["import React, { useState } from 'react';\nimport { Copy, Check } from 'lucide-react';\nimport { useTranslation } from 'react-i18next';\nimport { useToast } from '@/contexts/ToastContext';\nimport { cn } from '@/utils/cn';\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 // fall through to fallback\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\ninterface EndpointCopyProps {\n url: string;\n label?: string;\n prefix?: string;\n className?: string;\n /** Optionally override the value placed on the clipboard. */\n copyValue?: string;\n ariaLabel?: string;\n}\n\nexport const EndpointCopy: React.FC<EndpointCopyProps> = ({\n url,\n label,\n prefix,\n className,\n copyValue,\n ariaLabel,\n}) => {\n const { t } = useTranslation();\n const { showToast } = useToast();\n const [copied, setCopied] = useState(false);\n\n const onCopy = async (e: React.MouseEvent) => {\n e.stopPropagation();\n const ok = await copyText(copyValue ?? url);\n if (!ok) {\n showToast(t('common.copyFailed') || 'Copy failed', 'error');\n return;\n }\n setCopied(true);\n showToast(t('common.copySuccess') || 'Copied to clipboard', 'success');\n setTimeout(() => setCopied(false), 1200);\n };\n\n return (\n <div className={cn('hub-endpoint', className)} role=\"group\" aria-label={ariaLabel || url}>\n {label && <div className=\"hub-endpoint-label\">{label}</div>}\n <div className=\"hub-endpoint-url\" title={url}>\n {prefix && <span style={{ color: 'var(--hub-ink-3)' }}>{prefix}</span>}\n {url}\n </div>\n <button\n type=\"button\"\n onClick={onCopy}\n className={cn('hub-endpoint-copy', copied ? 'copied' : '')}\n title={t('common.copy') || 'Copy'}\n aria-label={t('common.copy') || 'Copy'}\n >\n {copied ? <Check size={13} /> : <Copy size={13} />}\n </button>\n </div>\n );\n};\n\ninterface MonoCopyProps {\n text: string;\n className?: string;\n copyValue?: string;\n title?: string;\n}\n\n/** Inline monospace value with hover-to-copy icon. */\nexport const MonoCopy: React.FC<MonoCopyProps> = ({ text, className, copyValue, title }) => {\n const { t } = useTranslation();\n const { showToast } = useToast();\n const [copied, setCopied] = useState(false);\n\n const onCopy = async (e: React.MouseEvent) => {\n e.stopPropagation();\n const ok = await copyText(copyValue ?? text);\n if (!ok) {\n showToast(t('common.copyFailed') || 'Copy failed', 'error');\n return;\n }\n setCopied(true);\n showToast(t('common.copySuccess') || 'Copied to clipboard', 'success');\n setTimeout(() => setCopied(false), 1200);\n };\n\n return (\n <span\n className={cn(\n 'hub-mono inline-flex items-center gap-1.5 group cursor-pointer text-[12.5px]',\n className,\n )}\n onClick={onCopy}\n title={title || text}\n role=\"button\"\n >\n <span className=\"truncate\">{text}</span>\n {copied ? (\n <Check size={12} className=\"text-[var(--hub-ok)] flex-shrink-0\" />\n ) : (\n <Copy\n size={12}\n className=\"text-[var(--hub-ink-3)] opacity-0 group-hover:opacity-100 transition-opacity flex-shrink-0\"\n />\n )}\n </span>\n );\n};\n"],"names":["copyText","value","el","ok","EndpointCopy","url","label","prefix","className","copyValue","ariaLabel","t","useTranslation","showToast","useToast","copied","setCopied","useState","onCopy","e","jsxs","cn","jsx","Check","Copy"],"mappings":"wMAMA,MAAMA,EAAW,MAAOC,GAAoC,CAC1D,GAAI,CACF,GAAI,UAAU,WAAa,OAAO,gBAChC,aAAM,UAAU,UAAU,UAAUA,CAAK,EAClC,EAEX,MAAQ,CAER,CACA,GAAI,CACF,MAAMC,EAAK,SAAS,cAAc,UAAU,EAC5CA,EAAG,MAAQD,EACXC,EAAG,MAAM,SAAW,QACpBA,EAAG,MAAM,KAAO,UAChB,SAAS,KAAK,YAAYA,CAAE,EAC5BA,EAAG,MAAA,EACHA,EAAG,OAAA,EACH,MAAMC,EAAK,SAAS,YAAY,MAAM,EACtC,gBAAS,KAAK,YAAYD,CAAE,EACrBC,CACT,MAAQ,CACN,MAAO,EACT,CACF,EAYaC,EAA4C,CAAC,CACxD,IAAAC,EACA,MAAAC,EACA,OAAAC,EACA,UAAAC,EACA,UAAAC,EACA,UAAAC,CACF,IAAM,CACJ,KAAM,CAAE,EAAAC,CAAA,EAAMC,EAAA,EACR,CAAE,UAAAC,CAAA,EAAcC,EAAA,EAChB,CAACC,EAAQC,CAAS,EAAIC,EAAAA,SAAS,EAAK,EAEpCC,EAAS,MAAOC,GAAwB,CAG5C,GAFAA,EAAE,gBAAA,EAEE,CADO,MAAMnB,EAASS,GAAaJ,CAAG,EACjC,CACPQ,EAAUF,EAAE,mBAAmB,GAAK,cAAe,OAAO,EAC1D,MACF,CACAK,EAAU,EAAI,EACdH,EAAUF,EAAE,oBAAoB,GAAK,sBAAuB,SAAS,EACrE,WAAW,IAAMK,EAAU,EAAK,EAAG,IAAI,CACzC,EAEA,OACEI,EAAAA,KAAC,MAAA,CAAI,UAAWC,EAAG,eAAgBb,CAAS,EAAG,KAAK,QAAQ,aAAYE,GAAaL,EAClF,SAAA,CAAAC,GAASgB,EAAAA,IAAC,MAAA,CAAI,UAAU,qBAAsB,SAAAhB,EAAM,EACrDc,EAAAA,KAAC,MAAA,CAAI,UAAU,mBAAmB,MAAOf,EACtC,SAAA,CAAAE,SAAW,OAAA,CAAK,MAAO,CAAE,MAAO,kBAAA,EAAuB,SAAAA,EAAO,EAC9DF,CAAA,EACH,EACAiB,EAAAA,IAAC,SAAA,CACC,KAAK,SACL,QAASJ,EACT,UAAWG,EAAG,oBAAqBN,EAAS,SAAW,EAAE,EACzD,MAAOJ,EAAE,aAAa,GAAK,OAC3B,aAAYA,EAAE,aAAa,GAAK,OAE/B,SAAAI,QAAUQ,EAAA,CAAM,KAAM,GAAI,EAAKD,EAAAA,IAACE,EAAA,CAAK,KAAM,EAAA,CAAI,CAAA,CAAA,CAClD,EACF,CAEJ"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import{r as h,R as U,j as e}from"./framework-vendor-X-WP1v0m.js";import{m as se,d as H,i as me,e as X}from"./index-BsgjLwhT.js";import{f as B,u as Y,a as Z,p as ue}from"./contextCost-DrQqHXcP.js";import{u as ne}from"./useSettingsData-BwKohDXD.js";import{g as pe}from"./toolDescription-Dw3sa28K.js";import{u as $}from"./i18n-vendor-BLr2MLKp.js";import{W as xe,g as he,F as ge,C as be,q as _,D as fe,x as ye,v as je,T as ve,u as Ne,y as te,G as ke,P as re,l as Se,X as we}from"./icons-vendor-DMtsx1SI.js";import{D as Ce}from"./DeleteDialog-7NPk3Hro.js";const Ee={tools:[],prompts:[],resources:[]},ae={tools:"all",prompts:"all",resources:"all"},le=({servers:n,value:p,onChange:t,className:d,serverCosts:y=[]})=>{const{t:c}=$(),{nameSeparator:b}=ne(),[C,A]=h.useState(new Set),m=U.useMemo(()=>p.map(s=>typeof s=="string"?{name:s,...ae}:{...s,tools:s.tools||"all",prompts:s.prompts||"all",resources:s.resources||"all"}),[p]),N=U.useMemo(()=>n.filter(s=>s.enabled!==!1),[n]);U.useEffect(()=>{const s=new Set(m.map(a=>a.name)),l=new Set(N.map(a=>a.name));A(a=>{const f=new Set;return a.forEach(i=>{(s.has(i)||l.has(i))&&f.add(i)}),f})},[m,N]);const k=s=>{if(m.findIndex(a=>a.name===s)>=0){const a=m.filter(f=>f.name!==s);t(a)}else{const a=[...m,{name:s,...ae}];t(a)}},E=s=>{A(l=>{const a=new Set(l);return a.has(s)?a.delete(s):a.add(s),a})},S=s=>["tools","prompts","resources"].some(l=>{const a=s[l];return a==="all"||Array.isArray(a)&&a.length>0}),D=(s,l,a,f=!1)=>{const i=m.find(z=>z.name===s),R={...i?{...i}:{name:s,...Ee},[l]:a};if(!S(R)){const z=m.filter(v=>v.name!==s);t(z),f||A(v=>{const q=new Set(v);return q.delete(s),q});return}if(i){t(m.map(z=>z.name===s?R:z));return}t([...m,R])},u=(s,l)=>{const a=m.find(i=>i.name===s);if(!a)return;const f={...a};l?f.alias=l:delete f.alias,t(m.map(i=>i.name===s?f:i))},g=(s,l)=>{const a=`${s}${b}`;return l.startsWith(a)?l.slice(a.length):l},x=(s,l)=>l==="tools"?(s.tools||[]).filter(a=>a.enabled!==!1).map(a=>({key:a.name,value:g(s.name,a.name),description:a.description,defaultDescription:a.defaultDescription,hasDescriptionOverride:a.hasDescriptionOverride})):l==="prompts"?(s.prompts||[]).filter(a=>a.enabled!==!1).map(a=>({key:a.name,value:g(s.name,a.name),description:a.description})):(s.resources||[]).filter(a=>a.enabled!==!1).map(a=>({key:a.uri,value:a.uri,description:a.description})),r=U.useMemo(()=>{const s=new Map;return y.forEach(l=>{const a=new Map;l.items.forEach(f=>a.set(f.name,f.cost)),s.set(l.name,a)}),s},[y]),o=s=>r.get(s)??new Map,j=(s,l)=>{const a=o(s.name);return x(s,l).filter(f=>T(s.name,l,f.value)).reduce((f,i)=>f+(a.get(i.key)??0),0)},I=s=>["tools","prompts","resources"].reduce((l,a)=>l+j(s,a),0),F=(s,l,a)=>{const f=N.find(z=>z.name===s);if(!f)return;const i=x(f,l).map(z=>z.value),P=m.find(z=>z.name===s);if(!P){D(s,l,[a]);return}const R=P[l];if(R==="all"){const z=i.filter(v=>v!==a);D(s,l,z);return}if(Array.isArray(R)){if(R.includes(a)){D(s,l,R.filter(v=>v!==a));return}const z=[...R,a];D(s,l,z.length===i.length?"all":z);return}D(s,l,[a])},M=s=>{const l=m.find(a=>a.name===s);return!!(l&&S(l))},w=s=>{const l=m.find(a=>a.name===s);return l?["tools","prompts","resources"].some(a=>{const f=l[a];return Array.isArray(f)&&f.length>0}):!1},T=(s,l,a)=>{const f=m.find(P=>P.name===s);if(!f)return!1;const i=f[l];return i==="all"?!0:Array.isArray(i)?i.includes(a):!1},O=(s,l)=>{const a=m.find(P=>P.name===s.name);if(!a)return 0;const f=x(s,l),i=a[l];if(i==="all")return f.length;if(Array.isArray(i)){const P=new Set(f.map(R=>R.value));return i.filter(R=>P.has(R)).length}return 0},W=[{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"}],J=s=>W.map(({key:l})=>({key:l,count:O(s,l)})).filter(l=>l.count>0);return e.jsxs("div",{className:se("space-y-4",d),children:[e.jsx("div",{className:"space-y-3",children:N.map(s=>{const l=M(s.name),a=w(s.name),f=C.has(s.name),i=m.find(v=>v.name===s.name),P=J(s),R=W.filter(({key:v})=>x(s,v).length>0),z=o(s.name);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:()=>E(s.name),children:[e.jsxs("div",{className:"flex items-center space-x-3",onClick:v=>{v.stopPropagation(),k(s.name)},children:[e.jsx("input",{type:"checkbox",checked:l||a,onChange:()=>k(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:[I(s)>0&&e.jsxs("span",{className:"text-sm text-gray-400 hub-mono",title:c("cost.estimate"),children:["Σ ",B(I(s))]}),P.map(({key:v,count:q})=>e.jsxs("span",{className:"text-sm text-green-600 flex items-center gap-1",children:[v==="tools"?e.jsx(xe,{size:14}):v==="prompts"?e.jsx(he,{size:14}):e.jsx(ge,{size:14})," ",q]},v)),R.length>0&&e.jsx("button",{type:"button",className:"p-1 text-gray-400 hover:text-gray-600 transition-colors",children:e.jsx("svg",{className:se("w-5 h-5 transition-transform",f&&"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"})})})]})]}),f&&R.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.jsxs("div",{className:"space-y-4",children:[i&&e.jsxs("div",{className:"space-y-1",onClick:v=>v.stopPropagation(),children:[e.jsx("label",{className:"block text-xs font-medium text-gray-600",children:c("groups.alias")}),e.jsx("input",{type:"text",value:i.alias||"",placeholder:s.name,onChange:v=>u(s.name,v.target.value),className:"w-full rounded-md border border-gray-300 bg-white px-2.5 py-1.5 text-sm text-gray-800 focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-900 dark:text-gray-100"})]}),R.map(({key:v,titleKey:q,countKey:ie,allKey:ce})=>{const K=x(s,v),Q=O(s,v),V=(i==null?void 0:i[v])==="all"||Q===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:c(q)}),e.jsxs("div",{className:"flex items-center gap-3",children:[i&&e.jsx("span",{className:"text-xs text-green-600",children:V?`(${c(ce)} ${K.length}/${K.length})`:`(${c(ie)} ${Q}/${K.length})`}),i&&j(s,v)>0&&e.jsxs("span",{className:"text-xs text-gray-400 hub-mono",title:c("cost.estimate"),children:["Σ ",B(j(s,v))]}),e.jsx("button",{type:"button",onClick:()=>{D(s.name,v,V?[]:"all",!0)},className:"text-sm text-blue-600 hover:text-blue-800 transition-colors",children:c(V?"groups.selectNone":"groups.selectAll")})]})]}),e.jsx("div",{className:"grid grid-cols-1 gap-2 max-h-32 overflow-y-auto",children:K.map(G=>{const de=T(s.name,v,G.value),L=v==="tools"?pe({description:G.description,defaultDescription:G.defaultDescription,hasDescriptionOverride:G.hasDescriptionOverride},c("tool.noDescription")):null,ee=L!=null&&L.hasDescriptionOverride?c("tool.defaultDescriptionTooltip",{description:L.defaultDescription}):G.description;return e.jsxs("label",{className:"flex min-w-0 items-center gap-2 text-sm",children:[e.jsx("input",{type:"checkbox",checked:de,onChange:()=>F(s.name,v,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 flex-shrink-0",children:G.value}),(G.description||(L==null?void 0:L.hasDescriptionOverride))&&e.jsxs("span",{className:"min-w-0 flex items-center gap-1 text-gray-400 text-xs truncate",children:[e.jsx("span",{className:"truncate",title:ee||void 0,children:L?L.currentDescription:G.description}),(L==null?void 0:L.hasDescriptionOverride)&&e.jsx("span",{className:"inline-flex flex-shrink-0 items-center rounded-full border border-amber-200 bg-amber-50 px-1.5 py-0.5 text-[10px] font-medium text-amber-700 dark:border-amber-700/60 dark:bg-amber-900/20 dark:text-amber-300",title:ee||void 0,children:c("tool.descriptionModifiedBadge")})]}),z.get(G.key)!=null&&e.jsxs("span",{className:"text-xs text-gray-400 hub-mono whitespace-nowrap ml-auto flex-shrink-0",title:c("cost.estimate"),children:["Σ ",B(z.get(G.key))]})]},G.key)})})]},v)})]})})]},s.name)})}),N.length===0&&e.jsx("p",{className:"text-gray-500 text-sm",children:c("groups.noServerOptions")})]})},De=({onAdd:n,onCancel:p})=>{const{t}=$(),{createGroup:d}=H(),{allServers:y}=Y(),{serverCosts:c}=Z(),[b,C]=h.useState([]),[A,m]=h.useState(null),[N,k]=h.useState(!1),[E,S]=h.useState({name:"",description:"",servers:[]});h.useEffect(()=>{C(y.filter(g=>g.enabled!==!1))},[y]);const D=g=>{const{name:x,value:r}=g.target;S(o=>({...o,[x]:r}))},u=async g=>{g.preventDefault(),k(!0),m(null);try{if(!E.name.trim()){m(t("groups.nameRequired")),k(!1);return}const x=await d(E.name,E.description,E.servers);if(!x||!x.success){m((x==null?void 0:x.message)||t("groups.createError")),k(!1);return}n()}catch(x){m(x instanceof Error?x.message:String(x)),k(!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")}),A&&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:A})]}),e.jsxs("form",{onSubmit:u,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:E.name,onChange:D,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(le,{servers:b,value:E.servers,onChange:g=>S(x=>({...x,servers:g})),className:"border border-gray-200 dark:border-gray-700 rounded-lg p-4 bg-gray-50 dark:bg-gray-800",serverCosts:c})]})]})}),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:p,className:"hub-btn",disabled:N,children:t("common.cancel")}),e.jsx("button",{type:"submit",className:"hub-btn primary",disabled:N,children:t(N?"common.submitting":"common.create")})]})]})]})})},Ae=({group:n,onEdit:p,onCancel:t})=>{const{t:d}=$(),{updateGroup:y}=H(),{allServers:c}=Y(),{serverCosts:b}=Z(),[C,A]=h.useState([]),[m,N]=h.useState(null),[k,E]=h.useState(!1),[S,D]=h.useState({name:n.name,description:n.description||"",servers:n.servers||[]});h.useEffect(()=>{A(c.filter(x=>x.enabled!==!1))},[c]);const u=x=>{const{name:r,value:o}=x.target;D(j=>({...j,[r]:o}))},g=async x=>{x.preventDefault(),E(!0),N(null);try{if(!S.name.trim()){N(d("groups.nameRequired")),E(!1);return}const r=await y(n.id,{name:S.name,description:S.description,servers:S.servers});if(!r||!r.success){N((r==null?void 0:r.message)||d("groups.updateError")),E(!1);return}p()}catch(r){N(r instanceof Error?r.message:String(r)),E(!1)}};return e.jsx("div",{className:"fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4",children:e.jsxs("div",{className:"bg-white dark:bg-gray-800 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")}),m&&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:m})]}),e.jsxs("form",{onSubmit:g,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:S.name,onChange:u,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(le,{servers:C,value:S.servers,onChange:x=>D(r=>({...r,servers:x})),className:"border border-gray-200 dark:border-gray-700 rounded-lg p-4 bg-gray-50 dark:bg-gray-800",serverCosts:b})]})]})}),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:k,children:d("common.cancel")}),e.jsx("button",{type:"submit",className:"hub-btn primary",disabled:k,children:d(k?"common.submitting":"common.save")})]})]})]})})},Ie=n=>n.map(p=>typeof p=="string"?p:p.name),oe=(n,p)=>{const t=n.servers.find(d=>typeof d=="string"?d===p:d.name===p);return t?typeof t=="string"?{name:t,tools:"all",prompts:"all",resources:"all"}:t:{name:p,tools:"all",prompts:"all",resources:"all"}},Te=(n,p)=>{var d;return((d=oe(n,p).alias)==null?void 0:d.trim())||p},ze=async n=>{try{if(navigator.clipboard&&window.isSecureContext)return await navigator.clipboard.writeText(n),!0}catch{}try{const p=document.createElement("textarea");p.value=n,p.style.position="fixed",p.style.left="-9999px",document.body.appendChild(p),p.focus(),p.select();const t=document.execCommand("copy");return document.body.removeChild(p),t}catch{return!1}},Fe=({group:n,servers:p,onEdit:t,onDelete:d,cost:y})=>{var M;const{t:c}=$(),{showToast:b}=me(),{installConfig:C,nameSeparator:A}=ne(),m=((M=C==null?void 0:C.baseUrl)==null?void 0:M.replace(/\/+$/,""))||"",[N,k]=h.useState(!1),[E,S]=h.useState(!1),[D,u]=h.useState(!1),g=h.useRef(null);h.useEffect(()=>{const w=T=>{g.current&&!g.current.contains(T.target)&&u(!1)};return document.addEventListener("mousedown",w),()=>document.removeEventListener("mousedown",w)},[]);const x=async w=>{await ze(w)?(S(!0),u(!1),b(c("common.copySuccess")||"Copied","success"),setTimeout(()=>S(!1),1500)):b(c("common.copyFailed")||"Copy failed","error")},r=`${m}/mcp/${n.name}`,o=Ie(n.servers),j=p.filter(w=>o.includes(w.name)),I=w=>{const T=oe(n,w.name),O=`${w.name}${A}`,W=w.tools||[],J=w.prompts||[],s=w.resources||[],l=Array.isArray(T.tools)?W.filter(i=>{if(i.enabled===!1)return!1;const P=i.name.startsWith(O)?i.name.slice(O.length):i.name;return T.tools.includes(P)}).length:W.filter(i=>i.enabled!==!1).length,a=Array.isArray(T.prompts)?J.filter(i=>{if(i.enabled===!1)return!1;const P=i.name.startsWith(O)?i.name.slice(O.length):i.name;return T.prompts.includes(P)}).length:J.filter(i=>i.enabled!==!1).length,f=Array.isArray(T.resources)?s.filter(i=>i.enabled!==!1&&T.resources.includes(i.uri)).length:s.filter(i=>i.enabled!==!1).length;return{visibleTools:l,totalTools:W.length,visiblePrompts:a,totalPrompts:J.length,visibleResources:f,totalResources:s.length}},F=j.reduce((w,T)=>w+I(T).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:n.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:n.id,children:n.id})]}),n.description&&e.jsx("div",{style:{fontSize:12.5,color:"var(--hub-ink-3)",marginTop:2},children:n.description})]}),e.jsxs("div",{className:"flex items-center gap-1",ref:g,children:[e.jsxs("div",{className:"relative",children:[e.jsx("button",{onClick:()=>u(w=>!w),className:"hub-icon-btn sm",title:c("common.copy"),children:E?e.jsx(be,{size:13,className:"text-[var(--hub-ok)]"}):e.jsx(_,{size:13})}),D&&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:()=>x(n.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(_,{size:12})," ",c("common.copyId")]}),e.jsxs("button",{onClick:()=>x(r),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(fe,{size:12})," ",c("common.copyUrl")]}),e.jsxs("button",{onClick:()=>x(JSON.stringify({mcpServers:{mcphub:{url:r,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(ye,{size:12})," ",c("common.copyJson")]})]})]}),e.jsx("button",{onClick:()=>t(n),className:"hub-icon-btn sm",title:c("groups.edit"),children:e.jsx(je,{size:13})}),e.jsx("button",{onClick:()=>k(!0),className:"hub-icon-btn sm",title:c("groups.delete"),style:{color:"var(--hub-ink-3)"},children:e.jsx(ve,{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:j.length===0?e.jsx("div",{style:{fontSize:12,color:"var(--hub-ink-3)"},children:c("groups.noServers")}):j.map(w=>{const T=I(w);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:w.status==="connected"?"var(--hub-ok)":w.status==="connecting"?"var(--hub-warn)":"var(--hub-err)"}}),e.jsx("span",{className:"hub-mono truncate flex-1",style:{fontSize:12.5},children:e.jsx("span",{title:w.name,children:Te(n,w.name)})}),e.jsxs("span",{className:"hub-mono hub-num flex-shrink-0",style:{fontSize:11,color:"var(--hub-ink-3)"},children:[T.visibleTools,"/",T.totalTools," tools"]})]},w.name)})}),e.jsxs("svg",{width:"80",height:"80",viewBox:"0 0 80 80",className:"self-center",children:[j.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"}):j.map((w,T)=>{const O=12+60/Math.max(j.length,1)*(T+.5);return e.jsx("path",{d:`M0,${O} C 30,${O} 50,40 80,40`,stroke:"var(--hub-line)",strokeWidth:"1",fill:"none",strokeDasharray:"3 3"},T)}),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:n.name})]}),e.jsx("div",{className:"flex gap-1.5 mt-2",children:e.jsxs("button",{className:"hub-btn sm flex-1 justify-center",onClick:()=>x(r),children:[e.jsx(_,{size:11})," ",c("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",{children:[e.jsxs("div",{className:"hub-mono",children:[e.jsx("span",{style:{color:"var(--hub-ink-2)"},children:j.length})," ",c("nav.servers").toLowerCase()," ·"," ",e.jsx("span",{style:{color:"var(--hub-ink-2)"},children:F})," ",c("server.tools").toLowerCase()]}),y&&e.jsxs("div",{className:"hub-mono mt-1",style:{fontSize:11.5,color:"var(--hub-ink-3)"},children:[e.jsxs("div",{title:c("cost.estimate"),children:[c("cost.totalFootprint"),": ",B(y.direct.exposed),"/",B(y.direct.gross)]}),y.smartRouting&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{children:[c("cost.smartRouting"),": ",B(y.smartRouting.base)," (",c("cost.saved",{percent:ue(y.direct.exposed,y.smartRouting.base)}),")"]}),e.jsxs("div",{title:c("cost.smartRoutingPdHint"),children:[c("cost.smartRoutingPd"),":"," ",B(y.smartRouting.progressiveDisclosure)]})]}),y.connectedCount<y.totalCount&&e.jsx("div",{children:c("cost.connectedOf",{connected:y.connectedCount,total:y.totalCount})})]})]}),e.jsxs("button",{className:"hub-btn ghost sm",style:{color:"var(--hub-ink-3)"},onClick:()=>t(n),children:[c("groups.configureTools")||c("groups.edit"),e.jsx(Ne,{size:11,style:{transform:"rotate(-90deg)"}})]})]}),e.jsx(Ce,{isOpen:N,onClose:()=>k(!1),onConfirm:()=>{d(n.id),k(!1)},serverName:n.name,isGroup:!0})]})},Re=({onSuccess:n,onCancel:p})=>{const{t}=$(),[d,y]=h.useState(""),[c,b]=h.useState(null),[C,A]=h.useState(!1),[m,N]=h.useState(null),k=`{
|
|
2
|
+
"groups": [
|
|
3
|
+
{
|
|
4
|
+
"name": "AI Assistants",
|
|
5
|
+
"servers": ["openai-server", "anthropic-server"]
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"name": "Development Tools",
|
|
9
|
+
"servers": [
|
|
10
|
+
{
|
|
11
|
+
"name": "github-server",
|
|
12
|
+
"tools": ["create_issue", "list_repos"],
|
|
13
|
+
"prompts": ["triage_prompt"],
|
|
14
|
+
"resources": ["resource://docs/repo-guide"]
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"name": "gitlab-server",
|
|
18
|
+
"tools": "all",
|
|
19
|
+
"prompts": "all",
|
|
20
|
+
"resources": "all"
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
Supports:
|
|
28
|
+
- Simple server list: ["server1", "server2"]
|
|
29
|
+
- Advanced server config: [{"name": "server1", "tools": ["tool1"], "prompts": ["prompt1"], "resources": ["resource://docs/guide"]}]
|
|
30
|
+
- All groups will be imported in a single efficient batch operation.`,E=r=>{try{const o=JSON.parse(r.trim());if(!o.groups||!Array.isArray(o.groups))return b(t("groupImport.invalidFormat")),null;for(const j of o.groups)if(!j.name||typeof j.name!="string")return b(t("groupImport.missingName")),null;return o}catch{return b(t("groupImport.parseError")),null}},S=()=>{b(null);const r=E(d);r&&N(r.groups)},D=async()=>{if(m){A(!0),b(null);try{const r=await X("/groups/batch",{groups:m});if(r.success){const{successCount:o,failureCount:j,results:I}=r;if(j>0){const F=I.filter(M=>!M.success).map(M=>`${M.name}: ${M.message||t("groupImport.addFailed")}`);b(t("groupImport.partialSuccess",{count:o,total:m.length})+`
|
|
31
|
+
`+F.join(`
|
|
32
|
+
`))}o>0&&n()}else b(r.message||t("groupImport.importFailed"))}catch(r){console.error("Import error:",r),b(t("groupImport.importFailed"))}finally{A(!1)}}},u=r=>e.jsx("span",{className:"text-gray-500 ml-2",children:t(`groups.${r}`)}),g=(r,o)=>{if(!o||o==="all")return null;const j=Array.isArray(o)?o.join(", "):o;return e.jsx("span",{className:"text-gray-500 ml-2",children:t(`groups.${r}`,{items:j})})},x=r=>!r||r.length===0?e.jsx("span",{className:"text-gray-500",children:t("groups.noServers")}):e.jsx("div",{className:"space-y-1",children:r.map((o,j)=>typeof o=="string"?e.jsxs("div",{className:"text-sm",children:["• ",o]},j):e.jsxs("div",{className:"text-sm",children:["• ",o.name,o.tools&&o.tools!=="all"&&e.jsxs("span",{className:"text-gray-500 ml-2",children:["(",Array.isArray(o.tools)?o.tools.join(", "):o.tools,")"]}),o.tools==="all"&&u("previewAllTools"),g("previewPrompts",o.prompts),o.prompts==="all"&&u("previewAllPrompts"),g("previewResources",o.resources),o.resources==="all"&&u("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:p,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 whitespace-pre-wrap",children:c})}),m?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:m.map((r,o)=>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:r.name}),r.description&&e.jsx("p",{className:"text-sm text-gray-600 mt-1",children:r.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:x(r.servers)})]})]})})},o))})]}),e.jsxs("div",{className:"flex justify-end space-x-4",children:[e.jsx("button",{onClick:()=>N(null),disabled:C,className:"hub-btn",children:t("common.back")}),e.jsx("button",{onClick:D,disabled:C,className:"hub-btn primary",children:C?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:r=>y(r.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:k}),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:p,className:"hub-btn",children:t("common.cancel")}),e.jsx("button",{onClick:S,disabled:!d.trim(),className:"hub-btn primary",children:t("groupImport.preview")})]})]})]})})},Pe=({groups:n,onCancel:p})=>{const{t}=$(),[d,y]=h.useState(""),[c,b]=h.useState(""),[C,A]=h.useState([]),[m,N]=h.useState(!1),[k,E]=h.useState(!1),[S,D]=h.useState(null),u=r=>{A(o=>o.includes(r)?o.filter(j=>j!==r):[...o,r])},g=()=>{C.length===n.length?A([]):A(n.map(r=>r.id))},x=async()=>{if(!d.trim()){D(t("template.nameRequired"));return}E(!0),D(null);try{const r=await X("/templates/export",{name:d.trim(),description:c.trim()||void 0,groupIds:C.length>0?C:void 0,includeDisabledServers:m});if(r.success&&r.data){const o=r.data,j=new Blob([JSON.stringify(o,null,2)],{type:"application/json"}),I=URL.createObjectURL(j),F=document.createElement("a");F.href=I,F.download=`${o.name.replace(/[^a-zA-Z0-9-_]/g,"_")}.mcphub-template.json`,document.body.appendChild(F),F.click(),document.body.removeChild(F),URL.revokeObjectURL(I),p()}else D(r.message||t("template.exportFailed"))}catch(r){console.error("Export error:",r),D(t("template.exportFailed"))}finally{E(!1)}};return e.jsx("div",{className:"fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4",children:e.jsxs("div",{className:"bg-white dark:bg-gray-800 shadow rounded-lg p-6 w-full max-w-3xl max-h-[90vh] overflow-y-auto",children:[e.jsxs("div",{className:"flex justify-between items-center mb-6",children:[e.jsx("h2",{className:"text-xl font-semibold text-gray-900",children:t("template.exportTitle")}),e.jsx("button",{onClick:p,className:"text-gray-500 hover:text-gray-700",children:"✕"})]}),S&&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:S})}),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:r=>y(r.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:c,onChange:r=>b(r.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:g,className:"text-sm text-blue-600 hover:text-blue-800",children:C.length===n.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:n.map(r=>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:C.includes(r.id),onChange:()=>u(r.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:r.name}),r.description&&e.jsx("span",{className:"text-xs text-gray-500 ml-2",children:r.description})]})]},r.id))})]}),e.jsxs("label",{className:"flex items-center space-x-2 cursor-pointer",children:[e.jsx("input",{type:"checkbox",checked:m,onChange:r=>N(r.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:p,className:"hub-btn",children:t("common.cancel")}),e.jsx("button",{onClick:x,disabled:k||!d.trim(),className:"hub-btn primary",children:t(k?"template.exporting":"template.export")})]})]})})},Ge=({onSuccess:n,onCancel:p})=>{const{t}=$(),[d,y]=h.useState(null),[c,b]=h.useState(null),[C,A]=h.useState(!1),[m,N]=h.useState(null),k=h.useRef(null),E=u=>{var r;b(null),y(null),N(null);const g=(r=u.target.files)==null?void 0:r[0];if(!g)return;const x=new FileReader;x.onload=o=>{var j;try{const I=JSON.parse((j=o.target)==null?void 0:j.result);if(!I.version||!I.name||!I.servers||!I.groups){b(t("template.invalidFormat"));return}y(I)}catch{b(t("template.parseError"))}},x.readAsText(g)},S=u=>{if(b(null),y(null),N(null),!!u.trim())try{const g=JSON.parse(u.trim());if(!g.version||!g.name||!g.servers||!g.groups){b(t("template.invalidFormat"));return}y(g)}catch{b(t("template.parseError"))}},D=async()=>{if(d){A(!0),b(null);try{const u=await X("/templates/import",d);u.data?(N(u.data),u.data.success&&n()):b(u.message||t("template.importFailed"))}catch(u){console.error("Import error:",u),b(t("template.importFailed"))}finally{A(!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:p,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})}),m&&e.jsxs("div",{className:`mb-4 p-4 rounded border-l-4 ${m.success?"bg-green-50 border-green-500":"bg-yellow-50 border-yellow-500"}`,children:[e.jsx("p",{className:m.success?"text-green-700":"text-yellow-700",children:t("template.importResult",{serversCreated:m.serversCreated,serversSkipped:m.serversSkipped,groupsCreated:m.groupsCreated,groupsSkipped:m.groupsSkipped})}),m.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:m.requiredEnvVars.map(u=>e.jsx("li",{className:"font-mono",children:u},u))})]})]}),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(([u,g])=>e.jsxs("div",{className:"px-3 py-2",children:[e.jsx("span",{className:"text-sm font-medium text-gray-900",children:u}),e.jsx("span",{className:"text-xs text-gray-500 ml-2",children:g.type||"stdio"})]},u))})]}),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((u,g)=>e.jsxs("div",{className:"px-3 py-2",children:[e.jsx("span",{className:"text-sm font-medium text-gray-900",children:u.name}),u.description&&e.jsx("span",{className:"text-xs text-gray-500 ml-2",children:u.description}),e.jsxs("div",{className:"text-xs text-gray-500 mt-1",children:[u.servers.length," ",t("template.serversInGroup")]})]},g))})]}),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(u=>e.jsx("li",{children:u},u))})]})]}),e.jsxs("div",{className:"flex justify-end space-x-4 mt-6",children:[e.jsx("button",{onClick:()=>{y(null),N(null),k.current&&(k.current.value="")},className:"hub-btn",children:t("common.back")}),e.jsx("button",{onClick:D,disabled:C,className:"hub-btn primary",children:t(C?"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:k,type:"file",accept:".json",onChange:E,className:"block text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100"})})]}),e.jsxs("div",{className:"relative my-4",children:[e.jsx("div",{className:"absolute inset-0 flex items-center",children:e.jsx("div",{className:"w-full border-t border-gray-300"})}),e.jsx("div",{className:"relative flex justify-center text-sm",children:e.jsx("span",{className:"bg-white dark:bg-gray-800 px-2 text-gray-500",children: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:u=>S(u.target.value)})]}),e.jsx("div",{className:"flex justify-end space-x-4",children:e.jsx("button",{onClick:p,className:"hub-btn",children:t("common.cancel")})})]})]})})},Ke=()=>{const{t:n}=$(),{groups:p,loading:t,error:d,setError:y,deleteGroup:c,triggerRefresh:b}=H(),{allServers:C}=Y({refreshOnMount:!0}),{groupCosts:A,refetch:m}=Z();h.useEffect(()=>{m()},[p,C,m]);const[N,k]=h.useState(null),[E,S]=h.useState(!1),[D,u]=h.useState(!1),[g,x]=h.useState(!1),[r,o]=h.useState(!1),j=async I=>{const F=await c(I);(!F||!F.success)&&y((F==null?void 0:F.message)||n("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:n("pages.groups.title")}),e.jsxs("p",{className:"hub-sub",children:[e.jsx("span",{className:"hub-num",children:p.length})," ",n("nav.groups").toLowerCase()]})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs("button",{className:"hub-btn",onClick:()=>u(!0),children:[e.jsx(te,{size:13})," ",n("groupImport.button")]}),e.jsxs("button",{className:"hub-btn",onClick:()=>x(!0),children:[e.jsx(ke,{size:13})," ",n("template.exportButton")]}),e.jsxs("button",{className:"hub-btn",onClick:()=>o(!0),children:[e.jsx(te,{size:13})," ",n("template.importButton")]}),e.jsxs("button",{className:"hub-btn primary",onClick:()=>S(!0),children:[e.jsx(re,{size:13})," ",n("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(Se,{size:14,className:"flex-shrink-0"}),e.jsx("span",{className:"truncate text-[13px]",children:d})]}),e.jsx("button",{className:"hub-icon-btn sm",onClick:()=>y(null),children:e.jsx(we,{size:13})})]}),t?e.jsx("div",{className:"hub-card p-6 text-center",style:{color:"var(--hub-ink-3)"},children:n("app.loading")}):p.length===0?e.jsx("div",{className:"hub-card p-10 text-center",style:{color:"var(--hub-ink-3)"},children:n("groups.noGroups")}):e.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-2 gap-3.5",children:[p.map(I=>e.jsx(Fe,{group:I,servers:C,onEdit:k,onDelete:j,cost:A.find(F=>F.id===I.id)},I.id)),e.jsx("button",{onClick:()=>S(!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(re,{size:16})}),e.jsx("div",{style:{fontSize:13,fontWeight:500,color:"var(--hub-ink-2)"},children:n("groups.add")}),e.jsx("div",{style:{fontSize:12,marginTop:2},children:n("groups.addNew")})]})})]}),E&&e.jsx(De,{onAdd:()=>{S(!1),b()},onCancel:()=>S(!1)}),D&&e.jsx(Re,{onSuccess:()=>{u(!1),b()},onCancel:()=>u(!1)}),N&&e.jsx(Ae,{group:N,onEdit:()=>{k(null),b()},onCancel:()=>k(null)}),g&&e.jsx(Pe,{groups:p,onCancel:()=>x(!1)}),r&&e.jsx(Ge,{onSuccess:()=>{o(!1),b()},onCancel:()=>o(!1)})]})};export{Ke as default};
|
|
33
|
+
//# sourceMappingURL=GroupsPage-Dp-NU5wZ.js.map
|