@burdenoff/microfe-botmanor 2026.813.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/README.md +12 -0
- package/dist/botlit/BotlitRoutes.js +357 -0
- package/dist/botlit/BotlitRoutes.js.map +1 -0
- package/dist/botlit/BotmanorRoot.js +12 -0
- package/dist/botlit/BotmanorRoot.js.map +1 -0
- package/dist/botlit/BotmanorRootInner.js +101 -0
- package/dist/botlit/BotmanorRootInner.js.map +1 -0
- package/dist/botlit/components/Badge.js +39 -0
- package/dist/botlit/components/Badge.js.map +1 -0
- package/dist/botlit/components/Button.js +46 -0
- package/dist/botlit/components/Button.js.map +1 -0
- package/dist/botlit/components/Card.js +31 -0
- package/dist/botlit/components/Card.js.map +1 -0
- package/dist/botlit/components/ChannelSelectorSheet.js +130 -0
- package/dist/botlit/components/ChannelSelectorSheet.js.map +1 -0
- package/dist/botlit/components/EmptyState.js +35 -0
- package/dist/botlit/components/EmptyState.js.map +1 -0
- package/dist/botlit/components/ErrorState.js +33 -0
- package/dist/botlit/components/ErrorState.js.map +1 -0
- package/dist/botlit/components/Input.js +59 -0
- package/dist/botlit/components/Input.js.map +1 -0
- package/dist/botlit/components/PageLayout.js +34 -0
- package/dist/botlit/components/PageLayout.js.map +1 -0
- package/dist/botlit/components/QuotaExhaustedDialog.js +20 -0
- package/dist/botlit/components/QuotaExhaustedDialog.js.map +1 -0
- package/dist/botlit/components/RouteLoader.js +52 -0
- package/dist/botlit/components/RouteLoader.js.map +1 -0
- package/dist/botlit/components/VersionHistoryPanel.js +144 -0
- package/dist/botlit/components/VersionHistoryPanel.js.map +1 -0
- package/dist/botlit/components/WorkspaceRequired.js +70 -0
- package/dist/botlit/components/WorkspaceRequired.js.map +1 -0
- package/dist/botlit/hooks/index.js +16 -0
- package/dist/botlit/hooks/internal/mutationInput.js +16 -0
- package/dist/botlit/hooks/internal/mutationInput.js.map +1 -0
- package/dist/botlit/hooks/internal/workspaceGateway.js +99 -0
- package/dist/botlit/hooks/internal/workspaceGateway.js.map +1 -0
- package/dist/botlit/hooks/useA2A.js +184 -0
- package/dist/botlit/hooks/useA2A.js.map +1 -0
- package/dist/botlit/hooks/useAgents.js +469 -0
- package/dist/botlit/hooks/useAgents.js.map +1 -0
- package/dist/botlit/hooks/useBots.js +306 -0
- package/dist/botlit/hooks/useBots.js.map +1 -0
- package/dist/botlit/hooks/useBuckets.js +119 -0
- package/dist/botlit/hooks/useBuckets.js.map +1 -0
- package/dist/botlit/hooks/useChannels.js +213 -0
- package/dist/botlit/hooks/useChannels.js.map +1 -0
- package/dist/botlit/hooks/useConversations.js +64 -0
- package/dist/botlit/hooks/useConversations.js.map +1 -0
- package/dist/botlit/hooks/useExecutions.js +86 -0
- package/dist/botlit/hooks/useExecutions.js.map +1 -0
- package/dist/botlit/hooks/useKnowledgeBases.js +113 -0
- package/dist/botlit/hooks/useKnowledgeBases.js.map +1 -0
- package/dist/botlit/hooks/useLLMProviders.js +83 -0
- package/dist/botlit/hooks/useLLMProviders.js.map +1 -0
- package/dist/botlit/hooks/useMCPServers.js +134 -0
- package/dist/botlit/hooks/useMCPServers.js.map +1 -0
- package/dist/botlit/hooks/usePolicies.js +120 -0
- package/dist/botlit/hooks/usePolicies.js.map +1 -0
- package/dist/botlit/hooks/usePromptTemplates.js +161 -0
- package/dist/botlit/hooks/usePromptTemplates.js.map +1 -0
- package/dist/botlit/hooks/useRetrievalProfiles.js +146 -0
- package/dist/botlit/hooks/useRetrievalProfiles.js.map +1 -0
- package/dist/botlit/hooks/useSkills.js +132 -0
- package/dist/botlit/hooks/useSkills.js.map +1 -0
- package/dist/botlit/hooks/useStore.js +167 -0
- package/dist/botlit/hooks/useStore.js.map +1 -0
- package/dist/botlit/hooks/useUserPlatformConnection.js +172 -0
- package/dist/botlit/hooks/useUserPlatformConnection.js.map +1 -0
- package/dist/botlit/index.js +11 -0
- package/dist/botlit/pages/A2AAgentDetailPage.js +589 -0
- package/dist/botlit/pages/A2AAgentDetailPage.js.map +1 -0
- package/dist/botlit/pages/A2ARegistryPage.js +240 -0
- package/dist/botlit/pages/A2ARegistryPage.js.map +1 -0
- package/dist/botlit/pages/AgentBuilderPage.js +486 -0
- package/dist/botlit/pages/AgentBuilderPage.js.map +1 -0
- package/dist/botlit/pages/AgentDetailPage.js +517 -0
- package/dist/botlit/pages/AgentDetailPage.js.map +1 -0
- package/dist/botlit/pages/AgentsListPage.js +294 -0
- package/dist/botlit/pages/AgentsListPage.js.map +1 -0
- package/dist/botlit/pages/BotChannelsTab.js +530 -0
- package/dist/botlit/pages/BotChannelsTab.js.map +1 -0
- package/dist/botlit/pages/BotDetailPage.js +872 -0
- package/dist/botlit/pages/BotDetailPage.js.map +1 -0
- package/dist/botlit/pages/BotWorkflowTab.js +229 -0
- package/dist/botlit/pages/BotWorkflowTab.js.map +1 -0
- package/dist/botlit/pages/BotsListPage.js +313 -0
- package/dist/botlit/pages/BotsListPage.js.map +1 -0
- package/dist/botlit/pages/BucketsPage.js +604 -0
- package/dist/botlit/pages/BucketsPage.js.map +1 -0
- package/dist/botlit/pages/ChannelDetailPage.js +955 -0
- package/dist/botlit/pages/ChannelDetailPage.js.map +1 -0
- package/dist/botlit/pages/ChannelRoutesPage.js +218 -0
- package/dist/botlit/pages/ChannelRoutesPage.js.map +1 -0
- package/dist/botlit/pages/ChannelsPage.js +304 -0
- package/dist/botlit/pages/ChannelsPage.js.map +1 -0
- package/dist/botlit/pages/ChatPage.js +247 -0
- package/dist/botlit/pages/ChatPage.js.map +1 -0
- package/dist/botlit/pages/DashboardPage.js +318 -0
- package/dist/botlit/pages/DashboardPage.js.map +1 -0
- package/dist/botlit/pages/ExecutionDetailPage.js +313 -0
- package/dist/botlit/pages/ExecutionDetailPage.js.map +1 -0
- package/dist/botlit/pages/ExecutionsPage.js +294 -0
- package/dist/botlit/pages/ExecutionsPage.js.map +1 -0
- package/dist/botlit/pages/InstalledPackagesPage.js +619 -0
- package/dist/botlit/pages/InstalledPackagesPage.js.map +1 -0
- package/dist/botlit/pages/KnowledgeBaseDetailPage.js +473 -0
- package/dist/botlit/pages/KnowledgeBaseDetailPage.js.map +1 -0
- package/dist/botlit/pages/KnowledgeBasesPage.js +150 -0
- package/dist/botlit/pages/KnowledgeBasesPage.js.map +1 -0
- package/dist/botlit/pages/MCPServerDetailPage.js +550 -0
- package/dist/botlit/pages/MCPServerDetailPage.js.map +1 -0
- package/dist/botlit/pages/MCPServersPage.js +321 -0
- package/dist/botlit/pages/MCPServersPage.js.map +1 -0
- package/dist/botlit/pages/PoliciesPage.js +224 -0
- package/dist/botlit/pages/PoliciesPage.js.map +1 -0
- package/dist/botlit/pages/PolicyDetailPage.js +861 -0
- package/dist/botlit/pages/PolicyDetailPage.js.map +1 -0
- package/dist/botlit/pages/PromptStudioPage.js +236 -0
- package/dist/botlit/pages/PromptStudioPage.js.map +1 -0
- package/dist/botlit/pages/PromptTemplateDetailPage.js +812 -0
- package/dist/botlit/pages/PromptTemplateDetailPage.js.map +1 -0
- package/dist/botlit/pages/PromptTemplatesPage.js +269 -0
- package/dist/botlit/pages/PromptTemplatesPage.js.map +1 -0
- package/dist/botlit/pages/RetrievalProfileDetailPage.js +559 -0
- package/dist/botlit/pages/RetrievalProfileDetailPage.js.map +1 -0
- package/dist/botlit/pages/RetrievalProfilesPage.js +219 -0
- package/dist/botlit/pages/RetrievalProfilesPage.js.map +1 -0
- package/dist/botlit/pages/SettingsPage.js +655 -0
- package/dist/botlit/pages/SettingsPage.js.map +1 -0
- package/dist/botlit/pages/SkillDetailPage.js +778 -0
- package/dist/botlit/pages/SkillDetailPage.js.map +1 -0
- package/dist/botlit/pages/SkillsPage.js +206 -0
- package/dist/botlit/pages/SkillsPage.js.map +1 -0
- package/dist/botlit/pages/StorePackageDetailPage.js +462 -0
- package/dist/botlit/pages/StorePackageDetailPage.js.map +1 -0
- package/dist/botlit/pages/StorePage.js +315 -0
- package/dist/botlit/pages/StorePage.js.map +1 -0
- package/dist/botlit/providers/BotlitProvider.js +93 -0
- package/dist/botlit/providers/BotlitProvider.js.map +1 -0
- package/dist/botlit/store/botlitStore.js +101 -0
- package/dist/botlit/store/botlitStore.js.map +1 -0
- package/dist/botlit/utils/cn.js +10 -0
- package/dist/botlit/utils/cn.js.map +1 -0
- package/dist/botlit/utils/formatters.js +37 -0
- package/dist/botlit/utils/formatters.js.map +1 -0
- package/dist/botlit/utils/navigation.js +9 -0
- package/dist/botlit/utils/navigation.js.map +1 -0
- package/dist/botlit/utils/unions.js +14 -0
- package/dist/botlit/utils/unions.js.map +1 -0
- package/dist/generated/global-operations.js +66 -0
- package/dist/generated/global-operations.js.map +1 -0
- package/dist/generated/global-types.js +8 -0
- package/dist/generated/global-types.js.map +1 -0
- package/dist/generated/wspace-operations.js +1591 -0
- package/dist/generated/wspace-operations.js.map +1 -0
- package/dist/generated/wspace-types.js +12 -0
- package/dist/generated/wspace-types.js.map +1 -0
- package/dist/index.js +13 -0
- package/dist/utils/nativeBridge.js +2 -0
- package/package.json +133 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InstalledPackagesPage.js","names":[],"sources":["../../../src/botlit/pages/InstalledPackagesPage.tsx"],"sourcesContent":["/**\n * Installed Packages Page\n * @module botlit/pages\n */\n\nimport type { FC } from 'react';\nimport { useMemo, useState } from 'react';\nimport { Link } from 'react-router';\nimport { useBotlit } from '../providers/BotlitProvider';\nimport {\n Package,\n Download,\n RefreshCw,\n Trash2,\n GitBranch,\n Link2,\n Copy,\n Settings,\n ArrowUpCircle,\n Search,\n ExternalLink,\n} from 'lucide-react';\nimport { toast } from 'sonner';\nimport { PagePurpose, GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { Card, CardHeader, CardTitle, CardContent, CardDescription } from '../components/Card';\nimport { Button } from '../components/Button';\nimport { Input } from '../components/Input';\nimport { Badge } from '../components/Badge';\nimport { EmptyState } from '../components/EmptyState';\nimport { ErrorState } from '../components/ErrorState';\nimport {\n useInstalledPackages,\n useUninstallPackage,\n useUpdateInstalledPackage,\n usePinPackageVersion,\n storePackageName,\n type InstalledPackageRecord,\n} from '../hooks';\nimport { formatRelativeTime } from '../utils/formatters';\nimport { PackageKind as GqlPackageKind } from '../../generated/wspace-types';\nimport type { Environment, PackageKind } from '../types';\n\nconst ENVIRONMENTS: Environment[] = ['DEV', 'STAGING', 'PROD'];\n\nconst PACKAGE_KIND_CONFIG: Record<string, { label: string; icon: string }> = {\n APP: { label: 'Bot', icon: '📱' },\n AGENT: { label: 'Agent', icon: '🤖' },\n SKILL: { label: 'Skill', icon: '⚡' },\n MCP_PACK: { label: 'MCP Pack', icon: '🔧' },\n PROMPT_TEMPLATE: { label: 'Template', icon: '📝' },\n PROMPT: { label: 'Prompt', icon: '📝' },\n RETRIEVAL_PROFILE: { label: 'Retrieval', icon: '🔍' },\n POLICY_BUNDLE: { label: 'Policy', icon: '🛡️' },\n CHANNEL_TEMPLATE: { label: 'Channel', icon: '📡' },\n A2A_AGENT: { label: 'A2A Agent', icon: '🔗' },\n KB_TEMPLATE: { label: 'KB Template', icon: '📚' },\n KNOWLEDGE_BASE: { label: 'Knowledge Base', icon: '📚' },\n INTEGRATION: { label: 'Integration', icon: '🔌' },\n TEMPLATE: { label: 'Template', icon: '📝' },\n TOOL: { label: 'Tool', icon: '🔧' },\n WORKFLOW: { label: 'Workflow', icon: '🔀' },\n};\n\nconst UNKNOWN_KIND = { label: 'Package', icon: '📦' };\n\n/** Store slug for an install, falling back to the package id when unresolved. */\nfunction installSlug(pkg: InstalledPackageRecord): string {\n return pkg.package?.slug ?? pkg.packageId;\n}\n\nfunction installKind(pkg: InstalledPackageRecord): string | undefined {\n return pkg.package?.kind;\n}\n\nfunction kindConfigFor(pkg: InstalledPackageRecord) {\n const kind = installKind(pkg);\n return (kind && PACKAGE_KIND_CONFIG[kind]) || UNKNOWN_KIND;\n}\n\nfunction hasUpdate(pkg: InstalledPackageRecord): boolean {\n const latest = pkg.package?.version;\n return Boolean(latest && latest !== pkg.installedVersion);\n}\n\n/** `pinnedByEnv` is free-form JSON — read one environment's pin defensively. */\nfunction pinnedVersionFor(pkg: InstalledPackageRecord, env: Environment): string | undefined {\n const value = pkg.pinnedByEnv?.[env];\n return typeof value === 'string' && value !== '' ? value : undefined;\n}\n\nexport const InstalledPackagesPage: FC = () => {\n const { navigateTo, getPath } = useBotlit();\n const { data: packages, loading, error, refetch } = useInstalledPackages();\n const { uninstall, loading: uninstallLoading } = useUninstallPackage();\n const { update, loading: updateLoading } = useUpdateInstalledPackage();\n const { pin } = usePinPackageVersion();\n\n const [search, setSearch] = useState('');\n const [refreshing, setRefreshing] = useState(false);\n const [kindFilter, setKindFilter] = useState<PackageKind | 'all'>('all');\n const [selectedPackageId, setSelectedPackageId] = useState<string | null>(null);\n\n const filteredPackages = useMemo(() => {\n const needle = search.trim().toLowerCase();\n return packages.filter((pkg) => {\n const matchesSearch =\n !needle ||\n installSlug(pkg).toLowerCase().includes(needle) ||\n storePackageName(pkg.package).toLowerCase().includes(needle);\n const matchesKind = kindFilter === 'all' || installKind(pkg) === kindFilter;\n return matchesSearch && matchesKind;\n });\n }, [packages, search, kindFilter]);\n\n const selectedPackage = useMemo(\n () => packages.find((pkg) => pkg.id === selectedPackageId) ?? null,\n [packages, selectedPackageId]\n );\n\n const updatesAvailable = useMemo(() => packages.filter(hasUpdate).length, [packages]);\n\n const handleCheckUpdates = async () => {\n setRefreshing(true);\n try {\n await refetch();\n } finally {\n setRefreshing(false);\n }\n };\n\n const handleUninstall = async (pkg: InstalledPackageRecord) => {\n if (!confirm('Are you sure you want to uninstall this package?')) return;\n try {\n await uninstall(pkg.id);\n toast.success(`Uninstalled \"${storePackageName(pkg.package) || installSlug(pkg)}\"`);\n setSelectedPackageId(null);\n void refetch();\n } catch (err) {\n toast.error(err instanceof Error ? err.message : 'Failed to uninstall package');\n }\n };\n\n const handleUpdate = async (pkg: InstalledPackageRecord) => {\n const latest = pkg.package?.version;\n if (!latest) return;\n try {\n await update({ installedId: pkg.id, input: { installedVersion: latest } });\n toast.success(`Updated to v${latest}`);\n void refetch();\n } catch (err) {\n toast.error(err instanceof Error ? err.message : 'Failed to update package');\n }\n };\n\n const handlePinVersion = async (\n pkg: InstalledPackageRecord,\n env: Environment,\n version: string\n ) => {\n try {\n await pin({\n installedId: pkg.id,\n environment: env,\n version,\n pinnedByEnv: pkg.pinnedByEnv,\n });\n toast.success(version ? `Pinned ${env} to v${version}` : `Unpinned ${env}`);\n void refetch();\n } catch (err) {\n toast.error(err instanceof Error ? err.message : 'Failed to change version pin');\n }\n };\n\n const getModeIcon = (mode: string) =>\n mode === 'LINKED' ? (\n <Link2 className=\"h-4 w-4 text-status-info-text\" />\n ) : (\n <Copy className=\"h-4 w-4 text-status-warning-text\" />\n );\n\n const PackageCard: FC<{ pkg: InstalledPackageRecord }> = ({ pkg }) => {\n const kindConfig = kindConfigFor(pkg);\n const slug = installSlug(pkg);\n const updateAvailable = hasUpdate(pkg);\n const pinnedEnvironments = ENVIRONMENTS.filter((env) => pinnedVersionFor(pkg, env));\n\n return (\n <Card\n className=\"hover:shadow-md transition-shadow cursor-pointer\"\n onClick={() => setSelectedPackageId(pkg.id)}\n >\n <CardContent className=\"pt-6\">\n <div className=\"flex items-start justify-between\">\n <div className=\"flex items-start gap-3\">\n <div className=\"p-2 rounded-lg bg-bg-sunken text-2xl\">{kindConfig.icon}</div>\n <div>\n <div className=\"flex items-center gap-2\">\n <h3 className=\"font-semibold\">{storePackageName(pkg.package) || slug}</h3>\n {updateAvailable && (\n <Badge variant=\"warning\" className=\"gap-1\">\n <ArrowUpCircle className=\"h-3 w-3\" />\n Update\n </Badge>\n )}\n </div>\n <p className=\"text-sm text-text-secondary font-mono\">{slug}</p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n {getModeIcon(pkg.mode)}\n <Badge variant=\"outline\">{kindConfig.label}</Badge>\n </div>\n </div>\n\n <div className=\"mt-4 grid grid-cols-3 gap-4 text-sm\">\n <div>\n <p className=\"text-text-secondary\">Installed Version</p>\n <p className=\"font-medium font-mono\">v{pkg.installedVersion}</p>\n </div>\n <div>\n <p className=\"text-text-secondary\">Mode</p>\n <p className=\"font-medium flex items-center gap-1\">\n {getModeIcon(pkg.mode)}\n {pkg.mode}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">Installed</p>\n <p className=\"font-medium\">{formatRelativeTime(pkg.installedAt)}</p>\n </div>\n </div>\n\n {/* Environment Pins */}\n {pinnedEnvironments.length > 0 && (\n <div className=\"mt-4 pt-4 border-t border-border-subtle\">\n <p className=\"text-xs text-text-secondary mb-2\">Pinned Versions</p>\n <div className=\"flex gap-2\">\n {ENVIRONMENTS.map((env) => {\n const pinnedVersion = pinnedVersionFor(pkg, env);\n return (\n <Badge\n key={env}\n variant={pinnedVersion ? 'default' : 'outline'}\n className=\"text-xs\"\n >\n {env}: {pinnedVersion ?? 'latest'}\n </Badge>\n );\n })}\n </div>\n </div>\n )}\n\n <div className=\"mt-4 flex justify-end gap-2\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={(e) => {\n e.stopPropagation();\n navigateTo(`store/package/${slug}`);\n }}\n >\n <ExternalLink className=\"h-4 w-4 mr-1\" />\n View in Store\n </Button>\n {updateAvailable && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={(e) => {\n e.stopPropagation();\n void handleUpdate(pkg);\n }}\n isLoading={updateLoading}\n >\n <ArrowUpCircle className=\"h-4 w-4 mr-1\" />\n Update\n </Button>\n )}\n <Button\n variant=\"ghost\"\n size=\"sm\"\n aria-label=\"Uninstall package\"\n className=\"text-status-error-text hover:text-status-error-text\"\n onClick={(e) => {\n e.stopPropagation();\n void handleUninstall(pkg);\n }}\n isLoading={uninstallLoading}\n >\n <Trash2 className=\"h-4 w-4\" />\n </Button>\n </div>\n </CardContent>\n </Card>\n );\n };\n\n const PackageDetailModal: FC<{ pkg: InstalledPackageRecord; onClose: () => void }> = ({\n pkg,\n onClose,\n }) => {\n const kindConfig = kindConfigFor(pkg);\n const slug = installSlug(pkg);\n const bindings = pkg.bindings ?? {};\n\n return (\n <div className=\"fixed inset-0 bg-black/50 flex items-center justify-center z-50\">\n <div className=\"bg-bg-surface rounded-lg shadow-xl max-w-2xl w-full mx-4 max-h-[90vh] overflow-y-auto\">\n <div className=\"p-6 border-b border-border-subtle\">\n <div className=\"flex items-start justify-between\">\n <div className=\"flex items-start gap-3\">\n <div className=\"p-3 rounded-lg bg-bg-sunken text-3xl\">{kindConfig.icon}</div>\n <div>\n <h2 className=\"text-xl font-bold\">{storePackageName(pkg.package) || slug}</h2>\n <p className=\"text-sm text-text-secondary font-mono\">{slug}</p>\n </div>\n </div>\n <Button variant=\"ghost\" size=\"sm\" aria-label=\"Close\" onClick={onClose}>\n ×\n </Button>\n </div>\n </div>\n\n <div className=\"p-6 space-y-6\">\n {/* Version Info */}\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Version Information</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <p className=\"text-sm text-text-secondary\">Installed Version</p>\n <p className=\"font-mono font-medium\">v{pkg.installedVersion}</p>\n </div>\n {pkg.package && (\n <div>\n <p className=\"text-sm text-text-secondary\">Latest Version</p>\n <p className=\"font-mono font-medium\">v{pkg.package.version}</p>\n </div>\n )}\n </div>\n\n <div>\n <p className=\"text-sm text-text-secondary mb-2\">Installation Mode</p>\n <div className=\"flex items-center gap-2\">\n {getModeIcon(pkg.mode)}\n <span className=\"font-medium\">{pkg.mode}</span>\n <span className=\"text-sm text-text-secondary\">\n {pkg.mode === 'LINKED'\n ? '- Receives updates automatically'\n : '- Independent copy, no auto-updates'}\n </span>\n </div>\n </div>\n </CardContent>\n </Card>\n\n {/* Environment Pinning */}\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base flex items-center gap-2\">\n <GitBranch className=\"h-4 w-4\" />\n Environment Version Pins\n </CardTitle>\n <CardDescription>Pin specific versions for each environment</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n {ENVIRONMENTS.map((env) => {\n const pinnedVersion = pinnedVersionFor(pkg, env);\n return (\n <div\n key={env}\n className=\"flex items-center justify-between p-3 bg-bg-sunken rounded-lg\"\n >\n <div>\n <p className=\"font-medium\">{env}</p>\n <p className=\"text-sm text-text-secondary font-mono\">\n {pinnedVersion ? `v${pinnedVersion}` : 'Using latest'}\n </p>\n </div>\n <div className=\"flex gap-2\">\n {pinnedVersion ? (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => void handlePinVersion(pkg, env, '')}\n >\n Unpin\n </Button>\n ) : (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => void handlePinVersion(pkg, env, pkg.installedVersion)}\n >\n Pin Current\n </Button>\n )}\n </div>\n </div>\n );\n })}\n </div>\n </CardContent>\n </Card>\n\n {/* Bindings */}\n {Object.keys(bindings).length > 0 && (\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base flex items-center gap-2\">\n <Settings className=\"h-4 w-4\" />\n Configuration Bindings\n </CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-2\">\n {Object.entries(bindings).map(([key, value]) => (\n <div key={key} className=\"flex justify-between p-2 bg-bg-sunken rounded\">\n <span className=\"font-mono text-sm\">{key}</span>\n <span className=\"font-mono text-sm text-text-secondary\">\n {typeof value === 'string' ? value : JSON.stringify(value)}\n </span>\n </div>\n ))}\n </div>\n </CardContent>\n </Card>\n )}\n\n {/* Metadata */}\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Metadata</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"grid grid-cols-2 gap-4 text-sm\">\n <div>\n <p className=\"text-text-secondary\">Installed At</p>\n <p className=\"font-medium\">{formatRelativeTime(pkg.installedAt)}</p>\n </div>\n <div>\n <p className=\"text-text-secondary\">Installed By</p>\n <p className=\"font-medium\">{pkg.installedBy}</p>\n </div>\n <div>\n <p className=\"text-text-secondary\">Package ID</p>\n <p className=\"font-mono text-xs\">{pkg.packageId}</p>\n </div>\n <div>\n <p className=\"text-text-secondary\">Installation ID</p>\n <p className=\"font-mono text-xs\">{pkg.id}</p>\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n\n <div className=\"p-6 border-t border-border-subtle flex justify-between\">\n <Button variant=\"destructive\" onClick={() => void handleUninstall(pkg)}>\n <Trash2 className=\"h-4 w-4 mr-2\" />\n Uninstall\n </Button>\n <div className=\"flex gap-2\">\n <Link to={getPath(`store/package/${slug}`)}>\n <Button variant=\"outline\">View in Store</Button>\n </Link>\n <Button variant=\"outline\" onClick={onClose}>\n Close\n </Button>\n </div>\n </div>\n </div>\n </div>\n );\n };\n\n if (loading) {\n return (\n <PageLayout title=\"Installed Packages\" description=\"Manage installed store packages\">\n <div className=\"space-y-4\">\n {[1, 2, 3].map((i) => (\n <div key={i} className=\"h-48 bg-bg-sunken motion-safe:animate-vc-shimmer rounded-lg\" />\n ))}\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Installed Packages\"\n description=\"Manage packages installed from the Botlit Store\"\n actions={\n <div className=\"flex gap-2\">\n <Button\n variant=\"outline\"\n leftIcon={<RefreshCw size={16} />}\n onClick={() => void handleCheckUpdates()}\n isLoading={refreshing}\n >\n Check Updates\n </Button>\n <Button leftIcon={<Package size={16} />} onClick={() => navigateTo('store')}>\n Browse Store\n </Button>\n </div>\n }\n >\n <PagePurpose className=\"mb-6\" icon={<Package size={18} />}>\n These are the packages you have installed from the Botlit Store and how they are wired into\n your workspace. Manage each one’s installation mode (linked, so it auto-updates, or\n forked as an independent copy), pin specific versions per environment, and update or\n uninstall — this is your dependency manager for everything you pulled from the store.\n </PagePurpose>\n\n {error ? (\n <ErrorState resource=\"installed packages\" error={error} onRetry={() => void refetch()} />\n ) : (\n <>\n {/* Summary Cards */}\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4 mb-6\">\n <GlassCard treatment=\"glass\" glow className=\"p-6 text-center\">\n <p className=\"text-3xl font-bold tabular-nums text-text-primary\">{packages.length}</p>\n <p className=\"text-sm text-text-secondary\">Total Installed</p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-6 text-center\">\n <p className=\"text-3xl font-bold tabular-nums text-text-primary\">\n {packages.filter((pkg) => pkg.mode === 'LINKED').length}\n </p>\n <p className=\"text-sm text-text-secondary\">Linked</p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-6 text-center\">\n <p className=\"text-3xl font-bold tabular-nums text-text-primary\">\n {packages.filter((pkg) => pkg.mode === 'FORKED').length}\n </p>\n <p className=\"text-sm text-text-secondary\">Forked</p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-6 text-center\">\n <p className=\"text-3xl font-bold tabular-nums text-text-primary\">\n {updatesAvailable}\n </p>\n <p className=\"text-sm text-text-secondary\">Updates Available</p>\n </GlassCard>\n </div>\n\n {/* Filters */}\n <div className=\"flex flex-col md:flex-row gap-4 mb-6\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-text-secondary\" />\n <Input\n placeholder=\"Search packages...\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n className=\"pl-9\"\n />\n </div>\n <div className=\"flex gap-2\">\n <select\n aria-label=\"Filter by package type\"\n className=\"flex h-10 rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm\"\n value={kindFilter}\n onChange={(e) => setKindFilter(e.target.value as PackageKind | 'all')}\n >\n <option value=\"all\">All Types</option>\n <option value={GqlPackageKind.Agent}>Agents</option>\n <option value={GqlPackageKind.Skill}>Skills</option>\n <option value={GqlPackageKind.McpPack}>MCP Packs</option>\n <option value={GqlPackageKind.PromptTemplate}>Templates</option>\n <option value={GqlPackageKind.RetrievalProfile}>Retrieval Profiles</option>\n </select>\n </div>\n </div>\n\n {/* Package List */}\n {filteredPackages.length > 0 ? (\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n {filteredPackages.map((pkg) => (\n <PackageCard key={pkg.id} pkg={pkg} />\n ))}\n </div>\n ) : (\n <EmptyState\n illustration=\"empty-generic\"\n title={\n search || kindFilter !== 'all' ? 'No matching packages' : 'No packages installed'\n }\n description={\n search || kindFilter !== 'all'\n ? 'Try adjusting your search or type filter.'\n : 'Browse the Botlit Store to find agents, skills, and tools to install.'\n }\n action={\n <Button\n onClick={() => navigateTo('store')}\n leftIcon={<Download className=\"h-4 w-4\" />}\n >\n Browse Store\n </Button>\n }\n />\n )}\n </>\n )}\n\n {/* Detail Modal */}\n {selectedPackage && (\n <PackageDetailModal pkg={selectedPackage} onClose={() => setSelectedPackageId(null)} />\n )}\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
import { useBotlit as e } from "../providers/BotlitProvider.js";
|
|
2
|
+
import { useCreateKnowledgeBase as t, useKnowledgeBase as n, useKnowledgeDocuments as r, useRebuildKnowledgeBaseIndex as i, useUpdateKnowledgeBase as ee } from "../hooks/useKnowledgeBases.js";
|
|
3
|
+
import { formatRelativeTime as a } from "../utils/formatters.js";
|
|
4
|
+
import { PageLayout as o } from "../components/PageLayout.js";
|
|
5
|
+
import { Card as s, CardContent as c, CardDescription as l, CardHeader as u, CardTitle as d } from "../components/Card.js";
|
|
6
|
+
import { Button as f } from "../components/Button.js";
|
|
7
|
+
import { StatusBadge as p } from "../components/Badge.js";
|
|
8
|
+
import { EmptyState as m } from "../components/EmptyState.js";
|
|
9
|
+
import { FormField as h, Input as g, Textarea as _ } from "../components/Input.js";
|
|
10
|
+
import { useEffect as te, useState as v } from "react";
|
|
11
|
+
import { GlassCard as ne, PagePurpose as re } from "@burdenoff/fe-libs/ui";
|
|
12
|
+
import { Fragment as y, jsx as b, jsxs as x } from "react/jsx-runtime";
|
|
13
|
+
import { useParams as S } from "react-router";
|
|
14
|
+
import { ArrowLeft as C, Boxes as w, Database as T, FileText as E, Layers as D, RefreshCw as O, Save as k, Settings as A } from "lucide-react";
|
|
15
|
+
import { toast as j } from "sonner";
|
|
16
|
+
//#region src/botlit/pages/KnowledgeBaseDetailPage.tsx
|
|
17
|
+
var ie = [
|
|
18
|
+
"RECURSIVE",
|
|
19
|
+
"FIXED",
|
|
20
|
+
"SENTENCE"
|
|
21
|
+
], M = [
|
|
22
|
+
{
|
|
23
|
+
id: "text-embedding-3-small",
|
|
24
|
+
label: "OpenAI text-embedding-3-small",
|
|
25
|
+
dimensions: 1536
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: "text-embedding-3-large",
|
|
29
|
+
label: "OpenAI text-embedding-3-large",
|
|
30
|
+
dimensions: 3072
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: "text-embedding-ada-002",
|
|
34
|
+
label: "OpenAI text-embedding-ada-002",
|
|
35
|
+
dimensions: 1536
|
|
36
|
+
}
|
|
37
|
+
], N = {
|
|
38
|
+
name: "",
|
|
39
|
+
description: "",
|
|
40
|
+
embeddingModel: M[0].id,
|
|
41
|
+
embeddingDimensions: M[0].dimensions,
|
|
42
|
+
chunkingStrategy: "RECURSIVE",
|
|
43
|
+
chunkSize: 512,
|
|
44
|
+
chunkOverlap: 50
|
|
45
|
+
};
|
|
46
|
+
function P(e) {
|
|
47
|
+
return {
|
|
48
|
+
name: e.name,
|
|
49
|
+
description: e.description ?? "",
|
|
50
|
+
embeddingModel: e.embeddingModel,
|
|
51
|
+
embeddingDimensions: e.embeddingDimensions,
|
|
52
|
+
chunkingStrategy: e.chunkingStrategy,
|
|
53
|
+
chunkSize: e.chunkSize,
|
|
54
|
+
chunkOverlap: e.chunkOverlap
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function F(e, t) {
|
|
58
|
+
let n = Number(e);
|
|
59
|
+
return e.trim() !== "" && Number.isFinite(n) ? n : t;
|
|
60
|
+
}
|
|
61
|
+
var I = ({ isNew: I }) => {
|
|
62
|
+
let { kbId: L } = S(), { navigateTo: R } = e(), { data: z, loading: B, refetch: V } = n(I ? void 0 : L), { data: H } = r(I ? void 0 : L), [U, W] = t(), [G, K] = ee(), [q, J] = i(), [Y, X] = v(!1), [Z, Q] = v(N);
|
|
63
|
+
te(() => {
|
|
64
|
+
z && !I && Q(P(z));
|
|
65
|
+
}, [z, I]);
|
|
66
|
+
let ae = async () => {
|
|
67
|
+
if (!Z.name.trim()) {
|
|
68
|
+
j.error("Name is required");
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
let e = await U({
|
|
73
|
+
name: Z.name.trim(),
|
|
74
|
+
description: Z.description.trim() || null,
|
|
75
|
+
embeddingModel: Z.embeddingModel,
|
|
76
|
+
embeddingDimensions: Z.embeddingDimensions,
|
|
77
|
+
chunkingStrategy: Z.chunkingStrategy,
|
|
78
|
+
chunkSize: Z.chunkSize,
|
|
79
|
+
chunkOverlap: Z.chunkOverlap
|
|
80
|
+
});
|
|
81
|
+
j.success(`Knowledge base "${e.name}" created`), R(`knowledge/${e.id}`);
|
|
82
|
+
} catch (e) {
|
|
83
|
+
j.error(e instanceof Error ? e.message : "Failed to create knowledge base");
|
|
84
|
+
}
|
|
85
|
+
}, oe = async () => {
|
|
86
|
+
if (!L || !Z.name.trim()) {
|
|
87
|
+
j.error("Name is required");
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
await G({
|
|
92
|
+
kbId: L,
|
|
93
|
+
input: {
|
|
94
|
+
name: Z.name.trim(),
|
|
95
|
+
description: Z.description.trim() || null,
|
|
96
|
+
chunkingStrategy: Z.chunkingStrategy,
|
|
97
|
+
chunkSize: Z.chunkSize,
|
|
98
|
+
chunkOverlap: Z.chunkOverlap
|
|
99
|
+
}
|
|
100
|
+
}), await V(), j.success("Knowledge base updated"), X(!1);
|
|
101
|
+
} catch (e) {
|
|
102
|
+
j.error(e instanceof Error ? e.message : "Failed to update knowledge base");
|
|
103
|
+
}
|
|
104
|
+
}, se = async () => {
|
|
105
|
+
if (L) try {
|
|
106
|
+
await q(L), await V(), j.success("Re-indexing started");
|
|
107
|
+
} catch (e) {
|
|
108
|
+
j.error(e instanceof Error ? e.message : "Failed to rebuild the index");
|
|
109
|
+
}
|
|
110
|
+
}, $ = (e) => /* @__PURE__ */ x(y, { children: [
|
|
111
|
+
/* @__PURE__ */ b(h, {
|
|
112
|
+
label: "Name",
|
|
113
|
+
required: !0,
|
|
114
|
+
children: /* @__PURE__ */ b(g, {
|
|
115
|
+
placeholder: "e.g., Product Documentation",
|
|
116
|
+
value: Z.name,
|
|
117
|
+
onChange: (e) => Q({
|
|
118
|
+
...Z,
|
|
119
|
+
name: e.target.value
|
|
120
|
+
})
|
|
121
|
+
})
|
|
122
|
+
}),
|
|
123
|
+
/* @__PURE__ */ b(h, {
|
|
124
|
+
label: "Description",
|
|
125
|
+
children: /* @__PURE__ */ b(_, {
|
|
126
|
+
placeholder: "What kind of knowledge will this store?",
|
|
127
|
+
value: Z.description,
|
|
128
|
+
onChange: (e) => Q({
|
|
129
|
+
...Z,
|
|
130
|
+
description: e.target.value
|
|
131
|
+
}),
|
|
132
|
+
rows: 3
|
|
133
|
+
})
|
|
134
|
+
}),
|
|
135
|
+
e && /* @__PURE__ */ b(h, {
|
|
136
|
+
label: "Embedding Model",
|
|
137
|
+
required: !0,
|
|
138
|
+
children: /* @__PURE__ */ b("select", {
|
|
139
|
+
className: "flex h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm",
|
|
140
|
+
value: Z.embeddingModel,
|
|
141
|
+
onChange: (e) => {
|
|
142
|
+
let t = M.find((t) => t.id === e.target.value);
|
|
143
|
+
Q({
|
|
144
|
+
...Z,
|
|
145
|
+
embeddingModel: e.target.value,
|
|
146
|
+
embeddingDimensions: t?.dimensions ?? Z.embeddingDimensions
|
|
147
|
+
});
|
|
148
|
+
},
|
|
149
|
+
children: M.map((e) => /* @__PURE__ */ x("option", {
|
|
150
|
+
value: e.id,
|
|
151
|
+
children: [
|
|
152
|
+
e.label,
|
|
153
|
+
" (",
|
|
154
|
+
e.dimensions,
|
|
155
|
+
"d)"
|
|
156
|
+
]
|
|
157
|
+
}, e.id))
|
|
158
|
+
})
|
|
159
|
+
}),
|
|
160
|
+
/* @__PURE__ */ x("div", {
|
|
161
|
+
className: "grid grid-cols-3 gap-4",
|
|
162
|
+
children: [
|
|
163
|
+
/* @__PURE__ */ b(h, {
|
|
164
|
+
label: "Chunking Strategy",
|
|
165
|
+
children: /* @__PURE__ */ b("select", {
|
|
166
|
+
className: "flex h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm",
|
|
167
|
+
value: Z.chunkingStrategy,
|
|
168
|
+
onChange: (e) => Q({
|
|
169
|
+
...Z,
|
|
170
|
+
chunkingStrategy: e.target.value
|
|
171
|
+
}),
|
|
172
|
+
children: ie.map((e) => /* @__PURE__ */ b("option", {
|
|
173
|
+
value: e,
|
|
174
|
+
children: e
|
|
175
|
+
}, e))
|
|
176
|
+
})
|
|
177
|
+
}),
|
|
178
|
+
/* @__PURE__ */ b(h, {
|
|
179
|
+
label: "Chunk Size",
|
|
180
|
+
children: /* @__PURE__ */ b(g, {
|
|
181
|
+
type: "number",
|
|
182
|
+
value: Z.chunkSize,
|
|
183
|
+
onChange: (e) => Q({
|
|
184
|
+
...Z,
|
|
185
|
+
chunkSize: F(e.target.value, N.chunkSize)
|
|
186
|
+
})
|
|
187
|
+
})
|
|
188
|
+
}),
|
|
189
|
+
/* @__PURE__ */ b(h, {
|
|
190
|
+
label: "Chunk Overlap",
|
|
191
|
+
children: /* @__PURE__ */ b(g, {
|
|
192
|
+
type: "number",
|
|
193
|
+
value: Z.chunkOverlap,
|
|
194
|
+
onChange: (e) => Q({
|
|
195
|
+
...Z,
|
|
196
|
+
chunkOverlap: F(e.target.value, N.chunkOverlap)
|
|
197
|
+
})
|
|
198
|
+
})
|
|
199
|
+
})
|
|
200
|
+
]
|
|
201
|
+
})
|
|
202
|
+
] });
|
|
203
|
+
return I ? /* @__PURE__ */ b(o, {
|
|
204
|
+
title: "Create Knowledge Base",
|
|
205
|
+
description: "Set up an embedding index your agents can retrieve from",
|
|
206
|
+
actions: /* @__PURE__ */ x("div", {
|
|
207
|
+
className: "flex gap-2",
|
|
208
|
+
children: [/* @__PURE__ */ b(f, {
|
|
209
|
+
variant: "outline",
|
|
210
|
+
onClick: () => R("knowledge"),
|
|
211
|
+
children: "Cancel"
|
|
212
|
+
}), /* @__PURE__ */ b(f, {
|
|
213
|
+
leftIcon: /* @__PURE__ */ b(k, { size: 16 }),
|
|
214
|
+
onClick: ae,
|
|
215
|
+
isLoading: W.loading,
|
|
216
|
+
disabled: !Z.name.trim(),
|
|
217
|
+
children: "Create"
|
|
218
|
+
})]
|
|
219
|
+
}),
|
|
220
|
+
children: /* @__PURE__ */ b("div", {
|
|
221
|
+
className: "max-w-3xl space-y-6",
|
|
222
|
+
children: /* @__PURE__ */ x(s, { children: [/* @__PURE__ */ x(u, { children: [/* @__PURE__ */ b(d, { children: "Index Configuration" }), /* @__PURE__ */ b(l, { children: "The embedding model and its vector width are fixed once documents are indexed." })] }), /* @__PURE__ */ b(c, {
|
|
223
|
+
className: "space-y-4",
|
|
224
|
+
children: $(!0)
|
|
225
|
+
})] })
|
|
226
|
+
})
|
|
227
|
+
}) : B ? /* @__PURE__ */ b(o, {
|
|
228
|
+
title: "Loading...",
|
|
229
|
+
children: /* @__PURE__ */ b("div", { className: "h-96 bg-bg-sunken motion-safe:animate-vc-shimmer rounded-lg" })
|
|
230
|
+
}) : z ? /* @__PURE__ */ b(o, {
|
|
231
|
+
title: /* @__PURE__ */ x("div", {
|
|
232
|
+
className: "flex items-center gap-3",
|
|
233
|
+
children: [/* @__PURE__ */ b(f, {
|
|
234
|
+
variant: "ghost",
|
|
235
|
+
size: "icon",
|
|
236
|
+
onClick: () => R("knowledge"),
|
|
237
|
+
children: /* @__PURE__ */ b(C, { size: 20 })
|
|
238
|
+
}), /* @__PURE__ */ x("div", { children: [/* @__PURE__ */ x("div", {
|
|
239
|
+
className: "flex items-center gap-2",
|
|
240
|
+
children: [/* @__PURE__ */ b("span", { children: z.name }), /* @__PURE__ */ b(p, { status: z.indexStatus })]
|
|
241
|
+
}), z.description && /* @__PURE__ */ b("p", {
|
|
242
|
+
className: "text-sm font-normal text-text-secondary",
|
|
243
|
+
children: z.description
|
|
244
|
+
})] })]
|
|
245
|
+
}),
|
|
246
|
+
actions: /* @__PURE__ */ x("div", {
|
|
247
|
+
className: "flex gap-2",
|
|
248
|
+
children: [/* @__PURE__ */ b(f, {
|
|
249
|
+
variant: "outline",
|
|
250
|
+
leftIcon: /* @__PURE__ */ b(O, { size: 16 }),
|
|
251
|
+
onClick: se,
|
|
252
|
+
isLoading: J.loading,
|
|
253
|
+
children: "Rebuild Index"
|
|
254
|
+
}), Y ? /* @__PURE__ */ x(y, { children: [/* @__PURE__ */ b(f, {
|
|
255
|
+
variant: "outline",
|
|
256
|
+
onClick: () => {
|
|
257
|
+
Q(P(z)), X(!1);
|
|
258
|
+
},
|
|
259
|
+
children: "Cancel"
|
|
260
|
+
}), /* @__PURE__ */ b(f, {
|
|
261
|
+
leftIcon: /* @__PURE__ */ b(k, { size: 16 }),
|
|
262
|
+
onClick: oe,
|
|
263
|
+
isLoading: K.loading,
|
|
264
|
+
children: "Save"
|
|
265
|
+
})] }) : /* @__PURE__ */ b(f, {
|
|
266
|
+
leftIcon: /* @__PURE__ */ b(A, { size: 16 }),
|
|
267
|
+
onClick: () => X(!0),
|
|
268
|
+
children: "Configure"
|
|
269
|
+
})]
|
|
270
|
+
}),
|
|
271
|
+
children: /* @__PURE__ */ x("div", {
|
|
272
|
+
className: "space-y-6",
|
|
273
|
+
children: [
|
|
274
|
+
/* @__PURE__ */ b(re, {
|
|
275
|
+
icon: /* @__PURE__ */ b(T, { size: 18 }),
|
|
276
|
+
children: "This knowledge base is one searchable library your agents can draw on. Review how its documents are chunked and indexed, rebuild the index when content changes, and confirm it is healthy — so agents retrieve accurate, up-to-date passages and can cite them in their answers."
|
|
277
|
+
}),
|
|
278
|
+
/* @__PURE__ */ x("div", {
|
|
279
|
+
className: "grid grid-cols-2 md:grid-cols-4 gap-4",
|
|
280
|
+
children: [
|
|
281
|
+
/* @__PURE__ */ x(ne, {
|
|
282
|
+
treatment: "glass",
|
|
283
|
+
glow: !0,
|
|
284
|
+
className: "p-6 text-center",
|
|
285
|
+
children: [
|
|
286
|
+
/* @__PURE__ */ b("div", {
|
|
287
|
+
className: "p-3 rounded-full bg-action-primary-bg/10 w-fit mx-auto mb-2",
|
|
288
|
+
children: /* @__PURE__ */ b(T, { className: "h-5 w-5 text-primary" })
|
|
289
|
+
}),
|
|
290
|
+
/* @__PURE__ */ b("p", {
|
|
291
|
+
className: "text-lg font-bold text-text-primary capitalize",
|
|
292
|
+
children: z.indexStatus.toLowerCase()
|
|
293
|
+
}),
|
|
294
|
+
/* @__PURE__ */ b("p", {
|
|
295
|
+
className: "text-sm text-text-secondary",
|
|
296
|
+
children: "Index Status"
|
|
297
|
+
})
|
|
298
|
+
]
|
|
299
|
+
}),
|
|
300
|
+
/* @__PURE__ */ b(s, { children: /* @__PURE__ */ x(c, {
|
|
301
|
+
className: "pt-6 text-center",
|
|
302
|
+
children: [/* @__PURE__ */ b("p", {
|
|
303
|
+
className: "text-3xl font-bold text-text-primary",
|
|
304
|
+
children: z.totalDocuments
|
|
305
|
+
}), /* @__PURE__ */ b("p", {
|
|
306
|
+
className: "text-sm text-text-secondary",
|
|
307
|
+
children: "Documents"
|
|
308
|
+
})]
|
|
309
|
+
}) }),
|
|
310
|
+
/* @__PURE__ */ b(s, { children: /* @__PURE__ */ x(c, {
|
|
311
|
+
className: "pt-6 text-center",
|
|
312
|
+
children: [/* @__PURE__ */ b("p", {
|
|
313
|
+
className: "text-3xl font-bold text-text-primary",
|
|
314
|
+
children: z.totalChunks
|
|
315
|
+
}), /* @__PURE__ */ b("p", {
|
|
316
|
+
className: "text-sm text-text-secondary",
|
|
317
|
+
children: "Chunks"
|
|
318
|
+
})]
|
|
319
|
+
}) }),
|
|
320
|
+
/* @__PURE__ */ b(s, { children: /* @__PURE__ */ x(c, {
|
|
321
|
+
className: "pt-6 text-center",
|
|
322
|
+
children: [/* @__PURE__ */ b("p", {
|
|
323
|
+
className: "text-3xl font-bold text-text-primary",
|
|
324
|
+
children: z.vectorCount
|
|
325
|
+
}), /* @__PURE__ */ b("p", {
|
|
326
|
+
className: "text-sm text-text-secondary",
|
|
327
|
+
children: "Vectors"
|
|
328
|
+
})]
|
|
329
|
+
}) })
|
|
330
|
+
]
|
|
331
|
+
}),
|
|
332
|
+
/* @__PURE__ */ x(s, { children: [/* @__PURE__ */ x(u, { children: [/* @__PURE__ */ x(d, {
|
|
333
|
+
className: "flex items-center gap-2",
|
|
334
|
+
children: [/* @__PURE__ */ b(D, { size: 18 }), "Index Configuration"]
|
|
335
|
+
}), /* @__PURE__ */ b(l, { children: "How documents are embedded and split before they are indexed" })] }), /* @__PURE__ */ b(c, {
|
|
336
|
+
className: "space-y-4",
|
|
337
|
+
children: Y ? $(!1) : /* @__PURE__ */ x("div", {
|
|
338
|
+
className: "grid grid-cols-2 md:grid-cols-3 gap-6",
|
|
339
|
+
children: [
|
|
340
|
+
/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("p", {
|
|
341
|
+
className: "text-sm text-text-secondary",
|
|
342
|
+
children: "Embedding Model"
|
|
343
|
+
}), /* @__PURE__ */ b("p", {
|
|
344
|
+
className: "font-medium font-mono text-sm",
|
|
345
|
+
children: z.embeddingModel
|
|
346
|
+
})] }),
|
|
347
|
+
/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("p", {
|
|
348
|
+
className: "text-sm text-text-secondary",
|
|
349
|
+
children: "Dimensions"
|
|
350
|
+
}), /* @__PURE__ */ b("p", {
|
|
351
|
+
className: "font-medium",
|
|
352
|
+
children: z.embeddingDimensions
|
|
353
|
+
})] }),
|
|
354
|
+
/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("p", {
|
|
355
|
+
className: "text-sm text-text-secondary",
|
|
356
|
+
children: "Chunking Strategy"
|
|
357
|
+
}), /* @__PURE__ */ b("p", {
|
|
358
|
+
className: "font-medium",
|
|
359
|
+
children: z.chunkingStrategy
|
|
360
|
+
})] }),
|
|
361
|
+
/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("p", {
|
|
362
|
+
className: "text-sm text-text-secondary",
|
|
363
|
+
children: "Chunk Size"
|
|
364
|
+
}), /* @__PURE__ */ b("p", {
|
|
365
|
+
className: "font-medium",
|
|
366
|
+
children: z.chunkSize
|
|
367
|
+
})] }),
|
|
368
|
+
/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("p", {
|
|
369
|
+
className: "text-sm text-text-secondary",
|
|
370
|
+
children: "Chunk Overlap"
|
|
371
|
+
}), /* @__PURE__ */ b("p", {
|
|
372
|
+
className: "font-medium",
|
|
373
|
+
children: z.chunkOverlap
|
|
374
|
+
})] }),
|
|
375
|
+
/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("p", {
|
|
376
|
+
className: "text-sm text-text-secondary",
|
|
377
|
+
children: "Last Indexed"
|
|
378
|
+
}), /* @__PURE__ */ b("p", {
|
|
379
|
+
className: "font-medium",
|
|
380
|
+
children: z.lastIndexedAt ? a(z.lastIndexedAt) : "Never"
|
|
381
|
+
})] })
|
|
382
|
+
]
|
|
383
|
+
})
|
|
384
|
+
})] }),
|
|
385
|
+
/* @__PURE__ */ x(s, { children: [/* @__PURE__ */ x(u, { children: [/* @__PURE__ */ x(d, {
|
|
386
|
+
className: "flex items-center gap-2",
|
|
387
|
+
children: [
|
|
388
|
+
/* @__PURE__ */ b(E, { size: 18 }),
|
|
389
|
+
"Documents (",
|
|
390
|
+
H.length,
|
|
391
|
+
")"
|
|
392
|
+
]
|
|
393
|
+
}), /* @__PURE__ */ b(l, { children: "Source documents indexed into this knowledge base" })] }), /* @__PURE__ */ b(c, {
|
|
394
|
+
className: "p-0",
|
|
395
|
+
children: H.length > 0 ? /* @__PURE__ */ b("div", {
|
|
396
|
+
className: "divide-y divide-border-subtle",
|
|
397
|
+
children: H.map((e) => /* @__PURE__ */ x("div", {
|
|
398
|
+
className: "flex items-center justify-between gap-4 p-4",
|
|
399
|
+
children: [/* @__PURE__ */ x("div", {
|
|
400
|
+
className: "min-w-0",
|
|
401
|
+
children: [
|
|
402
|
+
/* @__PURE__ */ b("p", {
|
|
403
|
+
className: "font-medium truncate",
|
|
404
|
+
children: e.title
|
|
405
|
+
}),
|
|
406
|
+
/* @__PURE__ */ x("p", {
|
|
407
|
+
className: "text-sm text-text-secondary",
|
|
408
|
+
children: [
|
|
409
|
+
e.chunkCount,
|
|
410
|
+
" chunks · added ",
|
|
411
|
+
a(e.addedAt)
|
|
412
|
+
]
|
|
413
|
+
}),
|
|
414
|
+
e.processingError && /* @__PURE__ */ b("p", {
|
|
415
|
+
className: "mt-1 text-sm text-status-error-text",
|
|
416
|
+
children: e.processingError
|
|
417
|
+
})
|
|
418
|
+
]
|
|
419
|
+
}), /* @__PURE__ */ b(p, { status: e.status })]
|
|
420
|
+
}, e.id))
|
|
421
|
+
}) : /* @__PURE__ */ x("div", {
|
|
422
|
+
className: "p-8 text-center text-text-secondary",
|
|
423
|
+
children: [/* @__PURE__ */ b(w, { className: "h-8 w-8 mx-auto mb-2 opacity-50" }), /* @__PURE__ */ b("p", { children: "No documents indexed yet" })]
|
|
424
|
+
})
|
|
425
|
+
})] }),
|
|
426
|
+
/* @__PURE__ */ x(s, { children: [/* @__PURE__ */ b(u, { children: /* @__PURE__ */ x(d, {
|
|
427
|
+
className: "flex items-center gap-2",
|
|
428
|
+
children: [/* @__PURE__ */ b(A, { size: 18 }), "Metadata"]
|
|
429
|
+
}) }), /* @__PURE__ */ b(c, { children: /* @__PURE__ */ x("div", {
|
|
430
|
+
className: "grid grid-cols-2 md:grid-cols-3 gap-6 text-sm",
|
|
431
|
+
children: [
|
|
432
|
+
/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("p", {
|
|
433
|
+
className: "text-text-secondary",
|
|
434
|
+
children: "Created"
|
|
435
|
+
}), /* @__PURE__ */ b("p", {
|
|
436
|
+
className: "font-medium",
|
|
437
|
+
children: a(z.createdAt)
|
|
438
|
+
})] }),
|
|
439
|
+
/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("p", {
|
|
440
|
+
className: "text-text-secondary",
|
|
441
|
+
children: "Updated"
|
|
442
|
+
}), /* @__PURE__ */ b("p", {
|
|
443
|
+
className: "font-medium",
|
|
444
|
+
children: a(z.updatedAt)
|
|
445
|
+
})] }),
|
|
446
|
+
/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("p", {
|
|
447
|
+
className: "text-text-secondary",
|
|
448
|
+
children: "Last Indexed"
|
|
449
|
+
}), /* @__PURE__ */ b("p", {
|
|
450
|
+
className: "font-medium",
|
|
451
|
+
children: z.lastIndexedAt ? a(z.lastIndexedAt) : "—"
|
|
452
|
+
})] })
|
|
453
|
+
]
|
|
454
|
+
}) })] })
|
|
455
|
+
]
|
|
456
|
+
})
|
|
457
|
+
}) : /* @__PURE__ */ b(o, {
|
|
458
|
+
title: "Knowledge Base Not Found",
|
|
459
|
+
children: /* @__PURE__ */ b(m, {
|
|
460
|
+
illustration: "empty-files",
|
|
461
|
+
title: "Knowledge Base Not Found",
|
|
462
|
+
description: "This knowledge base doesn't exist.",
|
|
463
|
+
action: /* @__PURE__ */ b(f, {
|
|
464
|
+
onClick: () => R("knowledge"),
|
|
465
|
+
children: "Back to Knowledge Bases"
|
|
466
|
+
})
|
|
467
|
+
})
|
|
468
|
+
});
|
|
469
|
+
};
|
|
470
|
+
//#endregion
|
|
471
|
+
export { I as KnowledgeBaseDetailPage };
|
|
472
|
+
|
|
473
|
+
//# sourceMappingURL=KnowledgeBaseDetailPage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KnowledgeBaseDetailPage.js","names":[],"sources":["../../../src/botlit/pages/KnowledgeBaseDetailPage.tsx"],"sourcesContent":["/**\n * Knowledge Base Detail Page\n * @module botlit/pages\n *\n * A `KnowledgeBase` on wspace-botlit-svc is an embedding index owned by the\n * platform: `indexStatus`, `embeddingModel`/`embeddingDimensions`, the chunking\n * configuration and the document/chunk/vector counters. It carries no vector\n * store connection, no ingestion-flow references and no visibility column, so\n * this page renders only what the type actually exposes.\n */\n\nimport type { FC } from 'react';\nimport { useEffect, useState } from 'react';\nimport { useParams } from 'react-router';\nimport { toast } from 'sonner';\nimport { useBotlit } from '../providers/BotlitProvider';\nimport {\n ArrowLeft,\n Database,\n Settings,\n Save,\n FileText,\n Layers,\n RefreshCw,\n Boxes,\n} from 'lucide-react';\nimport { PageLayout } from '../components/PageLayout';\nimport { PagePurpose, GlassCard } from '@burdenoff/fe-libs/ui';\nimport { EmptyState } from '../components/EmptyState';\nimport { Card, CardHeader, CardTitle, CardContent, CardDescription } from '../components/Card';\nimport { Button } from '../components/Button';\nimport { Input, Textarea, FormField } from '../components/Input';\nimport { StatusBadge } from '../components/Badge';\nimport {\n useKnowledgeBase,\n useKnowledgeDocuments,\n useCreateKnowledgeBase,\n useUpdateKnowledgeBase,\n useRebuildKnowledgeBaseIndex,\n type KnowledgeBaseRecord,\n} from '../hooks/useKnowledgeBases';\nimport { formatRelativeTime, MISSING_VALUE } from '../utils/formatters';\n\ninterface KnowledgeBaseDetailPageProps {\n isNew?: boolean;\n}\n\n/**\n * Chunking strategies the service actually implements.\n *\n * `SEMANTIC` was offered here until 2026-07-26 but no ingestion path ever\n * implemented it — and, until the same change, `chunkingStrategy` was not\n * persisted at all, so every option in this list was equally inert. The\n * service now validates the value and rejects anything it cannot honour, so\n * listing SEMANTIC would surface a VALIDATION_FAILED error on save. It stays\n * out until real embedding-based splitting exists in the ingestion pipeline.\n */\nconst CHUNKING_STRATEGIES = ['RECURSIVE', 'FIXED', 'SENTENCE'] as const;\n\n/** Embedding models the platform indexes with, and their vector width. */\nconst EMBEDDING_MODELS: { id: string; label: string; dimensions: number }[] = [\n { id: 'text-embedding-3-small', label: 'OpenAI text-embedding-3-small', dimensions: 1536 },\n { id: 'text-embedding-3-large', label: 'OpenAI text-embedding-3-large', dimensions: 3072 },\n { id: 'text-embedding-ada-002', label: 'OpenAI text-embedding-ada-002', dimensions: 1536 },\n];\n\ninterface KnowledgeBaseForm {\n name: string;\n description: string;\n embeddingModel: string;\n embeddingDimensions: number;\n chunkingStrategy: string;\n chunkSize: number;\n chunkOverlap: number;\n}\n\nconst DEFAULT_FORM: KnowledgeBaseForm = {\n name: '',\n description: '',\n embeddingModel: EMBEDDING_MODELS[0].id,\n embeddingDimensions: EMBEDDING_MODELS[0].dimensions,\n chunkingStrategy: 'RECURSIVE',\n chunkSize: 512,\n chunkOverlap: 50,\n};\n\nfunction toForm(kb: KnowledgeBaseRecord): KnowledgeBaseForm {\n return {\n name: kb.name,\n description: kb.description ?? '',\n embeddingModel: kb.embeddingModel,\n embeddingDimensions: kb.embeddingDimensions,\n chunkingStrategy: kb.chunkingStrategy,\n chunkSize: kb.chunkSize,\n chunkOverlap: kb.chunkOverlap,\n };\n}\n\n/** `Number('')` is 0 and `Number('abc')` is NaN — keep the previous value instead. */\nfunction toNumber(value: string, fallback: number): number {\n const parsed = Number(value);\n return value.trim() !== '' && Number.isFinite(parsed) ? parsed : fallback;\n}\n\nexport const KnowledgeBaseDetailPage: FC<KnowledgeBaseDetailPageProps> = ({ isNew }) => {\n const { kbId } = useParams();\n const { navigateTo } = useBotlit();\n const { data: kb, loading, refetch } = useKnowledgeBase(isNew ? undefined : kbId);\n const { data: documents } = useKnowledgeDocuments(isNew ? undefined : kbId);\n const [createKB, createKBResult] = useCreateKnowledgeBase();\n const [updateKB, updateKBResult] = useUpdateKnowledgeBase();\n const [rebuildIndex, rebuildResult] = useRebuildKnowledgeBaseIndex();\n\n const [isEditing, setIsEditing] = useState(false);\n const [formData, setFormData] = useState<KnowledgeBaseForm>(DEFAULT_FORM);\n\n useEffect(() => {\n if (kb && !isNew) {\n setFormData(toForm(kb));\n }\n }, [kb, isNew]);\n\n const handleCreate = async () => {\n if (!formData.name.trim()) {\n toast.error('Name is required');\n return;\n }\n try {\n const newKB = await createKB({\n name: formData.name.trim(),\n description: formData.description.trim() || null,\n embeddingModel: formData.embeddingModel,\n embeddingDimensions: formData.embeddingDimensions,\n chunkingStrategy: formData.chunkingStrategy,\n chunkSize: formData.chunkSize,\n chunkOverlap: formData.chunkOverlap,\n });\n toast.success(`Knowledge base \"${newKB.name}\" created`);\n navigateTo(`knowledge/${newKB.id}`);\n } catch (err) {\n toast.error(err instanceof Error ? err.message : 'Failed to create knowledge base');\n }\n };\n\n const handleUpdate = async () => {\n if (!kbId || !formData.name.trim()) {\n toast.error('Name is required');\n return;\n }\n try {\n // `UpdateKnowledgeBaseInput` does not accept the embedding model or its\n // dimensions — changing those would invalidate every stored vector.\n await updateKB({\n kbId,\n input: {\n name: formData.name.trim(),\n description: formData.description.trim() || null,\n chunkingStrategy: formData.chunkingStrategy,\n chunkSize: formData.chunkSize,\n chunkOverlap: formData.chunkOverlap,\n },\n });\n await refetch();\n toast.success('Knowledge base updated');\n setIsEditing(false);\n } catch (err) {\n toast.error(err instanceof Error ? err.message : 'Failed to update knowledge base');\n }\n };\n\n const handleRebuild = async () => {\n if (!kbId) return;\n try {\n await rebuildIndex(kbId);\n await refetch();\n toast.success('Re-indexing started');\n } catch (err) {\n toast.error(err instanceof Error ? err.message : 'Failed to rebuild the index');\n }\n };\n\n const renderConfigFields = (includeEmbedding: boolean) => (\n <>\n <FormField label=\"Name\" required>\n <Input\n placeholder=\"e.g., Product Documentation\"\n value={formData.name}\n onChange={(e) => setFormData({ ...formData, name: e.target.value })}\n />\n </FormField>\n\n <FormField label=\"Description\">\n <Textarea\n placeholder=\"What kind of knowledge will this store?\"\n value={formData.description}\n onChange={(e) => setFormData({ ...formData, description: e.target.value })}\n rows={3}\n />\n </FormField>\n\n {includeEmbedding && (\n <FormField label=\"Embedding Model\" required>\n <select\n className=\"flex h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm\"\n value={formData.embeddingModel}\n onChange={(e) => {\n const model = EMBEDDING_MODELS.find((entry) => entry.id === e.target.value);\n setFormData({\n ...formData,\n embeddingModel: e.target.value,\n embeddingDimensions: model?.dimensions ?? formData.embeddingDimensions,\n });\n }}\n >\n {EMBEDDING_MODELS.map((model) => (\n <option key={model.id} value={model.id}>\n {model.label} ({model.dimensions}d)\n </option>\n ))}\n </select>\n </FormField>\n )}\n\n <div className=\"grid grid-cols-3 gap-4\">\n <FormField label=\"Chunking Strategy\">\n <select\n className=\"flex h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm\"\n value={formData.chunkingStrategy}\n onChange={(e) => setFormData({ ...formData, chunkingStrategy: e.target.value })}\n >\n {CHUNKING_STRATEGIES.map((strategy) => (\n <option key={strategy} value={strategy}>\n {strategy}\n </option>\n ))}\n </select>\n </FormField>\n\n <FormField label=\"Chunk Size\">\n <Input\n type=\"number\"\n value={formData.chunkSize}\n onChange={(e) =>\n setFormData({\n ...formData,\n chunkSize: toNumber(e.target.value, DEFAULT_FORM.chunkSize),\n })\n }\n />\n </FormField>\n\n <FormField label=\"Chunk Overlap\">\n <Input\n type=\"number\"\n value={formData.chunkOverlap}\n onChange={(e) =>\n setFormData({\n ...formData,\n chunkOverlap: toNumber(e.target.value, DEFAULT_FORM.chunkOverlap),\n })\n }\n />\n </FormField>\n </div>\n </>\n );\n\n if (isNew) {\n return (\n <PageLayout\n title=\"Create Knowledge Base\"\n description=\"Set up an embedding index your agents can retrieve from\"\n actions={\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" onClick={() => navigateTo('knowledge')}>\n Cancel\n </Button>\n <Button\n leftIcon={<Save size={16} />}\n onClick={handleCreate}\n isLoading={createKBResult.loading}\n disabled={!formData.name.trim()}\n >\n Create\n </Button>\n </div>\n }\n >\n <div className=\"max-w-3xl space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Index Configuration</CardTitle>\n <CardDescription>\n The embedding model and its vector width are fixed once documents are indexed.\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">{renderConfigFields(true)}</CardContent>\n </Card>\n </div>\n </PageLayout>\n );\n }\n\n if (loading) {\n return (\n <PageLayout title=\"Loading...\">\n <div className=\"h-96 bg-bg-sunken motion-safe:animate-vc-shimmer rounded-lg\" />\n </PageLayout>\n );\n }\n\n if (!kb) {\n return (\n <PageLayout title=\"Knowledge Base Not Found\">\n <EmptyState\n illustration=\"empty-files\"\n title=\"Knowledge Base Not Found\"\n description=\"This knowledge base doesn't exist.\"\n action={<Button onClick={() => navigateTo('knowledge')}>Back to Knowledge Bases</Button>}\n />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title={\n <div className=\"flex items-center gap-3\">\n <Button variant=\"ghost\" size=\"icon\" onClick={() => navigateTo('knowledge')}>\n <ArrowLeft size={20} />\n </Button>\n <div>\n <div className=\"flex items-center gap-2\">\n <span>{kb.name}</span>\n <StatusBadge status={kb.indexStatus} />\n </div>\n {kb.description && (\n <p className=\"text-sm font-normal text-text-secondary\">{kb.description}</p>\n )}\n </div>\n </div>\n }\n actions={\n <div className=\"flex gap-2\">\n <Button\n variant=\"outline\"\n leftIcon={<RefreshCw size={16} />}\n onClick={handleRebuild}\n isLoading={rebuildResult.loading}\n >\n Rebuild Index\n </Button>\n {isEditing ? (\n <>\n <Button\n variant=\"outline\"\n onClick={() => {\n setFormData(toForm(kb));\n setIsEditing(false);\n }}\n >\n Cancel\n </Button>\n <Button\n leftIcon={<Save size={16} />}\n onClick={handleUpdate}\n isLoading={updateKBResult.loading}\n >\n Save\n </Button>\n </>\n ) : (\n <Button leftIcon={<Settings size={16} />} onClick={() => setIsEditing(true)}>\n Configure\n </Button>\n )}\n </div>\n }\n >\n <div className=\"space-y-6\">\n <PagePurpose icon={<Database size={18} />}>\n This knowledge base is one searchable library your agents can draw on. Review how its\n documents are chunked and indexed, rebuild the index when content changes, and confirm it\n is healthy — so agents retrieve accurate, up-to-date passages and can cite them in their\n answers.\n </PagePurpose>\n\n {/* Index counters */}\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n <GlassCard treatment=\"glass\" glow className=\"p-6 text-center\">\n <div className=\"p-3 rounded-full bg-action-primary-bg/10 w-fit mx-auto mb-2\">\n <Database className=\"h-5 w-5 text-primary\" />\n </div>\n <p className=\"text-lg font-bold text-text-primary capitalize\">\n {kb.indexStatus.toLowerCase()}\n </p>\n <p className=\"text-sm text-text-secondary\">Index Status</p>\n </GlassCard>\n <Card>\n <CardContent className=\"pt-6 text-center\">\n <p className=\"text-3xl font-bold text-text-primary\">{kb.totalDocuments}</p>\n <p className=\"text-sm text-text-secondary\">Documents</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-6 text-center\">\n <p className=\"text-3xl font-bold text-text-primary\">{kb.totalChunks}</p>\n <p className=\"text-sm text-text-secondary\">Chunks</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-6 text-center\">\n <p className=\"text-3xl font-bold text-text-primary\">{kb.vectorCount}</p>\n <p className=\"text-sm text-text-secondary\">Vectors</p>\n </CardContent>\n </Card>\n </div>\n\n {/* Index configuration */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Layers size={18} />\n Index Configuration\n </CardTitle>\n <CardDescription>\n How documents are embedded and split before they are indexed\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {isEditing ? (\n renderConfigFields(false)\n ) : (\n <div className=\"grid grid-cols-2 md:grid-cols-3 gap-6\">\n <div>\n <p className=\"text-sm text-text-secondary\">Embedding Model</p>\n <p className=\"font-medium font-mono text-sm\">{kb.embeddingModel}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Dimensions</p>\n <p className=\"font-medium\">{kb.embeddingDimensions}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Chunking Strategy</p>\n <p className=\"font-medium\">{kb.chunkingStrategy}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Chunk Size</p>\n <p className=\"font-medium\">{kb.chunkSize}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Chunk Overlap</p>\n <p className=\"font-medium\">{kb.chunkOverlap}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Last Indexed</p>\n <p className=\"font-medium\">\n {kb.lastIndexedAt ? formatRelativeTime(kb.lastIndexedAt) : 'Never'}\n </p>\n </div>\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* Documents */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <FileText size={18} />\n Documents ({documents.length})\n </CardTitle>\n <CardDescription>Source documents indexed into this knowledge base</CardDescription>\n </CardHeader>\n <CardContent className=\"p-0\">\n {documents.length > 0 ? (\n <div className=\"divide-y divide-border-subtle\">\n {documents.map((document) => (\n <div key={document.id} className=\"flex items-center justify-between gap-4 p-4\">\n <div className=\"min-w-0\">\n <p className=\"font-medium truncate\">{document.title}</p>\n <p className=\"text-sm text-text-secondary\">\n {document.chunkCount} chunks · added {formatRelativeTime(document.addedAt)}\n </p>\n {document.processingError && (\n <p className=\"mt-1 text-sm text-status-error-text\">\n {document.processingError}\n </p>\n )}\n </div>\n <StatusBadge status={document.status} />\n </div>\n ))}\n </div>\n ) : (\n <div className=\"p-8 text-center text-text-secondary\">\n <Boxes className=\"h-8 w-8 mx-auto mb-2 opacity-50\" />\n <p>No documents indexed yet</p>\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* Metadata */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Settings size={18} />\n Metadata\n </CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"grid grid-cols-2 md:grid-cols-3 gap-6 text-sm\">\n <div>\n <p className=\"text-text-secondary\">Created</p>\n <p className=\"font-medium\">{formatRelativeTime(kb.createdAt)}</p>\n </div>\n <div>\n <p className=\"text-text-secondary\">Updated</p>\n <p className=\"font-medium\">{formatRelativeTime(kb.updatedAt)}</p>\n </div>\n <div>\n <p className=\"text-text-secondary\">Last Indexed</p>\n <p className=\"font-medium\">\n {kb.lastIndexedAt ? formatRelativeTime(kb.lastIndexedAt) : MISSING_VALUE}\n </p>\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|