@samanhappy/mcphub 1.2.2 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/controllers/bearerKeyController.js +50 -2
  2. package/dist/controllers/bearerKeyController.js.map +1 -1
  3. package/dist/services/mcpService.js +131 -5
  4. package/dist/services/mcpService.js.map +1 -1
  5. package/dist/services/personalOAuthService.js +3 -1
  6. package/dist/services/personalOAuthService.js.map +1 -1
  7. package/dist/services/sseService.js +7 -5
  8. package/dist/services/sseService.js.map +1 -1
  9. package/dist/utils/resourceTemplateMatch.js +229 -0
  10. package/dist/utils/resourceTemplateMatch.js.map +1 -0
  11. package/dist/utils/upstreamError.js +10 -1
  12. package/dist/utils/upstreamError.js.map +1 -1
  13. package/frontend/dist/assets/{ActivityPage-CB41zWix.js → ActivityPage-DhNQ7kmd.js} +2 -2
  14. package/frontend/dist/assets/{ActivityPage-CB41zWix.js.map → ActivityPage-DhNQ7kmd.js.map} +1 -1
  15. package/frontend/dist/assets/{CredentialsPage-BecFRfro.js → CredentialsPage-DysT6EhJ.js} +2 -2
  16. package/frontend/dist/assets/{CredentialsPage-BecFRfro.js.map → CredentialsPage-DysT6EhJ.js.map} +1 -1
  17. package/frontend/dist/assets/{Dashboard-CwifBwm4.js → Dashboard-CIkS2wIr.js} +2 -2
  18. package/frontend/dist/assets/{Dashboard-CwifBwm4.js.map → Dashboard-CIkS2wIr.js.map} +1 -1
  19. package/frontend/dist/assets/{EndpointCopy-BeLf_ow_.js → EndpointCopy-zuPM6wTV.js} +2 -2
  20. package/frontend/dist/assets/{EndpointCopy-BeLf_ow_.js.map → EndpointCopy-zuPM6wTV.js.map} +1 -1
  21. package/frontend/dist/assets/{GroupsPage-DLL1tFj8.js → GroupsPage-DXE8on9r.js} +2 -2
  22. package/frontend/dist/assets/{GroupsPage-DLL1tFj8.js.map → GroupsPage-DXE8on9r.js.map} +1 -1
  23. package/frontend/dist/assets/{LoginPage-DvHL1HNV.js → LoginPage-DP5A-fpr.js} +2 -2
  24. package/frontend/dist/assets/{LoginPage-DvHL1HNV.js.map → LoginPage-DP5A-fpr.js.map} +1 -1
  25. package/frontend/dist/assets/{LogsPage-Bva6Y11K.js → LogsPage-kFZF0A3x.js} +2 -2
  26. package/frontend/dist/assets/{LogsPage-Bva6Y11K.js.map → LogsPage-kFZF0A3x.js.map} +1 -1
  27. package/frontend/dist/assets/{MarketPage-DGxNuifk.js → MarketPage-DaodfOD8.js} +2 -2
  28. package/frontend/dist/assets/{MarketPage-DGxNuifk.js.map → MarketPage-DaodfOD8.js.map} +1 -1
  29. package/frontend/dist/assets/{OAuthApprovalsPage-TzVNIJ27.js → OAuthApprovalsPage-BbkfaOs3.js} +2 -2
  30. package/frontend/dist/assets/{OAuthApprovalsPage-TzVNIJ27.js.map → OAuthApprovalsPage-BbkfaOs3.js.map} +1 -1
  31. package/frontend/dist/assets/{OAuthConsentPage-yQSgIbZ8.js → OAuthConsentPage-BX5MIl4y.js} +2 -2
  32. package/frontend/dist/assets/{OAuthConsentPage-yQSgIbZ8.js.map → OAuthConsentPage-BX5MIl4y.js.map} +1 -1
  33. package/frontend/dist/assets/{PromptsPage-CIAVpo9B.js → PromptsPage-DEIMpdNn.js} +2 -2
  34. package/frontend/dist/assets/{PromptsPage-CIAVpo9B.js.map → PromptsPage-DEIMpdNn.js.map} +1 -1
  35. package/frontend/dist/assets/{ResourcesPage-C5g-98tB.js → ResourcesPage-CjkaRPQd.js} +2 -2
  36. package/frontend/dist/assets/{ResourcesPage-C5g-98tB.js.map → ResourcesPage-CjkaRPQd.js.map} +1 -1
  37. package/frontend/dist/assets/{ServersPage-CJEgBQr-.js → ServersPage-LpIlO6by.js} +4 -4
  38. package/frontend/dist/assets/{ServersPage-CJEgBQr-.js.map → ServersPage-LpIlO6by.js.map} +1 -1
  39. package/frontend/dist/assets/SettingsPage-fL5-63Ve.js +14 -0
  40. package/frontend/dist/assets/SettingsPage-fL5-63Ve.js.map +1 -0
  41. package/frontend/dist/assets/{StatusDot-B81gkUBo.js → StatusDot-DyEnhqJV.js} +2 -2
  42. package/frontend/dist/assets/{StatusDot-B81gkUBo.js.map → StatusDot-DyEnhqJV.js.map} +1 -1
  43. package/frontend/dist/assets/{ToggleGroup-DZ39486g.js → ToggleGroup-CNkUGOeP.js} +2 -2
  44. package/frontend/dist/assets/{ToggleGroup-DZ39486g.js.map → ToggleGroup-CNkUGOeP.js.map} +1 -1
  45. package/frontend/dist/assets/{UsersPage-pdkVxTsL.js → UsersPage-DeGScomQ.js} +2 -2
  46. package/frontend/dist/assets/{UsersPage-pdkVxTsL.js.map → UsersPage-DeGScomQ.js.map} +1 -1
  47. package/frontend/dist/assets/{clipboard-BYlVdxdy.js → clipboard-PC3TyCTa.js} +2 -2
  48. package/frontend/dist/assets/{clipboard-BYlVdxdy.js.map → clipboard-PC3TyCTa.js.map} +1 -1
  49. package/frontend/dist/assets/{costRevision-D-WY0B73.js → costRevision-DkLRM8DI.js} +2 -2
  50. package/frontend/dist/assets/{costRevision-D-WY0B73.js.map → costRevision-DkLRM8DI.js.map} +1 -1
  51. package/frontend/dist/assets/index-8GliUQTA.js +3 -0
  52. package/frontend/dist/assets/{index-DKX_BExz.js.map → index-8GliUQTA.js.map} +1 -1
  53. package/frontend/dist/assets/{resourceService-CKAaXBkJ.js → resourceService-BN-FT_uq.js} +2 -2
  54. package/frontend/dist/assets/{resourceService-CKAaXBkJ.js.map → resourceService-BN-FT_uq.js.map} +1 -1
  55. package/frontend/dist/assets/{useCostData-DzjFGnw4.js → useCostData-DNk0WL-a.js} +2 -2
  56. package/frontend/dist/assets/{useCostData-DzjFGnw4.js.map → useCostData-DNk0WL-a.js.map} +1 -1
  57. package/frontend/dist/assets/{variableDetection-D75LR7nZ.js → variableDetection-DQg-vRNn.js} +2 -2
  58. package/frontend/dist/assets/{variableDetection-D75LR7nZ.js.map → variableDetection-DQg-vRNn.js.map} +1 -1
  59. package/frontend/dist/index.html +1 -1
  60. package/package.json +1 -1
  61. package/frontend/dist/assets/SettingsPage-Bub-g0mD.js +0 -14
  62. package/frontend/dist/assets/SettingsPage-Bub-g0mD.js.map +0 -1
  63. package/frontend/dist/assets/index-DKX_BExz.js +0 -3
