crca 1.4.0__py3-none-any.whl
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.
- .github/ISSUE_TEMPLATE/bug_report.md +65 -0
- .github/ISSUE_TEMPLATE/feature_request.md +41 -0
- .github/PULL_REQUEST_TEMPLATE.md +20 -0
- .github/workflows/publish-manual.yml +61 -0
- .github/workflows/publish.yml +64 -0
- .gitignore +214 -0
- CRCA.py +4156 -0
- LICENSE +201 -0
- MANIFEST.in +43 -0
- PKG-INFO +5035 -0
- README.md +4959 -0
- __init__.py +17 -0
- branches/CRCA-Q.py +2728 -0
- branches/crca_cg/corposwarm.py +9065 -0
- branches/crca_cg/fix_rancher_docker_creds.ps1 +155 -0
- branches/crca_cg/package.json +5 -0
- branches/crca_cg/test_bolt_integration.py +446 -0
- branches/crca_cg/test_corposwarm_comprehensive.py +773 -0
- branches/crca_cg/test_new_features.py +163 -0
- branches/crca_sd/__init__.py +149 -0
- branches/crca_sd/crca_sd_core.py +770 -0
- branches/crca_sd/crca_sd_governance.py +1325 -0
- branches/crca_sd/crca_sd_mpc.py +1130 -0
- branches/crca_sd/crca_sd_realtime.py +1844 -0
- branches/crca_sd/crca_sd_tui.py +1133 -0
- crca-1.4.0.dist-info/METADATA +5035 -0
- crca-1.4.0.dist-info/RECORD +501 -0
- crca-1.4.0.dist-info/WHEEL +4 -0
- crca-1.4.0.dist-info/licenses/LICENSE +201 -0
- docs/CRCA-Q.md +2333 -0
- examples/config.yaml.example +25 -0
- examples/crca_sd_example.py +513 -0
- examples/data_broker_example.py +294 -0
- examples/logistics_corporation.py +861 -0
- examples/palantir_example.py +299 -0
- examples/policy_bench.py +934 -0
- examples/pridnestrovia-sd.py +705 -0
- examples/pridnestrovia_realtime.py +1902 -0
- prompts/__init__.py +10 -0
- prompts/default_crca.py +101 -0
- pyproject.toml +151 -0
- requirements.txt +76 -0
- schemas/__init__.py +43 -0
- schemas/mcpSchemas.py +51 -0
- schemas/policy.py +458 -0
- templates/__init__.py +38 -0
- templates/base_specialized_agent.py +195 -0
- templates/drift_detection.py +325 -0
- templates/examples/causal_agent_template.py +309 -0
- templates/examples/drag_drop_example.py +213 -0
- templates/examples/logistics_agent_template.py +207 -0
- templates/examples/trading_agent_template.py +206 -0
- templates/feature_mixins.py +253 -0
- templates/graph_management.py +442 -0
- templates/llm_integration.py +194 -0
- templates/module_registry.py +276 -0
- templates/mpc_planner.py +280 -0
- templates/policy_loop.py +1168 -0
- templates/prediction_framework.py +448 -0
- templates/statistical_methods.py +778 -0
- tests/sanity.yml +31 -0
- tests/sanity_check +406 -0
- tests/test_core.py +47 -0
- tests/test_crca_excel.py +166 -0
- tests/test_crca_sd.py +780 -0
- tests/test_data_broker.py +424 -0
- tests/test_palantir.py +349 -0
- tools/__init__.py +38 -0
- tools/actuators.py +437 -0
- tools/bolt.diy/Dockerfile +103 -0
- tools/bolt.diy/app/components/@settings/core/AvatarDropdown.tsx +175 -0
- tools/bolt.diy/app/components/@settings/core/ControlPanel.tsx +345 -0
- tools/bolt.diy/app/components/@settings/core/constants.tsx +108 -0
- tools/bolt.diy/app/components/@settings/core/types.ts +114 -0
- tools/bolt.diy/app/components/@settings/index.ts +12 -0
- tools/bolt.diy/app/components/@settings/shared/components/TabTile.tsx +151 -0
- tools/bolt.diy/app/components/@settings/shared/service-integration/ConnectionForm.tsx +193 -0
- tools/bolt.diy/app/components/@settings/shared/service-integration/ConnectionTestIndicator.tsx +60 -0
- tools/bolt.diy/app/components/@settings/shared/service-integration/ErrorState.tsx +102 -0
- tools/bolt.diy/app/components/@settings/shared/service-integration/LoadingState.tsx +94 -0
- tools/bolt.diy/app/components/@settings/shared/service-integration/ServiceHeader.tsx +72 -0
- tools/bolt.diy/app/components/@settings/shared/service-integration/index.ts +6 -0
- tools/bolt.diy/app/components/@settings/tabs/data/DataTab.tsx +721 -0
- tools/bolt.diy/app/components/@settings/tabs/data/DataVisualization.tsx +384 -0
- tools/bolt.diy/app/components/@settings/tabs/event-logs/EventLogsTab.tsx +1013 -0
- tools/bolt.diy/app/components/@settings/tabs/features/FeaturesTab.tsx +295 -0
- tools/bolt.diy/app/components/@settings/tabs/github/GitHubTab.tsx +281 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubAuthDialog.tsx +173 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubCacheManager.tsx +367 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubConnection.tsx +233 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubErrorBoundary.tsx +105 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubProgressiveLoader.tsx +266 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubRepositoryCard.tsx +121 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubRepositorySelector.tsx +312 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubStats.tsx +291 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubUserProfile.tsx +46 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/shared/GitHubStateIndicators.tsx +264 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/shared/RepositoryCard.tsx +361 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/shared/index.ts +11 -0
- tools/bolt.diy/app/components/@settings/tabs/gitlab/GitLabTab.tsx +305 -0
- tools/bolt.diy/app/components/@settings/tabs/gitlab/components/GitLabAuthDialog.tsx +186 -0
- tools/bolt.diy/app/components/@settings/tabs/gitlab/components/GitLabConnection.tsx +253 -0
- tools/bolt.diy/app/components/@settings/tabs/gitlab/components/GitLabRepositorySelector.tsx +358 -0
- tools/bolt.diy/app/components/@settings/tabs/gitlab/components/RepositoryCard.tsx +79 -0
- tools/bolt.diy/app/components/@settings/tabs/gitlab/components/RepositoryList.tsx +142 -0
- tools/bolt.diy/app/components/@settings/tabs/gitlab/components/StatsDisplay.tsx +91 -0
- tools/bolt.diy/app/components/@settings/tabs/gitlab/components/index.ts +4 -0
- tools/bolt.diy/app/components/@settings/tabs/mcp/McpServerList.tsx +99 -0
- tools/bolt.diy/app/components/@settings/tabs/mcp/McpServerListItem.tsx +70 -0
- tools/bolt.diy/app/components/@settings/tabs/mcp/McpStatusBadge.tsx +37 -0
- tools/bolt.diy/app/components/@settings/tabs/mcp/McpTab.tsx +239 -0
- tools/bolt.diy/app/components/@settings/tabs/netlify/NetlifyTab.tsx +1393 -0
- tools/bolt.diy/app/components/@settings/tabs/netlify/components/NetlifyConnection.tsx +990 -0
- tools/bolt.diy/app/components/@settings/tabs/netlify/components/index.ts +1 -0
- tools/bolt.diy/app/components/@settings/tabs/notifications/NotificationsTab.tsx +300 -0
- tools/bolt.diy/app/components/@settings/tabs/profile/ProfileTab.tsx +181 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/cloud/CloudProvidersTab.tsx +308 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/local/ErrorBoundary.tsx +68 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/local/HealthStatusBadge.tsx +64 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/local/LoadingSkeleton.tsx +107 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/local/LocalProvidersTab.tsx +556 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/local/ModelCard.tsx +106 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/local/ProviderCard.tsx +120 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/local/SetupGuide.tsx +671 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/local/StatusDashboard.tsx +91 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/local/types.ts +44 -0
- tools/bolt.diy/app/components/@settings/tabs/settings/SettingsTab.tsx +215 -0
- tools/bolt.diy/app/components/@settings/tabs/supabase/SupabaseTab.tsx +1089 -0
- tools/bolt.diy/app/components/@settings/tabs/vercel/VercelTab.tsx +909 -0
- tools/bolt.diy/app/components/@settings/tabs/vercel/components/VercelConnection.tsx +368 -0
- tools/bolt.diy/app/components/@settings/tabs/vercel/components/index.ts +1 -0
- tools/bolt.diy/app/components/@settings/utils/tab-helpers.ts +54 -0
- tools/bolt.diy/app/components/chat/APIKeyManager.tsx +169 -0
- tools/bolt.diy/app/components/chat/Artifact.tsx +296 -0
- tools/bolt.diy/app/components/chat/AssistantMessage.tsx +192 -0
- tools/bolt.diy/app/components/chat/BaseChat.module.scss +47 -0
- tools/bolt.diy/app/components/chat/BaseChat.tsx +522 -0
- tools/bolt.diy/app/components/chat/Chat.client.tsx +670 -0
- tools/bolt.diy/app/components/chat/ChatAlert.tsx +108 -0
- tools/bolt.diy/app/components/chat/ChatBox.tsx +334 -0
- tools/bolt.diy/app/components/chat/CodeBlock.module.scss +10 -0
- tools/bolt.diy/app/components/chat/CodeBlock.tsx +85 -0
- tools/bolt.diy/app/components/chat/DicussMode.tsx +17 -0
- tools/bolt.diy/app/components/chat/ExamplePrompts.tsx +37 -0
- tools/bolt.diy/app/components/chat/FilePreview.tsx +38 -0
- tools/bolt.diy/app/components/chat/GitCloneButton.tsx +327 -0
- tools/bolt.diy/app/components/chat/ImportFolderButton.tsx +141 -0
- tools/bolt.diy/app/components/chat/LLMApiAlert.tsx +109 -0
- tools/bolt.diy/app/components/chat/MCPTools.tsx +129 -0
- tools/bolt.diy/app/components/chat/Markdown.module.scss +171 -0
- tools/bolt.diy/app/components/chat/Markdown.spec.ts +48 -0
- tools/bolt.diy/app/components/chat/Markdown.tsx +252 -0
- tools/bolt.diy/app/components/chat/Messages.client.tsx +102 -0
- tools/bolt.diy/app/components/chat/ModelSelector.tsx +797 -0
- tools/bolt.diy/app/components/chat/NetlifyDeploymentLink.client.tsx +51 -0
- tools/bolt.diy/app/components/chat/ProgressCompilation.tsx +110 -0
- tools/bolt.diy/app/components/chat/ScreenshotStateManager.tsx +33 -0
- tools/bolt.diy/app/components/chat/SendButton.client.tsx +39 -0
- tools/bolt.diy/app/components/chat/SpeechRecognition.tsx +28 -0
- tools/bolt.diy/app/components/chat/StarterTemplates.tsx +38 -0
- tools/bolt.diy/app/components/chat/SupabaseAlert.tsx +199 -0
- tools/bolt.diy/app/components/chat/SupabaseConnection.tsx +339 -0
- tools/bolt.diy/app/components/chat/ThoughtBox.tsx +43 -0
- tools/bolt.diy/app/components/chat/ToolInvocations.tsx +409 -0
- tools/bolt.diy/app/components/chat/UserMessage.tsx +101 -0
- tools/bolt.diy/app/components/chat/VercelDeploymentLink.client.tsx +158 -0
- tools/bolt.diy/app/components/chat/chatExportAndImport/ExportChatButton.tsx +49 -0
- tools/bolt.diy/app/components/chat/chatExportAndImport/ImportButtons.tsx +96 -0
- tools/bolt.diy/app/components/deploy/DeployAlert.tsx +197 -0
- tools/bolt.diy/app/components/deploy/DeployButton.tsx +277 -0
- tools/bolt.diy/app/components/deploy/GitHubDeploy.client.tsx +171 -0
- tools/bolt.diy/app/components/deploy/GitHubDeploymentDialog.tsx +1041 -0
- tools/bolt.diy/app/components/deploy/GitLabDeploy.client.tsx +171 -0
- tools/bolt.diy/app/components/deploy/GitLabDeploymentDialog.tsx +764 -0
- tools/bolt.diy/app/components/deploy/NetlifyDeploy.client.tsx +246 -0
- tools/bolt.diy/app/components/deploy/VercelDeploy.client.tsx +235 -0
- tools/bolt.diy/app/components/editor/codemirror/BinaryContent.tsx +7 -0
- tools/bolt.diy/app/components/editor/codemirror/CodeMirrorEditor.tsx +555 -0
- tools/bolt.diy/app/components/editor/codemirror/EnvMasking.ts +80 -0
- tools/bolt.diy/app/components/editor/codemirror/cm-theme.ts +192 -0
- tools/bolt.diy/app/components/editor/codemirror/indent.ts +68 -0
- tools/bolt.diy/app/components/editor/codemirror/languages.ts +112 -0
- tools/bolt.diy/app/components/git/GitUrlImport.client.tsx +147 -0
- tools/bolt.diy/app/components/header/Header.tsx +42 -0
- tools/bolt.diy/app/components/header/HeaderActionButtons.client.tsx +54 -0
- tools/bolt.diy/app/components/mandate/MandateSubmission.tsx +167 -0
- tools/bolt.diy/app/components/observability/DeploymentStatus.tsx +168 -0
- tools/bolt.diy/app/components/observability/EventTimeline.tsx +119 -0
- tools/bolt.diy/app/components/observability/FileDiffViewer.tsx +121 -0
- tools/bolt.diy/app/components/observability/GovernanceStatus.tsx +197 -0
- tools/bolt.diy/app/components/observability/GovernorMetrics.tsx +246 -0
- tools/bolt.diy/app/components/observability/LogStream.tsx +244 -0
- tools/bolt.diy/app/components/observability/MandateDetails.tsx +201 -0
- tools/bolt.diy/app/components/observability/ObservabilityDashboard.tsx +200 -0
- tools/bolt.diy/app/components/sidebar/HistoryItem.tsx +187 -0
- tools/bolt.diy/app/components/sidebar/Menu.client.tsx +536 -0
- tools/bolt.diy/app/components/sidebar/date-binning.ts +59 -0
- tools/bolt.diy/app/components/txt +1 -0
- tools/bolt.diy/app/components/ui/BackgroundRays/index.tsx +18 -0
- tools/bolt.diy/app/components/ui/BackgroundRays/styles.module.scss +246 -0
- tools/bolt.diy/app/components/ui/Badge.tsx +53 -0
- tools/bolt.diy/app/components/ui/BranchSelector.tsx +270 -0
- tools/bolt.diy/app/components/ui/Breadcrumbs.tsx +101 -0
- tools/bolt.diy/app/components/ui/Button.tsx +46 -0
- tools/bolt.diy/app/components/ui/Card.tsx +55 -0
- tools/bolt.diy/app/components/ui/Checkbox.tsx +32 -0
- tools/bolt.diy/app/components/ui/CloseButton.tsx +49 -0
- tools/bolt.diy/app/components/ui/CodeBlock.tsx +103 -0
- tools/bolt.diy/app/components/ui/Collapsible.tsx +9 -0
- tools/bolt.diy/app/components/ui/ColorSchemeDialog.tsx +378 -0
- tools/bolt.diy/app/components/ui/Dialog.tsx +449 -0
- tools/bolt.diy/app/components/ui/Dropdown.tsx +63 -0
- tools/bolt.diy/app/components/ui/EmptyState.tsx +154 -0
- tools/bolt.diy/app/components/ui/FileIcon.tsx +346 -0
- tools/bolt.diy/app/components/ui/FilterChip.tsx +92 -0
- tools/bolt.diy/app/components/ui/GlowingEffect.tsx +192 -0
- tools/bolt.diy/app/components/ui/GradientCard.tsx +100 -0
- tools/bolt.diy/app/components/ui/IconButton.tsx +84 -0
- tools/bolt.diy/app/components/ui/Input.tsx +22 -0
- tools/bolt.diy/app/components/ui/Label.tsx +20 -0
- tools/bolt.diy/app/components/ui/LoadingDots.tsx +27 -0
- tools/bolt.diy/app/components/ui/LoadingOverlay.tsx +32 -0
- tools/bolt.diy/app/components/ui/PanelHeader.tsx +20 -0
- tools/bolt.diy/app/components/ui/PanelHeaderButton.tsx +36 -0
- tools/bolt.diy/app/components/ui/Popover.tsx +29 -0
- tools/bolt.diy/app/components/ui/Progress.tsx +22 -0
- tools/bolt.diy/app/components/ui/RepositoryStats.tsx +87 -0
- tools/bolt.diy/app/components/ui/ScrollArea.tsx +41 -0
- tools/bolt.diy/app/components/ui/SearchInput.tsx +80 -0
- tools/bolt.diy/app/components/ui/SearchResultItem.tsx +134 -0
- tools/bolt.diy/app/components/ui/Separator.tsx +22 -0
- tools/bolt.diy/app/components/ui/SettingsButton.tsx +35 -0
- tools/bolt.diy/app/components/ui/Slider.tsx +73 -0
- tools/bolt.diy/app/components/ui/StatusIndicator.tsx +90 -0
- tools/bolt.diy/app/components/ui/Switch.tsx +37 -0
- tools/bolt.diy/app/components/ui/Tabs.tsx +52 -0
- tools/bolt.diy/app/components/ui/TabsWithSlider.tsx +112 -0
- tools/bolt.diy/app/components/ui/ThemeSwitch.tsx +29 -0
- tools/bolt.diy/app/components/ui/Tooltip.tsx +122 -0
- tools/bolt.diy/app/components/ui/index.ts +38 -0
- tools/bolt.diy/app/components/ui/use-toast.ts +66 -0
- tools/bolt.diy/app/components/workbench/DiffView.tsx +796 -0
- tools/bolt.diy/app/components/workbench/EditorPanel.tsx +174 -0
- tools/bolt.diy/app/components/workbench/ExpoQrModal.tsx +55 -0
- tools/bolt.diy/app/components/workbench/FileBreadcrumb.tsx +150 -0
- tools/bolt.diy/app/components/workbench/FileTree.tsx +565 -0
- tools/bolt.diy/app/components/workbench/Inspector.tsx +126 -0
- tools/bolt.diy/app/components/workbench/InspectorPanel.tsx +146 -0
- tools/bolt.diy/app/components/workbench/LockManager.tsx +262 -0
- tools/bolt.diy/app/components/workbench/PortDropdown.tsx +91 -0
- tools/bolt.diy/app/components/workbench/Preview.tsx +1049 -0
- tools/bolt.diy/app/components/workbench/ScreenshotSelector.tsx +293 -0
- tools/bolt.diy/app/components/workbench/Search.tsx +257 -0
- tools/bolt.diy/app/components/workbench/Workbench.client.tsx +506 -0
- tools/bolt.diy/app/components/workbench/terminal/Terminal.tsx +131 -0
- tools/bolt.diy/app/components/workbench/terminal/TerminalManager.tsx +68 -0
- tools/bolt.diy/app/components/workbench/terminal/TerminalTabs.tsx +277 -0
- tools/bolt.diy/app/components/workbench/terminal/theme.ts +36 -0
- tools/bolt.diy/app/components/workflow/WorkflowPhase.tsx +109 -0
- tools/bolt.diy/app/components/workflow/WorkflowStatus.tsx +60 -0
- tools/bolt.diy/app/components/workflow/WorkflowTimeline.tsx +150 -0
- tools/bolt.diy/app/entry.client.tsx +7 -0
- tools/bolt.diy/app/entry.server.tsx +80 -0
- tools/bolt.diy/app/root.tsx +156 -0
- tools/bolt.diy/app/routes/_index.tsx +175 -0
- tools/bolt.diy/app/routes/api.bug-report.ts +254 -0
- tools/bolt.diy/app/routes/api.chat.ts +463 -0
- tools/bolt.diy/app/routes/api.check-env-key.ts +41 -0
- tools/bolt.diy/app/routes/api.configured-providers.ts +110 -0
- tools/bolt.diy/app/routes/api.corporate-swarm-status.ts +55 -0
- tools/bolt.diy/app/routes/api.enhancer.ts +137 -0
- tools/bolt.diy/app/routes/api.export-api-keys.ts +44 -0
- tools/bolt.diy/app/routes/api.git-info.ts +69 -0
- tools/bolt.diy/app/routes/api.git-proxy.$.ts +178 -0
- tools/bolt.diy/app/routes/api.github-branches.ts +166 -0
- tools/bolt.diy/app/routes/api.github-deploy.ts +67 -0
- tools/bolt.diy/app/routes/api.github-stats.ts +198 -0
- tools/bolt.diy/app/routes/api.github-template.ts +242 -0
- tools/bolt.diy/app/routes/api.github-user.ts +287 -0
- tools/bolt.diy/app/routes/api.gitlab-branches.ts +143 -0
- tools/bolt.diy/app/routes/api.gitlab-deploy.ts +67 -0
- tools/bolt.diy/app/routes/api.gitlab-projects.ts +105 -0
- tools/bolt.diy/app/routes/api.health.ts +8 -0
- tools/bolt.diy/app/routes/api.llmcall.ts +298 -0
- tools/bolt.diy/app/routes/api.mandate.ts +351 -0
- tools/bolt.diy/app/routes/api.mcp-check.ts +16 -0
- tools/bolt.diy/app/routes/api.mcp-update-config.ts +23 -0
- tools/bolt.diy/app/routes/api.models.$provider.ts +2 -0
- tools/bolt.diy/app/routes/api.models.ts +90 -0
- tools/bolt.diy/app/routes/api.netlify-deploy.ts +240 -0
- tools/bolt.diy/app/routes/api.netlify-user.ts +142 -0
- tools/bolt.diy/app/routes/api.supabase-user.ts +199 -0
- tools/bolt.diy/app/routes/api.supabase.query.ts +92 -0
- tools/bolt.diy/app/routes/api.supabase.ts +56 -0
- tools/bolt.diy/app/routes/api.supabase.variables.ts +32 -0
- tools/bolt.diy/app/routes/api.system.diagnostics.ts +142 -0
- tools/bolt.diy/app/routes/api.system.disk-info.ts +311 -0
- tools/bolt.diy/app/routes/api.system.git-info.ts +332 -0
- tools/bolt.diy/app/routes/api.update.ts +21 -0
- tools/bolt.diy/app/routes/api.vercel-deploy.ts +497 -0
- tools/bolt.diy/app/routes/api.vercel-user.ts +161 -0
- tools/bolt.diy/app/routes/api.workflow-status.$proposalId.ts +309 -0
- tools/bolt.diy/app/routes/chat.$id.tsx +8 -0
- tools/bolt.diy/app/routes/execute.$mandateId.tsx +432 -0
- tools/bolt.diy/app/routes/git.tsx +25 -0
- tools/bolt.diy/app/routes/observability.$mandateId.tsx +50 -0
- tools/bolt.diy/app/routes/webcontainer.connect.$id.tsx +32 -0
- tools/bolt.diy/app/routes/webcontainer.preview.$id.tsx +97 -0
- tools/bolt.diy/app/routes/workflow.$proposalId.tsx +170 -0
- tools/bolt.diy/app/styles/animations.scss +49 -0
- tools/bolt.diy/app/styles/components/code.scss +9 -0
- tools/bolt.diy/app/styles/components/editor.scss +135 -0
- tools/bolt.diy/app/styles/components/resize-handle.scss +30 -0
- tools/bolt.diy/app/styles/components/terminal.scss +3 -0
- tools/bolt.diy/app/styles/components/toast.scss +23 -0
- tools/bolt.diy/app/styles/diff-view.css +72 -0
- tools/bolt.diy/app/styles/index.scss +73 -0
- tools/bolt.diy/app/styles/variables.scss +255 -0
- tools/bolt.diy/app/styles/z-index.scss +37 -0
- tools/bolt.diy/app/types/GitHub.ts +182 -0
- tools/bolt.diy/app/types/GitLab.ts +103 -0
- tools/bolt.diy/app/types/actions.ts +85 -0
- tools/bolt.diy/app/types/artifact.ts +5 -0
- tools/bolt.diy/app/types/context.ts +26 -0
- tools/bolt.diy/app/types/design-scheme.ts +93 -0
- tools/bolt.diy/app/types/global.d.ts +13 -0
- tools/bolt.diy/app/types/mandate.ts +333 -0
- tools/bolt.diy/app/types/model.ts +25 -0
- tools/bolt.diy/app/types/netlify.ts +94 -0
- tools/bolt.diy/app/types/supabase.ts +54 -0
- tools/bolt.diy/app/types/template.ts +8 -0
- tools/bolt.diy/app/types/terminal.ts +9 -0
- tools/bolt.diy/app/types/theme.ts +1 -0
- tools/bolt.diy/app/types/vercel.ts +67 -0
- tools/bolt.diy/app/utils/buffer.ts +29 -0
- tools/bolt.diy/app/utils/classNames.ts +65 -0
- tools/bolt.diy/app/utils/constants.ts +147 -0
- tools/bolt.diy/app/utils/debounce.ts +13 -0
- tools/bolt.diy/app/utils/debugLogger.ts +1284 -0
- tools/bolt.diy/app/utils/diff.spec.ts +11 -0
- tools/bolt.diy/app/utils/diff.ts +117 -0
- tools/bolt.diy/app/utils/easings.ts +3 -0
- tools/bolt.diy/app/utils/fileLocks.ts +96 -0
- tools/bolt.diy/app/utils/fileUtils.ts +121 -0
- tools/bolt.diy/app/utils/folderImport.ts +73 -0
- tools/bolt.diy/app/utils/formatSize.ts +12 -0
- tools/bolt.diy/app/utils/getLanguageFromExtension.ts +24 -0
- tools/bolt.diy/app/utils/githubStats.ts +9 -0
- tools/bolt.diy/app/utils/gitlabStats.ts +54 -0
- tools/bolt.diy/app/utils/logger.ts +162 -0
- tools/bolt.diy/app/utils/markdown.ts +155 -0
- tools/bolt.diy/app/utils/mobile.ts +4 -0
- tools/bolt.diy/app/utils/os.ts +4 -0
- tools/bolt.diy/app/utils/path.ts +19 -0
- tools/bolt.diy/app/utils/projectCommands.ts +197 -0
- tools/bolt.diy/app/utils/promises.ts +19 -0
- tools/bolt.diy/app/utils/react.ts +6 -0
- tools/bolt.diy/app/utils/sampler.ts +49 -0
- tools/bolt.diy/app/utils/selectStarterTemplate.ts +255 -0
- tools/bolt.diy/app/utils/shell.ts +384 -0
- tools/bolt.diy/app/utils/stacktrace.ts +27 -0
- tools/bolt.diy/app/utils/stripIndent.ts +23 -0
- tools/bolt.diy/app/utils/terminal.ts +11 -0
- tools/bolt.diy/app/utils/unreachable.ts +3 -0
- tools/bolt.diy/app/vite-env.d.ts +2 -0
- tools/bolt.diy/assets/entitlements.mac.plist +25 -0
- tools/bolt.diy/assets/icons/icon.icns +0 -0
- tools/bolt.diy/assets/icons/icon.ico +0 -0
- tools/bolt.diy/assets/icons/icon.png +0 -0
- tools/bolt.diy/bindings.js +78 -0
- tools/bolt.diy/bindings.sh +33 -0
- tools/bolt.diy/docker-compose.yaml +145 -0
- tools/bolt.diy/electron/main/index.ts +201 -0
- tools/bolt.diy/electron/main/tsconfig.json +30 -0
- tools/bolt.diy/electron/main/ui/menu.ts +29 -0
- tools/bolt.diy/electron/main/ui/window.ts +54 -0
- tools/bolt.diy/electron/main/utils/auto-update.ts +110 -0
- tools/bolt.diy/electron/main/utils/constants.ts +4 -0
- tools/bolt.diy/electron/main/utils/cookie.ts +40 -0
- tools/bolt.diy/electron/main/utils/reload.ts +35 -0
- tools/bolt.diy/electron/main/utils/serve.ts +71 -0
- tools/bolt.diy/electron/main/utils/store.ts +3 -0
- tools/bolt.diy/electron/main/utils/vite-server.ts +44 -0
- tools/bolt.diy/electron/main/vite.config.ts +44 -0
- tools/bolt.diy/electron/preload/index.ts +22 -0
- tools/bolt.diy/electron/preload/tsconfig.json +7 -0
- tools/bolt.diy/electron/preload/vite.config.ts +31 -0
- tools/bolt.diy/electron-builder.yml +64 -0
- tools/bolt.diy/electron-update.yml +4 -0
- tools/bolt.diy/eslint.config.mjs +57 -0
- tools/bolt.diy/functions/[[path]].ts +12 -0
- tools/bolt.diy/icons/angular.svg +1 -0
- tools/bolt.diy/icons/astro.svg +8 -0
- tools/bolt.diy/icons/chat.svg +1 -0
- tools/bolt.diy/icons/expo-brand.svg +1 -0
- tools/bolt.diy/icons/expo.svg +4 -0
- tools/bolt.diy/icons/logo-text.svg +1 -0
- tools/bolt.diy/icons/logo.svg +4 -0
- tools/bolt.diy/icons/mcp.svg +1 -0
- tools/bolt.diy/icons/nativescript.svg +1 -0
- tools/bolt.diy/icons/netlify.svg +10 -0
- tools/bolt.diy/icons/nextjs.svg +1 -0
- tools/bolt.diy/icons/nuxt.svg +1 -0
- tools/bolt.diy/icons/qwik.svg +1 -0
- tools/bolt.diy/icons/react.svg +1 -0
- tools/bolt.diy/icons/remix.svg +24 -0
- tools/bolt.diy/icons/remotion.svg +1 -0
- tools/bolt.diy/icons/shadcn.svg +21 -0
- tools/bolt.diy/icons/slidev.svg +60 -0
- tools/bolt.diy/icons/solidjs.svg +1 -0
- tools/bolt.diy/icons/stars.svg +1 -0
- tools/bolt.diy/icons/svelte.svg +1 -0
- tools/bolt.diy/icons/typescript.svg +1 -0
- tools/bolt.diy/icons/vite.svg +1 -0
- tools/bolt.diy/icons/vue.svg +1 -0
- tools/bolt.diy/load-context.ts +9 -0
- tools/bolt.diy/notarize.cjs +31 -0
- tools/bolt.diy/package.json +218 -0
- tools/bolt.diy/playwright.config.preview.ts +35 -0
- tools/bolt.diy/pre-start.cjs +26 -0
- tools/bolt.diy/public/apple-touch-icon-precomposed.png +0 -0
- tools/bolt.diy/public/apple-touch-icon.png +0 -0
- tools/bolt.diy/public/favicon.ico +0 -0
- tools/bolt.diy/public/favicon.svg +4 -0
- tools/bolt.diy/public/icons/AmazonBedrock.svg +1 -0
- tools/bolt.diy/public/icons/Anthropic.svg +4 -0
- tools/bolt.diy/public/icons/Cohere.svg +4 -0
- tools/bolt.diy/public/icons/Deepseek.svg +5 -0
- tools/bolt.diy/public/icons/Default.svg +4 -0
- tools/bolt.diy/public/icons/Google.svg +4 -0
- tools/bolt.diy/public/icons/Groq.svg +4 -0
- tools/bolt.diy/public/icons/HuggingFace.svg +4 -0
- tools/bolt.diy/public/icons/Hyperbolic.svg +3 -0
- tools/bolt.diy/public/icons/LMStudio.svg +5 -0
- tools/bolt.diy/public/icons/Mistral.svg +4 -0
- tools/bolt.diy/public/icons/Ollama.svg +4 -0
- tools/bolt.diy/public/icons/OpenAI.svg +4 -0
- tools/bolt.diy/public/icons/OpenAILike.svg +4 -0
- tools/bolt.diy/public/icons/OpenRouter.svg +4 -0
- tools/bolt.diy/public/icons/Perplexity.svg +4 -0
- tools/bolt.diy/public/icons/Together.svg +4 -0
- tools/bolt.diy/public/icons/xAI.svg +5 -0
- tools/bolt.diy/public/inspector-script.js +292 -0
- tools/bolt.diy/public/logo-dark-styled.png +0 -0
- tools/bolt.diy/public/logo-dark.png +0 -0
- tools/bolt.diy/public/logo-light-styled.png +0 -0
- tools/bolt.diy/public/logo-light.png +0 -0
- tools/bolt.diy/public/logo.svg +15 -0
- tools/bolt.diy/public/social_preview_index.jpg +0 -0
- tools/bolt.diy/scripts/clean.js +45 -0
- tools/bolt.diy/scripts/electron-dev.mjs +181 -0
- tools/bolt.diy/scripts/setup-env.sh +41 -0
- tools/bolt.diy/scripts/update-imports.sh +7 -0
- tools/bolt.diy/scripts/update.sh +52 -0
- tools/bolt.diy/services/execution-governor/Dockerfile +41 -0
- tools/bolt.diy/services/execution-governor/config.ts +42 -0
- tools/bolt.diy/services/execution-governor/index.ts +683 -0
- tools/bolt.diy/services/execution-governor/metrics.ts +141 -0
- tools/bolt.diy/services/execution-governor/package.json +31 -0
- tools/bolt.diy/services/execution-governor/priority-queue.ts +139 -0
- tools/bolt.diy/services/execution-governor/tsconfig.json +21 -0
- tools/bolt.diy/services/execution-governor/types.ts +145 -0
- tools/bolt.diy/services/headless-executor/Dockerfile +43 -0
- tools/bolt.diy/services/headless-executor/executor.ts +210 -0
- tools/bolt.diy/services/headless-executor/index.ts +323 -0
- tools/bolt.diy/services/headless-executor/package.json +27 -0
- tools/bolt.diy/services/headless-executor/tsconfig.json +21 -0
- tools/bolt.diy/services/headless-executor/types.ts +38 -0
- tools/bolt.diy/test-workflows.sh +240 -0
- tools/bolt.diy/tests/integration/corporate-swarm.test.ts +208 -0
- tools/bolt.diy/tests/mandates/budget-limited.json +34 -0
- tools/bolt.diy/tests/mandates/complex.json +53 -0
- tools/bolt.diy/tests/mandates/constraint-enforced.json +36 -0
- tools/bolt.diy/tests/mandates/simple.json +35 -0
- tools/bolt.diy/tsconfig.json +37 -0
- tools/bolt.diy/types/istextorbinary.d.ts +15 -0
- tools/bolt.diy/uno.config.ts +279 -0
- tools/bolt.diy/vite-electron.config.ts +76 -0
- tools/bolt.diy/vite.config.ts +112 -0
- tools/bolt.diy/worker-configuration.d.ts +22 -0
- tools/bolt.diy/wrangler.toml +6 -0
- tools/code_generator.py +461 -0
- tools/file_operations.py +465 -0
- tools/mandate_generator.py +337 -0
- tools/mcpClientUtils.py +1216 -0
- tools/sensors.py +285 -0
- utils/Agent_types.py +15 -0
- utils/AnyToStr.py +0 -0
- utils/HHCS.py +277 -0
- utils/__init__.py +30 -0
- utils/agent.py +3627 -0
- utils/aop.py +2948 -0
- utils/canonical.py +143 -0
- utils/conversation.py +1195 -0
- utils/doctrine_versioning +230 -0
- utils/formatter.py +474 -0
- utils/ledger.py +311 -0
- utils/out_types.py +16 -0
- utils/rollback.py +339 -0
- utils/router.py +929 -0
- utils/tui.py +1908 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import React, { useState, useEffect, useMemo } from 'react';
|
|
2
|
+
import { motion } from 'framer-motion';
|
|
3
|
+
import { Button } from '~/components/ui/Button';
|
|
4
|
+
import { BranchSelector } from '~/components/ui/BranchSelector';
|
|
5
|
+
import { RepositoryCard } from './RepositoryCard';
|
|
6
|
+
import type { GitLabProjectInfo } from '~/types/GitLab';
|
|
7
|
+
import { useGitLabConnection } from '~/lib/hooks';
|
|
8
|
+
import { classNames } from '~/utils/classNames';
|
|
9
|
+
import { Search, RefreshCw, GitBranch, Calendar, Filter } from 'lucide-react';
|
|
10
|
+
|
|
11
|
+
interface GitLabRepositorySelectorProps {
|
|
12
|
+
onClone?: (repoUrl: string, branch?: string) => void;
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type SortOption = 'updated' | 'stars' | 'name' | 'created';
|
|
17
|
+
type FilterOption = 'all' | 'owned' | 'member';
|
|
18
|
+
|
|
19
|
+
export function GitLabRepositorySelector({ onClone, className }: GitLabRepositorySelectorProps) {
|
|
20
|
+
const { connection, isConnected } = useGitLabConnection();
|
|
21
|
+
const [repositories, setRepositories] = useState<GitLabProjectInfo[]>([]);
|
|
22
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
23
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
24
|
+
const [sortBy, setSortBy] = useState<SortOption>('updated');
|
|
25
|
+
const [filterBy, setFilterBy] = useState<FilterOption>('all');
|
|
26
|
+
const [currentPage, setCurrentPage] = useState(1);
|
|
27
|
+
const [error, setError] = useState<string | null>(null);
|
|
28
|
+
const [isRefreshing, setIsRefreshing] = useState(false);
|
|
29
|
+
const [selectedRepo, setSelectedRepo] = useState<GitLabProjectInfo | null>(null);
|
|
30
|
+
const [isBranchSelectorOpen, setIsBranchSelectorOpen] = useState(false);
|
|
31
|
+
|
|
32
|
+
const REPOS_PER_PAGE = 12;
|
|
33
|
+
|
|
34
|
+
// Fetch repositories
|
|
35
|
+
const fetchRepositories = async (refresh = false) => {
|
|
36
|
+
if (!isConnected || !connection?.token) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const loadingState = refresh ? setIsRefreshing : setIsLoading;
|
|
41
|
+
loadingState(true);
|
|
42
|
+
setError(null);
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const response = await fetch('/api/gitlab-projects', {
|
|
46
|
+
method: 'POST',
|
|
47
|
+
headers: {
|
|
48
|
+
'Content-Type': 'application/json',
|
|
49
|
+
},
|
|
50
|
+
body: JSON.stringify({
|
|
51
|
+
token: connection.token,
|
|
52
|
+
gitlabUrl: connection.gitlabUrl || 'https://gitlab.com',
|
|
53
|
+
}),
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (!response.ok) {
|
|
57
|
+
const errorData: any = await response.json().catch(() => ({ error: 'Failed to fetch repositories' }));
|
|
58
|
+
throw new Error(errorData.error || 'Failed to fetch repositories');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const data: any = await response.json();
|
|
62
|
+
setRepositories(data.projects || []);
|
|
63
|
+
} catch (err) {
|
|
64
|
+
console.error('Failed to fetch GitLab repositories:', err);
|
|
65
|
+
setError(err instanceof Error ? err.message : 'Failed to fetch repositories');
|
|
66
|
+
|
|
67
|
+
// Fallback to empty array on error
|
|
68
|
+
setRepositories([]);
|
|
69
|
+
} finally {
|
|
70
|
+
loadingState(false);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Filter and search repositories
|
|
75
|
+
const filteredRepositories = useMemo(() => {
|
|
76
|
+
if (!repositories) {
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const filtered = repositories.filter((repo: GitLabProjectInfo) => {
|
|
81
|
+
// Search filter
|
|
82
|
+
const matchesSearch =
|
|
83
|
+
!searchQuery ||
|
|
84
|
+
repo.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
|
85
|
+
repo.description?.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
|
86
|
+
repo.path_with_namespace.toLowerCase().includes(searchQuery.toLowerCase());
|
|
87
|
+
|
|
88
|
+
// Type filter
|
|
89
|
+
let matchesFilter = true;
|
|
90
|
+
|
|
91
|
+
switch (filterBy) {
|
|
92
|
+
case 'owned':
|
|
93
|
+
// This would need owner information from the API response
|
|
94
|
+
matchesFilter = true; // For now, show all
|
|
95
|
+
break;
|
|
96
|
+
case 'member':
|
|
97
|
+
// This would need member information from the API response
|
|
98
|
+
matchesFilter = true; // For now, show all
|
|
99
|
+
break;
|
|
100
|
+
case 'all':
|
|
101
|
+
default:
|
|
102
|
+
matchesFilter = true;
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return matchesSearch && matchesFilter;
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Sort repositories
|
|
110
|
+
filtered.sort((a: GitLabProjectInfo, b: GitLabProjectInfo) => {
|
|
111
|
+
switch (sortBy) {
|
|
112
|
+
case 'name':
|
|
113
|
+
return a.name.localeCompare(b.name);
|
|
114
|
+
case 'stars':
|
|
115
|
+
return b.star_count - a.star_count;
|
|
116
|
+
case 'created':
|
|
117
|
+
return new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime(); // Using updated_at as proxy
|
|
118
|
+
case 'updated':
|
|
119
|
+
default:
|
|
120
|
+
return new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime();
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
return filtered;
|
|
125
|
+
}, [repositories, searchQuery, sortBy, filterBy]);
|
|
126
|
+
|
|
127
|
+
// Pagination
|
|
128
|
+
const totalPages = Math.ceil(filteredRepositories.length / REPOS_PER_PAGE);
|
|
129
|
+
const startIndex = (currentPage - 1) * REPOS_PER_PAGE;
|
|
130
|
+
const currentRepositories = filteredRepositories.slice(startIndex, startIndex + REPOS_PER_PAGE);
|
|
131
|
+
|
|
132
|
+
const handleRefresh = () => {
|
|
133
|
+
fetchRepositories(true);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const handleCloneRepository = (repo: GitLabProjectInfo) => {
|
|
137
|
+
setSelectedRepo(repo);
|
|
138
|
+
setIsBranchSelectorOpen(true);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const handleBranchSelect = (branch: string) => {
|
|
142
|
+
if (onClone && selectedRepo) {
|
|
143
|
+
onClone(selectedRepo.http_url_to_repo, branch);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
setSelectedRepo(null);
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const handleCloseBranchSelector = () => {
|
|
150
|
+
setIsBranchSelectorOpen(false);
|
|
151
|
+
setSelectedRepo(null);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// Reset to first page when filters change
|
|
155
|
+
useEffect(() => {
|
|
156
|
+
setCurrentPage(1);
|
|
157
|
+
}, [searchQuery, sortBy, filterBy]);
|
|
158
|
+
|
|
159
|
+
// Fetch repositories when connection is ready
|
|
160
|
+
useEffect(() => {
|
|
161
|
+
if (isConnected && connection?.token) {
|
|
162
|
+
fetchRepositories();
|
|
163
|
+
}
|
|
164
|
+
}, [isConnected, connection?.token]);
|
|
165
|
+
|
|
166
|
+
if (!isConnected || !connection) {
|
|
167
|
+
return (
|
|
168
|
+
<div className="text-center p-8">
|
|
169
|
+
<p className="text-bolt-elements-textSecondary mb-4">Please connect to GitLab first to browse repositories</p>
|
|
170
|
+
<Button variant="outline" onClick={() => window.location.reload()}>
|
|
171
|
+
Refresh Connection
|
|
172
|
+
</Button>
|
|
173
|
+
</div>
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (error && !repositories.length) {
|
|
178
|
+
return (
|
|
179
|
+
<div className="text-center p-8">
|
|
180
|
+
<div className="text-red-500 mb-4">
|
|
181
|
+
<GitBranch className="w-12 h-12 mx-auto mb-2" />
|
|
182
|
+
<p className="font-medium">Failed to load repositories</p>
|
|
183
|
+
<p className="text-sm text-bolt-elements-textSecondary mt-1">{error}</p>
|
|
184
|
+
</div>
|
|
185
|
+
<Button variant="outline" onClick={handleRefresh} disabled={isRefreshing}>
|
|
186
|
+
<RefreshCw className={classNames('w-4 h-4 mr-2', { 'animate-spin': isRefreshing })} />
|
|
187
|
+
Try Again
|
|
188
|
+
</Button>
|
|
189
|
+
</div>
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (isLoading && !repositories.length) {
|
|
194
|
+
return (
|
|
195
|
+
<div className="flex flex-col items-center justify-center p-8 space-y-4">
|
|
196
|
+
<div className="animate-spin w-8 h-8 border-2 border-bolt-elements-borderColorActive border-t-transparent rounded-full" />
|
|
197
|
+
<p className="text-sm text-bolt-elements-textSecondary">Loading repositories...</p>
|
|
198
|
+
</div>
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (!repositories.length && !isLoading) {
|
|
203
|
+
return (
|
|
204
|
+
<div className="text-center p-8">
|
|
205
|
+
<GitBranch className="w-12 h-12 text-bolt-elements-textTertiary mx-auto mb-4" />
|
|
206
|
+
<p className="text-bolt-elements-textSecondary mb-4">No repositories found</p>
|
|
207
|
+
<Button variant="outline" onClick={handleRefresh} disabled={isRefreshing}>
|
|
208
|
+
<RefreshCw className={classNames('w-4 h-4 mr-2', { 'animate-spin': isRefreshing })} />
|
|
209
|
+
Refresh
|
|
210
|
+
</Button>
|
|
211
|
+
</div>
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return (
|
|
216
|
+
<motion.div
|
|
217
|
+
className={classNames('space-y-6', className)}
|
|
218
|
+
initial={{ opacity: 0, y: 20 }}
|
|
219
|
+
animate={{ opacity: 1, y: 0 }}
|
|
220
|
+
transition={{ duration: 0.3 }}
|
|
221
|
+
>
|
|
222
|
+
{/* Header with stats */}
|
|
223
|
+
<div className="flex items-center justify-between">
|
|
224
|
+
<div>
|
|
225
|
+
<h3 className="text-lg font-semibold text-bolt-elements-textPrimary">Select Repository to Clone</h3>
|
|
226
|
+
<p className="text-sm text-bolt-elements-textSecondary">
|
|
227
|
+
{filteredRepositories.length} of {repositories.length} repositories
|
|
228
|
+
</p>
|
|
229
|
+
</div>
|
|
230
|
+
<Button
|
|
231
|
+
onClick={handleRefresh}
|
|
232
|
+
disabled={isRefreshing}
|
|
233
|
+
variant="outline"
|
|
234
|
+
size="sm"
|
|
235
|
+
className="flex items-center gap-2"
|
|
236
|
+
>
|
|
237
|
+
<RefreshCw className={classNames('w-4 h-4', { 'animate-spin': isRefreshing })} />
|
|
238
|
+
Refresh
|
|
239
|
+
</Button>
|
|
240
|
+
</div>
|
|
241
|
+
|
|
242
|
+
{error && repositories.length > 0 && (
|
|
243
|
+
<div className="p-3 rounded-lg bg-yellow-50 border border-yellow-200 dark:bg-yellow-900/20 dark:border-yellow-700">
|
|
244
|
+
<p className="text-sm text-yellow-800 dark:text-yellow-200">Warning: {error}. Showing cached data.</p>
|
|
245
|
+
</div>
|
|
246
|
+
)}
|
|
247
|
+
|
|
248
|
+
{/* Search and Filters */}
|
|
249
|
+
<div className="flex flex-col sm:flex-row gap-4">
|
|
250
|
+
{/* Search */}
|
|
251
|
+
<div className="relative flex-1">
|
|
252
|
+
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-bolt-elements-textTertiary" />
|
|
253
|
+
<input
|
|
254
|
+
type="text"
|
|
255
|
+
placeholder="Search repositories..."
|
|
256
|
+
value={searchQuery}
|
|
257
|
+
onChange={(e) => setSearchQuery(e.target.value)}
|
|
258
|
+
className="w-full pl-10 pr-4 py-2 rounded-lg bg-bolt-elements-background-depth-1 border border-bolt-elements-borderColor text-bolt-elements-textPrimary placeholder-bolt-elements-textTertiary focus:outline-none focus:ring-1 focus:ring-bolt-elements-borderColorActive"
|
|
259
|
+
/>
|
|
260
|
+
</div>
|
|
261
|
+
|
|
262
|
+
{/* Sort */}
|
|
263
|
+
<div className="flex items-center gap-2">
|
|
264
|
+
<Calendar className="w-4 h-4 text-bolt-elements-textTertiary" />
|
|
265
|
+
<select
|
|
266
|
+
value={sortBy}
|
|
267
|
+
onChange={(e) => setSortBy(e.target.value as SortOption)}
|
|
268
|
+
className="px-3 py-2 rounded-lg bg-bolt-elements-background-depth-1 border border-bolt-elements-borderColor text-bolt-elements-textPrimary text-sm focus:outline-none focus:ring-1 focus:ring-bolt-elements-borderColorActive"
|
|
269
|
+
>
|
|
270
|
+
<option value="updated">Recently updated</option>
|
|
271
|
+
<option value="stars">Most starred</option>
|
|
272
|
+
<option value="name">Name (A-Z)</option>
|
|
273
|
+
<option value="created">Recently created</option>
|
|
274
|
+
</select>
|
|
275
|
+
</div>
|
|
276
|
+
|
|
277
|
+
{/* Filter */}
|
|
278
|
+
<div className="flex items-center gap-2">
|
|
279
|
+
<Filter className="w-4 h-4 text-bolt-elements-textTertiary" />
|
|
280
|
+
<select
|
|
281
|
+
value={filterBy}
|
|
282
|
+
onChange={(e) => setFilterBy(e.target.value as FilterOption)}
|
|
283
|
+
className="px-3 py-2 rounded-lg bg-bolt-elements-background-depth-1 border border-bolt-elements-borderColor text-bolt-elements-textPrimary text-sm focus:outline-none focus:ring-1 focus:ring-bolt-elements-borderColorActive"
|
|
284
|
+
>
|
|
285
|
+
<option value="all">All repositories</option>
|
|
286
|
+
<option value="owned">Owned repositories</option>
|
|
287
|
+
<option value="member">Member repositories</option>
|
|
288
|
+
</select>
|
|
289
|
+
</div>
|
|
290
|
+
</div>
|
|
291
|
+
|
|
292
|
+
{/* Repository Grid */}
|
|
293
|
+
{currentRepositories.length > 0 ? (
|
|
294
|
+
<>
|
|
295
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
296
|
+
{currentRepositories.map((repo) => (
|
|
297
|
+
<div key={repo.id} className="relative">
|
|
298
|
+
<RepositoryCard repo={repo} onClone={() => handleCloneRepository(repo)} />
|
|
299
|
+
</div>
|
|
300
|
+
))}
|
|
301
|
+
</div>
|
|
302
|
+
|
|
303
|
+
{/* Pagination */}
|
|
304
|
+
{totalPages > 1 && (
|
|
305
|
+
<div className="flex items-center justify-between pt-4 border-t border-bolt-elements-borderColor">
|
|
306
|
+
<div className="text-sm text-bolt-elements-textSecondary">
|
|
307
|
+
Showing {Math.min(startIndex + 1, filteredRepositories.length)} to{' '}
|
|
308
|
+
{Math.min(startIndex + REPOS_PER_PAGE, filteredRepositories.length)} of {filteredRepositories.length}{' '}
|
|
309
|
+
repositories
|
|
310
|
+
</div>
|
|
311
|
+
<div className="flex items-center gap-2">
|
|
312
|
+
<Button
|
|
313
|
+
onClick={() => setCurrentPage((prev) => Math.max(1, prev - 1))}
|
|
314
|
+
disabled={currentPage === 1}
|
|
315
|
+
variant="outline"
|
|
316
|
+
size="sm"
|
|
317
|
+
>
|
|
318
|
+
Previous
|
|
319
|
+
</Button>
|
|
320
|
+
<span className="text-sm text-bolt-elements-textSecondary px-3">
|
|
321
|
+
{currentPage} of {totalPages}
|
|
322
|
+
</span>
|
|
323
|
+
<Button
|
|
324
|
+
onClick={() => setCurrentPage((prev) => Math.min(totalPages, prev + 1))}
|
|
325
|
+
disabled={currentPage === totalPages}
|
|
326
|
+
variant="outline"
|
|
327
|
+
size="sm"
|
|
328
|
+
>
|
|
329
|
+
Next
|
|
330
|
+
</Button>
|
|
331
|
+
</div>
|
|
332
|
+
</div>
|
|
333
|
+
)}
|
|
334
|
+
</>
|
|
335
|
+
) : (
|
|
336
|
+
<div className="text-center py-8">
|
|
337
|
+
<p className="text-bolt-elements-textSecondary">No repositories found matching your search criteria.</p>
|
|
338
|
+
</div>
|
|
339
|
+
)}
|
|
340
|
+
|
|
341
|
+
{/* Branch Selector Modal */}
|
|
342
|
+
{selectedRepo && (
|
|
343
|
+
<BranchSelector
|
|
344
|
+
provider="gitlab"
|
|
345
|
+
repoOwner={selectedRepo.path_with_namespace.split('/')[0]}
|
|
346
|
+
repoName={selectedRepo.path_with_namespace.split('/')[1]}
|
|
347
|
+
projectId={selectedRepo.id}
|
|
348
|
+
token={connection?.token || ''}
|
|
349
|
+
gitlabUrl={connection?.gitlabUrl}
|
|
350
|
+
defaultBranch={selectedRepo.default_branch}
|
|
351
|
+
onBranchSelect={handleBranchSelect}
|
|
352
|
+
onClose={handleCloseBranchSelector}
|
|
353
|
+
isOpen={isBranchSelectorOpen}
|
|
354
|
+
/>
|
|
355
|
+
)}
|
|
356
|
+
</motion.div>
|
|
357
|
+
);
|
|
358
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { GitLabProjectInfo } from '~/types/GitLab';
|
|
3
|
+
|
|
4
|
+
interface RepositoryCardProps {
|
|
5
|
+
repo: GitLabProjectInfo;
|
|
6
|
+
onClone?: (repo: GitLabProjectInfo) => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function RepositoryCard({ repo, onClone }: RepositoryCardProps) {
|
|
10
|
+
return (
|
|
11
|
+
<a
|
|
12
|
+
key={repo.name}
|
|
13
|
+
href={repo.http_url_to_repo}
|
|
14
|
+
target="_blank"
|
|
15
|
+
rel="noopener noreferrer"
|
|
16
|
+
className="group block p-4 rounded-lg bg-bolt-elements-background-depth-1 border border-bolt-elements-borderColor hover:border-bolt-elements-borderColorActive transition-all duration-200"
|
|
17
|
+
>
|
|
18
|
+
<div className="space-y-3">
|
|
19
|
+
<div className="flex items-start justify-between">
|
|
20
|
+
<div className="flex items-center gap-2">
|
|
21
|
+
<div className="i-ph:git-repository w-4 h-4 text-bolt-elements-icon-info" />
|
|
22
|
+
<h5 className="text-sm font-medium text-bolt-elements-textPrimary group-hover:text-bolt-elements-item-contentAccent transition-colors">
|
|
23
|
+
{repo.name}
|
|
24
|
+
</h5>
|
|
25
|
+
</div>
|
|
26
|
+
<div className="flex items-center gap-3 text-xs text-bolt-elements-textSecondary">
|
|
27
|
+
<span className="flex items-center gap-1" title="Stars">
|
|
28
|
+
<div className="i-ph:star w-3.5 h-3.5 text-bolt-elements-icon-warning" />
|
|
29
|
+
{repo.star_count.toLocaleString()}
|
|
30
|
+
</span>
|
|
31
|
+
<span className="flex items-center gap-1" title="Forks">
|
|
32
|
+
<div className="i-ph:git-fork w-3.5 h-3.5 text-bolt-elements-icon-info" />
|
|
33
|
+
{repo.forks_count.toLocaleString()}
|
|
34
|
+
</span>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
{repo.description && (
|
|
39
|
+
<p className="text-xs text-bolt-elements-textSecondary line-clamp-2">{repo.description}</p>
|
|
40
|
+
)}
|
|
41
|
+
|
|
42
|
+
<div className="flex items-center gap-3 text-xs text-bolt-elements-textSecondary">
|
|
43
|
+
<span className="flex items-center gap-1" title="Default Branch">
|
|
44
|
+
<div className="i-ph:git-branch w-3.5 h-3.5" />
|
|
45
|
+
{repo.default_branch}
|
|
46
|
+
</span>
|
|
47
|
+
<span className="flex items-center gap-1" title="Last Updated">
|
|
48
|
+
<div className="i-ph:clock w-3.5 h-3.5" />
|
|
49
|
+
{new Date(repo.updated_at).toLocaleDateString(undefined, {
|
|
50
|
+
year: 'numeric',
|
|
51
|
+
month: 'short',
|
|
52
|
+
day: 'numeric',
|
|
53
|
+
})}
|
|
54
|
+
</span>
|
|
55
|
+
<div className="flex items-center gap-2 ml-auto">
|
|
56
|
+
{onClone && (
|
|
57
|
+
<button
|
|
58
|
+
onClick={(e) => {
|
|
59
|
+
e.preventDefault();
|
|
60
|
+
e.stopPropagation();
|
|
61
|
+
onClone(repo);
|
|
62
|
+
}}
|
|
63
|
+
className="flex items-center gap-1 px-2 py-1 rounded text-xs bg-bolt-elements-background-depth-2 hover:bg-bolt-elements-background-depth-3 text-bolt-elements-textSecondary hover:text-bolt-elements-textPrimary transition-colors"
|
|
64
|
+
title="Clone repository"
|
|
65
|
+
>
|
|
66
|
+
<div className="i-ph:git-branch w-3.5 h-3.5" />
|
|
67
|
+
Clone
|
|
68
|
+
</button>
|
|
69
|
+
)}
|
|
70
|
+
<span className="flex items-center gap-1 group-hover:text-bolt-elements-item-contentAccent transition-colors">
|
|
71
|
+
<div className="i-ph:arrow-square-out w-3.5 h-3.5" />
|
|
72
|
+
View
|
|
73
|
+
</span>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
</a>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import React, { useState, useMemo } from 'react';
|
|
2
|
+
import { Button } from '~/components/ui/Button';
|
|
3
|
+
import { RepositoryCard } from './RepositoryCard';
|
|
4
|
+
import type { GitLabProjectInfo } from '~/types/GitLab';
|
|
5
|
+
|
|
6
|
+
interface RepositoryListProps {
|
|
7
|
+
repositories: GitLabProjectInfo[];
|
|
8
|
+
onClone?: (repo: GitLabProjectInfo) => void;
|
|
9
|
+
onRefresh?: () => void;
|
|
10
|
+
isRefreshing?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const MAX_REPOS_PER_PAGE = 20;
|
|
14
|
+
|
|
15
|
+
export function RepositoryList({ repositories, onClone, onRefresh, isRefreshing }: RepositoryListProps) {
|
|
16
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
17
|
+
const [currentPage, setCurrentPage] = useState(1);
|
|
18
|
+
const [isSearching, setIsSearching] = useState(false);
|
|
19
|
+
|
|
20
|
+
const filteredRepositories = useMemo(() => {
|
|
21
|
+
if (!searchQuery) {
|
|
22
|
+
return repositories;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
setIsSearching(true);
|
|
26
|
+
|
|
27
|
+
const filtered = repositories.filter(
|
|
28
|
+
(repo) =>
|
|
29
|
+
repo.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
|
30
|
+
repo.path_with_namespace.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
|
31
|
+
(repo.description && repo.description.toLowerCase().includes(searchQuery.toLowerCase())),
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
setIsSearching(false);
|
|
35
|
+
|
|
36
|
+
return filtered;
|
|
37
|
+
}, [repositories, searchQuery]);
|
|
38
|
+
|
|
39
|
+
const totalPages = Math.ceil(filteredRepositories.length / MAX_REPOS_PER_PAGE);
|
|
40
|
+
const startIndex = (currentPage - 1) * MAX_REPOS_PER_PAGE;
|
|
41
|
+
const endIndex = startIndex + MAX_REPOS_PER_PAGE;
|
|
42
|
+
const currentRepositories = filteredRepositories.slice(startIndex, endIndex);
|
|
43
|
+
|
|
44
|
+
const handleSearch = (query: string) => {
|
|
45
|
+
setSearchQuery(query);
|
|
46
|
+
setCurrentPage(1); // Reset to first page when searching
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<div className="space-y-4">
|
|
51
|
+
<div className="flex items-center justify-between">
|
|
52
|
+
<h4 className="text-sm font-medium text-bolt-elements-textPrimary">
|
|
53
|
+
Repositories ({filteredRepositories.length})
|
|
54
|
+
</h4>
|
|
55
|
+
{onRefresh && (
|
|
56
|
+
<Button
|
|
57
|
+
onClick={onRefresh}
|
|
58
|
+
disabled={isRefreshing}
|
|
59
|
+
variant="outline"
|
|
60
|
+
size="sm"
|
|
61
|
+
className="flex items-center gap-2"
|
|
62
|
+
>
|
|
63
|
+
{isRefreshing ? (
|
|
64
|
+
<div className="i-ph:spinner animate-spin w-4 h-4" />
|
|
65
|
+
) : (
|
|
66
|
+
<div className="i-ph:arrows-clockwise w-4 h-4" />
|
|
67
|
+
)}
|
|
68
|
+
Refresh
|
|
69
|
+
</Button>
|
|
70
|
+
)}
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
{/* Search Input */}
|
|
74
|
+
<div className="relative">
|
|
75
|
+
<input
|
|
76
|
+
type="text"
|
|
77
|
+
placeholder="Search repositories..."
|
|
78
|
+
value={searchQuery}
|
|
79
|
+
onChange={(e) => handleSearch(e.target.value)}
|
|
80
|
+
className="w-full px-4 py-2 pl-10 rounded-lg bg-bolt-elements-background-depth-2 border border-bolt-elements-borderColor text-bolt-elements-textPrimary placeholder-bolt-elements-textTertiary focus:outline-none focus:ring-1 focus:ring-bolt-elements-borderColorActive"
|
|
81
|
+
/>
|
|
82
|
+
<div className="absolute left-3 top-1/2 -translate-y-1/2">
|
|
83
|
+
{isSearching ? (
|
|
84
|
+
<div className="i-ph:spinner animate-spin w-4 h-4 text-bolt-elements-textSecondary" />
|
|
85
|
+
) : (
|
|
86
|
+
<div className="i-ph:magnifying-glass w-4 h-4 text-bolt-elements-textSecondary" />
|
|
87
|
+
)}
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
{/* Repository Grid */}
|
|
92
|
+
<div className="space-y-4">
|
|
93
|
+
{filteredRepositories.length === 0 ? (
|
|
94
|
+
<div className="text-center py-8 text-bolt-elements-textSecondary">
|
|
95
|
+
{searchQuery ? 'No repositories found matching your search.' : 'No repositories available.'}
|
|
96
|
+
</div>
|
|
97
|
+
) : (
|
|
98
|
+
<>
|
|
99
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
100
|
+
{currentRepositories.map((repo) => (
|
|
101
|
+
<RepositoryCard key={repo.id} repo={repo} onClone={onClone} />
|
|
102
|
+
))}
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
{/* Pagination Controls */}
|
|
106
|
+
{totalPages > 1 && (
|
|
107
|
+
<div className="flex items-center justify-between pt-4 border-t border-bolt-elements-borderColor">
|
|
108
|
+
<div className="text-sm text-bolt-elements-textSecondary">
|
|
109
|
+
Showing {Math.min(startIndex + 1, filteredRepositories.length)} to{' '}
|
|
110
|
+
{Math.min(endIndex, filteredRepositories.length)} of {filteredRepositories.length} repositories
|
|
111
|
+
</div>
|
|
112
|
+
<div className="flex items-center gap-2">
|
|
113
|
+
<Button
|
|
114
|
+
onClick={() => setCurrentPage((prev) => Math.max(1, prev - 1))}
|
|
115
|
+
disabled={currentPage === 1}
|
|
116
|
+
variant="outline"
|
|
117
|
+
size="sm"
|
|
118
|
+
>
|
|
119
|
+
<div className="i-ph:caret-left w-4 h-4" />
|
|
120
|
+
Previous
|
|
121
|
+
</Button>
|
|
122
|
+
<span className="text-sm text-bolt-elements-textSecondary px-3">
|
|
123
|
+
{currentPage} of {totalPages}
|
|
124
|
+
</span>
|
|
125
|
+
<Button
|
|
126
|
+
onClick={() => setCurrentPage((prev) => Math.min(totalPages, prev + 1))}
|
|
127
|
+
disabled={currentPage === totalPages}
|
|
128
|
+
variant="outline"
|
|
129
|
+
size="sm"
|
|
130
|
+
>
|
|
131
|
+
Next
|
|
132
|
+
<div className="i-ph:caret-right w-4 h-4" />
|
|
133
|
+
</Button>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
)}
|
|
137
|
+
</>
|
|
138
|
+
)}
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Button } from '~/components/ui/Button';
|
|
3
|
+
import type { GitLabStats } from '~/types/GitLab';
|
|
4
|
+
|
|
5
|
+
interface StatsDisplayProps {
|
|
6
|
+
stats: GitLabStats;
|
|
7
|
+
onRefresh?: () => void;
|
|
8
|
+
isRefreshing?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function StatsDisplay({ stats, onRefresh, isRefreshing }: StatsDisplayProps) {
|
|
12
|
+
return (
|
|
13
|
+
<div className="space-y-4">
|
|
14
|
+
{/* Repository Stats */}
|
|
15
|
+
<div>
|
|
16
|
+
<h5 className="text-sm font-medium text-bolt-elements-textPrimary mb-2">Repository Stats</h5>
|
|
17
|
+
<div className="grid grid-cols-2 gap-4">
|
|
18
|
+
{[
|
|
19
|
+
{
|
|
20
|
+
label: 'Public Repos',
|
|
21
|
+
value: stats.publicProjects,
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
label: 'Private Repos',
|
|
25
|
+
value: stats.privateProjects,
|
|
26
|
+
},
|
|
27
|
+
].map((stat, index) => (
|
|
28
|
+
<div
|
|
29
|
+
key={index}
|
|
30
|
+
className="flex flex-col p-3 rounded-lg bg-bolt-elements-background-depth-2 border border-bolt-elements-borderColor"
|
|
31
|
+
>
|
|
32
|
+
<span className="text-xs text-bolt-elements-textSecondary">{stat.label}</span>
|
|
33
|
+
<span className="text-lg font-medium text-bolt-elements-textPrimary">{stat.value}</span>
|
|
34
|
+
</div>
|
|
35
|
+
))}
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
{/* Contribution Stats */}
|
|
40
|
+
<div>
|
|
41
|
+
<h5 className="text-sm font-medium text-bolt-elements-textPrimary mb-2">Contribution Stats</h5>
|
|
42
|
+
<div className="grid grid-cols-3 gap-4">
|
|
43
|
+
{[
|
|
44
|
+
{
|
|
45
|
+
label: 'Stars',
|
|
46
|
+
value: stats.stars || 0,
|
|
47
|
+
icon: 'i-ph:star',
|
|
48
|
+
iconColor: 'text-bolt-elements-icon-warning',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
label: 'Forks',
|
|
52
|
+
value: stats.forks || 0,
|
|
53
|
+
icon: 'i-ph:git-fork',
|
|
54
|
+
iconColor: 'text-bolt-elements-icon-info',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
label: 'Followers',
|
|
58
|
+
value: stats.followers || 0,
|
|
59
|
+
icon: 'i-ph:users',
|
|
60
|
+
iconColor: 'text-bolt-elements-icon-success',
|
|
61
|
+
},
|
|
62
|
+
].map((stat, index) => (
|
|
63
|
+
<div
|
|
64
|
+
key={index}
|
|
65
|
+
className="flex flex-col p-3 rounded-lg bg-bolt-elements-background-depth-2 border border-bolt-elements-borderColor"
|
|
66
|
+
>
|
|
67
|
+
<span className="text-xs text-bolt-elements-textSecondary">{stat.label}</span>
|
|
68
|
+
<span className="text-lg font-medium text-bolt-elements-textPrimary flex items-center gap-1">
|
|
69
|
+
<div className={`${stat.icon} w-4 h-4 ${stat.iconColor}`} />
|
|
70
|
+
{stat.value}
|
|
71
|
+
</span>
|
|
72
|
+
</div>
|
|
73
|
+
))}
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<div className="pt-2 border-t border-bolt-elements-borderColor">
|
|
78
|
+
<div className="flex items-center justify-between">
|
|
79
|
+
<span className="text-xs text-bolt-elements-textSecondary">
|
|
80
|
+
Last updated: {new Date(stats.lastUpdated).toLocaleString()}
|
|
81
|
+
</span>
|
|
82
|
+
{onRefresh && (
|
|
83
|
+
<Button onClick={onRefresh} disabled={isRefreshing} variant="outline" size="sm" className="text-xs">
|
|
84
|
+
{isRefreshing ? 'Refreshing...' : 'Refresh'}
|
|
85
|
+
</Button>
|
|
86
|
+
)}
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
}
|