From d1d23d80e702822007fc52268fa707528cfa4d24 Mon Sep 17 00:00:00 2001 From: Stijnus <72551117+Stijnus@users.noreply.github.com> Date: Thu, 30 Jan 2025 17:17:36 +0100 Subject: [PATCH] big fixes fixes feedback from thecodacus --- .cursorrules | 1 + .gitignore | 2 + app/components/chat/GitCloneButton.tsx | 4 +- app/components/chat/ImportFolderButton.tsx | 5 +- app/components/chat/StarterTemplates.tsx | 11 +- .../chatExportAndImport/ImportButtons.tsx | 6 +- .../connections/components/ConnectionForm.tsx | 2 +- .../components/PushToGitHubDialog.tsx | 2 +- .../components/RepositorySelectionDialog.tsx | 19 +- app/components/settings/debug/DebugTab.tsx | 633 +++++++-------- .../settings/developer/DeveloperWindow.tsx | 11 +- .../settings/developer/TabManagement.tsx | 12 +- .../settings/event-logs/EventLogsTab.tsx | 28 +- .../settings/profile/ProfileTab.tsx | 16 +- .../settings/providers/CloudProvidersTab.tsx | 3 +- .../settings/providers/LocalProvidersTab.tsx | 44 +- .../settings/providers/OllamaModelUpdater.tsx | 205 ++--- .../service-status/ServiceStatusTab.tsx | 135 ++++ .../service-status/provider-factory.ts | 118 ++- .../providers/amazon-bedrock.ts | 76 ++ .../service-status/providers/anthropic.ts | 80 ++ .../service-status/providers/cohere.ts | 91 +++ .../service-status/providers/deepseek.ts | 40 + .../service-status/providers/google.ts | 77 ++ .../service-status/providers/groq.ts | 72 ++ .../service-status/providers/huggingface.ts | 98 +++ .../service-status/providers/hyperbolic.ts | 40 + .../service-status/providers/mistral.ts | 76 ++ .../service-status/providers/openrouter.ts | 91 +++ .../service-status/providers/perplexity.ts | 91 +++ .../service-status/providers/together.ts | 91 +++ .../providers/service-status/providers/xai.ts | 40 + .../providers/service-status/types.ts | 13 +- app/components/settings/settings.styles.ts | 43 -- app/components/settings/settings.types.ts | 14 +- .../settings/settings/SettingsTab.tsx | 8 +- .../settings/task-manager/TaskManagerTab.tsx | 722 ++++-------------- app/components/settings/user/UsersWindow.tsx | 2 - app/components/ui/Badge.tsx | 23 +- app/components/ui/Button.tsx | 4 +- app/components/ui/Card.tsx | 25 +- app/components/ui/Input.tsx | 9 +- app/components/ui/Label.tsx | 36 +- app/components/ui/Progress.tsx | 24 +- app/components/ui/ScrollArea.tsx | 14 +- app/components/ui/Tabs.tsx | 26 +- app/lib/hooks/useSettings.ts | 2 +- app/lib/modules/llm/providers/github.ts | 53 ++ app/lib/modules/llm/registry.ts | 2 + app/lib/persistence/index.ts | 1 + .../persistence}/localStorage.ts | 15 +- app/lib/runtime/action-runner.ts | 8 +- app/lib/stores/files.ts | 4 +- app/lib/stores/workbench.ts | 9 +- app/lib/utils.ts | 6 - app/routes/api.system.app-info.ts | 172 +++-- app/routes/api.system.git-info.ts | 119 ++- app/utils/constants.ts | 2 +- app/utils/path.ts | 19 + icons/slidev.svg | 4 +- package.json | 3 + pages/api/system/git-info.ts | 24 +- pnpm-lock.yaml | 36 + public/icons/astro.svg | 5 - public/icons/nextjs.svg | 5 - public/icons/qwik.svg | 4 - uno.config.ts | 20 +- vite.config.ts | 77 +- 68 files changed, 2436 insertions(+), 1337 deletions(-) create mode 100644 app/components/settings/providers/service-status/ServiceStatusTab.tsx create mode 100644 app/components/settings/providers/service-status/providers/amazon-bedrock.ts create mode 100644 app/components/settings/providers/service-status/providers/anthropic.ts create mode 100644 app/components/settings/providers/service-status/providers/cohere.ts create mode 100644 app/components/settings/providers/service-status/providers/deepseek.ts create mode 100644 app/components/settings/providers/service-status/providers/google.ts create mode 100644 app/components/settings/providers/service-status/providers/groq.ts create mode 100644 app/components/settings/providers/service-status/providers/huggingface.ts create mode 100644 app/components/settings/providers/service-status/providers/hyperbolic.ts create mode 100644 app/components/settings/providers/service-status/providers/mistral.ts create mode 100644 app/components/settings/providers/service-status/providers/openrouter.ts create mode 100644 app/components/settings/providers/service-status/providers/perplexity.ts create mode 100644 app/components/settings/providers/service-status/providers/together.ts create mode 100644 app/components/settings/providers/service-status/providers/xai.ts delete mode 100644 app/components/settings/settings.styles.ts create mode 100644 app/lib/modules/llm/providers/github.ts rename app/{utils => lib/persistence}/localStorage.ts (53%) delete mode 100644 app/lib/utils.ts create mode 100644 app/utils/path.ts delete mode 100644 public/icons/astro.svg delete mode 100644 public/icons/nextjs.svg delete mode 100644 public/icons/qwik.svg diff --git a/.cursorrules b/.cursorrules index 3a57535c5b..de6183f2b8 100644 --- a/.cursorrules +++ b/.cursorrules @@ -154,3 +154,4 @@ bolt.diy (previously oTToDev) is an open-source AI-powered full-stack web develo - Don't use white background for dark mode - Don't use white text on white background for dark mode - Match the style of the existing codebase +- Use consistent naming conventions for components and variables diff --git a/.gitignore b/.gitignore index 9e2a98c383..064ad973f1 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,5 @@ site app/commit.json changelogUI.md docs/instructions/Roadmap.md +.cursorrules +.cursorrules diff --git a/app/components/chat/GitCloneButton.tsx b/app/components/chat/GitCloneButton.tsx index 872d9dec98..b0b89c0d11 100644 --- a/app/components/chat/GitCloneButton.tsx +++ b/app/components/chat/GitCloneButton.tsx @@ -7,7 +7,7 @@ import { useState } from 'react'; import { toast } from 'react-toastify'; import { LoadingOverlay } from '~/components/ui/LoadingOverlay'; import { RepositorySelectionDialog } from '~/components/settings/connections/components/RepositorySelectionDialog'; -import { cn } from '~/lib/utils'; +import { classNames } from '~/utils/classNames'; import { Button } from '~/components/ui/Button'; import type { IChatMetadata } from '~/lib/persistence/db'; @@ -158,7 +158,7 @@ ${escapeBoltTags(file.content)} title="Clone a Git Repo" variant="outline" size="lg" - className={cn( + className={classNames( 'gap-2 bg-[#F5F5F5] dark:bg-[#252525]', 'text-bolt-elements-textPrimary dark:text-white', 'hover:bg-[#E5E5E5] dark:hover:bg-[#333333]', diff --git a/app/components/chat/ImportFolderButton.tsx b/app/components/chat/ImportFolderButton.tsx index 6cd5a6f922..27887113bc 100644 --- a/app/components/chat/ImportFolderButton.tsx +++ b/app/components/chat/ImportFolderButton.tsx @@ -5,7 +5,7 @@ import { MAX_FILES, isBinaryFile, shouldIncludeFile } from '~/utils/fileUtils'; import { createChatFromFolder } from '~/utils/folderImport'; import { logStore } from '~/lib/stores/logs'; // Assuming logStore is imported from this location import { Button } from '~/components/ui/Button'; -import { cn } from '~/lib/utils'; +import { classNames } from '~/utils/classNames'; interface ImportFolderButtonProps { className?: string; @@ -119,9 +119,10 @@ export const ImportFolderButton: React.FC = ({ classNam const input = document.getElementById('folder-import'); input?.click(); }} + title="Import Folder" variant="outline" size="lg" - className={cn( + className={classNames( 'gap-2 bg-[#F5F5F5] dark:bg-[#252525]', 'text-bolt-elements-textPrimary dark:text-white', 'hover:bg-[#E5E5E5] dark:hover:bg-[#333333]', diff --git a/app/components/chat/StarterTemplates.tsx b/app/components/chat/StarterTemplates.tsx index 7e0ee5b167..fa51961bbe 100644 --- a/app/components/chat/StarterTemplates.tsx +++ b/app/components/chat/StarterTemplates.tsx @@ -11,15 +11,24 @@ const FrameworkLink: React.FC = ({ template }) => ( href={`/git?url=https://github.com/${template.githubRepo}.git`} data-state="closed" data-discover="true" - className="items-center justify-center " + className="items-center justify-center" >
); const StarterTemplates: React.FC = () => { + // Debug: Log available templates and their icons + React.useEffect(() => { + console.log( + 'Available templates:', + STARTER_TEMPLATES.map((t) => ({ name: t.name, icon: t.icon })), + ); + }, []); + return (
or start a blank app with your favorite stack diff --git a/app/components/chat/chatExportAndImport/ImportButtons.tsx b/app/components/chat/chatExportAndImport/ImportButtons.tsx index e01595bc91..b91aab3557 100644 --- a/app/components/chat/chatExportAndImport/ImportButtons.tsx +++ b/app/components/chat/chatExportAndImport/ImportButtons.tsx @@ -2,7 +2,7 @@ import type { Message } from 'ai'; import { toast } from 'react-toastify'; import { ImportFolderButton } from '~/components/chat/ImportFolderButton'; import { Button } from '~/components/ui/Button'; -import { cn } from '~/lib/utils'; +import { classNames } from '~/utils/classNames'; type ChatData = { messages?: Message[]; // Standard Bolt format @@ -66,7 +66,7 @@ export function ImportButtons(importChat: ((description: string, messages: Messa }} variant="outline" size="lg" - className={cn( + className={classNames( 'gap-2 bg-[#F5F5F5] dark:bg-[#252525]', 'text-bolt-elements-textPrimary dark:text-white', 'hover:bg-[#E5E5E5] dark:hover:bg-[#333333]', @@ -80,7 +80,7 @@ export function ImportButtons(importChat: ((description: string, messages: Messa - setCustomUrl(e.target.value)} - className="w-full px-4 py-2 rounded-lg bg-[#F5F5F5] dark:bg-[#252525] border border-[#E5E5E5] dark:border-[#333333] text-bolt-elements-textPrimary" + className={classNames('w-full', { + 'border-red-500': false, + })} /> +
+ ) : ( +
+
+

Basic Information

+
-
- Build Time: - {webAppInfo.buildTime} +
+ Name: + {webAppInfo.name}
- )} - {webAppInfo.buildNumber && (
-
- Build Number: - {webAppInfo.buildNumber} +
+ Version: + {webAppInfo.version} +
+
+
+ License: + {webAppInfo.license}
- )} - {webAppInfo.environment && (
- Environment: + Environment: {webAppInfo.environment}
- )} +
+
+ Node Version: + {webAppInfo.runtimeInfo.nodeVersion} +
+
-
-
-
-
- Key Dependencies: + +
+

Git Information

+
+
+
+ Branch: + {webAppInfo.gitInfo.local.branch}
-
- {Object.entries(webAppInfo.dependencies) - .filter(([key]) => ['react', '@remix-run/react', 'next', 'typescript'].includes(key)) - .map(([key, version]) => ( -
- {key}: {version} -
- ))} +
+
+ Commit: + {webAppInfo.gitInfo.local.commitHash}
-
- {webAppInfo.gitInfo && ( -
-
-
- Git Info: -
-
-
- Branch: {webAppInfo.gitInfo.branch} -
-
- Commit: {webAppInfo.gitInfo.commit} -
-
- Commit Time: {webAppInfo.gitInfo.commitTime} -
-
- Author: {webAppInfo.gitInfo.author} -
-
- Remote URL: {webAppInfo.gitInfo.remoteUrl} -
-
+
+
+ Author: + {webAppInfo.gitInfo.local.author}
- )} - {webAppInfo.repoInfo && ( -
-
-
- GitHub Repository: -
-
-
- {`${webAppInfo.repoInfo.owner.login}'s -
-
- Owner: {webAppInfo.repoInfo.owner.login} +
+
+ Commit Time: + {webAppInfo.gitInfo.local.commitTime} +
+ + {webAppInfo.gitInfo.github && ( + <> +
+
+
+ Repository: + + {webAppInfo.gitInfo.github.currentRepo.fullName} + {webAppInfo.gitInfo.isForked && ' (fork)'} + +
+ +
+
+
+ + {webAppInfo.gitInfo.github.currentRepo.stars} + +
+
+
+ + {webAppInfo.gitInfo.github.currentRepo.forks} +
-
- Last Update: {new Date(webAppInfo.repoInfo.lastUpdate).toLocaleDateString()} +
+
+ + {webAppInfo.gitInfo.github.currentRepo.openIssues} +
-
-
-
- {webAppInfo.repoInfo.stars.toLocaleString()} stars -
-
-
- {webAppInfo.repoInfo.forks.toLocaleString()} forks -
-
-
- {webAppInfo.repoInfo.openIssues.toLocaleString()} issues + {webAppInfo.gitInfo.github.upstream && ( +
+
+
+ Upstream: + + {webAppInfo.gitInfo.github.upstream.fullName} + +
+ +
+
+
+ + {webAppInfo.gitInfo.github.upstream.stars} + +
+
+
+ + {webAppInfo.gitInfo.github.upstream.forks} + +
+
-
-
-
- )} + )} + + )} +
- ) : ( -
- {loading.webAppInfo ? 'Loading webapp information...' : 'No webapp information available'} + )} + + {webAppInfo && ( +
+

Dependencies

+
+ + + + +
)}
@@ -1138,7 +1165,7 @@ export default function DebugTab() { {/* Error Check */} setOpenSections((prev) => ({ ...prev, errors: open }))} + onOpenChange={(open) => setOpenSections((prev) => ({ ...prev, errors: open }))} className="w-full" > @@ -1146,14 +1173,14 @@ export default function DebugTab() {

Error Check

- {errorLog.errors.length > 0 && ( + {errorLogs.length > 0 && ( - {errorLog.errors.length} Errors + {errorLogs.length} Errors )}
- Last Check: + Status: {loading.errors ? 'Checking...' - : errorLog.lastCheck - ? `Last checked ${new Date(errorLog.lastCheck).toLocaleString()} (${errorLog.errors.length} errors found)` - : 'Click to check for errors'} + : errorLogs.length > 0 + ? `${errorLogs.length} errors found` + : 'No errors found'}
- {errorLog.errors.length > 0 && ( + {errorLogs.length > 0 && (
Recent Errors:
- {errorLog.errors.slice(0, 3).map((error, index) => ( -
- {error.type === 'error' && `${error.message} (${error.filename}:${error.lineNumber})`} - {error.type === 'unhandledRejection' && `Unhandled Promise Rejection: ${error.reason}`} - {error.type === 'networkError' && `Network Error: Failed to load ${error.resource}`} + {errorLogs.map((error) => ( +
+
{error.message}
+ {error.source && ( +
+ Source: {error.source} + {error.details?.lineNumber && `:${error.details.lineNumber}`} +
+ )} + {error.stack && ( +
{error.stack}
+ )}
))} - {errorLog.errors.length > 3 && ( -
- And {errorLog.errors.length - 3} more errors... -
- )}
)} diff --git a/app/components/settings/developer/DeveloperWindow.tsx b/app/components/settings/developer/DeveloperWindow.tsx index c2a59481c4..d5c0714b60 100644 --- a/app/components/settings/developer/DeveloperWindow.tsx +++ b/app/components/settings/developer/DeveloperWindow.tsx @@ -30,6 +30,7 @@ import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; import CloudProvidersTab from '~/components/settings/providers/CloudProvidersTab'; import LocalProvidersTab from '~/components/settings/providers/LocalProvidersTab'; import TaskManagerTab from '~/components/settings/task-manager/TaskManagerTab'; +import ServiceStatusTab from '~/components/settings/providers/ServiceStatusTab'; import { Switch } from '~/components/ui/Switch'; interface DraggableTabTileProps { @@ -57,7 +58,7 @@ const TAB_DESCRIPTIONS: Record = { 'event-logs': 'View application event logs', update: 'Check for updates', 'task-manager': 'Manage running tasks', - 'service-status': 'View service health and status', + 'service-status': 'Monitor provider service health and status', }; const DraggableTabTile = ({ @@ -207,8 +208,6 @@ export const DeveloperWindow = ({ open, onClose }: DeveloperWindowProps) => { // Only show tabs that are assigned to the developer window AND are visible const visibleDeveloperTabs = useMemo(() => { - console.log('Filtering developer tabs with configuration:', tabConfiguration); - if (!tabConfiguration?.developerTabs || !Array.isArray(tabConfiguration.developerTabs)) { console.warn('Invalid tab configuration, using empty array'); return []; @@ -223,7 +222,6 @@ export const DeveloperWindow = ({ open, onClose }: DeveloperWindowProps) => { // Hide notifications tab if notifications are disabled if (tab.id === 'notifications' && !profile.notifications) { - console.log('Hiding notifications tab due to disabled notifications'); return false; } @@ -235,7 +233,6 @@ export const DeveloperWindow = ({ open, onClose }: DeveloperWindowProps) => { // Only show tabs that are explicitly visible and assigned to the developer window const isVisible = tab.visible && tab.window === 'developer'; - console.log(`Tab ${tab.id} visibility:`, isVisible); return isVisible; }) @@ -247,8 +244,6 @@ export const DeveloperWindow = ({ open, onClose }: DeveloperWindowProps) => { }); }, [tabConfiguration, profile.notifications]); - console.log('Filtered visible developer tabs:', visibleDeveloperTabs); - const moveTab = (dragIndex: number, hoverIndex: number) => { const draggedTab = visibleDeveloperTabs[dragIndex]; const targetTab = visibleDeveloperTabs[hoverIndex]; @@ -324,6 +319,8 @@ export const DeveloperWindow = ({ open, onClose }: DeveloperWindowProps) => { return ; case 'task-manager': return ; + case 'service-status': + return ; default: return null; } diff --git a/app/components/settings/developer/TabManagement.tsx b/app/components/settings/developer/TabManagement.tsx index ea90feedde..f1523de0d3 100644 --- a/app/components/settings/developer/TabManagement.tsx +++ b/app/components/settings/developer/TabManagement.tsx @@ -177,7 +177,15 @@ export const TabManagement = () => { const [searchQuery, setSearchQuery] = useState(''); // Define standard (visible by default) tabs for each window - const standardUserTabs: TabType[] = ['features', 'data', 'local-providers', 'cloud-providers', 'connection', 'debug']; + const standardUserTabs: TabType[] = [ + 'features', + 'data', + 'local-providers', + 'cloud-providers', + 'connection', + 'debug', + 'service-status', + ]; const standardDeveloperTabs: TabType[] = [ 'profile', 'settings', @@ -190,6 +198,8 @@ export const TabManagement = () => { 'debug', 'event-logs', 'update', + 'task-manager', + 'service-status', ]; const handleVisibilityChange = (tabId: TabType, enabled: boolean, targetWindow: 'user' | 'developer') => { diff --git a/app/components/settings/event-logs/EventLogsTab.tsx b/app/components/settings/event-logs/EventLogsTab.tsx index ce713565f3..5d53184170 100644 --- a/app/components/settings/event-logs/EventLogsTab.tsx +++ b/app/components/settings/event-logs/EventLogsTab.tsx @@ -5,7 +5,6 @@ import { logStore, type LogEntry } from '~/lib/stores/logs'; import { useStore } from '@nanostores/react'; import { classNames } from '~/utils/classNames'; import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; -import { settingsStyles } from '~/components/settings/settings.styles'; interface SelectOption { value: string; @@ -231,7 +230,13 @@ export function EventLogsTab() { const selectedCategoryOption = logCategoryOptions.find((opt) => opt.value === selectedCategory); return ( -
+
{/* Header */}
@@ -245,10 +250,11 @@ export function EventLogsTab() { -
- -
- - - - - - - - - - - - - - {processes.map((process, index) => ( - - - - - - - - - - ))} - -
ProcessTypeCPUMemoryStatusImpact - Last Update -
-
-
- {process.name} -
-
- {process.type} - - - {process.cpuUsage.toFixed(1)}% - - - - {process.memoryUsage.toFixed(1)} MB - - - - {process.status} - - - {process.impact} - - - {new Date(process.lastUpdate).toLocaleTimeString()} - -
-
-
- - {/* Energy Savings */} -
-

Energy Savings

-
-
-
-
- Time in Saver Mode -
-

- {Math.floor(energySavings.timeInSaverMode / 60)}m {Math.floor(energySavings.timeInSaverMode % 60)}s -

-
- -
-
-
- Updates Reduced -
-

{energySavings.updatesReduced}

-
-
-
-
- Estimated Energy Saved + {/* Energy Savings */} + {energySaverMode && ( +
+

Energy Savings

+
+
+ Updates Reduced +

{energySavings.updatesReduced}

+
+
+ Time in Saver Mode +

+ {Math.floor(energySavings.timeInSaverMode / 60)}m {Math.floor(energySavings.timeInSaverMode % 60)}s +

+
+
+ Energy Saved +

+ {energySavings.estimatedEnergySaved.toFixed(2)} mWh +

+
-

- {energySavings.estimatedEnergySaved.toFixed(2)} mWh -

-
+ )}
); diff --git a/app/components/settings/user/UsersWindow.tsx b/app/components/settings/user/UsersWindow.tsx index 21156b9051..d4439e57b4 100644 --- a/app/components/settings/user/UsersWindow.tsx +++ b/app/components/settings/user/UsersWindow.tsx @@ -263,8 +263,6 @@ export const UsersWindow = ({ open, onClose }: UsersWindowProps) => { }); }, [tabConfiguration, profile.notifications]); - console.log('Filtered visible user tabs:', visibleUserTabs); - const moveTab = (dragIndex: number, hoverIndex: number) => { const draggedTab = visibleUserTabs[dragIndex]; const targetTab = visibleUserTabs[hoverIndex]; diff --git a/app/components/ui/Badge.tsx b/app/components/ui/Badge.tsx index c57afd93ff..5f2ccdb21b 100644 --- a/app/components/ui/Badge.tsx +++ b/app/components/ui/Badge.tsx @@ -2,17 +2,19 @@ import * as React from 'react'; import { cva, type VariantProps } from 'class-variance-authority'; -import { cn } from '~/lib/utils'; +import { classNames } from '~/utils/classNames'; const badgeVariants = cva( - 'inline-flex items-center rounded-md px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2', + 'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-bolt-elements-ring focus:ring-offset-2', { variants: { variant: { - default: 'border-transparent bg-primary text-primary-foreground', - secondary: 'border-transparent bg-secondary text-secondary-foreground', - destructive: 'border-transparent bg-red-500/10 text-red-500 dark:bg-red-900/30', - outline: 'text-foreground', + default: + 'border-transparent bg-bolt-elements-background text-bolt-elements-textPrimary hover:bg-bolt-elements-background/80', + secondary: + 'border-transparent bg-bolt-elements-background text-bolt-elements-textSecondary hover:bg-bolt-elements-background/80', + destructive: 'border-transparent bg-red-500/10 text-red-500 hover:bg-red-500/20', + outline: 'text-bolt-elements-textPrimary', }, }, defaultVariants: { @@ -21,13 +23,10 @@ const badgeVariants = cva( }, ); -interface BadgeProps extends React.HTMLAttributes, VariantProps { - variant?: 'default' | 'secondary' | 'destructive' | 'outline'; -} +export interface BadgeProps extends React.HTMLAttributes, VariantProps {} -function Badge({ className, variant = 'default', ...props }: BadgeProps) { - return
; +function Badge({ className, variant, ...props }: BadgeProps) { + return
; } export { Badge, badgeVariants }; -export type { BadgeProps }; diff --git a/app/components/ui/Button.tsx b/app/components/ui/Button.tsx index 6271072fa7..14cc562ca4 100644 --- a/app/components/ui/Button.tsx +++ b/app/components/ui/Button.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { cva, type VariantProps } from 'class-variance-authority'; -import { cn } from '~/lib/utils'; +import { classNames } from '~/utils/classNames'; const buttonVariants = cva( 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-bolt-elements-borderColor disabled:pointer-events-none disabled:opacity-50', @@ -38,7 +38,7 @@ export interface ButtonProps const Button = React.forwardRef( ({ className, variant, size, _asChild = false, ...props }, ref) => { - return