@@ -1 +1 @@
1
- {"version":3,"file":"CredentialsPage-BecFRfro.js","sources":["../../src/utils/credentialCopySources.ts","../../src/pages/CredentialsPage.tsx"],"sourcesContent":["import type { MyCredentialBinding, CredentialSlot } from '../types';\n\nconst slotId = (slot: CredentialSlot) =>\n `${slot.target}.${slot.target === 'headers' ? slot.name.toLowerCase() : slot.name}`;\n\nexport function getCredentialCopySources(\n target: MyCredentialBinding,\n bindings: MyCredentialBinding[],\n) {\n return bindings\n .filter((source) => source.serverName !== target.serverName)\n .map((source) => {\n const missing = target.credentialTemplate\n .filter(\n (slot) =>\n !source.credentialTemplate.some((candidate) => slotId(candidate) === slotId(slot)),\n )\n .map((slot) => `${slot.target}.${slot.name}`);\n const incompatibleUrl = target.credentialTemplate.some((slot) => {\n const candidate = source.credentialTemplate.find((item) => slotId(item) === slotId(slot));\n return (\n slot.target === 'url' &&\n candidate &&\n !candidate.allowed?.every((value) => slot.allowed?.includes(value))\n );\n });\n const reason = !source.configured\n ? 'unconfigured'\n : missing.length\n ? 'missing'\n : incompatibleUrl\n ? 'url'\n : undefined;\n return { source, reason, missing };\n });\n}\n","import { useCallback, useEffect, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { useSearchParams } from 'react-router-dom';\nimport { getCredentialCopySources } from '../utils/credentialCopySources';\nimport { useAuth } from '../contexts/AuthContext';\nimport type { User, MyCredentialBinding } from '../types';\nimport { apiDelete, apiGet, apiPost, apiPut, type ApiResponse } from '../utils/fetchInterceptor';\n\nconst CredentialForm = ({\n binding,\n baseUrl,\n bindings,\n onChange,\n}: {\n binding: MyCredentialBinding;\n baseUrl: string;\n bindings: MyCredentialBinding[];\n onChange: () => Promise<void>;\n}) => {\n const { t } = useTranslation();\n const [mode, setMode] = useState<'view' | 'edit' | 'copy' | 'delete'>(\n binding.configured ? 'view' : 'edit',\n );\n const [values, setValues] = useState<Record<string, string>>({});\n const [sourceServer, setSourceServer] = useState('');\n const [overwrite, setOverwrite] = useState(false);\n const [busy, setBusy] = useState(false);\n const [feedback, setFeedback] = useState<{ message: string; error: boolean }>();\n const sources = getCredentialCopySources(binding, bindings);\n const selectedSource = sources.find(({ source }) => source.serverName === sourceServer);\n const hasBinding = Boolean(binding.updatedAt);\n const changeMode = (next: typeof mode) => {\n setMode(next);\n setValues({});\n setSourceServer('');\n setOverwrite(false);\n setFeedback(undefined);\n };\n const submit = async (action: 'save' | 'copy' | 'delete') => {\n if (busy) return;\n setBusy(true);\n setFeedback(undefined);\n try {\n const url = `${baseUrl}/${encodeURIComponent(binding.serverName)}`;\n const result =\n action === 'delete'\n ? await apiDelete<ApiResponse>(url)\n : action === 'copy'\n ? await apiPost<ApiResponse>(`${url}/copy`, { sourceServer, overwrite })\n : await apiPut<ApiResponse>(url, { values });\n if (!result.success) throw new Error(result.message || t('credentials.failed'));\n await onChange();\n changeMode('view');\n setFeedback({\n message: t(\n `credentials.${action === 'copy' ? 'copied' : action === 'delete' ? 'deleted' : 'saved'}`,\n ),\n error: false,\n });\n } catch (error) {\n setFeedback({\n message: error instanceof Error ? error.message : t('credentials.failed'),\n error: true,\n });\n } finally {\n setBusy(false);\n }\n };\n const sourceReason = (entry: (typeof sources)[number]) => {\n if (entry.reason === 'missing')\n return t('credentials.sourceMissing', { slots: entry.missing.join(', ') });\n if (entry.reason === 'unconfigured') return t('credentials.sourceUnconfigured');\n if (entry.reason === 'url') return t('credentials.sourceUrlMismatch');\n return '';\n };\n return (\n <section className=\"hub-card p-5 space-y-4\" aria-label={binding.serverName}>\n <div className=\"flex flex-col items-start gap-2 sm:flex-row sm:justify-between sm:gap-3\">\n <div className=\"min-w-0 space-y-1\">\n <h2 className=\"font-semibold break-words\">{binding.serverName}</h2>\n {binding.updatedAt && (\n <p className=\"text-xs text-[var(--hub-ink-3)]\">\n {t('credentials.updatedAt')}:{' '}\n <time dateTime={binding.updatedAt}>\n {new Date(binding.updatedAt).toLocaleString(undefined, {\n dateStyle: 'short',\n timeStyle: 'short',\n })}\n </time>\n </p>\n )}\n </div>\n <span className=\"hub-tag shrink-0\">\n {t(binding.configured ? 'credentials.savedStatus' : 'credentials.required')}\n </span>\n </div>\n {mode !== 'edit' && (\n <dl className=\"divide-y divide-[var(--hub-line-2)]\">\n {binding.credentialTemplate.map((slot) => {\n const id = `${slot.target}.${slot.name}`;\n return (\n <div key={id} className=\"flex items-start justify-between gap-4 py-2 text-sm\">\n <dt className=\"min-w-0\">\n <span className=\"font-medium break-words\">{slot.label || slot.name}</span>\n <span className=\"hub-mono block break-all text-xs text-[var(--hub-ink-3)]\">\n {id}\n </span>\n </dt>\n <dd className=\"hub-mono text-[var(--hub-ink-3)]\">\n {binding.valuePreviews?.[id] ||\n t(\n binding.configuredSlots.includes(id)\n ? 'credentials.savedStatus'\n : 'credentials.notSet',\n )}\n </dd>\n </div>\n );\n })}\n </dl>\n )}\n {mode === 'view' && (\n <div className=\"flex flex-wrap items-center gap-2\">\n <button type=\"button\" className=\"hub-btn primary\" onClick={() => changeMode('edit')}>\n {t(hasBinding ? 'credentials.update' : 'credentials.configure')}\n </button>\n <button type=\"button\" className=\"hub-btn\" onClick={() => changeMode('copy')}>\n {t('credentials.copyFrom')}\n </button>\n {hasBinding && (\n <details className=\"relative ml-auto\">\n <summary className=\"hub-btn cursor-pointer\">{t('credentials.moreActions')}</summary>\n <div className=\"absolute right-0 z-10 mt-2 rounded-lg bg-[var(--hub-surface)] p-2 shadow-lg hub-border\">\n <button\n type=\"button\"\n className=\"hub-btn danger whitespace-nowrap\"\n onClick={() => changeMode('delete')}\n >\n {t('credentials.delete')}\n </button>\n </div>\n </details>\n )}\n </div>\n )}\n {mode === 'edit' && (\n <form\n onSubmit={(event) => {\n event.preventDefault();\n void submit('save');\n }}\n >\n <fieldset disabled={busy} className=\"space-y-4\">\n <legend className=\"mb-2 text-sm font-medium\">\n {t(hasBinding ? 'credentials.update' : 'credentials.configure')}\n </legend>\n <p className=\"text-xs text-[var(--hub-ink-3)]\">\n {t(hasBinding ? 'credentials.replaceHint' : 'credentials.configureHint')}\n </p>\n {binding.credentialTemplate.map((slot) => {\n const id = `${slot.target}.${slot.name}`;\n return (\n <label key={id} className=\"block space-y-1 text-sm\">\n <span className=\"font-medium\">{slot.label || slot.name}</span>\n <span className=\"hub-mono block text-xs text-[var(--hub-ink-3)]\">{id}</span>\n {slot.target === 'url' ? (\n <select\n required\n className=\"hub-input\"\n value={values[id] || ''}\n onChange={(event) => setValues({ ...values, [id]: event.target.value })}\n >\n <option value=\"\">{t('credentials.enterValue')}</option>\n {slot.allowed?.map((value) => (\n <option key={value} value={value}>\n {value}\n </option>\n ))}\n </select>\n ) : (\n <input\n type=\"password\"\n required\n autoComplete=\"new-password\"\n maxLength={16384}\n value={values[id] || ''}\n placeholder={t(\n binding.configuredSlots.includes(id)\n ? 'credentials.replaceValue'\n : 'credentials.enterValue',\n )}\n className=\"hub-input\"\n onChange={(event) => setValues({ ...values, [id]: event.target.value })}\n />\n )}\n </label>\n );\n })}\n <div className=\"flex flex-wrap gap-2\">\n <button type=\"submit\" className=\"hub-btn primary\">\n {t(hasBinding ? 'credentials.replace' : 'credentials.save')}\n </button>\n <button type=\"button\" className=\"hub-btn\" onClick={() => changeMode('view')}>\n {t('credentials.cancel')}\n </button>\n <button type=\"button\" className=\"hub-btn\" onClick={() => changeMode('copy')}>\n {t('credentials.copyFrom')}\n </button>\n </div>\n </fieldset>\n </form>\n )}\n {mode === 'copy' && (\n <div className=\"space-y-3 border-t border-[var(--hub-line)] pt-4\">\n <label className=\"block space-y-1 text-sm\">\n <span className=\"font-medium\">{t('credentials.copyFrom')}</span>\n <select\n className=\"hub-input\"\n value={sourceServer}\n disabled={busy || !sources.length}\n onChange={(event) => {\n setSourceServer(event.target.value);\n setOverwrite(false);\n }}\n >\n <option value=\"\">\n {t(sources.length ? 'credentials.chooseSource' : 'credentials.noOtherServers')}\n </option>\n {sources.map((entry) => (\n <option\n key={entry.source.serverName}\n value={entry.source.serverName}\n disabled={Boolean(entry.reason)}\n >\n {entry.source.serverName}\n {entry.reason ? ` — ${sourceReason(entry)}` : ''}\n </option>\n ))}\n </select>\n </label>\n {!sources.some((entry) => !entry.reason) && (\n <div className=\"space-y-1 text-sm text-[var(--hub-ink-3)]\">\n <p>{t('credentials.noSourcesHelp')}</p>\n {sources.length > 0 && (\n <ul className=\"list-disc pl-5\">\n {sources.map((entry) => (\n <li key={entry.source.serverName} className=\"break-words\">\n {entry.source.serverName}: {sourceReason(entry)}\n </li>\n ))}\n </ul>\n )}\n </div>\n )}\n <p className=\"text-xs text-[var(--hub-ink-3)]\">{t('credentials.copyHint')}</p>\n {sourceServer && hasBinding && (\n <label className=\"flex items-start gap-2 text-sm\">\n <input\n type=\"checkbox\"\n checked={overwrite}\n disabled={busy}\n onChange={(event) => setOverwrite(event.target.checked)}\n />\n <span>{t('credentials.confirmOverwrite')}</span>\n </label>\n )}\n <div className=\"flex flex-wrap gap-2\">\n <button\n type=\"button\"\n className=\"hub-btn primary\"\n disabled={\n busy ||\n !selectedSource ||\n Boolean(selectedSource.reason) ||\n (hasBinding && !overwrite)\n }\n onClick={() => void submit('copy')}\n >\n {t('credentials.copy')}\n </button>\n <button\n type=\"button\"\n className=\"hub-btn\"\n disabled={busy}\n onClick={() => changeMode('view')}\n >\n {t('credentials.cancel')}\n </button>\n </div>\n </div>\n )}\n {mode === 'delete' && (\n <div className=\"space-y-3 border-t border-[var(--hub-line)] pt-4\">\n <p className=\"text-sm\">\n {t('credentials.deleteConfirm', { server: binding.serverName })}\n </p>\n <div className=\"flex flex-wrap gap-2\">\n <button\n type=\"button\"\n className=\"hub-btn danger\"\n disabled={busy}\n onClick={() => void submit('delete')}\n >\n {t('credentials.delete')}\n </button>\n <button\n type=\"button\"\n className=\"hub-btn\"\n disabled={busy}\n onClick={() => changeMode('view')}\n >\n {t('credentials.cancel')}\n </button>\n </div>\n </div>\n )}\n {feedback && (\n <p\n role={feedback.error ? 'alert' : 'status'}\n className=\"text-sm\"\n style={{ color: feedback.error ? 'var(--hub-err)' : 'var(--hub-ink-2)' }}\n >\n {feedback.message}\n </p>\n )}\n </section>\n );\n};\n\nfunction CredentialList({ baseUrl }: { baseUrl: string }) {\n const { t } = useTranslation();\n const [search, setSearch] = useSearchParams();\n const [bindings, setBindings] = useState<MyCredentialBinding[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState('');\n const reload = useCallback(async () => {\n try {\n const result = await apiGet<ApiResponse<MyCredentialBinding[]>>(baseUrl);\n if (!result.success) throw new Error(result.message || t('credentials.failed'));\n setBindings(result.data || []);\n setError('');\n } catch (error) {\n setError(error instanceof Error ? error.message : t('credentials.failed'));\n } finally {\n setLoading(false);\n }\n }, [baseUrl, t]);\n useEffect(() => {\n void reload();\n }, [reload]);\n const visible = bindings.filter(\n (binding) => !search.get('server') || binding.serverName === search.get('server'),\n );\n return (\n <div className=\"max-w-4xl mx-auto space-y-5\">\n <div>\n <h1 className=\"hub-h1\">{t('credentials.title')}</h1>\n <p className=\"hub-sub\">{t('credentials.description')}</p>\n </div>\n {search.has('server') && (\n <button\n className=\"hub-btn\"\n onClick={() =>\n setSearch((params) => {\n params.delete('server');\n return params;\n })\n }\n >\n {t('credentials.showAll')}\n </button>\n )}\n {error && (\n <div\n className=\"hub-card px-4 py-3 text-sm\"\n style={{ color: 'var(--hub-err)' }}\n role=\"alert\"\n >\n {error}\n </div>\n )}\n {loading ? (\n <div className=\"hub-card p-10 text-center text-sm\" style={{ color: 'var(--hub-ink-3)' }}>\n {t('credentials.loading')}\n </div>\n ) : !visible.length && !error ? (\n <div className=\"hub-card p-10 text-center text-sm\" style={{ color: 'var(--hub-ink-3)' }}>\n {t('credentials.empty')}\n </div>\n ) : null}\n {visible.map((binding) => (\n <CredentialForm\n key={binding.serverName}\n binding={binding}\n bindings={bindings}\n baseUrl={baseUrl}\n onChange={reload}\n />\n ))}\n </div>\n );\n}\n\nexport default function CredentialsPage() {\n const { t } = useTranslation();\n const { auth } = useAuth();\n const [search, setSearch] = useSearchParams();\n const [users, setUsers] = useState<User[]>([]);\n const [error, setError] = useState('');\n const admin = Boolean(auth.user?.isAdmin);\n const username = admin ? search.get('user') || '' : '';\n const baseUrl = username ? `/users/${encodeURIComponent(username)}/credentials` : '/credentials';\n useEffect(() => {\n if (!admin) return;\n let active = true;\n void apiGet<ApiResponse<User[]>>('/users')\n .then((result) => {\n if (!active) return;\n if (!result.success) throw new Error(result.message || t('credentials.failed'));\n setUsers(result.data || []);\n })\n .catch((error: unknown) => {\n if (active) setError(error instanceof Error ? error.message : t('credentials.failed'));\n });\n return () => {\n active = false;\n };\n }, [admin, t]);\n return (\n <div className=\"space-y-5\">\n {admin && (\n <div className=\"max-w-4xl mx-auto\">\n <label className=\"block space-y-1 text-sm\">\n <span>{t('credentials.targetUser')}</span>\n <select\n className=\"hub-input\"\n value={username}\n onChange={(event) =>\n setSearch((params) => {\n if (event.target.value) params.set('user', event.target.value);\n else params.delete('user');\n return params;\n })\n }\n >\n <option value=\"\">{t('credentials.myCredentials')}</option>\n {users.map((user) => (\n <option key={user.username} value={user.username}>\n {user.username}\n </option>\n ))}\n </select>\n </label>\n {error && <p role=\"alert\">{error}</p>}\n </div>\n )}\n <CredentialList key={baseUrl} baseUrl={baseUrl} />\n </div>\n );\n}\n"],"names":["slotId","slot","getCredentialCopySources","target","bindings","source","missing","candidate","incompatibleUrl","item","_a","value","reason","CredentialForm","binding","baseUrl","onChange","t","useTranslation","mode","setMode","useState","values","setValues","sourceServer","setSourceServer","overwrite","setOverwrite","busy","setBusy","feedback","setFeedback","sources","selectedSource","hasBinding","changeMode","next","submit","action","url","result","apiDelete","apiPost","apiPut","error","sourceReason","entry","jsxs","jsx","id","event","CredentialList","search","setSearch","useSearchParams","setBindings","loading","setLoading","setError","reload","useCallback","apiGet","useEffect","visible","params","CredentialsPage","auth","useAuth","users","setUsers","admin","username","active","user"],"mappings":"sNAEA,MAAMA,EAAUC,GACd,GAAGA,EAAK,MAAM,IAAIA,EAAK,SAAW,UAAYA,EAAK,KAAK,YAAA,EAAgBA,EAAK,IAAI,GAE5E,SAASC,EACdC,EACAC,EACA,CACA,OAAOA,EACJ,OAAQC,GAAWA,EAAO,aAAeF,EAAO,UAAU,EAC1D,IAAKE,GAAW,CACf,MAAMC,EAAUH,EAAO,mBACpB,OACEF,GACC,CAACI,EAAO,mBAAmB,KAAME,GAAcP,EAAOO,CAAS,IAAMP,EAAOC,CAAI,CAAC,CAAA,EAEpF,IAAKA,GAAS,GAAGA,EAAK,MAAM,IAAIA,EAAK,IAAI,EAAE,EACxCO,EAAkBL,EAAO,mBAAmB,KAAMF,GAAS,OAC/D,MAAMM,EAAYF,EAAO,mBAAmB,KAAMI,GAAST,EAAOS,CAAI,IAAMT,EAAOC,CAAI,CAAC,EACxF,OACEA,EAAK,SAAW,OAChBM,GACA,GAACG,EAAAH,EAAU,UAAV,MAAAG,EAAmB,MAAOC,GAAA,OAAU,OAAAD,EAAAT,EAAK,UAAL,YAAAS,EAAc,SAASC,KAEhE,CAAC,EACKC,EAAUP,EAAO,WAEnBC,EAAQ,OACN,UACAE,EACE,MACA,OALJ,eAMJ,MAAO,CAAE,OAAAH,EAAQ,OAAAO,EAAQ,QAAAN,CAAA,CAC3B,CAAC,CACL,CC3BA,MAAMO,EAAiB,CAAC,CACtB,QAAAC,EACA,QAAAC,EACA,SAAAX,EACA,SAAAY,CACF,IAKM,CACJ,KAAM,CAAE,EAAAC,CAAA,EAAMC,EAAA,EACR,CAACC,EAAMC,CAAO,EAAIC,EAAAA,SACtBP,EAAQ,WAAa,OAAS,MAAA,EAE1B,CAACQ,EAAQC,CAAS,EAAIF,EAAAA,SAAiC,CAAA,CAAE,EACzD,CAACG,EAAcC,CAAe,EAAIJ,EAAAA,SAAS,EAAE,EAC7C,CAACK,EAAWC,CAAY,EAAIN,EAAAA,SAAS,EAAK,EAC1C,CAACO,EAAMC,CAAO,EAAIR,EAAAA,SAAS,EAAK,EAChC,CAACS,EAAUC,CAAW,EAAIV,WAAA,EAC1BW,EAAU9B,EAAyBY,EAASV,CAAQ,EACpD6B,EAAiBD,EAAQ,KAAK,CAAC,CAAE,OAAA3B,KAAaA,EAAO,aAAemB,CAAY,EAChFU,EAAa,EAAQpB,EAAQ,UAC7BqB,EAAcC,GAAsB,CACxChB,EAAQgB,CAAI,EACZb,EAAU,CAAA,CAAE,EACZE,EAAgB,EAAE,EAClBE,EAAa,EAAK,EAClBI,EAAY,MAAS,CACvB,EACMM,EAAS,MAAOC,GAAuC,CAC3D,GAAI,CAAAV,EACJ,CAAAC,EAAQ,EAAI,EACZE,EAAY,MAAS,EACrB,GAAI,CACF,MAAMQ,EAAM,GAAGxB,CAAO,IAAI,mBAAmBD,EAAQ,UAAU,CAAC,GAC1D0B,EACJF,IAAW,SACP,MAAMG,EAAuBF,CAAG,EAChCD,IAAW,OACT,MAAMI,EAAqB,GAAGH,CAAG,QAAS,CAAE,aAAAf,EAAc,UAAAE,CAAA,CAAW,EACrE,MAAMiB,EAAoBJ,EAAK,CAAE,OAAAjB,EAAQ,EACjD,GAAI,CAACkB,EAAO,QAAS,MAAM,IAAI,MAAMA,EAAO,SAAWvB,EAAE,oBAAoB,CAAC,EAC9E,MAAMD,EAAA,EACNmB,EAAW,MAAM,EACjBJ,EAAY,CACV,QAASd,EACP,eAAeqB,IAAW,OAAS,SAAWA,IAAW,SAAW,UAAY,OAAO,EAAA,EAEzF,MAAO,EAAA,CACR,CACH,OAASM,EAAO,CACdb,EAAY,CACV,QAASa,aAAiB,MAAQA,EAAM,QAAU3B,EAAE,oBAAoB,EACxE,MAAO,EAAA,CACR,CACH,QAAA,CACEY,EAAQ,EAAK,CACf,EACF,EACMgB,EAAgBC,GAChBA,EAAM,SAAW,UACZ7B,EAAE,4BAA6B,CAAE,MAAO6B,EAAM,QAAQ,KAAK,IAAI,EAAG,EACvEA,EAAM,SAAW,eAAuB7B,EAAE,gCAAgC,EAC1E6B,EAAM,SAAW,MAAc7B,EAAE,+BAA+B,EAC7D,GAET,cACG,UAAA,CAAQ,UAAU,yBAAyB,aAAYH,EAAQ,WAC9D,SAAA,CAAAiC,EAAAA,KAAC,MAAA,CAAI,UAAU,0EACb,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,oBACb,SAAA,CAAAC,EAAAA,IAAC,KAAA,CAAG,UAAU,4BAA6B,SAAAlC,EAAQ,WAAW,EAC7DA,EAAQ,WACPiC,OAAC,IAAA,CAAE,UAAU,kCACV,SAAA,CAAA9B,EAAE,uBAAuB,EAAE,IAAE,IAC9B+B,EAAAA,IAAC,OAAA,CAAK,SAAUlC,EAAQ,UACrB,SAAA,IAAI,KAAKA,EAAQ,SAAS,EAAE,eAAe,OAAW,CACrD,UAAW,QACX,UAAW,OAAA,CACZ,CAAA,CACH,CAAA,CAAA,CACF,CAAA,EAEJ,EACAkC,EAAAA,IAAC,QAAK,UAAU,mBACb,WAAElC,EAAQ,WAAa,0BAA4B,sBAAsB,CAAA,CAC5E,CAAA,EACF,EACCK,IAAS,QACR6B,MAAC,KAAA,CAAG,UAAU,sCACX,SAAAlC,EAAQ,mBAAmB,IAAKb,GAAS,OACxC,MAAMgD,EAAK,GAAGhD,EAAK,MAAM,IAAIA,EAAK,IAAI,GACtC,OACE8C,EAAAA,KAAC,MAAA,CAAa,UAAU,sDACtB,SAAA,CAAAA,EAAAA,KAAC,KAAA,CAAG,UAAU,UACZ,SAAA,CAAAC,MAAC,QAAK,UAAU,0BAA2B,SAAA/C,EAAK,OAASA,EAAK,KAAK,EACnE+C,EAAAA,IAAC,OAAA,CAAK,UAAU,2DACb,SAAAC,CAAA,CACH,CAAA,EACF,QACC,KAAA,CAAG,UAAU,mCACX,WAAAvC,EAAAI,EAAQ,gBAAR,YAAAJ,EAAwBuC,KACvBhC,EACEH,EAAQ,gBAAgB,SAASmC,CAAE,EAC/B,0BACA,oBAAA,CACN,CACJ,CAAA,CAAA,EAdQA,CAeV,CAEJ,CAAC,CAAA,CACH,EAED9B,IAAS,QACR4B,OAAC,MAAA,CAAI,UAAU,oCACb,SAAA,CAAAC,EAAAA,IAAC,SAAA,CAAO,KAAK,SAAS,UAAU,kBAAkB,QAAS,IAAMb,EAAW,MAAM,EAC/E,SAAAlB,EAAEiB,EAAa,qBAAuB,uBAAuB,EAChE,EACAc,EAAAA,IAAC,SAAA,CAAO,KAAK,SAAS,UAAU,UAAU,QAAS,IAAMb,EAAW,MAAM,EACvE,SAAAlB,EAAE,sBAAsB,CAAA,CAC3B,EACCiB,GACCa,EAAAA,KAAC,UAAA,CAAQ,UAAU,mBACjB,SAAA,CAAAC,MAAC,UAAA,CAAQ,UAAU,yBAA0B,SAAA/B,EAAE,yBAAyB,EAAE,EAC1E+B,EAAAA,IAAC,MAAA,CAAI,UAAU,yFACb,SAAAA,EAAAA,IAAC,SAAA,CACC,KAAK,SACL,UAAU,mCACV,QAAS,IAAMb,EAAW,QAAQ,EAEjC,WAAE,oBAAoB,CAAA,CAAA,CACzB,CACF,CAAA,CAAA,CACF,CAAA,EAEJ,EAEDhB,IAAS,QACR6B,EAAAA,IAAC,OAAA,CACC,SAAWE,GAAU,CACnBA,EAAM,eAAA,EACDb,EAAO,MAAM,CACpB,EAEA,SAAAU,EAAAA,KAAC,WAAA,CAAS,SAAUnB,EAAM,UAAU,YAClC,SAAA,CAAAoB,EAAAA,IAAC,UAAO,UAAU,2BACf,WAAEd,EAAa,qBAAuB,uBAAuB,EAChE,EACAc,EAAAA,IAAC,KAAE,UAAU,kCACV,WAAEd,EAAa,0BAA4B,2BAA2B,EACzE,EACCpB,EAAQ,mBAAmB,IAAKb,GAAS,OACxC,MAAMgD,EAAK,GAAGhD,EAAK,MAAM,IAAIA,EAAK,IAAI,GACtC,OACE8C,EAAAA,KAAC,QAAA,CAAe,UAAU,0BACxB,SAAA,CAAAC,MAAC,QAAK,UAAU,cAAe,SAAA/C,EAAK,OAASA,EAAK,KAAK,EACvD+C,EAAAA,IAAC,OAAA,CAAK,UAAU,iDAAkD,SAAAC,EAAG,EACpEhD,EAAK,SAAW,MACf8C,EAAAA,KAAC,SAAA,CACC,SAAQ,GACR,UAAU,YACV,MAAOzB,EAAO2B,CAAE,GAAK,GACrB,SAAWC,GAAU3B,EAAU,CAAE,GAAGD,EAAQ,CAAC2B,CAAE,EAAGC,EAAM,OAAO,MAAO,EAEtE,SAAA,CAAAF,MAAC,SAAA,CAAO,MAAM,GAAI,SAAA/B,EAAE,wBAAwB,EAAE,GAC7CP,EAAAT,EAAK,UAAL,YAAAS,EAAc,IAAKC,SACjB,SAAA,CAAmB,MAAAA,EACjB,SAAAA,CAAA,EADUA,CAEb,EACD,CAAA,CAAA,EAGHqC,EAAAA,IAAC,QAAA,CACC,KAAK,WACL,SAAQ,GACR,aAAa,eACb,UAAW,MACX,MAAO1B,EAAO2B,CAAE,GAAK,GACrB,YAAahC,EACXH,EAAQ,gBAAgB,SAASmC,CAAE,EAC/B,2BACA,wBAAA,EAEN,UAAU,YACV,SAAWC,GAAU3B,EAAU,CAAE,GAAGD,EAAQ,CAAC2B,CAAE,EAAGC,EAAM,OAAO,MAAO,CAAA,CAAA,CACxE,CAAA,EA/BQD,CAiCZ,CAEJ,CAAC,EACDF,EAAAA,KAAC,MAAA,CAAI,UAAU,uBACb,SAAA,CAAAC,EAAAA,IAAC,SAAA,CAAO,KAAK,SAAS,UAAU,kBAC7B,SAAA/B,EAAEiB,EAAa,sBAAwB,kBAAkB,CAAA,CAC5D,EACAc,EAAAA,IAAC,SAAA,CAAO,KAAK,SAAS,UAAU,UAAU,QAAS,IAAMb,EAAW,MAAM,EACvE,SAAAlB,EAAE,oBAAoB,CAAA,CACzB,EACA+B,EAAAA,IAAC,SAAA,CAAO,KAAK,SAAS,UAAU,UAAU,QAAS,IAAMb,EAAW,MAAM,EACvE,SAAAlB,EAAE,sBAAsB,CAAA,CAC3B,CAAA,CAAA,CACF,CAAA,CAAA,CACF,CAAA,CAAA,EAGHE,IAAS,QACR4B,OAAC,MAAA,CAAI,UAAU,mDACb,SAAA,CAAAA,EAAAA,KAAC,QAAA,CAAM,UAAU,0BACf,SAAA,CAAAC,MAAC,OAAA,CAAK,UAAU,cAAe,SAAA/B,EAAE,sBAAsB,EAAE,EACzD8B,EAAAA,KAAC,SAAA,CACC,UAAU,YACV,MAAOvB,EACP,SAAUI,GAAQ,CAACI,EAAQ,OAC3B,SAAWkB,GAAU,CACnBzB,EAAgByB,EAAM,OAAO,KAAK,EAClCvB,EAAa,EAAK,CACpB,EAEA,SAAA,CAAAqB,EAAAA,IAAC,SAAA,CAAO,MAAM,GACX,SAAA/B,EAAEe,EAAQ,OAAS,2BAA6B,4BAA4B,CAAA,CAC/E,EACCA,EAAQ,IAAKc,GACZC,EAAAA,KAAC,SAAA,CAEC,MAAOD,EAAM,OAAO,WACpB,SAAU,EAAQA,EAAM,OAEvB,SAAA,CAAAA,EAAM,OAAO,WACbA,EAAM,OAAS,MAAMD,EAAaC,CAAK,CAAC,GAAK,EAAA,CAAA,EALzCA,EAAM,OAAO,UAAA,CAOrB,CAAA,CAAA,CAAA,CACH,EACF,EACC,CAACd,EAAQ,KAAMc,GAAU,CAACA,EAAM,MAAM,GACrCC,EAAAA,KAAC,MAAA,CAAI,UAAU,4CACb,SAAA,CAAAC,EAAAA,IAAC,IAAA,CAAG,SAAA/B,EAAE,2BAA2B,CAAA,CAAE,EAClCe,EAAQ,OAAS,GAChBgB,EAAAA,IAAC,MAAG,UAAU,iBACX,SAAAhB,EAAQ,IAAKc,GACZC,EAAAA,KAAC,KAAA,CAAiC,UAAU,cACzC,SAAA,CAAAD,EAAM,OAAO,WAAW,KAAGD,EAAaC,CAAK,CAAA,CAAA,EADvCA,EAAM,OAAO,UAEtB,CACD,CAAA,CACH,CAAA,EAEJ,QAED,IAAA,CAAE,UAAU,kCAAmC,SAAA7B,EAAE,sBAAsB,EAAE,EACzEO,GAAgBU,GACfa,OAAC,QAAA,CAAM,UAAU,iCACf,SAAA,CAAAC,EAAAA,IAAC,QAAA,CACC,KAAK,WACL,QAAStB,EACT,SAAUE,EACV,SAAWsB,GAAUvB,EAAauB,EAAM,OAAO,OAAO,CAAA,CAAA,EAExDF,EAAAA,IAAC,OAAA,CAAM,SAAA/B,EAAE,8BAA8B,CAAA,CAAE,CAAA,EAC3C,EAEF8B,EAAAA,KAAC,MAAA,CAAI,UAAU,uBACb,SAAA,CAAAC,EAAAA,IAAC,SAAA,CACC,KAAK,SACL,UAAU,kBACV,SACEpB,GACA,CAACK,GACD,EAAQA,EAAe,QACtBC,GAAc,CAACR,EAElB,QAAS,IAAM,KAAKW,EAAO,MAAM,EAEhC,WAAE,kBAAkB,CAAA,CAAA,EAEvBW,EAAAA,IAAC,SAAA,CACC,KAAK,SACL,UAAU,UACV,SAAUpB,EACV,QAAS,IAAMO,EAAW,MAAM,EAE/B,WAAE,oBAAoB,CAAA,CAAA,CACzB,CAAA,CACF,CAAA,EACF,EAEDhB,IAAS,UACR4B,OAAC,MAAA,CAAI,UAAU,mDACb,SAAA,CAAAC,EAAAA,IAAC,IAAA,CAAE,UAAU,UACV,SAAA/B,EAAE,4BAA6B,CAAE,OAAQH,EAAQ,UAAA,CAAY,CAAA,CAChE,EACAiC,EAAAA,KAAC,MAAA,CAAI,UAAU,uBACb,SAAA,CAAAC,EAAAA,IAAC,SAAA,CACC,KAAK,SACL,UAAU,iBACV,SAAUpB,EACV,QAAS,IAAM,KAAKS,EAAO,QAAQ,EAElC,WAAE,oBAAoB,CAAA,CAAA,EAEzBW,EAAAA,IAAC,SAAA,CACC,KAAK,SACL,UAAU,UACV,SAAUpB,EACV,QAAS,IAAMO,EAAW,MAAM,EAE/B,WAAE,oBAAoB,CAAA,CAAA,CACzB,CAAA,CACF,CAAA,EACF,EAEDL,GACCkB,EAAAA,IAAC,IAAA,CACC,KAAMlB,EAAS,MAAQ,QAAU,SACjC,UAAU,UACV,MAAO,CAAE,MAAOA,EAAS,MAAQ,iBAAmB,kBAAA,EAEnD,SAAAA,EAAS,OAAA,CAAA,CACZ,EAEJ,CAEJ,EAEA,SAASqB,EAAe,CAAE,QAAApC,GAAgC,CACxD,KAAM,CAAE,EAAAE,CAAA,EAAMC,EAAA,EACR,CAACkC,EAAQC,CAAS,EAAIC,EAAA,EACtB,CAAClD,EAAUmD,CAAW,EAAIlC,EAAAA,SAAgC,CAAA,CAAE,EAC5D,CAACmC,EAASC,CAAU,EAAIpC,EAAAA,SAAS,EAAI,EACrC,CAACuB,EAAOc,CAAQ,EAAIrC,EAAAA,SAAS,EAAE,EAC/BsC,EAASC,EAAAA,YAAY,SAAY,CACrC,GAAI,CACF,MAAMpB,EAAS,MAAMqB,EAA2C9C,CAAO,EACvE,GAAI,CAACyB,EAAO,QAAS,MAAM,IAAI,MAAMA,EAAO,SAAWvB,EAAE,oBAAoB,CAAC,EAC9EsC,EAAYf,EAAO,MAAQ,EAAE,EAC7BkB,EAAS,EAAE,CACb,OAASd,EAAO,CACdc,EAASd,aAAiB,MAAQA,EAAM,QAAU3B,EAAE,oBAAoB,CAAC,CAC3E,QAAA,CACEwC,EAAW,EAAK,CAClB,CACF,EAAG,CAAC1C,EAASE,CAAC,CAAC,EACf6C,EAAAA,UAAU,IAAM,CACTH,EAAA,CACP,EAAG,CAACA,CAAM,CAAC,EACX,MAAMI,EAAU3D,EAAS,OACtBU,GAAY,CAACsC,EAAO,IAAI,QAAQ,GAAKtC,EAAQ,aAAesC,EAAO,IAAI,QAAQ,CAAA,EAElF,OACEL,EAAAA,KAAC,MAAA,CAAI,UAAU,8BACb,SAAA,CAAAA,OAAC,MAAA,CACC,SAAA,CAAAC,MAAC,KAAA,CAAG,UAAU,SAAU,SAAA/B,EAAE,mBAAmB,EAAE,QAC9C,IAAA,CAAE,UAAU,UAAW,SAAAA,EAAE,yBAAyB,CAAA,CAAE,CAAA,EACvD,EACCmC,EAAO,IAAI,QAAQ,GAClBJ,EAAAA,IAAC,SAAA,CACC,UAAU,UACV,QAAS,IACPK,EAAWW,IACTA,EAAO,OAAO,QAAQ,EACfA,EACR,EAGF,WAAE,qBAAqB,CAAA,CAAA,EAG3BpB,GACCI,EAAAA,IAAC,MAAA,CACC,UAAU,6BACV,MAAO,CAAE,MAAO,gBAAA,EAChB,KAAK,QAEJ,SAAAJ,CAAA,CAAA,EAGJY,EACCR,EAAAA,IAAC,MAAA,CAAI,UAAU,oCAAoC,MAAO,CAAE,MAAO,kBAAA,EAChE,SAAA/B,EAAE,qBAAqB,EAC1B,EACE,CAAC8C,EAAQ,QAAU,CAACnB,EACtBI,EAAAA,IAAC,MAAA,CAAI,UAAU,oCAAoC,MAAO,CAAE,MAAO,kBAAA,EAChE,SAAA/B,EAAE,mBAAmB,EACxB,EACE,KACH8C,EAAQ,IAAKjD,GACZkC,EAAAA,IAACnC,EAAA,CAEC,QAAAC,EACA,SAAAV,EACA,QAAAW,EACA,SAAU4C,CAAA,EAJL7C,EAAQ,UAAA,CAMhB,CAAA,EACH,CAEJ,CAEA,SAAwBmD,GAAkB,OACxC,KAAM,CAAE,CAAA,EAAM/C,EAAA,EACR,CAAE,KAAAgD,CAAA,EAASC,EAAA,EACX,CAACf,EAAQC,CAAS,EAAIC,EAAA,EACtB,CAACc,EAAOC,CAAQ,EAAIhD,EAAAA,SAAiB,CAAA,CAAE,EACvC,CAACuB,EAAOc,CAAQ,EAAIrC,EAAAA,SAAS,EAAE,EAC/BiD,EAAQ,IAAQ5D,EAAAwD,EAAK,OAAL,MAAAxD,EAAW,SAC3B6D,EAAWD,GAAQlB,EAAO,IAAI,MAAM,GAAK,GACzCrC,EAAUwD,EAAW,UAAU,mBAAmBA,CAAQ,CAAC,eAAiB,eAClFT,OAAAA,EAAAA,UAAU,IAAM,CACd,GAAI,CAACQ,EAAO,OACZ,IAAIE,EAAS,GACb,OAAKX,EAA4B,QAAQ,EACtC,KAAMrB,GAAW,CAChB,GAAKgC,EACL,IAAI,CAAChC,EAAO,QAAS,MAAM,IAAI,MAAMA,EAAO,SAAW,EAAE,oBAAoB,CAAC,EAC9E6B,EAAS7B,EAAO,MAAQ,EAAE,EAC5B,CAAC,EACA,MAAOI,GAAmB,CACrB4B,KAAiB5B,aAAiB,MAAQA,EAAM,QAAU,EAAE,oBAAoB,CAAC,CACvF,CAAC,EACI,IAAM,CACX4B,EAAS,EACX,CACF,EAAG,CAACF,EAAO,CAAC,CAAC,EAEXvB,EAAAA,KAAC,MAAA,CAAI,UAAU,YACZ,SAAA,CAAAuB,GACCvB,EAAAA,KAAC,MAAA,CAAI,UAAU,oBACb,SAAA,CAAAA,EAAAA,KAAC,QAAA,CAAM,UAAU,0BACf,SAAA,CAAAC,EAAAA,IAAC,OAAA,CAAM,SAAA,EAAE,wBAAwB,CAAA,CAAE,EACnCD,EAAAA,KAAC,SAAA,CACC,UAAU,YACV,MAAOwB,EACP,SAAWrB,GACTG,EAAWW,IACLd,EAAM,OAAO,MAAOc,EAAO,IAAI,OAAQd,EAAM,OAAO,KAAK,EACxDc,EAAO,OAAO,MAAM,EAClBA,EACR,EAGH,SAAA,CAAAhB,MAAC,SAAA,CAAO,MAAM,GAAI,SAAA,EAAE,2BAA2B,EAAE,EAChDoB,EAAM,IAAKK,GACVzB,EAAAA,IAAC,SAAA,CAA2B,MAAOyB,EAAK,SACrC,SAAAA,EAAK,QAAA,EADKA,EAAK,QAElB,CACD,CAAA,CAAA,CAAA,CACH,EACF,EACC7B,GAASI,EAAAA,IAAC,IAAA,CAAE,KAAK,QAAS,SAAAJ,CAAA,CAAM,CAAA,EACnC,EAEFI,EAAAA,IAACG,EAAA,CAA6B,QAAApC,CAAA,EAATA,CAA2B,CAAA,EAClD,CAEJ"}
1
+ {"version":3,"file":"CredentialsPage-DysT6EhJ.js","sources":["../../src/utils/credentialCopySources.ts","../../src/pages/CredentialsPage.tsx"],"sourcesContent":["import type { MyCredentialBinding, CredentialSlot } from '../types';\n\nconst slotId = (slot: CredentialSlot) =>\n `${slot.target}.${slot.target === 'headers' ? slot.name.toLowerCase() : slot.name}`;\n\nexport function getCredentialCopySources(\n target: MyCredentialBinding,\n bindings: MyCredentialBinding[],\n) {\n return bindings\n .filter((source) => source.serverName !== target.serverName)\n .map((source) => {\n const missing = target.credentialTemplate\n .filter(\n (slot) =>\n !source.credentialTemplate.some((candidate) => slotId(candidate) === slotId(slot)),\n )\n .map((slot) => `${slot.target}.${slot.name}`);\n const incompatibleUrl = target.credentialTemplate.some((slot) => {\n const candidate = source.credentialTemplate.find((item) => slotId(item) === slotId(slot));\n return (\n slot.target === 'url' &&\n candidate &&\n !candidate.allowed?.every((value) => slot.allowed?.includes(value))\n );\n });\n const reason = !source.configured\n ? 'unconfigured'\n : missing.length\n ? 'missing'\n : incompatibleUrl\n ? 'url'\n : undefined;\n return { source, reason, missing };\n });\n}\n","import { useCallback, useEffect, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { useSearchParams } from 'react-router-dom';\nimport { getCredentialCopySources } from '../utils/credentialCopySources';\nimport { useAuth } from '../contexts/AuthContext';\nimport type { User, MyCredentialBinding } from '../types';\nimport { apiDelete, apiGet, apiPost, apiPut, type ApiResponse } from '../utils/fetchInterceptor';\n\nconst CredentialForm = ({\n binding,\n baseUrl,\n bindings,\n onChange,\n}: {\n binding: MyCredentialBinding;\n baseUrl: string;\n bindings: MyCredentialBinding[];\n onChange: () => Promise<void>;\n}) => {\n const { t } = useTranslation();\n const [mode, setMode] = useState<'view' | 'edit' | 'copy' | 'delete'>(\n binding.configured ? 'view' : 'edit',\n );\n const [values, setValues] = useState<Record<string, string>>({});\n const [sourceServer, setSourceServer] = useState('');\n const [overwrite, setOverwrite] = useState(false);\n const [busy, setBusy] = useState(false);\n const [feedback, setFeedback] = useState<{ message: string; error: boolean }>();\n const sources = getCredentialCopySources(binding, bindings);\n const selectedSource = sources.find(({ source }) => source.serverName === sourceServer);\n const hasBinding = Boolean(binding.updatedAt);\n const changeMode = (next: typeof mode) => {\n setMode(next);\n setValues({});\n setSourceServer('');\n setOverwrite(false);\n setFeedback(undefined);\n };\n const submit = async (action: 'save' | 'copy' | 'delete') => {\n if (busy) return;\n setBusy(true);\n setFeedback(undefined);\n try {\n const url = `${baseUrl}/${encodeURIComponent(binding.serverName)}`;\n const result =\n action === 'delete'\n ? await apiDelete<ApiResponse>(url)\n : action === 'copy'\n ? await apiPost<ApiResponse>(`${url}/copy`, { sourceServer, overwrite })\n : await apiPut<ApiResponse>(url, { values });\n if (!result.success) throw new Error(result.message || t('credentials.failed'));\n await onChange();\n changeMode('view');\n setFeedback({\n message: t(\n `credentials.${action === 'copy' ? 'copied' : action === 'delete' ? 'deleted' : 'saved'}`,\n ),\n error: false,\n });\n } catch (error) {\n setFeedback({\n message: error instanceof Error ? error.message : t('credentials.failed'),\n error: true,\n });\n } finally {\n setBusy(false);\n }\n };\n const sourceReason = (entry: (typeof sources)[number]) => {\n if (entry.reason === 'missing')\n return t('credentials.sourceMissing', { slots: entry.missing.join(', ') });\n if (entry.reason === 'unconfigured') return t('credentials.sourceUnconfigured');\n if (entry.reason === 'url') return t('credentials.sourceUrlMismatch');\n return '';\n };\n return (\n <section className=\"hub-card p-5 space-y-4\" aria-label={binding.serverName}>\n <div className=\"flex flex-col items-start gap-2 sm:flex-row sm:justify-between sm:gap-3\">\n <div className=\"min-w-0 space-y-1\">\n <h2 className=\"font-semibold break-words\">{binding.serverName}</h2>\n {binding.updatedAt && (\n <p className=\"text-xs text-[var(--hub-ink-3)]\">\n {t('credentials.updatedAt')}:{' '}\n <time dateTime={binding.updatedAt}>\n {new Date(binding.updatedAt).toLocaleString(undefined, {\n dateStyle: 'short',\n timeStyle: 'short',\n })}\n </time>\n </p>\n )}\n </div>\n <span className=\"hub-tag shrink-0\">\n {t(binding.configured ? 'credentials.savedStatus' : 'credentials.required')}\n </span>\n </div>\n {mode !== 'edit' && (\n <dl className=\"divide-y divide-[var(--hub-line-2)]\">\n {binding.credentialTemplate.map((slot) => {\n const id = `${slot.target}.${slot.name}`;\n return (\n <div key={id} className=\"flex items-start justify-between gap-4 py-2 text-sm\">\n <dt className=\"min-w-0\">\n <span className=\"font-medium break-words\">{slot.label || slot.name}</span>\n <span className=\"hub-mono block break-all text-xs text-[var(--hub-ink-3)]\">\n {id}\n </span>\n </dt>\n <dd className=\"hub-mono text-[var(--hub-ink-3)]\">\n {binding.valuePreviews?.[id] ||\n t(\n binding.configuredSlots.includes(id)\n ? 'credentials.savedStatus'\n : 'credentials.notSet',\n )}\n </dd>\n </div>\n );\n })}\n </dl>\n )}\n {mode === 'view' && (\n <div className=\"flex flex-wrap items-center gap-2\">\n <button type=\"button\" className=\"hub-btn primary\" onClick={() => changeMode('edit')}>\n {t(hasBinding ? 'credentials.update' : 'credentials.configure')}\n </button>\n <button type=\"button\" className=\"hub-btn\" onClick={() => changeMode('copy')}>\n {t('credentials.copyFrom')}\n </button>\n {hasBinding && (\n <details className=\"relative ml-auto\">\n <summary className=\"hub-btn cursor-pointer\">{t('credentials.moreActions')}</summary>\n <div className=\"absolute right-0 z-10 mt-2 rounded-lg bg-[var(--hub-surface)] p-2 shadow-lg hub-border\">\n <button\n type=\"button\"\n className=\"hub-btn danger whitespace-nowrap\"\n onClick={() => changeMode('delete')}\n >\n {t('credentials.delete')}\n </button>\n </div>\n </details>\n )}\n </div>\n )}\n {mode === 'edit' && (\n <form\n onSubmit={(event) => {\n event.preventDefault();\n void submit('save');\n }}\n >\n <fieldset disabled={busy} className=\"space-y-4\">\n <legend className=\"mb-2 text-sm font-medium\">\n {t(hasBinding ? 'credentials.update' : 'credentials.configure')}\n </legend>\n <p className=\"text-xs text-[var(--hub-ink-3)]\">\n {t(hasBinding ? 'credentials.replaceHint' : 'credentials.configureHint')}\n </p>\n {binding.credentialTemplate.map((slot) => {\n const id = `${slot.target}.${slot.name}`;\n return (\n <label key={id} className=\"block space-y-1 text-sm\">\n <span className=\"font-medium\">{slot.label || slot.name}</span>\n <span className=\"hub-mono block text-xs text-[var(--hub-ink-3)]\">{id}</span>\n {slot.target === 'url' ? (\n <select\n required\n className=\"hub-input\"\n value={values[id] || ''}\n onChange={(event) => setValues({ ...values, [id]: event.target.value })}\n >\n <option value=\"\">{t('credentials.enterValue')}</option>\n {slot.allowed?.map((value) => (\n <option key={value} value={value}>\n {value}\n </option>\n ))}\n </select>\n ) : (\n <input\n type=\"password\"\n required\n autoComplete=\"new-password\"\n maxLength={16384}\n value={values[id] || ''}\n placeholder={t(\n binding.configuredSlots.includes(id)\n ? 'credentials.replaceValue'\n : 'credentials.enterValue',\n )}\n className=\"hub-input\"\n onChange={(event) => setValues({ ...values, [id]: event.target.value })}\n />\n )}\n </label>\n );\n })}\n <div className=\"flex flex-wrap gap-2\">\n <button type=\"submit\" className=\"hub-btn primary\">\n {t(hasBinding ? 'credentials.replace' : 'credentials.save')}\n </button>\n <button type=\"button\" className=\"hub-btn\" onClick={() => changeMode('view')}>\n {t('credentials.cancel')}\n </button>\n <button type=\"button\" className=\"hub-btn\" onClick={() => changeMode('copy')}>\n {t('credentials.copyFrom')}\n </button>\n </div>\n </fieldset>\n </form>\n )}\n {mode === 'copy' && (\n <div className=\"space-y-3 border-t border-[var(--hub-line)] pt-4\">\n <label className=\"block space-y-1 text-sm\">\n <span className=\"font-medium\">{t('credentials.copyFrom')}</span>\n <select\n className=\"hub-input\"\n value={sourceServer}\n disabled={busy || !sources.length}\n onChange={(event) => {\n setSourceServer(event.target.value);\n setOverwrite(false);\n }}\n >\n <option value=\"\">\n {t(sources.length ? 'credentials.chooseSource' : 'credentials.noOtherServers')}\n </option>\n {sources.map((entry) => (\n <option\n key={entry.source.serverName}\n value={entry.source.serverName}\n disabled={Boolean(entry.reason)}\n >\n {entry.source.serverName}\n {entry.reason ? ` — ${sourceReason(entry)}` : ''}\n </option>\n ))}\n </select>\n </label>\n {!sources.some((entry) => !entry.reason) && (\n <div className=\"space-y-1 text-sm text-[var(--hub-ink-3)]\">\n <p>{t('credentials.noSourcesHelp')}</p>\n {sources.length > 0 && (\n <ul className=\"list-disc pl-5\">\n {sources.map((entry) => (\n <li key={entry.source.serverName} className=\"break-words\">\n {entry.source.serverName}: {sourceReason(entry)}\n </li>\n ))}\n </ul>\n )}\n </div>\n )}\n <p className=\"text-xs text-[var(--hub-ink-3)]\">{t('credentials.copyHint')}</p>\n {sourceServer && hasBinding && (\n <label className=\"flex items-start gap-2 text-sm\">\n <input\n type=\"checkbox\"\n checked={overwrite}\n disabled={busy}\n onChange={(event) => setOverwrite(event.target.checked)}\n />\n <span>{t('credentials.confirmOverwrite')}</span>\n </label>\n )}\n <div className=\"flex flex-wrap gap-2\">\n <button\n type=\"button\"\n className=\"hub-btn primary\"\n disabled={\n busy ||\n !selectedSource ||\n Boolean(selectedSource.reason) ||\n (hasBinding && !overwrite)\n }\n onClick={() => void submit('copy')}\n >\n {t('credentials.copy')}\n </button>\n <button\n type=\"button\"\n className=\"hub-btn\"\n disabled={busy}\n onClick={() => changeMode('view')}\n >\n {t('credentials.cancel')}\n </button>\n </div>\n </div>\n )}\n {mode === 'delete' && (\n <div className=\"space-y-3 border-t border-[var(--hub-line)] pt-4\">\n <p className=\"text-sm\">\n {t('credentials.deleteConfirm', { server: binding.serverName })}\n </p>\n <div className=\"flex flex-wrap gap-2\">\n <button\n type=\"button\"\n className=\"hub-btn danger\"\n disabled={busy}\n onClick={() => void submit('delete')}\n >\n {t('credentials.delete')}\n </button>\n <button\n type=\"button\"\n className=\"hub-btn\"\n disabled={busy}\n onClick={() => changeMode('view')}\n >\n {t('credentials.cancel')}\n </button>\n </div>\n </div>\n )}\n {feedback && (\n <p\n role={feedback.error ? 'alert' : 'status'}\n className=\"text-sm\"\n style={{ color: feedback.error ? 'var(--hub-err)' : 'var(--hub-ink-2)' }}\n >\n {feedback.message}\n </p>\n )}\n </section>\n );\n};\n\nfunction CredentialList({ baseUrl }: { baseUrl: string }) {\n const { t } = useTranslation();\n const [search, setSearch] = useSearchParams();\n const [bindings, setBindings] = useState<MyCredentialBinding[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState('');\n const reload = useCallback(async () => {\n try {\n const result = await apiGet<ApiResponse<MyCredentialBinding[]>>(baseUrl);\n if (!result.success) throw new Error(result.message || t('credentials.failed'));\n setBindings(result.data || []);\n setError('');\n } catch (error) {\n setError(error instanceof Error ? error.message : t('credentials.failed'));\n } finally {\n setLoading(false);\n }\n }, [baseUrl, t]);\n useEffect(() => {\n void reload();\n }, [reload]);\n const visible = bindings.filter(\n (binding) => !search.get('server') || binding.serverName === search.get('server'),\n );\n return (\n <div className=\"max-w-4xl mx-auto space-y-5\">\n <div>\n <h1 className=\"hub-h1\">{t('credentials.title')}</h1>\n <p className=\"hub-sub\">{t('credentials.description')}</p>\n </div>\n {search.has('server') && (\n <button\n className=\"hub-btn\"\n onClick={() =>\n setSearch((params) => {\n params.delete('server');\n return params;\n })\n }\n >\n {t('credentials.showAll')}\n </button>\n )}\n {error && (\n <div\n className=\"hub-card px-4 py-3 text-sm\"\n style={{ color: 'var(--hub-err)' }}\n role=\"alert\"\n >\n {error}\n </div>\n )}\n {loading ? (\n <div className=\"hub-card p-10 text-center text-sm\" style={{ color: 'var(--hub-ink-3)' }}>\n {t('credentials.loading')}\n </div>\n ) : !visible.length && !error ? (\n <div className=\"hub-card p-10 text-center text-sm\" style={{ color: 'var(--hub-ink-3)' }}>\n {t('credentials.empty')}\n </div>\n ) : null}\n {visible.map((binding) => (\n <CredentialForm\n key={binding.serverName}\n binding={binding}\n bindings={bindings}\n baseUrl={baseUrl}\n onChange={reload}\n />\n ))}\n </div>\n );\n}\n\nexport default function CredentialsPage() {\n const { t } = useTranslation();\n const { auth } = useAuth();\n const [search, setSearch] = useSearchParams();\n const [users, setUsers] = useState<User[]>([]);\n const [error, setError] = useState('');\n const admin = Boolean(auth.user?.isAdmin);\n const username = admin ? search.get('user') || '' : '';\n const baseUrl = username ? `/users/${encodeURIComponent(username)}/credentials` : '/credentials';\n useEffect(() => {\n if (!admin) return;\n let active = true;\n void apiGet<ApiResponse<User[]>>('/users')\n .then((result) => {\n if (!active) return;\n if (!result.success) throw new Error(result.message || t('credentials.failed'));\n setUsers(result.data || []);\n })\n .catch((error: unknown) => {\n if (active) setError(error instanceof Error ? error.message : t('credentials.failed'));\n });\n return () => {\n active = false;\n };\n }, [admin, t]);\n return (\n <div className=\"space-y-5\">\n {admin && (\n <div className=\"max-w-4xl mx-auto\">\n <label className=\"block space-y-1 text-sm\">\n <span>{t('credentials.targetUser')}</span>\n <select\n className=\"hub-input\"\n value={username}\n onChange={(event) =>\n setSearch((params) => {\n if (event.target.value) params.set('user', event.target.value);\n else params.delete('user');\n return params;\n })\n }\n >\n <option value=\"\">{t('credentials.myCredentials')}</option>\n {users.map((user) => (\n <option key={user.username} value={user.username}>\n {user.username}\n </option>\n ))}\n </select>\n </label>\n {error && <p role=\"alert\">{error}</p>}\n </div>\n )}\n <CredentialList key={baseUrl} baseUrl={baseUrl} />\n </div>\n );\n}\n"],"names":["slotId","slot","getCredentialCopySources","target","bindings","source","missing","candidate","incompatibleUrl","item","_a","value","reason","CredentialForm","binding","baseUrl","onChange","t","useTranslation","mode","setMode","useState","values","setValues","sourceServer","setSourceServer","overwrite","setOverwrite","busy","setBusy","feedback","setFeedback","sources","selectedSource","hasBinding","changeMode","next","submit","action","url","result","apiDelete","apiPost","apiPut","error","sourceReason","entry","jsxs","jsx","id","event","CredentialList","search","setSearch","useSearchParams","setBindings","loading","setLoading","setError","reload","useCallback","apiGet","useEffect","visible","params","CredentialsPage","auth","useAuth","users","setUsers","admin","username","active","user"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{d as $,R as u,r as p,j as e}from"./framework-vendor-D0S_ys6r.js";import{e as L,f as M}from"./index-DKX_BExz.js";import{u as P,a as A}from"./useCostData-DzjFGnw4.js";import{u as U}from"./clipboard-BYlVdxdy.js";import{f as H}from"./contextCost-CilWqGWT.js";import{E as h}from"./EndpointCopy-BeLf_ow_.js";import{S as O}from"./StatusDot-B81gkUBo.js";import{u as _}from"./i18n-vendor-CPneThYa.js";import{R as B,P as G,l as C,m as V}from"./icons-vendor-DZjjGrOu.js";const i=({label:r,value:l,tone:a="default"})=>{const o=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:o},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,Z=()=>{var S;const{t:r}=_(),l=$(),{allServers:a,error:o,setError:R,isLoading:b,triggerRefresh:z}=P({refreshOnMount:!0}),{groups:x}=L(),{installConfig:m}=U(),{serverCosts:v}=A(),[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||o)&&f(!0)},[b,a.length,o]);const n=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,t)=>{var c;return s+(((c=t.tools)==null?void 0:c.length)||0)},0)}),[a]),D=p.useMemo(()=>v.filter(s=>s.connected).reduce((s,t)=>s+t.exposed,0),[v]),T=p.useMemo(()=>a.slice(0,6),[a]),d=((S=m==null?void 0:m.baseUrl)==null?void 0:S.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:n.total})," · ",r("pages.dashboard.onlineServers")," · ",e.jsx("span",{className:"hub-num",children:n.online})]})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs("button",{className:"hub-btn",onClick:()=>z(),children:[e.jsx(B,{size:13})," ",r("common.refresh")]}),e.jsxs("button",{className:"hub-btn primary",onClick:()=>l("/servers"),children:[e.jsx(G,{size:13})," ",r("server.add")]})]})]}),o&&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:o})]}),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,t)=>e.jsx("div",{className:"hub-card animate-pulse",style:{padding:"14px 16px",height:78}},t))}):e.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-6 gap-3 mb-6",children:[e.jsx(i,{label:r("pages.dashboard.totalServers"),value:n.total}),e.jsx(i,{label:r("pages.dashboard.onlineServers"),value:n.online,tone:"ok"}),e.jsx(i,{label:r("pages.dashboard.connectingServers"),value:n.connecting,tone:"warn"}),e.jsx(i,{label:r("pages.dashboard.offlineServers"),value:n.offline,tone:"err"}),e.jsx(i,{label:r("pages.dashboard.disabledServers"),value:n.disabled,tone:"muted"}),e.jsx(i,{label:r("cost.totalFootprint"),value:H(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(V,{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 t,c,k,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(O,{status:s.status,enabled:s.enabled})}),e.jsx("div",{className:"min-w-0",children:(t=s.config)!=null&&t.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:((k=s.prompts)==null?void 0:k.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/${M()}`}),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{Z as default};
2
- //# sourceMappingURL=Dashboard-CwifBwm4.js.map
1
+ import{d as $,R as u,r as p,j as e}from"./framework-vendor-D0S_ys6r.js";import{e as L,f as M}from"./index-8GliUQTA.js";import{u as P,a as A}from"./useCostData-DNk0WL-a.js";import{u as U}from"./clipboard-PC3TyCTa.js";import{f as H}from"./contextCost-CilWqGWT.js";import{E as h}from"./EndpointCopy-zuPM6wTV.js";import{S as O}from"./StatusDot-DyEnhqJV.js";import{u as _}from"./i18n-vendor-CPneThYa.js";import{R as B,P as G,l as C,m as V}from"./icons-vendor-DZjjGrOu.js";const i=({label:r,value:l,tone:a="default"})=>{const o=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:o},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,Z=()=>{var S;const{t:r}=_(),l=$(),{allServers:a,error:o,setError:R,isLoading:b,triggerRefresh:z}=P({refreshOnMount:!0}),{groups:x}=L(),{installConfig:m}=U(),{serverCosts:v}=A(),[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||o)&&f(!0)},[b,a.length,o]);const n=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,t)=>{var c;return s+(((c=t.tools)==null?void 0:c.length)||0)},0)}),[a]),D=p.useMemo(()=>v.filter(s=>s.connected).reduce((s,t)=>s+t.exposed,0),[v]),T=p.useMemo(()=>a.slice(0,6),[a]),d=((S=m==null?void 0:m.baseUrl)==null?void 0:S.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:n.total})," · ",r("pages.dashboard.onlineServers")," · ",e.jsx("span",{className:"hub-num",children:n.online})]})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs("button",{className:"hub-btn",onClick:()=>z(),children:[e.jsx(B,{size:13})," ",r("common.refresh")]}),e.jsxs("button",{className:"hub-btn primary",onClick:()=>l("/servers"),children:[e.jsx(G,{size:13})," ",r("server.add")]})]})]}),o&&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:o})]}),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,t)=>e.jsx("div",{className:"hub-card animate-pulse",style:{padding:"14px 16px",height:78}},t))}):e.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-6 gap-3 mb-6",children:[e.jsx(i,{label:r("pages.dashboard.totalServers"),value:n.total}),e.jsx(i,{label:r("pages.dashboard.onlineServers"),value:n.online,tone:"ok"}),e.jsx(i,{label:r("pages.dashboard.connectingServers"),value:n.connecting,tone:"warn"}),e.jsx(i,{label:r("pages.dashboard.offlineServers"),value:n.offline,tone:"err"}),e.jsx(i,{label:r("pages.dashboard.disabledServers"),value:n.disabled,tone:"muted"}),e.jsx(i,{label:r("cost.totalFootprint"),value:H(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(V,{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 t,c,k,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(O,{status:s.status,enabled:s.enabled})}),e.jsx("div",{className:"min-w-0",children:(t=s.config)!=null&&t.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:((k=s.prompts)==null?void 0:k.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/${M()}`}),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{Z as default};
2
+ //# sourceMappingURL=Dashboard-CIkS2wIr.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Dashboard-CwifBwm4.js","sources":["../../src/pages/Dashboard.tsx"],"sourcesContent":["import { getSmartRoutingPath } from '@/utils/runtime';\nimport 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/${getSmartRoutingPath()}`} />\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","getSmartRoutingPath","g"],"mappings":"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"}
1
+ {"version":3,"file":"Dashboard-CIkS2wIr.js","sources":["../../src/pages/Dashboard.tsx"],"sourcesContent":["import { getSmartRoutingPath } from '@/utils/runtime';\nimport 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/${getSmartRoutingPath()}`} />\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","getSmartRoutingPath","g"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{r as y,j as o}from"./framework-vendor-D0S_ys6r.js";import{l as h,o as r}from"./index-DKX_BExz.js";import{c as x}from"./clipboard-BYlVdxdy.js";import{u as C}from"./i18n-vendor-CPneThYa.js";import{C as b,r as j}from"./icons-vendor-DZjjGrOu.js";const g=({url:s,label:e,prefix:a,className:p,copyValue:l,ariaLabel:m})=>{const{t}=C(),{showToast:c}=h(),[n,i]=y.useState(!1),d=async u=>{if(u.stopPropagation(),!await x(l??s)){c(t("common.copyFailed")||"Copy failed","error");return}i(!0),c(t("common.copySuccess")||"Copied to clipboard","success"),setTimeout(()=>i(!1),1200)};return o.jsxs("div",{className:r("hub-endpoint",p),role:"group","aria-label":m||s,children:[e&&o.jsx("div",{className:"hub-endpoint-label",children:e}),o.jsxs("div",{className:"hub-endpoint-url",title:s,children:[a&&o.jsx("span",{style:{color:"var(--hub-ink-3)"},children:a}),s]}),o.jsx("button",{type:"button",onClick:d,className:r("hub-endpoint-copy",n?"copied":""),title:t("common.copy")||"Copy","aria-label":t("common.copy")||"Copy",children:n?o.jsx(b,{size:13}):o.jsx(j,{size:13})})]})};export{g as E};
2
- //# sourceMappingURL=EndpointCopy-BeLf_ow_.js.map
1
+ import{r as y,j as o}from"./framework-vendor-D0S_ys6r.js";import{l as h,o as r}from"./index-8GliUQTA.js";import{c as x}from"./clipboard-PC3TyCTa.js";import{u as C}from"./i18n-vendor-CPneThYa.js";import{C as b,r as j}from"./icons-vendor-DZjjGrOu.js";const g=({url:s,label:e,prefix:a,className:p,copyValue:l,ariaLabel:m})=>{const{t}=C(),{showToast:c}=h(),[n,i]=y.useState(!1),d=async u=>{if(u.stopPropagation(),!await x(l??s)){c(t("common.copyFailed")||"Copy failed","error");return}i(!0),c(t("common.copySuccess")||"Copied to clipboard","success"),setTimeout(()=>i(!1),1200)};return o.jsxs("div",{className:r("hub-endpoint",p),role:"group","aria-label":m||s,children:[e&&o.jsx("div",{className:"hub-endpoint-label",children:e}),o.jsxs("div",{className:"hub-endpoint-url",title:s,children:[a&&o.jsx("span",{style:{color:"var(--hub-ink-3)"},children:a}),s]}),o.jsx("button",{type:"button",onClick:d,className:r("hub-endpoint-copy",n?"copied":""),title:t("common.copy")||"Copy","aria-label":t("common.copy")||"Copy",children:n?o.jsx(b,{size:13}):o.jsx(j,{size:13})})]})};export{g as E};
2
+ //# sourceMappingURL=EndpointCopy-zuPM6wTV.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"EndpointCopy-BeLf_ow_.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 { copyText } from '@/utils/clipboard';\nimport { cn } from '@/utils/cn';\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":["EndpointCopy","url","label","prefix","className","copyValue","ariaLabel","useTranslation","showToast","useToast","copied","setCopied","useState","onCopy","e","copyText","jsxs","cn","jsx","Check","Copy"],"mappings":"yPAiBO,MAAMA,EAA4C,CAAC,CACxD,IAAAC,EACA,MAAAC,EACA,OAAAC,EACA,UAAAC,EACA,UAAAC,EACA,UAAAC,CACF,IAAM,CACJ,KAAM,CAAE,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,MAAMC,EAASV,GAAaJ,CAAG,EACjC,CACPO,EAAU,EAAE,mBAAmB,GAAK,cAAe,OAAO,EAC1D,MACF,CACAG,EAAU,EAAI,EACdH,EAAU,EAAE,oBAAoB,GAAK,sBAAuB,SAAS,EACrE,WAAW,IAAMG,EAAU,EAAK,EAAG,IAAI,CACzC,EAEA,OACEK,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,QAASL,EACT,UAAWI,EAAG,oBAAqBP,EAAS,SAAW,EAAE,EACzD,MAAO,EAAE,aAAa,GAAK,OAC3B,aAAY,EAAE,aAAa,GAAK,OAE/B,SAAAA,QAAUS,EAAA,CAAM,KAAM,GAAI,EAAKD,EAAAA,IAACE,EAAA,CAAK,KAAM,EAAA,CAAI,CAAA,CAAA,CAClD,EACF,CAEJ"}
1
+ {"version":3,"file":"EndpointCopy-zuPM6wTV.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 { copyText } from '@/utils/clipboard';\nimport { cn } from '@/utils/cn';\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":["EndpointCopy","url","label","prefix","className","copyValue","ariaLabel","useTranslation","showToast","useToast","copied","setCopied","useState","onCopy","e","copyText","jsxs","cn","jsx","Check","Copy"],"mappings":"yPAiBO,MAAMA,EAA4C,CAAC,CACxD,IAAAC,EACA,MAAAC,EACA,OAAAC,EACA,UAAAC,EACA,UAAAC,EACA,UAAAC,CACF,IAAM,CACJ,KAAM,CAAE,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,MAAMC,EAASV,GAAaJ,CAAG,EACjC,CACPO,EAAU,EAAE,mBAAmB,GAAK,cAAe,OAAO,EAC1D,MACF,CACAG,EAAU,EAAI,EACdH,EAAU,EAAE,oBAAoB,GAAK,sBAAuB,SAAS,EACrE,WAAW,IAAMG,EAAU,EAAK,EAAG,IAAI,CACzC,EAEA,OACEK,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,QAASL,EACT,UAAWI,EAAG,oBAAqBP,EAAS,SAAW,EAAE,EACzD,MAAO,EAAE,aAAa,GAAK,OAC3B,aAAY,EAAE,aAAa,GAAK,OAE/B,SAAAA,QAAUS,EAAA,CAAM,KAAM,GAAI,EAAKD,EAAAA,IAACE,EAAA,CAAK,KAAM,EAAA,CAAI,CAAA,CAAA,CAClD,EACF,CAEJ"}