big fixes

fixes feedback from thecodacus
This commit is contained in:
Stijnus
2025-01-30 17:17:36 +01:00
parent d9a380f28a
commit d1d23d80e7
68 changed files with 2449 additions and 1350 deletions

View File

@@ -1,13 +1,12 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useState, useMemo } from 'react';
import { toast } from 'react-toastify';
import { classNames } from '~/utils/classNames';
import { logStore } from '~/lib/stores/logs';
import type { LogEntry } from '~/lib/stores/logs';
import { logStore, type LogEntry } from '~/lib/stores/logs';
import { useStore } from '@nanostores/react';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '~/components/ui/Collapsible';
import { Progress } from '~/components/ui/Progress';
import { ScrollArea } from '~/components/ui/ScrollArea';
import { Badge } from '~/components/ui/Badge';
import { cn } from '~/lib/utils';
interface SystemInfo {
os: string;
@@ -88,125 +87,134 @@ interface SystemInfo {
};
}
interface GitHubRepoInfo {
fullName: string;
defaultBranch: string;
stars: number;
forks: number;
openIssues?: number;
}
interface GitInfo {
local: {
commitHash: string;
branch: string;
commitTime: string;
author: string;
email: string;
remoteUrl: string;
repoName: string;
};
github?: {
currentRepo: GitHubRepoInfo;
upstream?: GitHubRepoInfo;
};
isForked?: boolean;
}
interface WebAppInfo {
// Local WebApp Info
name: string;
version: string;
description: string;
license: string;
nodeVersion: string;
dependencies: { [key: string]: string };
devDependencies: { [key: string]: string };
// Build Info
buildTime?: string;
buildNumber?: string;
environment?: string;
// Git Info
gitInfo?: {
branch: string;
commit: string;
commitTime: string;
author: string;
remoteUrl: string;
environment: string;
timestamp: string;
runtimeInfo: {
nodeVersion: string;
};
// GitHub Repository Info
repoInfo?: {
name: string;
fullName: string;
description: string;
stars: number;
forks: number;
openIssues: number;
defaultBranch: string;
lastUpdate: string;
owner: {
login: string;
avatarUrl: string;
};
dependencies: {
production: Array<{ name: string; version: string; type: string }>;
development: Array<{ name: string; version: string; type: string }>;
peer: Array<{ name: string; version: string; type: string }>;
optional: Array<{ name: string; version: string; type: string }>;
};
gitInfo: GitInfo;
}
// Add interface for GitHub API response
interface GitHubRepoResponse {
name: string;
full_name: string;
description: string | null;
stargazers_count: number;
forks_count: number;
open_issues_count: number;
default_branch: string;
updated_at: string;
owner: {
login: string;
avatar_url: string;
};
}
const DependencySection = ({
title,
deps,
}: {
title: string;
deps: Array<{ name: string; version: string; type: string }>;
}) => {
const [isOpen, setIsOpen] = useState(false);
// Add interface for Git info response
interface GitInfo {
branch: string;
commit: string;
commitTime: string;
author: string;
remoteUrl: string;
}
if (deps.length === 0) {
return null;
}
return (
<Collapsible open={isOpen} onOpenChange={setIsOpen}>
<CollapsibleTrigger className="flex w-full items-center justify-between p-4 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg">
<div className="flex items-center gap-3">
<div className="i-ph:package text-bolt-elements-textSecondary w-4 h-4" />
<span className="text-base text-bolt-elements-textPrimary">
{title} Dependencies ({deps.length})
</span>
</div>
<div className="flex items-center gap-2">
<span className="text-sm text-bolt-elements-textSecondary">{isOpen ? 'Hide' : 'Show'}</span>
<div
className={classNames(
'i-ph:caret-down w-4 h-4 transform transition-transform duration-200',
isOpen ? 'rotate-180' : '',
)}
/>
</div>
</CollapsibleTrigger>
<CollapsibleContent>
<ScrollArea className="h-[200px] w-full p-4">
<div className="space-y-2 pl-7">
{deps.map((dep) => (
<div key={dep.name} className="flex items-center justify-between text-sm">
<span className="text-bolt-elements-textPrimary">{dep.name}</span>
<span className="text-bolt-elements-textSecondary">{dep.version}</span>
</div>
))}
</div>
</ScrollArea>
</CollapsibleContent>
</Collapsible>
);
};
export default function DebugTab() {
const [systemInfo, setSystemInfo] = useState<SystemInfo | null>(null);
const [webAppInfo, setWebAppInfo] = useState<WebAppInfo | null>(null);
const [loading, setLoading] = useState({
systemInfo: false,
performance: false,
errors: false,
webAppInfo: false,
errors: false,
performance: false,
});
const [errorLog, setErrorLog] = useState<{
errors: any[];
lastCheck: string | null;
}>({
errors: [],
lastCheck: null,
});
// Add section collapse state
const [openSections, setOpenSections] = useState({
system: true,
performance: true,
webapp: true,
errors: true,
system: false,
webapp: false,
errors: false,
performance: false,
});
// Fetch initial data
useEffect(() => {
getSystemInfo();
getWebAppInfo();
}, []);
// Subscribe to logStore updates
const logs = useStore(logStore.logs);
const errorLogs = useMemo(() => {
return Object.values(logs).filter(
(log): log is LogEntry => typeof log === 'object' && log !== null && 'level' in log && log.level === 'error',
);
}, [logs]);
// Set up error listeners when component mounts
useEffect(() => {
const errors: any[] = [];
const handleError = (event: ErrorEvent) => {
errors.push({
type: 'error',
message: event.message,
logStore.logError(event.message, event.error, {
filename: event.filename,
lineNumber: event.lineno,
columnNumber: event.colno,
error: event.error,
timestamp: new Date().toISOString(),
});
};
const handleRejection = (event: PromiseRejectionEvent) => {
errors.push({
type: 'unhandledRejection',
reason: event.reason,
timestamp: new Date().toISOString(),
});
logStore.logError('Unhandled Promise Rejection', event.reason);
};
window.addEventListener('error', handleError);
@@ -218,6 +226,66 @@ export default function DebugTab() {
};
}, []);
// Check for errors when the errors section is opened
useEffect(() => {
if (openSections.errors) {
checkErrors();
}
}, [openSections.errors]);
// Load initial data when component mounts
useEffect(() => {
const loadInitialData = async () => {
await Promise.all([getSystemInfo(), getWebAppInfo()]);
};
loadInitialData();
}, []);
// Refresh data when sections are opened
useEffect(() => {
if (openSections.system) {
getSystemInfo();
}
if (openSections.webapp) {
getWebAppInfo();
}
}, [openSections.system, openSections.webapp]);
// Add periodic refresh of git info
useEffect(() => {
if (!openSections.webapp) {
return undefined;
}
const interval = setInterval(async () => {
try {
const response = await fetch('/api/system/git-info');
const updatedGitInfo = (await response.json()) as GitInfo;
setWebAppInfo((prev) => {
if (!prev) {
return null;
}
return {
...prev,
gitInfo: updatedGitInfo,
};
});
} catch (error) {
console.error('Failed to refresh git info:', error);
}
}, 5000);
const cleanup = () => {
clearInterval(interval);
};
return cleanup;
}, [openSections.webapp]);
const getSystemInfo = async () => {
try {
setLoading((prev) => ({ ...prev, systemInfo: true }));
@@ -367,67 +435,32 @@ export default function DebugTab() {
try {
setLoading((prev) => ({ ...prev, webAppInfo: true }));
// Fetch local app info
const appInfoResponse = await fetch('/api/system/app-info');
const [appResponse, gitResponse] = await Promise.all([
fetch('/api/system/app-info'),
fetch('/api/system/git-info'),
]);
if (!appInfoResponse.ok) {
if (!appResponse.ok || !gitResponse.ok) {
throw new Error('Failed to fetch webapp info');
}
const appData = (await appInfoResponse.json()) as Record<string, unknown>;
// Fetch git info
const gitInfoResponse = await fetch('/api/system/git-info');
let gitInfo: GitInfo | undefined;
if (gitInfoResponse.ok) {
gitInfo = (await gitInfoResponse.json()) as GitInfo;
}
// Fetch GitHub repository info
const repoInfoResponse = await fetch('https://api.github.com/repos/stackblitz-labs/bolt.diy');
let repoInfo: WebAppInfo['repoInfo'] | undefined;
if (repoInfoResponse.ok) {
const repoData = (await repoInfoResponse.json()) as GitHubRepoResponse;
repoInfo = {
name: repoData.name,
fullName: repoData.full_name,
description: repoData.description ?? '',
stars: repoData.stargazers_count,
forks: repoData.forks_count,
openIssues: repoData.open_issues_count,
defaultBranch: repoData.default_branch,
lastUpdate: repoData.updated_at,
owner: {
login: repoData.owner.login,
avatarUrl: repoData.owner.avatar_url,
},
};
}
// Get build info from environment variables or config
const buildInfo = {
buildTime: process.env.NEXT_PUBLIC_BUILD_TIME || new Date().toISOString(),
buildNumber: process.env.NEXT_PUBLIC_BUILD_NUMBER || 'development',
environment: process.env.NEXT_PUBLIC_ENV || 'development',
};
const appData = (await appResponse.json()) as Omit<WebAppInfo, 'gitInfo'>;
const gitData = (await gitResponse.json()) as GitInfo;
setWebAppInfo({
name: appData.name as string,
version: appData.version as string,
description: appData.description as string,
license: appData.license as string,
nodeVersion: appData.nodeVersion as string,
dependencies: appData.dependencies as Record<string, string>,
devDependencies: appData.devDependencies as Record<string, string>,
...buildInfo,
gitInfo,
repoInfo,
...appData,
gitInfo: gitData,
});
toast.success('WebApp information updated');
return true;
} catch (error) {
console.error('Failed to fetch webapp info:', error);
toast.error('Failed to fetch webapp information');
setWebAppInfo(null);
return false;
} finally {
setLoading((prev) => ({ ...prev, webAppInfo: false }));
}
@@ -536,28 +569,12 @@ export default function DebugTab() {
setLoading((prev) => ({ ...prev, errors: true }));
// Get errors from log store
const storedErrors = logStore.getLogs().filter((log: LogEntry) => log.level === 'error');
const storedErrors = errorLogs;
// Combine with runtime errors
const allErrors = [
...errorLog.errors,
...storedErrors.map((error) => ({
type: 'stored',
message: error.message,
timestamp: error.timestamp,
details: error.details || {},
})),
];
setErrorLog({
errors: allErrors,
lastCheck: new Date().toISOString(),
});
if (allErrors.length === 0) {
if (storedErrors.length === 0) {
toast.success('No errors found');
} else {
toast.warning(`Found ${allErrors.length} error(s)`);
toast.warning(`Found ${storedErrors.length} error(s)`);
}
} catch (error) {
toast.error('Failed to check errors');
@@ -573,7 +590,7 @@ export default function DebugTab() {
timestamp: new Date().toISOString(),
system: systemInfo,
webApp: webAppInfo,
errors: errorLog.errors,
errors: logStore.getLogs().filter((log: LogEntry) => log.level === 'error'),
performance: {
memory: (performance as any).memory || {},
timing: performance.timing,
@@ -629,10 +646,7 @@ export default function DebugTab() {
<div className="p-4 rounded-xl bg-gradient-to-br from-red-500/10 to-red-500/5 border border-red-500/20">
<div className="text-sm text-bolt-elements-textSecondary">Errors</div>
<div className="text-2xl font-semibold text-bolt-elements-textPrimary mt-1">{errorLog.errors.length}</div>
<div className="text-xs text-bolt-elements-textSecondary mt-2">
Last Check: {errorLog.lastCheck ? new Date(errorLog.lastCheck).toLocaleTimeString() : 'Never'}
</div>
<div className="text-2xl font-semibold text-bolt-elements-textPrimary mt-1">{errorLogs.length}</div>
</div>
</div>
@@ -746,7 +760,7 @@ export default function DebugTab() {
<h3 className="text-base font-medium text-bolt-elements-textPrimary">System Information</h3>
</div>
<div
className={cn(
className={classNames(
'i-ph:caret-down w-4 h-4 transform transition-transform duration-200',
openSections.system ? 'rotate-180' : '',
)}
@@ -893,7 +907,7 @@ export default function DebugTab() {
<h3 className="text-base font-medium text-bolt-elements-textPrimary">Performance Metrics</h3>
</div>
<div
className={cn(
className={classNames(
'i-ph:caret-down w-4 h-4 transform transition-transform duration-200',
openSections.performance ? 'rotate-180' : '',
)}
@@ -973,7 +987,7 @@ export default function DebugTab() {
{/* WebApp Information */}
<Collapsible
open={openSections.webapp}
onOpenChange={(open: boolean) => setOpenSections((prev) => ({ ...prev, webapp: open }))}
onOpenChange={(open) => setOpenSections((prev) => ({ ...prev, webapp: open }))}
className="w-full"
>
<CollapsibleTrigger className="w-full">
@@ -981,9 +995,10 @@ export default function DebugTab() {
<div className="flex items-center gap-3">
<div className="i-ph:info text-blue-500 w-5 h-5" />
<h3 className="text-base font-medium text-bolt-elements-textPrimary">WebApp Information</h3>
{loading.webAppInfo && <span className="loading loading-spinner loading-sm" />}
</div>
<div
className={cn(
className={classNames(
'i-ph:caret-down w-4 h-4 transform transition-transform duration-200',
openSections.webapp ? 'rotate-180' : '',
)}
@@ -993,142 +1008,154 @@ export default function DebugTab() {
<CollapsibleContent>
<div className="p-6 mt-2 rounded-xl bg-white dark:bg-[#0A0A0A] border border-[#E5E5E5] dark:border-[#1A1A1A]">
{webAppInfo ? (
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<div className="text-sm flex items-center gap-2">
<div className="i-ph:app-window text-bolt-elements-textSecondary w-4 h-4" />
<span className="text-bolt-elements-textSecondary">Name: </span>
<span className="text-bolt-elements-textPrimary">{webAppInfo.name}</span>
</div>
<div className="text-sm flex items-center gap-2">
<div className="i-ph:tag text-bolt-elements-textSecondary w-4 h-4" />
<span className="text-bolt-elements-textSecondary">Version: </span>
<span className="text-bolt-elements-textPrimary">{webAppInfo.version}</span>
</div>
<div className="text-sm flex items-center gap-2">
<div className="i-ph:file-text text-bolt-elements-textSecondary w-4 h-4" />
<span className="text-bolt-elements-textSecondary">Description: </span>
<span className="text-bolt-elements-textPrimary">{webAppInfo.description}</span>
</div>
<div className="text-sm flex items-center gap-2">
<div className="i-ph:certificate text-bolt-elements-textSecondary w-4 h-4" />
<span className="text-bolt-elements-textSecondary">License: </span>
<span className="text-bolt-elements-textPrimary">{webAppInfo.license}</span>
</div>
<div className="text-sm flex items-center gap-2">
<div className="i-ph:node text-bolt-elements-textSecondary w-4 h-4" />
<span className="text-bolt-elements-textSecondary">Node Version: </span>
<span className="text-bolt-elements-textPrimary">{webAppInfo.nodeVersion}</span>
</div>
{webAppInfo.buildTime && (
{loading.webAppInfo ? (
<div className="flex items-center justify-center p-8">
<span className="loading loading-spinner loading-lg" />
</div>
) : !webAppInfo ? (
<div className="flex flex-col items-center justify-center p-8 text-bolt-elements-textSecondary">
<div className="i-ph:warning-circle w-8 h-8 mb-2" />
<p>Failed to load WebApp information</p>
<button
onClick={() => getWebAppInfo()}
className="mt-4 px-4 py-2 text-sm bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors"
>
Retry
</button>
</div>
) : (
<div className="grid grid-cols-2 gap-6">
<div>
<h3 className="mb-4 text-base font-medium text-bolt-elements-textPrimary">Basic Information</h3>
<div className="space-y-3">
<div className="text-sm flex items-center gap-2">
<div className="i-ph:calendar text-bolt-elements-textSecondary w-4 h-4" />
<span className="text-bolt-elements-textSecondary">Build Time: </span>
<span className="text-bolt-elements-textPrimary">{webAppInfo.buildTime}</span>
<div className="i-ph:app-window text-bolt-elements-textSecondary w-4 h-4" />
<span className="text-bolt-elements-textSecondary">Name:</span>
<span className="text-bolt-elements-textPrimary">{webAppInfo.name}</span>
</div>
)}
{webAppInfo.buildNumber && (
<div className="text-sm flex items-center gap-2">
<div className="i-ph:hash text-bolt-elements-textSecondary w-4 h-4" />
<span className="text-bolt-elements-textSecondary">Build Number: </span>
<span className="text-bolt-elements-textPrimary">{webAppInfo.buildNumber}</span>
<div className="i-ph:tag text-bolt-elements-textSecondary w-4 h-4" />
<span className="text-bolt-elements-textSecondary">Version:</span>
<span className="text-bolt-elements-textPrimary">{webAppInfo.version}</span>
</div>
<div className="text-sm flex items-center gap-2">
<div className="i-ph:certificate text-bolt-elements-textSecondary w-4 h-4" />
<span className="text-bolt-elements-textSecondary">License:</span>
<span className="text-bolt-elements-textPrimary">{webAppInfo.license}</span>
</div>
)}
{webAppInfo.environment && (
<div className="text-sm flex items-center gap-2">
<div className="i-ph:cloud text-bolt-elements-textSecondary w-4 h-4" />
<span className="text-bolt-elements-textSecondary">Environment: </span>
<span className="text-bolt-elements-textSecondary">Environment:</span>
<span className="text-bolt-elements-textPrimary">{webAppInfo.environment}</span>
</div>
)}
</div>
<div className="space-y-2">
<div className="text-sm">
<div className="flex items-center gap-2 mb-2">
<div className="i-ph:package text-bolt-elements-textSecondary w-4 h-4" />
<span className="text-bolt-elements-textSecondary">Key Dependencies:</span>
</div>
<div className="pl-6 space-y-1">
{Object.entries(webAppInfo.dependencies)
.filter(([key]) => ['react', '@remix-run/react', 'next', 'typescript'].includes(key))
.map(([key, version]) => (
<div key={key} className="text-xs text-bolt-elements-textPrimary">
{key}: {version}
</div>
))}
<div className="text-sm flex items-center gap-2">
<div className="i-ph:node text-bolt-elements-textSecondary w-4 h-4" />
<span className="text-bolt-elements-textSecondary">Node Version:</span>
<span className="text-bolt-elements-textPrimary">{webAppInfo.runtimeInfo.nodeVersion}</span>
</div>
</div>
{webAppInfo.gitInfo && (
<div className="text-sm">
<div className="flex items-center gap-2 mb-2">
<div className="i-ph:git-branch text-bolt-elements-textSecondary w-4 h-4" />
<span className="text-bolt-elements-textSecondary">Git Info:</span>
</div>
<div className="pl-6 space-y-1">
<div className="text-xs text-bolt-elements-textPrimary">
Branch: {webAppInfo.gitInfo.branch}
</div>
<div className="text-xs text-bolt-elements-textPrimary">
Commit: {webAppInfo.gitInfo.commit}
</div>
<div className="text-xs text-bolt-elements-textPrimary">
Commit Time: {webAppInfo.gitInfo.commitTime}
</div>
<div className="text-xs text-bolt-elements-textPrimary">
Author: {webAppInfo.gitInfo.author}
</div>
<div className="text-xs text-bolt-elements-textPrimary">
Remote URL: {webAppInfo.gitInfo.remoteUrl}
</div>
</div>
</div>
<div>
<h3 className="mb-4 text-base font-medium text-bolt-elements-textPrimary">Git Information</h3>
<div className="space-y-3">
<div className="text-sm flex items-center gap-2">
<div className="i-ph:git-branch text-bolt-elements-textSecondary w-4 h-4" />
<span className="text-bolt-elements-textSecondary">Branch:</span>
<span className="text-bolt-elements-textPrimary">{webAppInfo.gitInfo.local.branch}</span>
</div>
)}
{webAppInfo.repoInfo && (
<div className="text-sm">
<div className="flex items-center gap-2 mb-2">
<div className="i-ph:github text-bolt-elements-textSecondary w-4 h-4" />
<span className="text-bolt-elements-textSecondary">GitHub Repository:</span>
</div>
<div className="pl-6 space-y-3">
<div className="flex items-center gap-3">
<img
src={webAppInfo.repoInfo.owner.avatarUrl}
alt={`${webAppInfo.repoInfo.owner.login}'s avatar`}
className="w-8 h-8 rounded-full border border-[#E5E5E5] dark:border-[#1A1A1A]"
/>
<div className="space-y-0.5">
<div className="text-xs text-bolt-elements-textPrimary font-medium">
Owner: {webAppInfo.repoInfo.owner.login}
<div className="text-sm flex items-center gap-2">
<div className="i-ph:git-commit text-bolt-elements-textSecondary w-4 h-4" />
<span className="text-bolt-elements-textSecondary">Commit:</span>
<span className="text-bolt-elements-textPrimary">{webAppInfo.gitInfo.local.commitHash}</span>
</div>
<div className="text-sm flex items-center gap-2">
<div className="i-ph:user text-bolt-elements-textSecondary w-4 h-4" />
<span className="text-bolt-elements-textSecondary">Author:</span>
<span className="text-bolt-elements-textPrimary">{webAppInfo.gitInfo.local.author}</span>
</div>
<div className="text-sm flex items-center gap-2">
<div className="i-ph:clock text-bolt-elements-textSecondary w-4 h-4" />
<span className="text-bolt-elements-textSecondary">Commit Time:</span>
<span className="text-bolt-elements-textPrimary">{webAppInfo.gitInfo.local.commitTime}</span>
</div>
{webAppInfo.gitInfo.github && (
<>
<div className="mt-4 pt-4 border-t border-gray-200 dark:border-gray-800">
<div className="text-sm flex items-center gap-2">
<div className="i-ph:git-fork text-bolt-elements-textSecondary w-4 h-4" />
<span className="text-bolt-elements-textSecondary">Repository:</span>
<span className="text-bolt-elements-textPrimary">
{webAppInfo.gitInfo.github.currentRepo.fullName}
{webAppInfo.gitInfo.isForked && ' (fork)'}
</span>
</div>
<div className="mt-2 flex items-center gap-4 text-sm">
<div className="flex items-center gap-1">
<div className="i-ph:star text-yellow-500 w-4 h-4" />
<span className="text-bolt-elements-textSecondary">
{webAppInfo.gitInfo.github.currentRepo.stars}
</span>
</div>
<div className="text-xs text-bolt-elements-textSecondary">
Last Update: {new Date(webAppInfo.repoInfo.lastUpdate).toLocaleDateString()}
<div className="flex items-center gap-1">
<div className="i-ph:git-fork text-blue-500 w-4 h-4" />
<span className="text-bolt-elements-textSecondary">
{webAppInfo.gitInfo.github.currentRepo.forks}
</span>
</div>
<div className="flex items-center gap-1">
<div className="i-ph:warning-circle text-red-500 w-4 h-4" />
<span className="text-bolt-elements-textSecondary">
{webAppInfo.gitInfo.github.currentRepo.openIssues}
</span>
</div>
</div>
</div>
<div className="grid grid-cols-3 gap-2 mt-2">
<div className="flex items-center gap-1 text-xs text-bolt-elements-textSecondary">
<div className="i-ph:star text-yellow-500 w-4 h-4" />
{webAppInfo.repoInfo.stars.toLocaleString()} stars
{webAppInfo.gitInfo.github.upstream && (
<div className="mt-2">
<div className="text-sm flex items-center gap-2">
<div className="i-ph:git-fork text-bolt-elements-textSecondary w-4 h-4" />
<span className="text-bolt-elements-textSecondary">Upstream:</span>
<span className="text-bolt-elements-textPrimary">
{webAppInfo.gitInfo.github.upstream.fullName}
</span>
</div>
<div className="mt-2 flex items-center gap-4 text-sm">
<div className="flex items-center gap-1">
<div className="i-ph:star text-yellow-500 w-4 h-4" />
<span className="text-bolt-elements-textSecondary">
{webAppInfo.gitInfo.github.upstream.stars}
</span>
</div>
<div className="flex items-center gap-1">
<div className="i-ph:git-fork text-blue-500 w-4 h-4" />
<span className="text-bolt-elements-textSecondary">
{webAppInfo.gitInfo.github.upstream.forks}
</span>
</div>
</div>
</div>
<div className="flex items-center gap-1 text-xs text-bolt-elements-textSecondary">
<div className="i-ph:git-fork text-blue-500 w-4 h-4" />
{webAppInfo.repoInfo.forks.toLocaleString()} forks
</div>
<div className="flex items-center gap-1 text-xs text-bolt-elements-textSecondary">
<div className="i-ph:warning-circle text-red-500 w-4 h-4" />
{webAppInfo.repoInfo.openIssues.toLocaleString()} issues
</div>
</div>
</div>
</div>
)}
)}
</>
)}
</div>
</div>
</div>
) : (
<div className="text-sm text-bolt-elements-textSecondary">
{loading.webAppInfo ? 'Loading webapp information...' : 'No webapp information available'}
)}
{webAppInfo && (
<div className="mt-6">
<h3 className="mb-4 text-base font-medium text-bolt-elements-textPrimary">Dependencies</h3>
<div className="space-y-2 bg-gray-50 dark:bg-[#1A1A1A] rounded-lg">
<DependencySection title="Production" deps={webAppInfo.dependencies.production} />
<DependencySection title="Development" deps={webAppInfo.dependencies.development} />
<DependencySection title="Peer" deps={webAppInfo.dependencies.peer} />
<DependencySection title="Optional" deps={webAppInfo.dependencies.optional} />
</div>
</div>
)}
</div>
@@ -1138,7 +1165,7 @@ export default function DebugTab() {
{/* Error Check */}
<Collapsible
open={openSections.errors}
onOpenChange={(open: boolean) => setOpenSections((prev) => ({ ...prev, errors: open }))}
onOpenChange={(open) => setOpenSections((prev) => ({ ...prev, errors: open }))}
className="w-full"
>
<CollapsibleTrigger className="w-full">
@@ -1146,14 +1173,14 @@ export default function DebugTab() {
<div className="flex items-center gap-3">
<div className="i-ph:warning text-red-500 w-5 h-5" />
<h3 className="text-base font-medium text-bolt-elements-textPrimary">Error Check</h3>
{errorLog.errors.length > 0 && (
{errorLogs.length > 0 && (
<Badge variant="destructive" className="ml-2">
{errorLog.errors.length} Errors
{errorLogs.length} Errors
</Badge>
)}
</div>
<div
className={cn(
className={classNames(
'i-ph:caret-down w-4 h-4 transform transition-transform duration-200',
openSections.errors ? 'rotate-180' : '',
)}
@@ -1175,31 +1202,33 @@ export default function DebugTab() {
</ul>
</div>
<div className="text-sm">
<span className="text-bolt-elements-textSecondary">Last Check: </span>
<span className="text-bolt-elements-textSecondary">Status: </span>
<span className="text-bolt-elements-textPrimary">
{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'}
</span>
</div>
{errorLog.errors.length > 0 && (
{errorLogs.length > 0 && (
<div className="mt-4">
<div className="text-sm font-medium text-bolt-elements-textPrimary mb-2">Recent Errors:</div>
<div className="space-y-2">
{errorLog.errors.slice(0, 3).map((error, index) => (
<div key={index} className="text-sm text-red-500 dark:text-red-400">
{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) => (
<div key={error.id} className="text-sm text-red-500 dark:text-red-400 p-2 rounded bg-red-500/5">
<div className="font-medium">{error.message}</div>
{error.source && (
<div className="text-xs mt-1 text-red-400">
Source: {error.source}
{error.details?.lineNumber && `:${error.details.lineNumber}`}
</div>
)}
{error.stack && (
<div className="text-xs mt-1 text-red-400 font-mono whitespace-pre-wrap">{error.stack}</div>
)}
</div>
))}
{errorLog.errors.length > 3 && (
<div className="text-sm text-bolt-elements-textSecondary">
And {errorLog.errors.length - 3} more errors...
</div>
)}
</div>
</div>
)}