@truefoundry/trueforge-ui 0.3.0-rc.0 → 0.3.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +48 -0
- package/README.md +14 -6
- package/dist/AgentCodeSnippets-NZOFJE57.js +74 -0
- package/dist/AgentCodeSnippets-NZOFJE57.js.map +1 -0
- package/dist/AgentMetricChart-JPU3MFQ2.js +15 -0
- package/dist/AgentOverview-EEBHJC32.js +167 -0
- package/dist/AgentOverview-EEBHJC32.js.map +1 -0
- package/dist/AgentSessionEventTimeline-26Z6IB3B.js +79 -0
- package/dist/AgentSessionEventTimeline-26Z6IB3B.js.map +1 -0
- package/dist/AgentSessionEventTimelineChart-CGCU7WBK.js +504 -0
- package/dist/AgentSessionEventTimelineChart-CGCU7WBK.js.map +1 -0
- package/dist/AgentSessions-3MPDZASK.js +275 -0
- package/dist/AgentSessions-3MPDZASK.js.map +1 -0
- package/dist/{ConnectorSettings-FXQ5B7TN.js → ConnectorSettings-MXFRWWR6.js} +8 -6
- package/dist/{ConnectorSettings-FXQ5B7TN.js.map → ConnectorSettings-MXFRWWR6.js.map} +1 -1
- package/dist/{DockLayout-LBNQJZNF.js → DockLayout-LM4AFMFY.js} +11 -10
- package/dist/{DockLayout-LBNQJZNF.js.map → DockLayout-LM4AFMFY.js.map} +1 -1
- package/dist/DrawerLayout-JR3O6RRB.js +184 -0
- package/dist/DrawerLayout-JR3O6RRB.js.map +1 -0
- package/dist/{ModelSettings-4L4LOQJA.js → ModelSettings-G7IFNC2Y.js} +8 -6
- package/dist/{ModelSettings-4L4LOQJA.js.map → ModelSettings-G7IFNC2Y.js.map} +1 -1
- package/dist/OpenUiFenceBlock-2TV5TQ6P.js +9 -0
- package/dist/OpenUiFenceBlock-2TV5TQ6P.js.map +1 -0
- package/dist/{SandboxSettings-Z4ZG6ZX4.js → SandboxSettings-NXAANMAX.js} +8 -6
- package/dist/{SandboxSettings-Z4ZG6ZX4.js.map → SandboxSettings-NXAANMAX.js.map} +1 -1
- package/dist/SchedulesPage-KKLUDO6B.js +15 -0
- package/dist/SchedulesPage-KKLUDO6B.js.map +1 -0
- package/dist/{SettingsBuilder-WF7ZWKQW.js → SettingsBuilder-L34IQ56S.js} +26 -22
- package/dist/SettingsBuilder-L34IQ56S.js.map +1 -0
- package/dist/{ShellRouteSync-T4WLY57Q.js → ShellRouteSync-CEBETGEJ.js} +95 -22
- package/dist/ShellRouteSync-CEBETGEJ.js.map +1 -0
- package/dist/SidebarLayout-A2HJPPHS.js +308 -0
- package/dist/SidebarLayout-A2HJPPHS.js.map +1 -0
- package/dist/{SkillSettings-3EPPC3IT.js → SkillSettings-FGGMSUNO.js} +8 -6
- package/dist/{SkillSettings-3EPPC3IT.js.map → SkillSettings-FGGMSUNO.js.map} +1 -1
- package/dist/{TrueForgeUIWithRouter-NZ4FGPE4.js → TrueForgeUIWithRouter-MZVJWJMB.js} +14 -10
- package/dist/{TrueForgeUIWithRouter-NZ4FGPE4.js.map → TrueForgeUIWithRouter-MZVJWJMB.js.map} +1 -1
- package/dist/{WidgetLayout-3QZ357FL.js → WidgetLayout-6QM45OQQ.js} +17 -13
- package/dist/WidgetLayout-6QM45OQQ.js.map +1 -0
- package/dist/chunk-2XPPBEYF.js +74 -0
- package/dist/chunk-2XPPBEYF.js.map +1 -0
- package/dist/chunk-6O6FWUDW.js +1376 -0
- package/dist/chunk-6O6FWUDW.js.map +1 -0
- package/dist/chunk-7Z2C2K7F.js +578 -0
- package/dist/chunk-7Z2C2K7F.js.map +1 -0
- package/dist/{chunk-WREC7D54.js → chunk-CYHJDKFB.js} +115 -32
- package/dist/chunk-CYHJDKFB.js.map +1 -0
- package/dist/chunk-DJUFS2BW.js +27 -0
- package/dist/chunk-DJUFS2BW.js.map +1 -0
- package/dist/chunk-DMMZSZTN.js +252 -0
- package/dist/chunk-DMMZSZTN.js.map +1 -0
- package/dist/chunk-FJW36PD7.js +180 -0
- package/dist/chunk-FJW36PD7.js.map +1 -0
- package/dist/chunk-M62XY4NC.js +555 -0
- package/dist/chunk-M62XY4NC.js.map +1 -0
- package/dist/{chunk-B52K63AW.js → chunk-MPNSO7E6.js} +2 -2
- package/dist/{chunk-CFLVPV5M.js → chunk-QC42MLAK.js} +7 -2
- package/dist/chunk-QC42MLAK.js.map +1 -0
- package/dist/chunk-RDXBMTP5.js +32 -0
- package/dist/chunk-RDXBMTP5.js.map +1 -0
- package/dist/chunk-SFZNIO6U.js +18 -0
- package/dist/chunk-SFZNIO6U.js.map +1 -0
- package/dist/chunk-U7KURWW3.js +840 -0
- package/dist/chunk-U7KURWW3.js.map +1 -0
- package/dist/chunk-WVE3F724.js +200 -0
- package/dist/chunk-WVE3F724.js.map +1 -0
- package/dist/{chunk-FJ5RSSMA.js → chunk-XAU7QFG3.js} +178 -36
- package/dist/chunk-XAU7QFG3.js.map +1 -0
- package/dist/chunk-Y6S4OVVQ.js +16 -0
- package/dist/chunk-Y6S4OVVQ.js.map +1 -0
- package/dist/chunk-ZKNUMH2M.js +119 -0
- package/dist/chunk-ZKNUMH2M.js.map +1 -0
- package/dist/chunk-ZTMKFZQ3.js +13214 -0
- package/dist/chunk-ZTMKFZQ3.js.map +1 -0
- package/dist/{createTrueFoundryServer-B72bU-J7.d.ts → createTrueFoundryServer-CmZnBh_l.d.ts} +15 -4
- package/dist/index.d.ts +1002 -22
- package/dist/index.js +689 -67
- package/dist/index.js.map +1 -1
- package/dist/plugins/trueforge-agent-server-adapter/index.d.ts +552 -8
- package/dist/plugins/trueforge-agent-server-adapter/index.js +503 -188
- package/dist/plugins/trueforge-agent-server-adapter/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +8 -7
- package/dist/DrawerLayout-Q6UK4VMI.js +0 -159
- package/dist/DrawerLayout-Q6UK4VMI.js.map +0 -1
- package/dist/OpenUiFenceBlock-6UIIAVBX.js +0 -9
- package/dist/SettingsBuilder-WF7ZWKQW.js.map +0 -1
- package/dist/ShellRouteSync-T4WLY57Q.js.map +0 -1
- package/dist/SidebarLayout-3WZ3CVON.js +0 -256
- package/dist/SidebarLayout-3WZ3CVON.js.map +0 -1
- package/dist/WidgetLayout-3QZ357FL.js.map +0 -1
- package/dist/chunk-36KOSC2J.js +0 -567
- package/dist/chunk-36KOSC2J.js.map +0 -1
- package/dist/chunk-3S6KHO7H.js +0 -2121
- package/dist/chunk-3S6KHO7H.js.map +0 -1
- package/dist/chunk-7L63OK3O.js +0 -91
- package/dist/chunk-7L63OK3O.js.map +0 -1
- package/dist/chunk-CFLVPV5M.js.map +0 -1
- package/dist/chunk-FJ5RSSMA.js.map +0 -1
- package/dist/chunk-OSV3HI5M.js +0 -6891
- package/dist/chunk-OSV3HI5M.js.map +0 -1
- package/dist/chunk-WQULF5AT.js +0 -460
- package/dist/chunk-WQULF5AT.js.map +0 -1
- package/dist/chunk-WREC7D54.js.map +0 -1
- package/dist/chunk-YIWUG6MC.js +0 -101
- package/dist/chunk-YIWUG6MC.js.map +0 -1
- package/dist/chunk-ZXOADNF3.js +0 -75
- package/dist/chunk-ZXOADNF3.js.map +0 -1
- /package/dist/{OpenUiFenceBlock-6UIIAVBX.js.map → AgentMetricChart-JPU3MFQ2.js.map} +0 -0
- /package/dist/{chunk-B52K63AW.js.map → chunk-MPNSO7E6.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/containers/SettingsBuilder/SandboxSettings.tsx","../src/containers/SettingsBuilder/ConfigureSandboxForm.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useMemo, useState } from 'react';\n\nimport { Spinner } from '@/atoms/primitives/Spinner.js';\nimport { Button } from '../../atoms/primitives/Button.js';\nimport { Tooltip } from '../../atoms/primitives/Tooltip.js';\nimport { Icon } from '../../icons/Icon.js';\nimport { useCatalogServer } from '../../server/ServerContext.js';\nimport type {\n SandboxProviderBase,\n SandboxProviderCatalogEntry,\n SandboxProviderConfig,\n SandboxProviderListEntry,\n SandboxSnapshotSyncStatus,\n} from '../../server/types.js';\nimport { getErrorMessage } from '../../utils/getErrorMessage.js';\nimport { useToasterOptional } from '../ToasterContainer.js';\nimport ConfigureSandboxForm, { type SandboxConfigDraft } from './ConfigureSandboxForm.js';\n\nconst SNAPSHOT_STATUS_POLL_INTERVAL_MS = 10000;\n\nconst configFrom = ({\n execTimeoutMs,\n autoStopIntervalInMinutes,\n autoArchiveIntervalInMinutes,\n autoDeleteIntervalInMinutes,\n}: SandboxProviderConfig): SandboxProviderConfig => ({\n execTimeoutMs,\n autoStopIntervalInMinutes,\n autoArchiveIntervalInMinutes,\n autoDeleteIntervalInMinutes,\n});\n\nconst statusPresentation = (status: SandboxSnapshotSyncStatus['status']): { label: string; className: string } => {\n switch (status) {\n case 'pending':\n return {\n label: 'Syncing image...',\n className: 'text-warning-bg',\n };\n case 'ready':\n return {\n label: 'Connected',\n className: 'text-success-bg',\n };\n case 'failed':\n return {\n label: 'Sync failed',\n className: 'text-failure-bg',\n };\n default:\n return {\n label: status,\n className: 'text-text-secondary',\n };\n }\n};\n\nconst SandboxSettings = () => {\n const { sandboxCatalog } = useCatalogServer();\n const toaster = useToasterOptional();\n\n const [providers, setProviders] = useState<SandboxProviderListEntry[]>([]);\n const [catalog, setCatalog] = useState<SandboxProviderCatalogEntry[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [formError, setFormError] = useState<string | null>(null);\n const [busy, setBusy] = useState(false);\n\n const [createEntry, setCreateEntry] = useState<SandboxProviderCatalogEntry | null>(null);\n const [updateProvider, setUpdateProvider] = useState<SandboxProviderBase | null>(null);\n\n const refresh = useCallback(\n async ({ quiet = false }: { quiet?: boolean } = {}) => {\n if (!sandboxCatalog) return;\n if (!quiet) setLoading(true);\n setError(null);\n try {\n const [listed, available] = await Promise.all([\n sandboxCatalog.listSandboxProviders(),\n sandboxCatalog.getSandboxProviderCatalog(),\n ]);\n setProviders(listed);\n setCatalog(available);\n } catch (err) {\n setError(getErrorMessage(err, 'Failed to load sandbox providers'));\n } finally {\n if (!quiet) setLoading(false);\n }\n },\n [sandboxCatalog],\n );\n\n useEffect(() => {\n void refresh();\n }, [refresh]);\n\n const hasPendingProvider = providers.some(entry => entry.snapshotSyncStatus.status === 'pending');\n\n useEffect(() => {\n if (!sandboxCatalog || !hasPendingProvider) return;\n const activeSandboxCatalog = sandboxCatalog;\n\n let cancelled = false;\n let timeoutId: number | undefined;\n\n function schedulePoll() {\n timeoutId = window.setTimeout(() => {\n void poll();\n }, SNAPSHOT_STATUS_POLL_INTERVAL_MS);\n }\n\n async function poll() {\n try {\n const listed = await activeSandboxCatalog.listSandboxProviders();\n if (cancelled) return;\n setProviders(listed);\n setError(null);\n if (listed.some(entry => entry.snapshotSyncStatus.status === 'pending')) {\n schedulePoll();\n }\n } catch (err) {\n if (cancelled) return;\n setError(getErrorMessage(err, 'Failed to refresh sandbox providers'));\n schedulePoll();\n }\n }\n\n schedulePoll();\n return () => {\n cancelled = true;\n if (timeoutId !== undefined) {\n window.clearTimeout(timeoutId);\n }\n };\n }, [sandboxCatalog, hasPendingProvider]);\n\n // Tenant/UI-wide: only one sandbox provider may be configured at a time.\n const hasConfiguredProvider = providers.length > 0;\n const availableEntries = useMemo(() => {\n if (hasConfiguredProvider) return [];\n const connectedCatalogIds = new Set(providers.map(provider => provider.data.catalogId));\n return catalog.filter(entry => !connectedCatalogIds.has(entry.id));\n }, [catalog, providers, hasConfiguredProvider]);\n\n const formInitialConfig = useMemo(\n () => (updateProvider ? configFrom(updateProvider) : createEntry ? configFrom(createEntry) : null),\n [updateProvider, createEntry],\n );\n\n if (!sandboxCatalog) {\n return <p className=\"text-sm text-text-secondary\">Sandbox provider catalog is not available.</p>;\n }\n\n const runMutation = async (fn: () => Promise<void>, setMutationError = setError) => {\n setBusy(true);\n setError(null);\n try {\n await fn();\n await refresh({ quiet: true });\n } catch (err) {\n setMutationError(getErrorMessage(err, 'Request failed'));\n throw err;\n } finally {\n setBusy(false);\n }\n };\n\n const handleCreate = async (draft: SandboxConfigDraft) => {\n if (!createEntry) return;\n setFormError(null);\n await runMutation(async () => {\n await sandboxCatalog.createSandboxProvider({\n catalogId: createEntry.id,\n name: createEntry.name,\n type: createEntry.type,\n ...configFrom(draft),\n apiKey: draft.apiKey,\n });\n }, setFormError);\n setCreateEntry(null);\n setTimeout(() => {\n toaster?.showSuccess({ title: `${createEntry.name} configured` });\n }, 0);\n };\n\n const handleUpdate = async (draft: SandboxConfigDraft) => {\n if (!updateProvider) return;\n setFormError(null);\n await runMutation(async () => {\n await sandboxCatalog.updateSandboxProvider({\n id: updateProvider.id,\n ...configFrom(draft),\n ...(draft.apiKey ? { apiKey: draft.apiKey } : {}),\n });\n }, setFormError);\n setUpdateProvider(null);\n setTimeout(() => {\n toaster?.showSuccess({ title: `${updateProvider.name} updated` });\n }, 0);\n };\n\n const handleRetry = (provider: SandboxProviderBase) => {\n void runMutation(async () => {\n await sandboxCatalog.updateSandboxProvider({\n id: provider.id,\n ...configFrom(provider),\n });\n }).catch(() => {});\n };\n\n const handleRemove = (provider: SandboxProviderBase) => {\n const deleteSandboxProvider = sandboxCatalog.deleteSandboxProvider;\n if (!deleteSandboxProvider) return;\n void runMutation(async () => {\n await deleteSandboxProvider({ id: provider.id });\n }).catch(() => {});\n };\n\n const formOpen = createEntry != null || updateProvider != null;\n const isUpdate = updateProvider != null;\n const formTitle = updateProvider\n ? `Update ${updateProvider.name}`\n : createEntry\n ? `Configure ${createEntry.name}`\n : 'Configure sandbox provider';\n\n return (\n <>\n <h3 className=\"text-xl font-semibold tracking-tight text-text-primary\">Sandbox providers</h3>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Choose a provider that runs sandboxes for code, files and shell commands. Only one can be active at a time.\n </p>\n\n {error ? (\n <p className=\"mt-3 rounded-md border border-failure-bg/30 bg-failure-bg/10 px-3 py-2 text-sm text-failure-bg\">\n {error}\n </p>\n ) : null}\n\n <div className=\"mt-4 flex-1 overflow-y-auto\">\n {loading ? (\n <p className=\"text-sm text-text-secondary\">Loading sandbox providers…</p>\n ) : (\n <div className=\"space-y-6\">\n {providers.length > 0 ? (\n <section>\n <h4 className=\"mb-2 text-xs font-semibold uppercase tracking-wide text-text-secondary\">\n Configured · {providers.length}\n </h4>\n <div className=\"overflow-hidden rounded-xl border border-border bg-card-bg\">\n {providers.map(entry => {\n const provider = entry.data;\n const status = statusPresentation(entry.snapshotSyncStatus.status);\n const statusReason = entry.snapshotSyncStatus.statusReason;\n const statusIndicator = (\n <span className={`inline-flex items-center gap-1.5 text-sm font-medium ${status.className}`}>\n {entry.snapshotSyncStatus.status === 'pending' ? (\n <Spinner size={16} aria-label=\"Syncing snapshot image\" />\n ) : entry.snapshotSyncStatus.status === 'ready' ? (\n <span className=\"size-2 rounded-full bg-success-bg\" aria-hidden />\n ) : entry.snapshotSyncStatus.status === 'failed' ? (\n <Icon name=\"triangle-exclamation\" className=\"size-4\" />\n ) : null}\n {status.label}\n </span>\n );\n return (\n <article\n key={provider.id}\n className=\"flex flex-col gap-3 border-b border-border p-3 last:border-b-0 sm:flex-row sm:items-center\"\n >\n <div className=\"flex min-w-0 flex-1 items-center gap-3\">\n <span\n className=\"flex size-9 shrink-0 items-center justify-center rounded-lg border border-border bg-secondary-bg text-text-primary\"\n aria-hidden\n >\n <Icon name=\"cube\" className=\"size-4.5\" />\n </span>\n <div className=\"min-w-0\">\n <h5 className=\"truncate text-sm font-medium text-text-primary\">{provider.name}</h5>\n </div>\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2.5 sm:justify-end\">\n {statusIndicator}\n {statusReason ? (\n <Tooltip content={statusReason}>\n <button\n type=\"button\"\n aria-label=\"Snapshot sync status details\"\n className=\"inline-flex text-text-secondary transition-colors hover:text-text-primary\"\n >\n <Icon name=\"info\" className=\"size-4\" />\n </button>\n </Tooltip>\n ) : null}\n {entry.snapshotSyncStatus.status === 'failed' ? (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n disabled={busy}\n onClick={() => {\n handleRetry(provider);\n }}\n >\n Retry\n </Button>\n ) : null}\n <Button\n variant=\"secondary\"\n size=\"sm\"\n type=\"button\"\n disabled={busy}\n onClick={() => {\n setFormError(null);\n setCreateEntry(null);\n setUpdateProvider(provider);\n }}\n >\n Update\n </Button>\n\n {sandboxCatalog.deleteSandboxProvider ? (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-text-secondary\"\n type=\"button\"\n disabled={busy}\n onClick={() => {\n handleRemove(provider);\n }}\n >\n Remove\n </Button>\n ) : null}\n </div>\n </article>\n );\n })}\n </div>\n </section>\n ) : null}\n\n {!hasConfiguredProvider ? (\n <section>\n <h4 className=\"mb-2 text-xs font-semibold uppercase tracking-wide text-text-secondary\">\n Available · {availableEntries.length}\n </h4>\n {availableEntries.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">\n {catalog.length > 0\n ? 'All catalog providers are configured.'\n : 'No sandbox providers in the catalog.'}\n </p>\n ) : (\n <div className=\"overflow-hidden rounded-xl border border-border bg-card-bg\">\n {availableEntries.map(entry => (\n <article\n key={entry.id}\n className=\"flex flex-col gap-3 border-b border-border p-3 last:border-b-0 sm:flex-row sm:items-center\"\n >\n <div className=\"flex min-w-0 flex-1 items-center gap-3\">\n <span\n className=\"flex size-9 shrink-0 items-center justify-center rounded-lg border border-border bg-secondary-bg text-text-primary\"\n aria-hidden\n >\n <Icon name=\"cube\" className=\"size-4.5\" />\n </span>\n <div className=\"min-w-0\">\n <h5 className=\"truncate text-sm font-medium text-text-primary\">{entry.name}</h5>\n </div>\n </div>\n\n <Button\n variant=\"secondary\"\n type=\"button\"\n disabled={busy}\n onClick={() => {\n setFormError(null);\n setUpdateProvider(null);\n setCreateEntry(entry);\n }}\n >\n Configure\n </Button>\n </article>\n ))}\n </div>\n )}\n </section>\n ) : null}\n </div>\n )}\n </div>\n\n <ConfigureSandboxForm\n open={formOpen}\n onOpenChange={open => {\n if (!open) {\n setFormError(null);\n setCreateEntry(null);\n setUpdateProvider(null);\n }\n }}\n onSave={isUpdate ? handleUpdate : handleCreate}\n title={formTitle}\n description={\n isUpdate\n ? 'Update this sandbox provider. Leave API key blank to keep the existing key.'\n : 'Configure this sandbox provider. API key is never stored in the catalog.'\n }\n initialConfig={formInitialConfig}\n requireApiKey={!isUpdate}\n busy={busy}\n error={formError}\n />\n </>\n );\n};\n\nexport default SandboxSettings;\n","'use client';\n\nimport { useEffect, useState, type FormEvent } from 'react';\n\nimport { cn } from '../../atoms/lib/cn.js';\nimport { auiInputClass } from '../../atoms/lib/inputClasses.js';\nimport { Accordion, AccordionDetails, AccordionSummary } from '../../atoms/primitives/Accordion.js';\nimport { Button } from '../../atoms/primitives/Button.js';\nimport { CenteredModal } from '../../atoms/primitives/CenteredModal.js';\nimport { Icon } from '../../icons/Icon.js';\nimport type { SandboxProviderConfig } from '../../server/types.js';\n\nexport type SandboxConfigDraft = SandboxProviderConfig & {\n apiKey: string;\n};\n\ntype ConfigureSandboxFormProps = {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n onSave: (draft: SandboxConfigDraft) => void | Promise<void>;\n title: string;\n description?: string;\n /** Prefills config fields; apiKey is never autofilled. */\n initialConfig?: SandboxProviderConfig | null;\n /** When false (updates), empty apiKey means keep the existing key. */\n requireApiKey?: boolean;\n busy?: boolean;\n error?: string | null;\n};\n\n/** Sensible defaults so the advanced fields are never blank, even without a catalog preset. */\nconst EMPTY_CONFIG: SandboxProviderConfig = {\n // Snapshot/image is release-owned now; kept only to satisfy the external SandboxProviderConfig type.\n execTimeoutMs: 300000,\n autoStopIntervalInMinutes: 15,\n autoArchiveIntervalInMinutes: 10080,\n autoDeleteIntervalInMinutes: 43200,\n};\n\nconst inputClassName = auiInputClass('h-11 shadow-sm');\n\nfunction parseNonNegInt(raw: string): number | null {\n if (raw.trim() === '') return null;\n const n = Number(raw);\n if (!Number.isFinite(n) || n < 0) return null;\n return n;\n}\n\nconst ConfigureSandboxForm = ({\n open,\n onOpenChange,\n onSave,\n title,\n description,\n initialConfig = null,\n requireApiKey = true,\n busy = false,\n error,\n}: ConfigureSandboxFormProps) => {\n const [execTimeoutMs, setExecTimeoutMs] = useState('');\n const [autoStopIntervalInMinutes, setAutoStopIntervalInMinutes] = useState('');\n const [autoArchiveIntervalInMinutes, setAutoArchiveIntervalInMinutes] = useState('');\n const [autoDeleteIntervalInMinutes, setAutoDeleteIntervalInMinutes] = useState('');\n const [apiKey, setApiKey] = useState('');\n const [advancedOpen, setAdvancedOpen] = useState(false);\n\n const resetForm = () => {\n setExecTimeoutMs('');\n setAutoStopIntervalInMinutes('');\n setAutoArchiveIntervalInMinutes('');\n setAutoDeleteIntervalInMinutes('');\n setApiKey('');\n setAdvancedOpen(false);\n };\n\n useEffect(() => {\n if (!open) return;\n const config = initialConfig ?? EMPTY_CONFIG;\n setExecTimeoutMs(String(config.execTimeoutMs));\n setAutoStopIntervalInMinutes(String(config.autoStopIntervalInMinutes));\n setAutoArchiveIntervalInMinutes(String(config.autoArchiveIntervalInMinutes));\n setAutoDeleteIntervalInMinutes(String(config.autoDeleteIntervalInMinutes));\n setApiKey('');\n setAdvancedOpen(false);\n }, [open, initialConfig]);\n\n const handleOpenChange = (nextOpen: boolean) => {\n if (!nextOpen) resetForm();\n onOpenChange(nextOpen);\n };\n\n const execTimeout = parseNonNegInt(execTimeoutMs);\n const autoStop = parseNonNegInt(autoStopIntervalInMinutes);\n const autoArchive = parseNonNegInt(autoArchiveIntervalInMinutes);\n const autoDelete = parseNonNegInt(autoDeleteIntervalInMinutes);\n const trimmedKey = apiKey.trim();\n\n const isValid =\n (!requireApiKey || !!trimmedKey) &&\n execTimeout != null &&\n autoStop != null &&\n autoArchive != null &&\n autoDelete != null;\n\n const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n if (!isValid || busy || execTimeout == null || autoStop == null || autoArchive == null || autoDelete == null) {\n return;\n }\n\n try {\n await onSave({\n // Snapshot/image is release-owned; the field is retained only for the external type.\n execTimeoutMs: execTimeout,\n autoStopIntervalInMinutes: autoStop,\n autoArchiveIntervalInMinutes: autoArchive,\n autoDeleteIntervalInMinutes: autoDelete,\n apiKey: trimmedKey,\n });\n resetForm();\n onOpenChange(false);\n } catch {\n // Parent surfaces error; keep form open.\n }\n };\n\n return (\n <CenteredModal\n open={open}\n onOpenChange={handleOpenChange}\n title={title}\n description={description}\n contentSized\n headerIcon={\n <span\n className=\"flex size-11 shrink-0 items-center justify-center rounded-lg border border-border bg-secondary-bg text-text-primary\"\n aria-hidden\n >\n <Icon name=\"cube\" className=\"size-6\" />\n </span>\n }\n >\n <form className=\"flex flex-col overflow-y-auto p-5 md:p-6\" onSubmit={handleSubmit}>\n <div className=\"space-y-4\">\n <div>\n <label htmlFor=\"sandbox-api-key\" className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n API key\n {!requireApiKey ? <span className=\"font-normal text-text-secondary\"> (optional)</span> : null}\n </label>\n <input\n id=\"sandbox-api-key\"\n type=\"password\"\n required={requireApiKey}\n value={apiKey}\n onChange={event => {\n setApiKey(event.target.value);\n }}\n placeholder={requireApiKey ? 'dtn_...' : 'Leave blank to keep existing'}\n autoFocus\n className={inputClassName}\n />\n </div>\n\n <Accordion\n expanded={advancedOpen}\n onChange={(_event, next) => setAdvancedOpen(next)}\n className=\"rounded-md border border-border\"\n >\n <AccordionSummary className=\"px-3 py-2.5\">\n <span className=\"flex w-full items-center justify-between text-sm font-medium text-text-primary\">\n Advanced settings\n <Icon\n name=\"chevron-down\"\n className={cn(\n 'size-4 shrink-0 text-text-secondary transition-transform duration-200',\n advancedOpen ? 'rotate-0' : '-rotate-90',\n )}\n />\n </span>\n </AccordionSummary>\n <AccordionDetails className=\"space-y-4 border-t border-border px-3 pb-3 pt-4\">\n <div>\n <label htmlFor=\"sandbox-exec-timeout\" className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n Exec timeout (ms)\n </label>\n <input\n id=\"sandbox-exec-timeout\"\n type=\"number\"\n min={0}\n required\n value={execTimeoutMs}\n onChange={event => {\n setExecTimeoutMs(event.target.value);\n }}\n placeholder=\"300000\"\n className={inputClassName}\n />\n </div>\n\n <div>\n <label htmlFor=\"sandbox-auto-stop\" className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n Auto-stop interval (minutes)\n </label>\n <input\n id=\"sandbox-auto-stop\"\n type=\"number\"\n min={0}\n required\n value={autoStopIntervalInMinutes}\n onChange={event => {\n setAutoStopIntervalInMinutes(event.target.value);\n }}\n placeholder=\"15\"\n className={inputClassName}\n />\n </div>\n\n <div>\n <label htmlFor=\"sandbox-auto-archive\" className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n Auto-archive interval (minutes)\n </label>\n <input\n id=\"sandbox-auto-archive\"\n type=\"number\"\n min={0}\n required\n value={autoArchiveIntervalInMinutes}\n onChange={event => {\n setAutoArchiveIntervalInMinutes(event.target.value);\n }}\n placeholder=\"10080\"\n className={inputClassName}\n />\n </div>\n\n <div>\n <label htmlFor=\"sandbox-auto-delete\" className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n Auto-delete interval (minutes)\n </label>\n <input\n id=\"sandbox-auto-delete\"\n type=\"number\"\n min={0}\n required\n value={autoDeleteIntervalInMinutes}\n onChange={event => {\n setAutoDeleteIntervalInMinutes(event.target.value);\n }}\n placeholder=\"43200\"\n className={inputClassName}\n />\n </div>\n </AccordionDetails>\n </Accordion>\n </div>\n\n <div className=\"mt-6 space-y-3\">\n {error ? <p className=\"text-failure-bg text-sm\">{error}</p> : null}\n <Button type=\"submit\" size=\"lg\" disabled={!isValid || busy} className=\"w-full shrink-0\">\n Save\n </Button>\n </div>\n </form>\n </CenteredModal>\n );\n};\n\nexport default ConfigureSandboxForm;\n"],"mappings":"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","names":["useEffect","useState","jsx","jsxs","useState","useEffect"]}
|
|
1
|
+
{"version":3,"sources":["../src/containers/SettingsBuilder/SandboxSettings.tsx","../src/containers/SettingsBuilder/ConfigureSandboxForm.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useMemo, useState } from 'react';\n\nimport { Spinner } from '@/atoms/primitives/Spinner.js';\nimport { Button } from '../../atoms/primitives/Button.js';\nimport { Tooltip } from '../../atoms/primitives/Tooltip.js';\nimport { Icon } from '../../icons/Icon.js';\nimport { useCatalogServer } from '../../server/ServerContext.js';\nimport type {\n SandboxProviderBase,\n SandboxProviderCatalogEntry,\n SandboxProviderConfig,\n SandboxProviderListEntry,\n SandboxSnapshotSyncStatus,\n} from '../../server/types.js';\nimport { getErrorMessage } from '../../utils/getErrorMessage.js';\nimport { useToasterOptional } from '../ToasterContainer.js';\nimport ConfigureSandboxForm, { type SandboxConfigDraft } from './ConfigureSandboxForm.js';\n\nconst SNAPSHOT_STATUS_POLL_INTERVAL_MS = 10000;\n\nconst configFrom = ({\n execTimeoutMs,\n autoStopIntervalInMinutes,\n autoArchiveIntervalInMinutes,\n autoDeleteIntervalInMinutes,\n}: SandboxProviderConfig): SandboxProviderConfig => ({\n execTimeoutMs,\n autoStopIntervalInMinutes,\n autoArchiveIntervalInMinutes,\n autoDeleteIntervalInMinutes,\n});\n\nconst statusPresentation = (status: SandboxSnapshotSyncStatus['status']): { label: string; className: string } => {\n switch (status) {\n case 'pending':\n return {\n label: 'Syncing image...',\n className: 'text-warning-bg',\n };\n case 'ready':\n return {\n label: 'Connected',\n className: 'text-success-bg',\n };\n case 'failed':\n return {\n label: 'Sync failed',\n className: 'text-failure-bg',\n };\n default:\n return {\n label: status,\n className: 'text-text-secondary',\n };\n }\n};\n\nconst SandboxSettings = () => {\n const { sandboxCatalog } = useCatalogServer();\n const toaster = useToasterOptional();\n\n const [providers, setProviders] = useState<SandboxProviderListEntry[]>([]);\n const [catalog, setCatalog] = useState<SandboxProviderCatalogEntry[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [formError, setFormError] = useState<string | null>(null);\n const [busy, setBusy] = useState(false);\n\n const [createEntry, setCreateEntry] = useState<SandboxProviderCatalogEntry | null>(null);\n const [updateProvider, setUpdateProvider] = useState<SandboxProviderBase | null>(null);\n\n const refresh = useCallback(\n async ({ quiet = false }: { quiet?: boolean } = {}) => {\n if (!sandboxCatalog) return;\n if (!quiet) setLoading(true);\n setError(null);\n try {\n const [listed, available] = await Promise.all([\n sandboxCatalog.listSandboxProviders(),\n sandboxCatalog.getSandboxProviderCatalog(),\n ]);\n setProviders(listed);\n setCatalog(available);\n } catch (err) {\n setError(getErrorMessage(err, 'Failed to load sandbox providers'));\n } finally {\n if (!quiet) setLoading(false);\n }\n },\n [sandboxCatalog],\n );\n\n useEffect(() => {\n void refresh();\n }, [refresh]);\n\n const hasPendingProvider = providers.some(entry => entry.snapshotSyncStatus.status === 'pending');\n\n useEffect(() => {\n if (!sandboxCatalog || !hasPendingProvider) return;\n const activeSandboxCatalog = sandboxCatalog;\n\n let cancelled = false;\n let timeoutId: number | undefined;\n\n function schedulePoll() {\n timeoutId = window.setTimeout(() => {\n void poll();\n }, SNAPSHOT_STATUS_POLL_INTERVAL_MS);\n }\n\n async function poll() {\n try {\n const listed = await activeSandboxCatalog.listSandboxProviders();\n if (cancelled) return;\n setProviders(listed);\n setError(null);\n if (listed.some(entry => entry.snapshotSyncStatus.status === 'pending')) {\n schedulePoll();\n }\n } catch (err) {\n if (cancelled) return;\n setError(getErrorMessage(err, 'Failed to refresh sandbox providers'));\n schedulePoll();\n }\n }\n\n schedulePoll();\n return () => {\n cancelled = true;\n if (timeoutId !== undefined) {\n window.clearTimeout(timeoutId);\n }\n };\n }, [sandboxCatalog, hasPendingProvider]);\n\n // Tenant/UI-wide: only one sandbox provider may be configured at a time.\n const hasConfiguredProvider = providers.length > 0;\n const availableEntries = useMemo(() => {\n if (hasConfiguredProvider) return [];\n const connectedCatalogIds = new Set(providers.map(provider => provider.data.catalogId));\n return catalog.filter(entry => !connectedCatalogIds.has(entry.id));\n }, [catalog, providers, hasConfiguredProvider]);\n\n const formInitialConfig = useMemo(\n () => (updateProvider ? configFrom(updateProvider) : createEntry ? configFrom(createEntry) : null),\n [updateProvider, createEntry],\n );\n\n if (!sandboxCatalog) {\n return <p className=\"text-sm text-text-secondary\">Sandbox provider catalog is not available.</p>;\n }\n\n const runMutation = async (fn: () => Promise<void>, setMutationError = setError) => {\n setBusy(true);\n setError(null);\n try {\n await fn();\n await refresh({ quiet: true });\n } catch (err) {\n setMutationError(getErrorMessage(err, 'Request failed'));\n throw err;\n } finally {\n setBusy(false);\n }\n };\n\n const handleCreate = async (draft: SandboxConfigDraft) => {\n if (!createEntry) return;\n setFormError(null);\n await runMutation(async () => {\n await sandboxCatalog.createSandboxProvider({\n catalogId: createEntry.id,\n name: createEntry.name,\n type: createEntry.type,\n ...configFrom(draft),\n apiKey: draft.apiKey,\n });\n }, setFormError);\n setCreateEntry(null);\n setTimeout(() => {\n toaster?.showSuccess({ title: `${createEntry.name} configured` });\n }, 0);\n };\n\n const handleUpdate = async (draft: SandboxConfigDraft) => {\n if (!updateProvider) return;\n setFormError(null);\n await runMutation(async () => {\n await sandboxCatalog.updateSandboxProvider({\n id: updateProvider.id,\n ...configFrom(draft),\n ...(draft.apiKey ? { apiKey: draft.apiKey } : {}),\n });\n }, setFormError);\n setUpdateProvider(null);\n setTimeout(() => {\n toaster?.showSuccess({ title: `${updateProvider.name} updated` });\n }, 0);\n };\n\n const handleRetry = (provider: SandboxProviderBase) => {\n void runMutation(async () => {\n await sandboxCatalog.updateSandboxProvider({\n id: provider.id,\n ...configFrom(provider),\n });\n }).catch(() => {});\n };\n\n const handleRemove = (provider: SandboxProviderBase) => {\n const deleteSandboxProvider = sandboxCatalog.deleteSandboxProvider;\n if (!deleteSandboxProvider) return;\n void runMutation(async () => {\n await deleteSandboxProvider({ id: provider.id });\n }).catch(() => {});\n };\n\n const formOpen = createEntry != null || updateProvider != null;\n const isUpdate = updateProvider != null;\n const formTitle = updateProvider\n ? `Update ${updateProvider.name}`\n : createEntry\n ? `Configure ${createEntry.name}`\n : 'Configure sandbox provider';\n\n return (\n <>\n <h3 className=\"text-xl font-semibold tracking-tight text-text-primary\">Sandbox providers</h3>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Choose a provider that runs sandboxes for code, files and shell commands. Only one can be active at a time.\n </p>\n\n {error ? (\n <p className=\"mt-3 rounded-md border border-failure-bg/30 bg-failure-bg/10 px-3 py-2 text-sm text-failure-bg\">\n {error}\n </p>\n ) : null}\n\n <div className=\"mt-4 flex-1 overflow-y-auto\">\n {loading ? (\n <p className=\"text-sm text-text-secondary\">Loading sandbox providers…</p>\n ) : (\n <div className=\"space-y-6\">\n {providers.length > 0 ? (\n <section>\n <h4 className=\"mb-2 text-xs font-semibold uppercase tracking-wide text-text-secondary\">\n Configured · {providers.length}\n </h4>\n <div className=\"overflow-hidden rounded-xl border border-border bg-card-bg\">\n {providers.map(entry => {\n const provider = entry.data;\n const status = statusPresentation(entry.snapshotSyncStatus.status);\n const statusReason = entry.snapshotSyncStatus.statusReason;\n const statusIndicator = (\n <span className={`inline-flex items-center gap-1.5 text-sm font-medium ${status.className}`}>\n {entry.snapshotSyncStatus.status === 'pending' ? (\n <Spinner size={16} aria-label=\"Syncing snapshot image\" />\n ) : entry.snapshotSyncStatus.status === 'ready' ? (\n <span className=\"size-2 rounded-full bg-success-bg\" aria-hidden />\n ) : entry.snapshotSyncStatus.status === 'failed' ? (\n <Icon name=\"triangle-exclamation\" className=\"size-4\" />\n ) : null}\n {status.label}\n </span>\n );\n return (\n <article\n key={provider.id}\n className=\"flex flex-col gap-3 border-b border-border p-3 last:border-b-0 sm:flex-row sm:items-center\"\n >\n <div className=\"flex min-w-0 flex-1 items-center gap-3\">\n <span\n className=\"flex size-9 shrink-0 items-center justify-center rounded-lg border border-border bg-secondary-bg text-text-primary\"\n aria-hidden\n >\n <Icon name=\"cube\" className=\"size-4.5\" />\n </span>\n <div className=\"min-w-0\">\n <h5 className=\"truncate text-sm font-medium text-text-primary\">{provider.name}</h5>\n </div>\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2.5 sm:justify-end\">\n {statusIndicator}\n {statusReason ? (\n <Tooltip content={statusReason}>\n <button\n type=\"button\"\n aria-label=\"Snapshot sync status details\"\n className=\"inline-flex text-text-secondary transition-colors hover:text-text-primary\"\n >\n <Icon name=\"info\" className=\"size-4\" />\n </button>\n </Tooltip>\n ) : null}\n {entry.snapshotSyncStatus.status === 'failed' ? (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n disabled={busy}\n onClick={() => {\n handleRetry(provider);\n }}\n >\n Retry\n </Button>\n ) : null}\n <Button\n variant=\"secondary\"\n size=\"sm\"\n type=\"button\"\n disabled={busy}\n onClick={() => {\n setFormError(null);\n setCreateEntry(null);\n setUpdateProvider(provider);\n }}\n >\n Update\n </Button>\n\n {sandboxCatalog.deleteSandboxProvider ? (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-text-secondary\"\n type=\"button\"\n disabled={busy}\n onClick={() => {\n handleRemove(provider);\n }}\n >\n Remove\n </Button>\n ) : null}\n </div>\n </article>\n );\n })}\n </div>\n </section>\n ) : null}\n\n {!hasConfiguredProvider ? (\n <section>\n <h4 className=\"mb-2 text-xs font-semibold uppercase tracking-wide text-text-secondary\">\n Available · {availableEntries.length}\n </h4>\n {availableEntries.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">\n {catalog.length > 0\n ? 'All catalog providers are configured.'\n : 'No sandbox providers in the catalog.'}\n </p>\n ) : (\n <div className=\"overflow-hidden rounded-xl border border-border bg-card-bg\">\n {availableEntries.map(entry => (\n <article\n key={entry.id}\n className=\"flex flex-col gap-3 border-b border-border p-3 last:border-b-0 sm:flex-row sm:items-center\"\n >\n <div className=\"flex min-w-0 flex-1 items-center gap-3\">\n <span\n className=\"flex size-9 shrink-0 items-center justify-center rounded-lg border border-border bg-secondary-bg text-text-primary\"\n aria-hidden\n >\n <Icon name=\"cube\" className=\"size-4.5\" />\n </span>\n <div className=\"min-w-0\">\n <h5 className=\"truncate text-sm font-medium text-text-primary\">{entry.name}</h5>\n </div>\n </div>\n\n <Button\n variant=\"secondary\"\n type=\"button\"\n disabled={busy}\n onClick={() => {\n setFormError(null);\n setUpdateProvider(null);\n setCreateEntry(entry);\n }}\n >\n Configure\n </Button>\n </article>\n ))}\n </div>\n )}\n </section>\n ) : null}\n </div>\n )}\n </div>\n\n <ConfigureSandboxForm\n open={formOpen}\n onOpenChange={open => {\n if (!open) {\n setFormError(null);\n setCreateEntry(null);\n setUpdateProvider(null);\n }\n }}\n onSave={isUpdate ? handleUpdate : handleCreate}\n title={formTitle}\n description={\n isUpdate\n ? 'Update this sandbox provider. Leave API key blank to keep the existing key.'\n : 'Configure this sandbox provider. API key is never stored in the catalog.'\n }\n initialConfig={formInitialConfig}\n requireApiKey={!isUpdate}\n busy={busy}\n error={formError}\n />\n </>\n );\n};\n\nexport default SandboxSettings;\n","'use client';\n\nimport { useEffect, useState, type FormEvent } from 'react';\n\nimport { cn } from '../../atoms/lib/cn.js';\nimport { auiInputClass } from '../../atoms/lib/inputClasses.js';\nimport { Accordion, AccordionDetails, AccordionSummary } from '../../atoms/primitives/Accordion.js';\nimport { Button } from '../../atoms/primitives/Button.js';\nimport { CenteredModal } from '../../atoms/primitives/CenteredModal.js';\nimport { Icon } from '../../icons/Icon.js';\nimport type { SandboxProviderConfig } from '../../server/types.js';\n\nexport type SandboxConfigDraft = SandboxProviderConfig & {\n apiKey: string;\n};\n\ntype ConfigureSandboxFormProps = {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n onSave: (draft: SandboxConfigDraft) => void | Promise<void>;\n title: string;\n description?: string;\n /** Prefills config fields; apiKey is never autofilled. */\n initialConfig?: SandboxProviderConfig | null;\n /** When false (updates), empty apiKey means keep the existing key. */\n requireApiKey?: boolean;\n busy?: boolean;\n error?: string | null;\n};\n\n/** Sensible defaults so the advanced fields are never blank, even without a catalog preset. */\nconst EMPTY_CONFIG: SandboxProviderConfig = {\n // Snapshot/image is release-owned now; kept only to satisfy the external SandboxProviderConfig type.\n execTimeoutMs: 300000,\n autoStopIntervalInMinutes: 15,\n autoArchiveIntervalInMinutes: 10080,\n autoDeleteIntervalInMinutes: 43200,\n};\n\nconst inputClassName = auiInputClass('h-11 shadow-sm');\n\nfunction parseNonNegInt(raw: string): number | null {\n if (raw.trim() === '') return null;\n const n = Number(raw);\n if (!Number.isFinite(n) || n < 0) return null;\n return n;\n}\n\nconst ConfigureSandboxForm = ({\n open,\n onOpenChange,\n onSave,\n title,\n description,\n initialConfig = null,\n requireApiKey = true,\n busy = false,\n error,\n}: ConfigureSandboxFormProps) => {\n const [execTimeoutMs, setExecTimeoutMs] = useState('');\n const [autoStopIntervalInMinutes, setAutoStopIntervalInMinutes] = useState('');\n const [autoArchiveIntervalInMinutes, setAutoArchiveIntervalInMinutes] = useState('');\n const [autoDeleteIntervalInMinutes, setAutoDeleteIntervalInMinutes] = useState('');\n const [apiKey, setApiKey] = useState('');\n const [advancedOpen, setAdvancedOpen] = useState(false);\n\n const resetForm = () => {\n setExecTimeoutMs('');\n setAutoStopIntervalInMinutes('');\n setAutoArchiveIntervalInMinutes('');\n setAutoDeleteIntervalInMinutes('');\n setApiKey('');\n setAdvancedOpen(false);\n };\n\n useEffect(() => {\n if (!open) return;\n const config = initialConfig ?? EMPTY_CONFIG;\n setExecTimeoutMs(String(config.execTimeoutMs));\n setAutoStopIntervalInMinutes(String(config.autoStopIntervalInMinutes));\n setAutoArchiveIntervalInMinutes(String(config.autoArchiveIntervalInMinutes));\n setAutoDeleteIntervalInMinutes(String(config.autoDeleteIntervalInMinutes));\n setApiKey('');\n setAdvancedOpen(false);\n }, [open, initialConfig]);\n\n const handleOpenChange = (nextOpen: boolean) => {\n if (!nextOpen) resetForm();\n onOpenChange(nextOpen);\n };\n\n const execTimeout = parseNonNegInt(execTimeoutMs);\n const autoStop = parseNonNegInt(autoStopIntervalInMinutes);\n const autoArchive = parseNonNegInt(autoArchiveIntervalInMinutes);\n const autoDelete = parseNonNegInt(autoDeleteIntervalInMinutes);\n const trimmedKey = apiKey.trim();\n\n const isValid =\n (!requireApiKey || !!trimmedKey) &&\n execTimeout != null &&\n autoStop != null &&\n autoArchive != null &&\n autoDelete != null;\n\n const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n if (!isValid || busy || execTimeout == null || autoStop == null || autoArchive == null || autoDelete == null) {\n return;\n }\n\n try {\n await onSave({\n // Snapshot/image is release-owned; the field is retained only for the external type.\n execTimeoutMs: execTimeout,\n autoStopIntervalInMinutes: autoStop,\n autoArchiveIntervalInMinutes: autoArchive,\n autoDeleteIntervalInMinutes: autoDelete,\n apiKey: trimmedKey,\n });\n resetForm();\n onOpenChange(false);\n } catch {\n // Parent surfaces error; keep form open.\n }\n };\n\n return (\n <CenteredModal\n open={open}\n onOpenChange={handleOpenChange}\n title={title}\n description={description}\n contentSized\n headerIcon={\n <span\n className=\"flex size-11 shrink-0 items-center justify-center rounded-lg border border-border bg-secondary-bg text-text-primary\"\n aria-hidden\n >\n <Icon name=\"cube\" className=\"size-6\" />\n </span>\n }\n >\n <form className=\"flex flex-col overflow-y-auto p-5 md:p-6\" onSubmit={handleSubmit}>\n <div className=\"space-y-4\">\n <div>\n <label htmlFor=\"sandbox-api-key\" className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n API key\n {!requireApiKey ? <span className=\"font-normal text-text-secondary\"> (optional)</span> : null}\n </label>\n <input\n id=\"sandbox-api-key\"\n type=\"password\"\n required={requireApiKey}\n value={apiKey}\n onChange={event => {\n setApiKey(event.target.value);\n }}\n placeholder={requireApiKey ? 'dtn_...' : 'Leave blank to keep existing'}\n autoFocus\n className={inputClassName}\n />\n </div>\n\n <Accordion\n expanded={advancedOpen}\n onChange={(_event, next) => setAdvancedOpen(next)}\n className=\"rounded-md border border-border\"\n >\n <AccordionSummary className=\"px-3 py-2.5\">\n <span className=\"flex w-full items-center justify-between text-sm font-medium text-text-primary\">\n Advanced settings\n <Icon\n name=\"chevron-down\"\n className={cn(\n 'size-4 shrink-0 text-text-secondary transition-transform duration-200',\n advancedOpen ? 'rotate-0' : '-rotate-90',\n )}\n />\n </span>\n </AccordionSummary>\n <AccordionDetails className=\"space-y-4 border-t border-border px-3 pb-3 pt-4\">\n <div>\n <label htmlFor=\"sandbox-exec-timeout\" className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n Exec timeout (ms)\n </label>\n <input\n id=\"sandbox-exec-timeout\"\n type=\"number\"\n min={0}\n required\n value={execTimeoutMs}\n onChange={event => {\n setExecTimeoutMs(event.target.value);\n }}\n placeholder=\"300000\"\n className={inputClassName}\n />\n </div>\n\n <div>\n <label htmlFor=\"sandbox-auto-stop\" className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n Auto-stop interval (minutes)\n </label>\n <input\n id=\"sandbox-auto-stop\"\n type=\"number\"\n min={0}\n required\n value={autoStopIntervalInMinutes}\n onChange={event => {\n setAutoStopIntervalInMinutes(event.target.value);\n }}\n placeholder=\"15\"\n className={inputClassName}\n />\n </div>\n\n <div>\n <label htmlFor=\"sandbox-auto-archive\" className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n Auto-archive interval (minutes)\n </label>\n <input\n id=\"sandbox-auto-archive\"\n type=\"number\"\n min={0}\n required\n value={autoArchiveIntervalInMinutes}\n onChange={event => {\n setAutoArchiveIntervalInMinutes(event.target.value);\n }}\n placeholder=\"10080\"\n className={inputClassName}\n />\n </div>\n\n <div>\n <label htmlFor=\"sandbox-auto-delete\" className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n Auto-delete interval (minutes)\n </label>\n <input\n id=\"sandbox-auto-delete\"\n type=\"number\"\n min={0}\n required\n value={autoDeleteIntervalInMinutes}\n onChange={event => {\n setAutoDeleteIntervalInMinutes(event.target.value);\n }}\n placeholder=\"43200\"\n className={inputClassName}\n />\n </div>\n </AccordionDetails>\n </Accordion>\n </div>\n\n <div className=\"mt-6 space-y-3\">\n {error ? <p className=\"text-failure-bg text-sm\">{error}</p> : null}\n <Button type=\"submit\" size=\"lg\" disabled={!isValid || busy} className=\"w-full shrink-0\">\n Save\n </Button>\n </div>\n </form>\n </CenteredModal>\n );\n};\n\nexport default ConfigureSandboxForm;\n"],"mappings":"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","names":["useEffect","useState","jsx","jsxs","useState","useEffect"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
SchedulesPage
|
|
4
|
+
} from "./chunk-6O6FWUDW.js";
|
|
5
|
+
import "./chunk-ZTMKFZQ3.js";
|
|
6
|
+
import "./chunk-2XPPBEYF.js";
|
|
7
|
+
import "./chunk-XAU7QFG3.js";
|
|
8
|
+
import "./chunk-U7KURWW3.js";
|
|
9
|
+
import "./chunk-M6MHLVSO.js";
|
|
10
|
+
import "./chunk-MPNSO7E6.js";
|
|
11
|
+
import "./chunk-7Z2C2K7F.js";
|
|
12
|
+
export {
|
|
13
|
+
SchedulesPage
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=SchedulesPage-KKLUDO6B.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -1,27 +1,28 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
3
|
Icon,
|
|
4
|
+
PageHeader,
|
|
4
5
|
Spinner,
|
|
5
6
|
auiButtonClass,
|
|
6
7
|
useCompactLayout,
|
|
7
8
|
useDraftCatalog
|
|
8
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-XAU7QFG3.js";
|
|
9
10
|
import {
|
|
10
11
|
useOptionalCatalogServer,
|
|
11
12
|
useOptionalRefreshServerCapabilities,
|
|
12
13
|
useShellMode
|
|
13
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-U7KURWW3.js";
|
|
14
15
|
import {
|
|
15
16
|
cn
|
|
16
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-7Z2C2K7F.js";
|
|
17
18
|
|
|
18
19
|
// src/containers/SettingsBuilder/index.tsx
|
|
19
20
|
import { lazy, Suspense, useCallback, useEffect, useMemo } from "react";
|
|
20
21
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
21
|
-
var ModelSettings = lazy(() => import("./ModelSettings-
|
|
22
|
-
var ConnectorSettings = lazy(() => import("./ConnectorSettings-
|
|
23
|
-
var SkillSettings = lazy(() => import("./SkillSettings-
|
|
24
|
-
var SandboxSettings = lazy(() => import("./SandboxSettings-
|
|
22
|
+
var ModelSettings = lazy(() => import("./ModelSettings-G7IFNC2Y.js"));
|
|
23
|
+
var ConnectorSettings = lazy(() => import("./ConnectorSettings-MXFRWWR6.js"));
|
|
24
|
+
var SkillSettings = lazy(() => import("./SkillSettings-FGGMSUNO.js"));
|
|
25
|
+
var SandboxSettings = lazy(() => import("./SandboxSettings-NXAANMAX.js"));
|
|
25
26
|
function SettingsSectionFallback() {
|
|
26
27
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-1 items-center justify-center py-8", role: "status", "aria-live": "polite", "aria-busy": "true", children: [
|
|
27
28
|
/* @__PURE__ */ jsx(Spinner, { size: 20, className: "text-text-secondary" }),
|
|
@@ -79,20 +80,23 @@ var TruefoundrySettingsBuilder = () => {
|
|
|
79
80
|
}, [hasSkills, hasSandbox]);
|
|
80
81
|
if (!settingsOpen || !catalog) return null;
|
|
81
82
|
return /* @__PURE__ */ jsxs("div", { className: "flex h-full min-h-0 w-full flex-col bg-primary-bg", children: [
|
|
82
|
-
/* @__PURE__ */
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
"
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
83
|
+
/* @__PURE__ */ jsx(
|
|
84
|
+
PageHeader,
|
|
85
|
+
{
|
|
86
|
+
title: "Settings",
|
|
87
|
+
start: /* @__PURE__ */ jsx(
|
|
88
|
+
"button",
|
|
89
|
+
{
|
|
90
|
+
type: "button",
|
|
91
|
+
"aria-label": "Back",
|
|
92
|
+
title: "Back",
|
|
93
|
+
className: auiButtonClass({ variant: "ghost", size: "icon" }),
|
|
94
|
+
onClick: closeSettings,
|
|
95
|
+
children: /* @__PURE__ */ jsx(Icon, { name: "arrow-left" })
|
|
96
|
+
}
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
),
|
|
96
100
|
/* @__PURE__ */ jsxs("div", { className: cn("flex min-h-0 flex-1 flex-col", !compact && "md:flex-row"), children: [
|
|
97
101
|
/* @__PURE__ */ jsx(
|
|
98
102
|
"nav",
|
|
@@ -140,4 +144,4 @@ var SettingsBuilder_default = TruefoundrySettingsBuilder;
|
|
|
140
144
|
export {
|
|
141
145
|
SettingsBuilder_default as default
|
|
142
146
|
};
|
|
143
|
-
//# sourceMappingURL=SettingsBuilder-
|
|
147
|
+
//# sourceMappingURL=SettingsBuilder-L34IQ56S.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/containers/SettingsBuilder/index.tsx"],"sourcesContent":["'use client';\n\nimport { lazy, Suspense, useCallback, useEffect, useMemo } from 'react';\n\nimport { useDraftCatalog } from '@/atoms/draft/DraftCatalogProvider.js';\nimport { auiButtonClass } from '@/atoms/lib/buttonClasses.js';\nimport { cn } from '@/atoms/lib/cn.js';\nimport { useCompactLayout } from '@/atoms/lib/CompactLayoutContext.js';\nimport { PageHeader } from '@/atoms/PageHeader.js';\nimport { Spinner } from '@/atoms/primitives/Spinner.js';\nimport { Icon } from '@/icons/Icon.js';\nimport { useOptionalCatalogServer, useOptionalRefreshServerCapabilities } from '@/server/ServerContext.js';\nimport { useShellMode, type SettingsSection } from '@/server/ShellModeContext.js';\n\n// Section modules (and their list/catalog APIs) load only when that tab mounts.\nconst ModelSettings = lazy(() => import('./ModelSettings.js'));\nconst ConnectorSettings = lazy(() => import('./ConnectorSettings.js'));\nconst SkillSettings = lazy(() => import('./SkillSettings.js'));\nconst SandboxSettings = lazy(() => import('./SandboxSettings.js'));\n\nfunction SettingsSectionFallback() {\n return (\n <div className=\"flex flex-1 items-center justify-center py-8\" role=\"status\" aria-live=\"polite\" aria-busy=\"true\">\n <Spinner size={20} className=\"text-text-secondary\" />\n <span className=\"sr-only\">Loading</span>\n </div>\n );\n}\n\nconst TruefoundrySettingsBuilder = () => {\n const { settingsOpen, settingsSection: section, setSettingsOpen } = useShellMode();\n const catalog = useOptionalCatalogServer();\n const refreshServerCapabilities = useOptionalRefreshServerCapabilities();\n const { refresh: refreshDraftCatalog } = useDraftCatalog();\n // dock/widget panels are ~mobile width even on a wide viewport — keep Settings stacked.\n const compact = useCompactLayout();\n const hasSkills = catalog?.skillCatalog != null;\n const hasSandbox = catalog?.sandboxCatalog != null;\n\n const closeSettings = useCallback(() => {\n setSettingsOpen(false);\n }, [setSettingsOpen]);\n\n // Refresh catalogs whenever settings are closed or navigated away from.\n useEffect(() => {\n if (!settingsOpen) return;\n return () => {\n refreshDraftCatalog();\n refreshServerCapabilities?.();\n };\n }, [settingsOpen, refreshDraftCatalog, refreshServerCapabilities]);\n\n useEffect(() => {\n if (!hasSkills && section === 'skills') {\n setSettingsOpen(settingsOpen, 'models');\n }\n if (!hasSandbox && section === 'sandbox') {\n setSettingsOpen(settingsOpen, 'models');\n }\n }, [hasSkills, hasSandbox, section, settingsOpen, setSettingsOpen]);\n\n useEffect(() => {\n if (!settingsOpen) return;\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key !== 'Escape') return;\n event.stopImmediatePropagation();\n closeSettings();\n };\n // Capture so layout Escape handlers (sessions drawer / widget) do not also fire.\n window.addEventListener('keydown', onKeyDown, true);\n return () => window.removeEventListener('keydown', onKeyDown, true);\n }, [closeSettings, settingsOpen]);\n\n const sections = useMemo<\n Array<{\n id: SettingsSection;\n label: string;\n icon: 'cpu' | 'plug' | 'lightbulb' | 'cube';\n }>\n >(() => {\n const baseSections: Array<{\n id: SettingsSection;\n label: string;\n icon: 'cpu' | 'plug' | 'lightbulb' | 'cube';\n }> = [\n { id: 'models', label: 'Models', icon: 'cpu' },\n { id: 'connectors', label: 'Connectors', icon: 'plug' },\n ];\n if (hasSkills) {\n baseSections.push({ id: 'skills', label: 'Skills', icon: 'lightbulb' });\n }\n if (hasSandbox) {\n baseSections.push({ id: 'sandbox', label: 'Sandbox providers', icon: 'cube' });\n }\n return baseSections;\n }, [hasSkills, hasSandbox]);\n\n if (!settingsOpen || !catalog) return null;\n\n return (\n <div className=\"flex h-full min-h-0 w-full flex-col bg-primary-bg\">\n <PageHeader\n title=\"Settings\"\n start={\n <button\n type=\"button\"\n aria-label=\"Back\"\n title=\"Back\"\n className={auiButtonClass({ variant: 'ghost', size: 'icon' })}\n onClick={closeSettings}\n >\n <Icon name=\"arrow-left\" />\n </button>\n }\n />\n\n <div className={cn('flex min-h-0 flex-1 flex-col', !compact && 'md:flex-row')}>\n <nav\n aria-label=\"Settings sections\"\n className={cn(\n 'flex w-full gap-1 border-b border-border bg-secondary-bg/40 p-2',\n compact ? 'min-w-0' : 'justify-center md:w-48 md:flex-col md:justify-start md:border-b-0 md:border-r',\n )}\n >\n {sections.map(item => (\n <button\n key={item.id}\n type=\"button\"\n aria-current={section === item.id ? 'page' : undefined}\n {...(compact ? { title: item.label } : {})}\n className={cn(\n 'flex min-h-9 items-center gap-2 rounded-md px-3 text-sm font-medium transition-colors',\n 'focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-focus-ring',\n // Narrow panels cannot fit fixed-width tabs, so tabs split the row instead.\n compact ? 'min-w-0 flex-1 justify-center gap-1.5 px-1.5' : 'shrink-0',\n section === item.id\n ? 'bg-primary-button-bg text-primary-button-text'\n : 'text-text-secondary hover:bg-ghost-button-hover/60 hover:text-text-primary',\n )}\n onClick={() => {\n setSettingsOpen(true, item.id);\n }}\n >\n <Icon name={item.icon} className=\"h-4 w-4 shrink-0\" />\n {compact ? <span className=\"truncate\">{item.label}</span> : item.label}\n </button>\n ))}\n </nav>\n\n <section className=\"flex flex-col h-full flex-1 overflow-y-hidden px-6 py-4\">\n <div className=\"w-full max-w-210 h-full min-h-0 flex flex-col mx-auto\">\n <Suspense fallback={<SettingsSectionFallback />}>\n {section === 'models' ? <ModelSettings /> : null}\n {section === 'connectors' ? <ConnectorSettings /> : null}\n {section === 'skills' && hasSkills ? <SkillSettings /> : null}\n {section === 'sandbox' && hasSandbox ? <SandboxSettings /> : null}\n </Suspense>\n </div>\n </section>\n </div>\n </div>\n );\n};\n\nexport default TruefoundrySettingsBuilder;\n"],"mappings":"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","names":[]}
|
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
3
|
buildPath,
|
|
4
|
-
|
|
5
|
-
placesEqual
|
|
6
|
-
|
|
4
|
+
matchLocation,
|
|
5
|
+
placesEqual,
|
|
6
|
+
sanitizeSearchForPlace
|
|
7
|
+
} from "./chunk-WVE3F724.js";
|
|
7
8
|
import {
|
|
9
|
+
sessionIsCreateAgent
|
|
10
|
+
} from "./chunk-RDXBMTP5.js";
|
|
11
|
+
import "./chunk-2XPPBEYF.js";
|
|
12
|
+
import {
|
|
13
|
+
isSettingsChromeEnabled,
|
|
14
|
+
useOptionalCatalogServer,
|
|
8
15
|
useOptionalServer,
|
|
16
|
+
useServerCapabilities,
|
|
9
17
|
useShellMode
|
|
10
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-U7KURWW3.js";
|
|
11
19
|
|
|
12
20
|
// src/routing/ShellRouteSync.tsx
|
|
13
|
-
import { useCallback, useEffect, useRef } from "react";
|
|
21
|
+
import { useCallback, useEffect, useMemo, useRef } from "react";
|
|
14
22
|
import { useLocation, useNavigate } from "react-router-dom";
|
|
15
23
|
|
|
16
24
|
// src/routing/derivePlace.ts
|
|
@@ -26,6 +34,12 @@ function deriveChatPlace(snapshot) {
|
|
|
26
34
|
}
|
|
27
35
|
function derivePlace(snapshot) {
|
|
28
36
|
if (snapshot.settingsOpen) return { type: "settings" };
|
|
37
|
+
if (snapshot.schedulesOpen) return { type: "schedules" };
|
|
38
|
+
if (snapshot.sessionsOpen) return { type: "sessionsBrowser" };
|
|
39
|
+
if (snapshot.libraryOpen && snapshot.libraryAgentId != null) {
|
|
40
|
+
return { type: "libraryAgent", agentId: snapshot.libraryAgentId };
|
|
41
|
+
}
|
|
42
|
+
if (snapshot.libraryOpen) return { type: "library" };
|
|
29
43
|
return deriveChatPlace(snapshot);
|
|
30
44
|
}
|
|
31
45
|
|
|
@@ -37,10 +51,21 @@ function ShellRouteSync({
|
|
|
37
51
|
}) {
|
|
38
52
|
const shell = useShellMode();
|
|
39
53
|
const server = useOptionalServer();
|
|
54
|
+
const catalog = useOptionalCatalogServer();
|
|
55
|
+
const capabilities = useServerCapabilities();
|
|
40
56
|
const navigate = useNavigate();
|
|
41
57
|
const location = useLocation();
|
|
58
|
+
const settingsChromeEnabled = isSettingsChromeEnabled({ catalog, capabilities });
|
|
59
|
+
const effectiveRoutes = useMemo(
|
|
60
|
+
() => settingsChromeEnabled ? routes : { ...routes, settings: null },
|
|
61
|
+
[routes, settingsChromeEnabled]
|
|
62
|
+
);
|
|
42
63
|
const snapshot = {
|
|
43
64
|
settingsOpen: shell.settingsOpen,
|
|
65
|
+
libraryOpen: shell.libraryOpen,
|
|
66
|
+
sessionsOpen: shell.sessionsOpen,
|
|
67
|
+
libraryAgentId: shell.libraryAgentId,
|
|
68
|
+
schedulesOpen: shell.schedulesOpen,
|
|
44
69
|
pendingSessionId: shell.pendingSessionId,
|
|
45
70
|
activeRemoteId,
|
|
46
71
|
mode: shell.mode,
|
|
@@ -66,6 +91,7 @@ function ShellRouteSync({
|
|
|
66
91
|
shell.openHistorySession({
|
|
67
92
|
sessionId,
|
|
68
93
|
isMutable: session.isMutable,
|
|
94
|
+
isCreateAgent: sessionIsCreateAgent(session),
|
|
69
95
|
...session.agentName != null ? { agentName: session.agentName } : {}
|
|
70
96
|
});
|
|
71
97
|
}).catch(() => {
|
|
@@ -81,15 +107,31 @@ function ShellRouteSync({
|
|
|
81
107
|
case "settings":
|
|
82
108
|
shell.setSettingsOpen(true);
|
|
83
109
|
return;
|
|
110
|
+
case "library":
|
|
111
|
+
shell.setLibraryOpen(true);
|
|
112
|
+
return;
|
|
113
|
+
case "sessionsBrowser":
|
|
114
|
+
shell.setSessionsOpen(true);
|
|
115
|
+
return;
|
|
116
|
+
case "libraryAgent":
|
|
117
|
+
shell.openLibraryAgent(target.agentId);
|
|
118
|
+
return;
|
|
119
|
+
case "schedules":
|
|
120
|
+
shell.setSchedulesOpen(true);
|
|
121
|
+
return;
|
|
84
122
|
case "session":
|
|
123
|
+
shell.setLibraryOpen(false);
|
|
85
124
|
if (shell.pendingSessionId === target.sessionId || activeRemoteId === target.sessionId) return;
|
|
86
125
|
openSession(target.sessionId);
|
|
87
126
|
return;
|
|
88
127
|
case "agent":
|
|
128
|
+
shell.setLibraryOpen(false);
|
|
89
129
|
shell.selectLibraryAgent({ isMutable: false, agentName: target.agentName });
|
|
90
130
|
return;
|
|
91
131
|
case "root":
|
|
92
132
|
shell.setSettingsOpen(false);
|
|
133
|
+
shell.setLibraryOpen(false);
|
|
134
|
+
shell.setSchedulesOpen(false);
|
|
93
135
|
switch (shell.agentConfigMode) {
|
|
94
136
|
case "AgentLibrary":
|
|
95
137
|
shell.openLibraryHome();
|
|
@@ -109,21 +151,34 @@ function ShellRouteSync({
|
|
|
109
151
|
useEffect(() => {
|
|
110
152
|
if (bootedRef.current) return;
|
|
111
153
|
bootedRef.current = true;
|
|
112
|
-
const urlPlace =
|
|
113
|
-
|
|
154
|
+
const urlPlace = matchLocation({
|
|
155
|
+
pathname: location.pathname,
|
|
156
|
+
search: location.search,
|
|
157
|
+
routes: effectiveRoutes
|
|
158
|
+
}) ?? { type: "root" };
|
|
159
|
+
const settingsOnBoot = settingsChromeEnabled && (initialSettingsOpen || urlPlace.type === "settings");
|
|
114
160
|
if (urlPlace.type === "settings") {
|
|
115
|
-
shell.setSettingsOpen(true);
|
|
161
|
+
if (settingsChromeEnabled) shell.setSettingsOpen(true);
|
|
162
|
+
} else if (urlPlace.type === "library") {
|
|
163
|
+
shell.setLibraryOpen(true);
|
|
164
|
+
} else if (urlPlace.type === "sessionsBrowser") {
|
|
165
|
+
shell.setSessionsOpen(true);
|
|
166
|
+
} else if (urlPlace.type === "libraryAgent") {
|
|
167
|
+
shell.openLibraryAgent(urlPlace.agentId);
|
|
168
|
+
} else if (urlPlace.type === "schedules") {
|
|
169
|
+
shell.setSchedulesOpen(true);
|
|
116
170
|
} else {
|
|
117
171
|
const chatPlace = deriveChatPlace(snapshot);
|
|
118
172
|
if (!placesEqual(chatPlace, urlPlace)) applyPlace(urlPlace);
|
|
119
|
-
if (
|
|
173
|
+
if (settingsOnBoot) shell.setSettingsOpen(true);
|
|
120
174
|
}
|
|
121
175
|
const desiredPlace = settingsOnBoot ? { type: "settings" } : urlPlace;
|
|
122
|
-
const desiredPath = buildPath(desiredPlace,
|
|
176
|
+
const desiredPath = buildPath(desiredPlace, effectiveRoutes);
|
|
177
|
+
const desiredSearch = sanitizeSearchForPlace(desiredPlace, location.search);
|
|
123
178
|
prevPlaceRef.current = desiredPlace;
|
|
124
|
-
if (desiredPath != null && desiredPath !== location.pathname) {
|
|
125
|
-
selfNavPathRef.current = desiredPath;
|
|
126
|
-
navigate({ pathname: desiredPath, search:
|
|
179
|
+
if (desiredPath != null && (desiredPath !== location.pathname || desiredSearch !== location.search)) {
|
|
180
|
+
selfNavPathRef.current = desiredPath !== location.pathname ? desiredPath : null;
|
|
181
|
+
navigate({ pathname: desiredPath, search: desiredSearch, hash: location.hash }, { replace: true });
|
|
127
182
|
}
|
|
128
183
|
}, []);
|
|
129
184
|
useEffect(() => {
|
|
@@ -132,15 +187,19 @@ function ShellRouteSync({
|
|
|
132
187
|
shellSyncStartedRef.current = true;
|
|
133
188
|
return;
|
|
134
189
|
}
|
|
135
|
-
const target = buildPath(place,
|
|
190
|
+
const target = buildPath(place, effectiveRoutes);
|
|
136
191
|
if (target == null) return;
|
|
192
|
+
const basename = effectiveRoutes.basename.endsWith("/") ? effectiveRoutes.basename.slice(0, -1) : effectiveRoutes.basename;
|
|
193
|
+
const browserPathname = `${basename}${location.pathname}` || "/";
|
|
194
|
+
const latestSearch = window.location.pathname === browserPathname ? window.location.search : location.search;
|
|
195
|
+
const targetSearch = sanitizeSearchForPlace(place, latestSearch);
|
|
137
196
|
const prev = prevPlaceRef.current;
|
|
138
197
|
prevPlaceRef.current = place;
|
|
139
|
-
if (target === location.pathname) return;
|
|
198
|
+
if (target === location.pathname && targetSearch === location.search) return;
|
|
140
199
|
const replace = place.type === "session" && shell.pendingSessionId == null && prev != null && prev.type !== "session";
|
|
141
|
-
selfNavPathRef.current = target;
|
|
142
|
-
navigate({ pathname: target, search:
|
|
143
|
-
}, [placeKey]);
|
|
200
|
+
selfNavPathRef.current = target !== location.pathname ? target : null;
|
|
201
|
+
navigate({ pathname: target, search: targetSearch, hash: location.hash }, { replace });
|
|
202
|
+
}, [placeKey, settingsChromeEnabled]);
|
|
144
203
|
useEffect(() => {
|
|
145
204
|
if (!bootedRef.current) return;
|
|
146
205
|
if (!urlSyncStartedRef.current) {
|
|
@@ -151,17 +210,31 @@ function ShellRouteSync({
|
|
|
151
210
|
selfNavPathRef.current = null;
|
|
152
211
|
return;
|
|
153
212
|
}
|
|
154
|
-
const urlPlace =
|
|
213
|
+
const urlPlace = matchLocation({
|
|
214
|
+
pathname: location.pathname,
|
|
215
|
+
search: location.search,
|
|
216
|
+
routes: effectiveRoutes
|
|
217
|
+
});
|
|
155
218
|
if (urlPlace == null) {
|
|
156
|
-
const rootPath =
|
|
219
|
+
const rootPath = effectiveRoutes.root;
|
|
220
|
+
const rootSearch = sanitizeSearchForPlace({ type: "root" }, location.search);
|
|
157
221
|
selfNavPathRef.current = rootPath;
|
|
158
|
-
navigate({ pathname: rootPath, search:
|
|
222
|
+
navigate({ pathname: rootPath, search: rootSearch, hash: location.hash }, { replace: true });
|
|
159
223
|
applyPlace({ type: "root" });
|
|
160
224
|
return;
|
|
161
225
|
}
|
|
162
226
|
if (urlPlace.type !== "settings" && shell.settingsOpen) {
|
|
163
227
|
shell.setSettingsOpen(false);
|
|
164
228
|
}
|
|
229
|
+
if (urlPlace.type !== "library" && urlPlace.type !== "libraryAgent" && shell.libraryOpen) {
|
|
230
|
+
shell.setLibraryOpen(false);
|
|
231
|
+
}
|
|
232
|
+
if (urlPlace.type !== "sessionsBrowser" && shell.sessionsOpen) {
|
|
233
|
+
shell.setSessionsOpen(false);
|
|
234
|
+
}
|
|
235
|
+
if (urlPlace.type !== "schedules" && shell.schedulesOpen) {
|
|
236
|
+
shell.setSchedulesOpen(false);
|
|
237
|
+
}
|
|
165
238
|
applyPlace(urlPlace);
|
|
166
239
|
}, [location.pathname]);
|
|
167
240
|
return null;
|
|
@@ -169,4 +242,4 @@ function ShellRouteSync({
|
|
|
169
242
|
export {
|
|
170
243
|
ShellRouteSync
|
|
171
244
|
};
|
|
172
|
-
//# sourceMappingURL=ShellRouteSync-
|
|
245
|
+
//# sourceMappingURL=ShellRouteSync-CEBETGEJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/routing/ShellRouteSync.tsx","../src/routing/derivePlace.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useMemo, useRef } from 'react';\nimport { useLocation, useNavigate } from 'react-router-dom';\n\nimport { sessionIsCreateAgent } from '../atoms/lib/sessionCreateAgent.js';\nimport { useOptionalCatalogServer, useOptionalServer, useServerCapabilities } from '../server/ServerContext.js';\nimport { useShellMode } from '../server/ShellModeContext.js';\nimport { isSettingsChromeEnabled } from '../server/settingsChrome.js';\nimport { deriveChatPlace, derivePlace } from './derivePlace.js';\nimport { buildPath, matchLocation, placesEqual, sanitizeSearchForPlace } from './paths.js';\nimport type { ResolvedRoutes, RoutePlace, ShellSnapshot } from './types.js';\n\n/**\n * Single bidirectional bridge between shell state and the URL. Mounted under\n * `ShellModeProvider` but outside the keyed chat runtime so boot applies once.\n */\nexport function ShellRouteSync({\n routes,\n activeRemoteId,\n initialSettingsOpen,\n}: {\n routes: ResolvedRoutes;\n activeRemoteId: string | undefined;\n initialSettingsOpen: boolean;\n}) {\n const shell = useShellMode();\n const server = useOptionalServer();\n const catalog = useOptionalCatalogServer();\n const capabilities = useServerCapabilities();\n const navigate = useNavigate();\n const location = useLocation();\n // Same gate as the Settings sidebar button: no catalog / settings capability\n // off → `/settings` is unregistered (match + build return null).\n const settingsChromeEnabled = isSettingsChromeEnabled({ catalog, capabilities });\n const effectiveRoutes = useMemo(\n () => (settingsChromeEnabled ? routes : { ...routes, settings: null }),\n [routes, settingsChromeEnabled],\n );\n\n const snapshot: ShellSnapshot = {\n settingsOpen: shell.settingsOpen,\n libraryOpen: shell.libraryOpen,\n sessionsOpen: shell.sessionsOpen,\n libraryAgentId: shell.libraryAgentId,\n schedulesOpen: shell.schedulesOpen,\n pendingSessionId: shell.pendingSessionId,\n activeRemoteId,\n mode: shell.mode,\n agentConfigMode: shell.agentConfigMode,\n };\n\n const place = derivePlace(snapshot);\n const placeKey = JSON.stringify(place);\n\n // Guards. `selfNavPathRef` marks a path we navigated to ourselves so the\n // URL->shell effect does not re-apply it. `prevPlaceRef` powers push/replace.\n const selfNavPathRef = useRef<string | null>(null);\n const prevPlaceRef = useRef<RoutePlace | null>(null);\n const bootedRef = useRef(false);\n // Latest session id the URL asked for, so slower lookups cannot bind over it.\n const requestedSessionRef = useRef<string | null>(null);\n // Boot owns the first URL; the ongoing effects skip their initial commit so\n // they do not fight boot with the stale first-render place.\n const shellSyncStartedRef = useRef(false);\n const urlSyncStartedRef = useRef(false);\n\n /**\n * A URL carries only the id, so ask the server whether it names a mutable\n * draft or an agent chat; guessing \"mutable\" opens an agent session as a\n * blank draft. `requestedSessionRef` drops replies a later place superseded.\n */\n const openSession = useCallback(\n (sessionId: string) => {\n requestedSessionRef.current = sessionId;\n if (server == null) {\n shell.openHistorySession({ sessionId });\n return;\n }\n void server\n .getSession({ sessionId })\n .then(session => {\n if (requestedSessionRef.current !== sessionId) return;\n shell.openHistorySession({\n sessionId,\n isMutable: session.isMutable,\n isCreateAgent: sessionIsCreateAgent(session),\n ...(session.agentName != null ? { agentName: session.agentName } : {}),\n });\n })\n .catch(() => {\n if (requestedSessionRef.current !== sessionId) return;\n // Unreachable session: bind by id alone rather than stranding the shell.\n shell.openHistorySession({ sessionId });\n });\n },\n [server, shell],\n );\n\n const applyPlace = useCallback(\n (target: RoutePlace) => {\n switch (target.type) {\n case 'settings':\n shell.setSettingsOpen(true);\n return;\n case 'library':\n shell.setLibraryOpen(true);\n return;\n case 'sessionsBrowser':\n shell.setSessionsOpen(true);\n return;\n case 'libraryAgent':\n shell.openLibraryAgent(target.agentId);\n return;\n case 'schedules':\n shell.setSchedulesOpen(true);\n return;\n case 'session':\n shell.setLibraryOpen(false);\n if (shell.pendingSessionId === target.sessionId || activeRemoteId === target.sessionId) return;\n openSession(target.sessionId);\n return;\n case 'agent':\n shell.setLibraryOpen(false);\n shell.selectLibraryAgent({ isMutable: false, agentName: target.agentName });\n return;\n case 'root':\n shell.setSettingsOpen(false);\n shell.setLibraryOpen(false);\n shell.setSchedulesOpen(false);\n switch (shell.agentConfigMode) {\n case 'AgentLibrary':\n shell.openLibraryHome();\n return;\n case 'AgentComposer':\n case 'AgentLibraryWithComposer':\n shell.openDraft();\n return;\n case 'SingleAgent':\n shell.clearChat();\n return;\n }\n }\n },\n [shell, activeRemoteId, openSession],\n );\n\n // Boot: URL wins, except an explicit `initialSettingsOpen` overlay. Boot is the\n // sole authority for the first commit and sets the final URL + `prevPlaceRef`.\n useEffect(() => {\n if (bootedRef.current) return;\n bootedRef.current = true;\n\n const urlPlace = matchLocation({\n pathname: location.pathname,\n search: location.search,\n routes: effectiveRoutes,\n }) ?? { type: 'root' };\n const settingsOnBoot = settingsChromeEnabled && (initialSettingsOpen || urlPlace.type === 'settings');\n\n if (urlPlace.type === 'settings') {\n if (settingsChromeEnabled) shell.setSettingsOpen(true);\n } else if (urlPlace.type === 'library') {\n shell.setLibraryOpen(true);\n } else if (urlPlace.type === 'sessionsBrowser') {\n shell.setSessionsOpen(true);\n } else if (urlPlace.type === 'libraryAgent') {\n shell.openLibraryAgent(urlPlace.agentId);\n } else if (urlPlace.type === 'schedules') {\n shell.setSchedulesOpen(true);\n } else {\n const chatPlace = deriveChatPlace(snapshot);\n if (!placesEqual(chatPlace, urlPlace)) applyPlace(urlPlace);\n if (settingsOnBoot) shell.setSettingsOpen(true);\n }\n\n const desiredPlace: RoutePlace = settingsOnBoot ? { type: 'settings' } : urlPlace;\n const desiredPath = buildPath(desiredPlace, effectiveRoutes);\n const desiredSearch = sanitizeSearchForPlace(desiredPlace, location.search);\n prevPlaceRef.current = desiredPlace;\n if (desiredPath != null && (desiredPath !== location.pathname || desiredSearch !== location.search)) {\n selfNavPathRef.current = desiredPath !== location.pathname ? desiredPath : null;\n navigate({ pathname: desiredPath, search: desiredSearch, hash: location.hash }, { replace: true });\n }\n // Boot runs once; snapshot is read imperatively here.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Shell -> URL: mirror the derived place. Skip the first commit (boot owns it).\n useEffect(() => {\n if (!bootedRef.current) return;\n if (!shellSyncStartedRef.current) {\n shellSyncStartedRef.current = true;\n return;\n }\n const target = buildPath(place, effectiveRoutes);\n if (target == null) return; // place has no configured URL (e.g. settings disabled)\n const basename = effectiveRoutes.basename.endsWith('/')\n ? effectiveRoutes.basename.slice(0, -1)\n : effectiveRoutes.basename;\n const browserPathname = `${basename}${location.pathname}` || '/';\n const latestSearch = window.location.pathname === browserPathname ? window.location.search : location.search;\n const targetSearch = sanitizeSearchForPlace(place, latestSearch);\n\n const prev = prevPlaceRef.current;\n prevPlaceRef.current = place;\n\n if (target === location.pathname && targetSearch === location.search) return;\n\n // Replace when a fresh chat just acquired its session id (same place, new id).\n const replace =\n place.type === 'session' && shell.pendingSessionId == null && prev != null && prev.type !== 'session';\n\n selfNavPathRef.current = target !== location.pathname ? target : null;\n // Query keys owned by other shell places are removed; host keys and hash survive.\n navigate({ pathname: target, search: targetSearch, hash: location.hash }, { replace });\n // location.pathname intentionally excluded: only react to shell-derived place changes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [placeKey, settingsChromeEnabled]);\n\n // URL -> shell: apply on genuine location changes (Back/Forward, manual edits).\n useEffect(() => {\n if (!bootedRef.current) return;\n if (!urlSyncStartedRef.current) {\n urlSyncStartedRef.current = true;\n return;\n }\n if (selfNavPathRef.current === location.pathname) {\n selfNavPathRef.current = null;\n return;\n }\n const urlPlace = matchLocation({\n pathname: location.pathname,\n search: location.search,\n routes: effectiveRoutes,\n });\n if (urlPlace == null) {\n // Unknown path: normalize to root.\n const rootPath = effectiveRoutes.root;\n const rootSearch = sanitizeSearchForPlace({ type: 'root' }, location.search);\n selfNavPathRef.current = rootPath;\n navigate({ pathname: rootPath, search: rootSearch, hash: location.hash }, { replace: true });\n applyPlace({ type: 'root' });\n return;\n }\n if (urlPlace.type !== 'settings' && shell.settingsOpen) {\n // Leaving settings via Back to a chat place.\n shell.setSettingsOpen(false);\n }\n if (urlPlace.type !== 'library' && urlPlace.type !== 'libraryAgent' && shell.libraryOpen) {\n shell.setLibraryOpen(false);\n }\n if (urlPlace.type !== 'sessionsBrowser' && shell.sessionsOpen) {\n shell.setSessionsOpen(false);\n }\n if (urlPlace.type !== 'schedules' && shell.schedulesOpen) {\n // Leaving schedules via Back to a chat place.\n shell.setSchedulesOpen(false);\n }\n applyPlace(urlPlace);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [location.pathname]);\n\n return null;\n}\n","import type { RoutePlace, ShellSnapshot } from './types.js';\n\n/**\n * The chat place implied by shell state, ignoring settings, schedules,\n * sessions-browser, and library overlays.\n * SingleAgent canonicalizes to `root` so `/` and `/agents/{name}` alias.\n */\nexport function deriveChatPlace(snapshot: ShellSnapshot): RoutePlace {\n // The live thread wins: reusing a mutable shell switches thread without a\n // remount, leaving `pendingSessionId` on the session the shell last opened.\n // It only leads while a requested session has not reported its thread yet.\n const sessionId = snapshot.activeRemoteId ?? snapshot.pendingSessionId;\n if (sessionId != null) return { type: 'session', sessionId };\n\n const { mode, agentConfigMode } = snapshot;\n if (mode.status === 'active' && !mode.isMutable && agentConfigMode !== 'SingleAgent') {\n const agentName = mode.agentName ?? mode.agentId;\n if (agentName != null) return { type: 'agent', agentName };\n }\n return { type: 'root' };\n}\n\n/** Full place with overlay priority: settings, schedules, sessions, library, chat. */\nexport function derivePlace(snapshot: ShellSnapshot): RoutePlace {\n if (snapshot.settingsOpen) return { type: 'settings' };\n if (snapshot.schedulesOpen) return { type: 'schedules' };\n if (snapshot.sessionsOpen) return { type: 'sessionsBrowser' };\n if (snapshot.libraryOpen && snapshot.libraryAgentId != null) {\n return { type: 'libraryAgent', agentId: snapshot.libraryAgentId };\n }\n if (snapshot.libraryOpen) return { type: 'library' };\n return deriveChatPlace(snapshot);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAEA,SAAS,aAAa,WAAW,SAAS,cAAc;AACxD,SAAS,aAAa,mBAAmB;;;ACIlC,SAAS,gBAAgB,UAAqC;AAInE,QAAM,YAAY,SAAS,kBAAkB,SAAS;AACtD,MAAI,aAAa,KAAM,QAAO,EAAE,MAAM,WAAW,UAAU;AAE3D,QAAM,EAAE,MAAM,gBAAgB,IAAI;AAClC,MAAI,KAAK,WAAW,YAAY,CAAC,KAAK,aAAa,oBAAoB,eAAe;AACpF,UAAM,YAAY,KAAK,aAAa,KAAK;AACzC,QAAI,aAAa,KAAM,QAAO,EAAE,MAAM,SAAS,UAAU;AAAA,EAC3D;AACA,SAAO,EAAE,MAAM,OAAO;AACxB;AAGO,SAAS,YAAY,UAAqC;AAC/D,MAAI,SAAS,aAAc,QAAO,EAAE,MAAM,WAAW;AACrD,MAAI,SAAS,cAAe,QAAO,EAAE,MAAM,YAAY;AACvD,MAAI,SAAS,aAAc,QAAO,EAAE,MAAM,kBAAkB;AAC5D,MAAI,SAAS,eAAe,SAAS,kBAAkB,MAAM;AAC3D,WAAO,EAAE,MAAM,gBAAgB,SAAS,SAAS,eAAe;AAAA,EAClE;AACA,MAAI,SAAS,YAAa,QAAO,EAAE,MAAM,UAAU;AACnD,SAAO,gBAAgB,QAAQ;AACjC;;;ADfO,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,QAAM,QAAQ,aAAa;AAC3B,QAAM,SAAS,kBAAkB;AACjC,QAAM,UAAU,yBAAyB;AACzC,QAAM,eAAe,sBAAsB;AAC3C,QAAM,WAAW,YAAY;AAC7B,QAAM,WAAW,YAAY;AAG7B,QAAM,wBAAwB,wBAAwB,EAAE,SAAS,aAAa,CAAC;AAC/E,QAAM,kBAAkB;AAAA,IACtB,MAAO,wBAAwB,SAAS,EAAE,GAAG,QAAQ,UAAU,KAAK;AAAA,IACpE,CAAC,QAAQ,qBAAqB;AAAA,EAChC;AAEA,QAAM,WAA0B;AAAA,IAC9B,cAAc,MAAM;AAAA,IACpB,aAAa,MAAM;AAAA,IACnB,cAAc,MAAM;AAAA,IACpB,gBAAgB,MAAM;AAAA,IACtB,eAAe,MAAM;AAAA,IACrB,kBAAkB,MAAM;AAAA,IACxB;AAAA,IACA,MAAM,MAAM;AAAA,IACZ,iBAAiB,MAAM;AAAA,EACzB;AAEA,QAAM,QAAQ,YAAY,QAAQ;AAClC,QAAM,WAAW,KAAK,UAAU,KAAK;AAIrC,QAAM,iBAAiB,OAAsB,IAAI;AACjD,QAAM,eAAe,OAA0B,IAAI;AACnD,QAAM,YAAY,OAAO,KAAK;AAE9B,QAAM,sBAAsB,OAAsB,IAAI;AAGtD,QAAM,sBAAsB,OAAO,KAAK;AACxC,QAAM,oBAAoB,OAAO,KAAK;AAOtC,QAAM,cAAc;AAAA,IAClB,CAAC,cAAsB;AACrB,0BAAoB,UAAU;AAC9B,UAAI,UAAU,MAAM;AAClB,cAAM,mBAAmB,EAAE,UAAU,CAAC;AACtC;AAAA,MACF;AACA,WAAK,OACF,WAAW,EAAE,UAAU,CAAC,EACxB,KAAK,aAAW;AACf,YAAI,oBAAoB,YAAY,UAAW;AAC/C,cAAM,mBAAmB;AAAA,UACvB;AAAA,UACA,WAAW,QAAQ;AAAA,UACnB,eAAe,qBAAqB,OAAO;AAAA,UAC3C,GAAI,QAAQ,aAAa,OAAO,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,QACtE,CAAC;AAAA,MACH,CAAC,EACA,MAAM,MAAM;AACX,YAAI,oBAAoB,YAAY,UAAW;AAE/C,cAAM,mBAAmB,EAAE,UAAU,CAAC;AAAA,MACxC,CAAC;AAAA,IACL;AAAA,IACA,CAAC,QAAQ,KAAK;AAAA,EAChB;AAEA,QAAM,aAAa;AAAA,IACjB,CAAC,WAAuB;AACtB,cAAQ,OAAO,MAAM;AAAA,QACnB,KAAK;AACH,gBAAM,gBAAgB,IAAI;AAC1B;AAAA,QACF,KAAK;AACH,gBAAM,eAAe,IAAI;AACzB;AAAA,QACF,KAAK;AACH,gBAAM,gBAAgB,IAAI;AAC1B;AAAA,QACF,KAAK;AACH,gBAAM,iBAAiB,OAAO,OAAO;AACrC;AAAA,QACF,KAAK;AACH,gBAAM,iBAAiB,IAAI;AAC3B;AAAA,QACF,KAAK;AACH,gBAAM,eAAe,KAAK;AAC1B,cAAI,MAAM,qBAAqB,OAAO,aAAa,mBAAmB,OAAO,UAAW;AACxF,sBAAY,OAAO,SAAS;AAC5B;AAAA,QACF,KAAK;AACH,gBAAM,eAAe,KAAK;AAC1B,gBAAM,mBAAmB,EAAE,WAAW,OAAO,WAAW,OAAO,UAAU,CAAC;AAC1E;AAAA,QACF,KAAK;AACH,gBAAM,gBAAgB,KAAK;AAC3B,gBAAM,eAAe,KAAK;AAC1B,gBAAM,iBAAiB,KAAK;AAC5B,kBAAQ,MAAM,iBAAiB;AAAA,YAC7B,KAAK;AACH,oBAAM,gBAAgB;AACtB;AAAA,YACF,KAAK;AAAA,YACL,KAAK;AACH,oBAAM,UAAU;AAChB;AAAA,YACF,KAAK;AACH,oBAAM,UAAU;AAChB;AAAA,UACJ;AAAA,MACJ;AAAA,IACF;AAAA,IACA,CAAC,OAAO,gBAAgB,WAAW;AAAA,EACrC;AAIA,YAAU,MAAM;AACd,QAAI,UAAU,QAAS;AACvB,cAAU,UAAU;AAEpB,UAAM,WAAW,cAAc;AAAA,MAC7B,UAAU,SAAS;AAAA,MACnB,QAAQ,SAAS;AAAA,MACjB,QAAQ;AAAA,IACV,CAAC,KAAK,EAAE,MAAM,OAAO;AACrB,UAAM,iBAAiB,0BAA0B,uBAAuB,SAAS,SAAS;AAE1F,QAAI,SAAS,SAAS,YAAY;AAChC,UAAI,sBAAuB,OAAM,gBAAgB,IAAI;AAAA,IACvD,WAAW,SAAS,SAAS,WAAW;AACtC,YAAM,eAAe,IAAI;AAAA,IAC3B,WAAW,SAAS,SAAS,mBAAmB;AAC9C,YAAM,gBAAgB,IAAI;AAAA,IAC5B,WAAW,SAAS,SAAS,gBAAgB;AAC3C,YAAM,iBAAiB,SAAS,OAAO;AAAA,IACzC,WAAW,SAAS,SAAS,aAAa;AACxC,YAAM,iBAAiB,IAAI;AAAA,IAC7B,OAAO;AACL,YAAM,YAAY,gBAAgB,QAAQ;AAC1C,UAAI,CAAC,YAAY,WAAW,QAAQ,EAAG,YAAW,QAAQ;AAC1D,UAAI,eAAgB,OAAM,gBAAgB,IAAI;AAAA,IAChD;AAEA,UAAM,eAA2B,iBAAiB,EAAE,MAAM,WAAW,IAAI;AACzE,UAAM,cAAc,UAAU,cAAc,eAAe;AAC3D,UAAM,gBAAgB,uBAAuB,cAAc,SAAS,MAAM;AAC1E,iBAAa,UAAU;AACvB,QAAI,eAAe,SAAS,gBAAgB,SAAS,YAAY,kBAAkB,SAAS,SAAS;AACnG,qBAAe,UAAU,gBAAgB,SAAS,WAAW,cAAc;AAC3E,eAAS,EAAE,UAAU,aAAa,QAAQ,eAAe,MAAM,SAAS,KAAK,GAAG,EAAE,SAAS,KAAK,CAAC;AAAA,IACnG;AAAA,EAGF,GAAG,CAAC,CAAC;AAGL,YAAU,MAAM;AACd,QAAI,CAAC,UAAU,QAAS;AACxB,QAAI,CAAC,oBAAoB,SAAS;AAChC,0BAAoB,UAAU;AAC9B;AAAA,IACF;AACA,UAAM,SAAS,UAAU,OAAO,eAAe;AAC/C,QAAI,UAAU,KAAM;AACpB,UAAM,WAAW,gBAAgB,SAAS,SAAS,GAAG,IAClD,gBAAgB,SAAS,MAAM,GAAG,EAAE,IACpC,gBAAgB;AACpB,UAAM,kBAAkB,GAAG,QAAQ,GAAG,SAAS,QAAQ,MAAM;AAC7D,UAAM,eAAe,OAAO,SAAS,aAAa,kBAAkB,OAAO,SAAS,SAAS,SAAS;AACtG,UAAM,eAAe,uBAAuB,OAAO,YAAY;AAE/D,UAAM,OAAO,aAAa;AAC1B,iBAAa,UAAU;AAEvB,QAAI,WAAW,SAAS,YAAY,iBAAiB,SAAS,OAAQ;AAGtE,UAAM,UACJ,MAAM,SAAS,aAAa,MAAM,oBAAoB,QAAQ,QAAQ,QAAQ,KAAK,SAAS;AAE9F,mBAAe,UAAU,WAAW,SAAS,WAAW,SAAS;AAEjE,aAAS,EAAE,UAAU,QAAQ,QAAQ,cAAc,MAAM,SAAS,KAAK,GAAG,EAAE,QAAQ,CAAC;AAAA,EAGvF,GAAG,CAAC,UAAU,qBAAqB,CAAC;AAGpC,YAAU,MAAM;AACd,QAAI,CAAC,UAAU,QAAS;AACxB,QAAI,CAAC,kBAAkB,SAAS;AAC9B,wBAAkB,UAAU;AAC5B;AAAA,IACF;AACA,QAAI,eAAe,YAAY,SAAS,UAAU;AAChD,qBAAe,UAAU;AACzB;AAAA,IACF;AACA,UAAM,WAAW,cAAc;AAAA,MAC7B,UAAU,SAAS;AAAA,MACnB,QAAQ,SAAS;AAAA,MACjB,QAAQ;AAAA,IACV,CAAC;AACD,QAAI,YAAY,MAAM;AAEpB,YAAM,WAAW,gBAAgB;AACjC,YAAM,aAAa,uBAAuB,EAAE,MAAM,OAAO,GAAG,SAAS,MAAM;AAC3E,qBAAe,UAAU;AACzB,eAAS,EAAE,UAAU,UAAU,QAAQ,YAAY,MAAM,SAAS,KAAK,GAAG,EAAE,SAAS,KAAK,CAAC;AAC3F,iBAAW,EAAE,MAAM,OAAO,CAAC;AAC3B;AAAA,IACF;AACA,QAAI,SAAS,SAAS,cAAc,MAAM,cAAc;AAEtD,YAAM,gBAAgB,KAAK;AAAA,IAC7B;AACA,QAAI,SAAS,SAAS,aAAa,SAAS,SAAS,kBAAkB,MAAM,aAAa;AACxF,YAAM,eAAe,KAAK;AAAA,IAC5B;AACA,QAAI,SAAS,SAAS,qBAAqB,MAAM,cAAc;AAC7D,YAAM,gBAAgB,KAAK;AAAA,IAC7B;AACA,QAAI,SAAS,SAAS,eAAe,MAAM,eAAe;AAExD,YAAM,iBAAiB,KAAK;AAAA,IAC9B;AACA,eAAW,QAAQ;AAAA,EAErB,GAAG,CAAC,SAAS,QAAQ,CAAC;AAEtB,SAAO;AACT;","names":[]}
|