mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-06-26 18:26:38 +00:00
style: Fix linting issues in UI components
This commit is contained in:
parent
7f694d0e4b
commit
3b0fbe7ccd
@ -21,6 +21,7 @@ export function RepositoryCard({ repo, onSelect }: RepositoryCardProps) {
|
||||
'from-pink-500/10 to-purple-500/5',
|
||||
];
|
||||
const index = name.length % colors.length;
|
||||
|
||||
return colors[index];
|
||||
};
|
||||
|
||||
@ -31,10 +32,21 @@ export function RepositoryCard({ repo, onSelect }: RepositoryCardProps) {
|
||||
const diffTime = Math.abs(now.getTime() - date.getTime());
|
||||
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
||||
|
||||
if (diffDays <= 1) return 'Today';
|
||||
if (diffDays <= 2) return 'Yesterday';
|
||||
if (diffDays <= 7) return `${diffDays} days ago`;
|
||||
if (diffDays <= 30) return `${Math.floor(diffDays / 7)} weeks ago`;
|
||||
if (diffDays <= 1) {
|
||||
return 'Today';
|
||||
}
|
||||
|
||||
if (diffDays <= 2) {
|
||||
return 'Yesterday';
|
||||
}
|
||||
|
||||
if (diffDays <= 7) {
|
||||
return `${diffDays} days ago`;
|
||||
}
|
||||
|
||||
if (diffDays <= 30) {
|
||||
return `${Math.floor(diffDays / 7)} weeks ago`;
|
||||
}
|
||||
|
||||
return date.toLocaleDateString(undefined, {
|
||||
year: 'numeric',
|
||||
|
@ -5,6 +5,8 @@ export interface RepositoryDialogContextType {
|
||||
setShowAuthDialog: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
// Default context value with a no-op function
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
export const RepositoryDialogContext = createContext<RepositoryDialogContextType>({
|
||||
setShowAuthDialog: () => {},
|
||||
});
|
||||
|
@ -1,5 +1,4 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import type { GitHubRepoInfo } from '~/types/GitHub';
|
||||
import { EmptyState, StatusIndicator } from '~/components/ui';
|
||||
import { RepositoryCard } from './RepositoryCard';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { GitHubRepoInfo, GitHubContent, RepositoryStats, GitHubUserResponse } from '~/types/GitHub';
|
||||
import { useState, useEffect, useContext } from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import * as Dialog from '@radix-ui/react-dialog';
|
||||
import { classNames } from '~/utils/classNames';
|
||||
@ -8,17 +8,7 @@ import { motion, AnimatePresence } from 'framer-motion';
|
||||
import Cookies from 'js-cookie';
|
||||
|
||||
// Import UI components
|
||||
import {
|
||||
Input,
|
||||
SearchInput,
|
||||
Badge,
|
||||
FilterChip,
|
||||
EmptyState,
|
||||
GradientCard,
|
||||
TabsWithSlider,
|
||||
StatusIndicator,
|
||||
RepositoryStats as RepoStats,
|
||||
} from '~/components/ui';
|
||||
import { Input, SearchInput, Badge, FilterChip } from '~/components/ui';
|
||||
|
||||
// Import the components we've extracted
|
||||
import { TabButton } from './TabButton';
|
||||
|
@ -3,7 +3,7 @@ import * as Dialog from '@radix-ui/react-dialog';
|
||||
import { motion } from 'framer-motion';
|
||||
import type { RepositoryStats } from '~/types/GitHub';
|
||||
import { formatSize } from '~/utils/formatSize';
|
||||
import { RepositoryStats as RepoStats, Badge } from '~/components/ui';
|
||||
import { RepositoryStats as RepoStats } from '~/components/ui';
|
||||
|
||||
interface StatsDialogProps {
|
||||
isOpen: boolean;
|
||||
|
@ -24,24 +24,27 @@ export function Breadcrumbs({
|
||||
maxItems = 0,
|
||||
renderItem,
|
||||
}: BreadcrumbsProps) {
|
||||
const displayItems = maxItems > 0 && items.length > maxItems
|
||||
? [
|
||||
...items.slice(0, 1),
|
||||
{ label: '...', onClick: undefined, href: undefined },
|
||||
...items.slice(-Math.max(1, maxItems - 2)),
|
||||
]
|
||||
: items;
|
||||
const displayItems =
|
||||
maxItems > 0 && items.length > maxItems
|
||||
? [
|
||||
...items.slice(0, 1),
|
||||
{ label: '...', onClick: undefined, href: undefined },
|
||||
...items.slice(-Math.max(1, maxItems - 2)),
|
||||
]
|
||||
: items;
|
||||
|
||||
const defaultRenderItem = (item: BreadcrumbItem, index: number, isLast: boolean) => {
|
||||
const content = (
|
||||
<div className="flex items-center gap-1.5">
|
||||
{item.icon && <span className={classNames(item.icon, 'w-3.5 h-3.5')} />}
|
||||
<span className={classNames(
|
||||
isLast
|
||||
? 'font-medium text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary-dark'
|
||||
: 'text-bolt-elements-textSecondary dark:text-bolt-elements-textSecondary-dark hover:text-bolt-elements-textPrimary dark:hover:text-bolt-elements-textPrimary-dark',
|
||||
item.onClick || item.href ? 'cursor-pointer' : ''
|
||||
)}>
|
||||
<span
|
||||
className={classNames(
|
||||
isLast
|
||||
? 'font-medium text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary-dark'
|
||||
: 'text-bolt-elements-textSecondary dark:text-bolt-elements-textSecondary-dark hover:text-bolt-elements-textPrimary dark:hover:text-bolt-elements-textPrimary-dark',
|
||||
item.onClick || item.href ? 'cursor-pointer' : '',
|
||||
)}
|
||||
>
|
||||
{item.label}
|
||||
</span>
|
||||
</div>
|
||||
@ -49,12 +52,7 @@ export function Breadcrumbs({
|
||||
|
||||
if (item.href && !isLast) {
|
||||
return (
|
||||
<motion.a
|
||||
href={item.href}
|
||||
className="hover:underline"
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
>
|
||||
<motion.a href={item.href} className="hover:underline" whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }}>
|
||||
{content}
|
||||
</motion.a>
|
||||
);
|
||||
@ -82,12 +80,17 @@ export function Breadcrumbs({
|
||||
<ol className="flex items-center gap-1.5">
|
||||
{displayItems.map((item, index) => {
|
||||
const isLast = index === displayItems.length - 1;
|
||||
|
||||
|
||||
return (
|
||||
<li key={index} className="flex items-center">
|
||||
{renderItem ? renderItem(item, index, isLast) : defaultRenderItem(item, index, isLast)}
|
||||
{!isLast && (
|
||||
<span className={classNames(separator, 'w-3 h-3 mx-1 text-bolt-elements-textTertiary dark:text-bolt-elements-textTertiary-dark')} />
|
||||
<span
|
||||
className={classNames(
|
||||
separator,
|
||||
'w-3 h-3 mx-1 text-bolt-elements-textTertiary dark:text-bolt-elements-textTertiary-dark',
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
|
@ -37,11 +37,13 @@ export function CodeBlock({
|
||||
const lines = code.split('\n');
|
||||
|
||||
return (
|
||||
<div className={classNames(
|
||||
'rounded-lg overflow-hidden border border-bolt-elements-borderColor dark:border-bolt-elements-borderColor-dark',
|
||||
'bg-bolt-elements-background-depth-2 dark:bg-bolt-elements-background-depth-3',
|
||||
className
|
||||
)}>
|
||||
<div
|
||||
className={classNames(
|
||||
'rounded-lg overflow-hidden border border-bolt-elements-borderColor dark:border-bolt-elements-borderColor-dark',
|
||||
'bg-bolt-elements-background-depth-2 dark:bg-bolt-elements-background-depth-3',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between px-4 py-2 bg-bolt-elements-background-depth-3 dark:bg-bolt-elements-background-depth-4 border-b border-bolt-elements-borderColor dark:border-bolt-elements-borderColor-dark">
|
||||
<div className="flex items-center gap-2">
|
||||
@ -66,36 +68,26 @@ export function CodeBlock({
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
>
|
||||
{copied ? (
|
||||
<span className="i-ph:check w-4 h-4 text-green-500" />
|
||||
) : (
|
||||
<span className="i-ph:copy w-4 h-4" />
|
||||
)}
|
||||
{copied ? <span className="i-ph:check w-4 h-4 text-green-500" /> : <span className="i-ph:copy w-4 h-4" />}
|
||||
</motion.button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
|
||||
{/* Code content */}
|
||||
<div className={classNames(
|
||||
'overflow-auto',
|
||||
'font-mono text-sm',
|
||||
'custom-scrollbar'
|
||||
)} style={{ maxHeight }}>
|
||||
<div className={classNames('overflow-auto', 'font-mono text-sm', 'custom-scrollbar')} style={{ maxHeight }}>
|
||||
<table className="min-w-full border-collapse">
|
||||
<tbody>
|
||||
{lines.map((line, index) => (
|
||||
<tr
|
||||
<tr
|
||||
key={index}
|
||||
className={classNames(
|
||||
highlightLines.includes(index + 1) ? 'bg-purple-500/10 dark:bg-purple-500/20' : '',
|
||||
'hover:bg-bolt-elements-background-depth-3 dark:hover:bg-bolt-elements-background-depth-4'
|
||||
'hover:bg-bolt-elements-background-depth-3 dark:hover:bg-bolt-elements-background-depth-4',
|
||||
)}
|
||||
>
|
||||
{showLineNumbers && (
|
||||
<td className="py-1 pl-4 pr-2 text-right select-none text-bolt-elements-textTertiary dark:text-bolt-elements-textTertiary-dark border-r border-bolt-elements-borderColor dark:border-bolt-elements-borderColor-dark">
|
||||
<span className="inline-block min-w-[1.5rem] text-xs">
|
||||
{index + 1}
|
||||
</span>
|
||||
<span className="inline-block min-w-[1.5rem] text-xs">{index + 1}</span>
|
||||
</td>
|
||||
)}
|
||||
<td className="py-1 pl-4 pr-4 text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary-dark whitespace-pre">
|
||||
|
@ -32,20 +32,28 @@ const VARIANT_STYLES = {
|
||||
interface EmptyStateProps {
|
||||
/** Icon class name */
|
||||
icon?: string;
|
||||
|
||||
/** Title text */
|
||||
title: string;
|
||||
|
||||
/** Optional description text */
|
||||
description?: string;
|
||||
|
||||
/** Primary action button label */
|
||||
actionLabel?: string;
|
||||
|
||||
/** Primary action button callback */
|
||||
onAction?: () => void;
|
||||
|
||||
/** Secondary action button label */
|
||||
secondaryActionLabel?: string;
|
||||
|
||||
/** Secondary action button callback */
|
||||
onSecondaryAction?: () => void;
|
||||
|
||||
/** Additional class name */
|
||||
className?: string;
|
||||
|
||||
/** Component size variant */
|
||||
variant?: 'default' | 'compact';
|
||||
}
|
||||
|
@ -14,110 +14,312 @@ export function FileIcon({ filename, size = 'md', className }: FileIconProps) {
|
||||
|
||||
const getIconForExtension = (extension: string): string => {
|
||||
// Code files
|
||||
if (['js', 'jsx', 'ts', 'tsx'].includes(extension)) return 'i-ph:file-js';
|
||||
if (['html', 'htm', 'xhtml'].includes(extension)) return 'i-ph:file-html';
|
||||
if (['css', 'scss', 'sass', 'less'].includes(extension)) return 'i-ph:file-css';
|
||||
if (['json', 'jsonc'].includes(extension)) return 'i-ph:brackets-curly';
|
||||
if (['md', 'markdown'].includes(extension)) return 'i-ph:file-text';
|
||||
if (['py', 'pyc', 'pyd', 'pyo'].includes(extension)) return 'i-ph:file-py';
|
||||
if (['java', 'class', 'jar'].includes(extension)) return 'i-ph:file-java';
|
||||
if (['php'].includes(extension)) return 'i-ph:file-php';
|
||||
if (['rb', 'ruby'].includes(extension)) return 'i-ph:file-rs';
|
||||
if (['c', 'cpp', 'h', 'hpp', 'cc'].includes(extension)) return 'i-ph:file-cpp';
|
||||
if (['go'].includes(extension)) return 'i-ph:file-rs';
|
||||
if (['rs', 'rust'].includes(extension)) return 'i-ph:file-rs';
|
||||
if (['swift'].includes(extension)) return 'i-ph:file-swift';
|
||||
if (['kt', 'kotlin'].includes(extension)) return 'i-ph:file-kotlin';
|
||||
if (['dart'].includes(extension)) return 'i-ph:file-dart';
|
||||
|
||||
if (['js', 'jsx', 'ts', 'tsx'].includes(extension)) {
|
||||
return 'i-ph:file-js';
|
||||
}
|
||||
|
||||
if (['html', 'htm', 'xhtml'].includes(extension)) {
|
||||
return 'i-ph:file-html';
|
||||
}
|
||||
|
||||
if (['css', 'scss', 'sass', 'less'].includes(extension)) {
|
||||
return 'i-ph:file-css';
|
||||
}
|
||||
|
||||
if (['json', 'jsonc'].includes(extension)) {
|
||||
return 'i-ph:brackets-curly';
|
||||
}
|
||||
|
||||
if (['md', 'markdown'].includes(extension)) {
|
||||
return 'i-ph:file-text';
|
||||
}
|
||||
|
||||
if (['py', 'pyc', 'pyd', 'pyo'].includes(extension)) {
|
||||
return 'i-ph:file-py';
|
||||
}
|
||||
|
||||
if (['java', 'class', 'jar'].includes(extension)) {
|
||||
return 'i-ph:file-java';
|
||||
}
|
||||
|
||||
if (['php'].includes(extension)) {
|
||||
return 'i-ph:file-php';
|
||||
}
|
||||
|
||||
if (['rb', 'ruby'].includes(extension)) {
|
||||
return 'i-ph:file-rs';
|
||||
}
|
||||
|
||||
if (['c', 'cpp', 'h', 'hpp', 'cc'].includes(extension)) {
|
||||
return 'i-ph:file-cpp';
|
||||
}
|
||||
|
||||
if (['go'].includes(extension)) {
|
||||
return 'i-ph:file-rs';
|
||||
}
|
||||
|
||||
if (['rs', 'rust'].includes(extension)) {
|
||||
return 'i-ph:file-rs';
|
||||
}
|
||||
|
||||
if (['swift'].includes(extension)) {
|
||||
return 'i-ph:file-swift';
|
||||
}
|
||||
|
||||
if (['kt', 'kotlin'].includes(extension)) {
|
||||
return 'i-ph:file-kotlin';
|
||||
}
|
||||
|
||||
if (['dart'].includes(extension)) {
|
||||
return 'i-ph:file-dart';
|
||||
}
|
||||
|
||||
// Config files
|
||||
if (['yml', 'yaml'].includes(extension)) return 'i-ph:file-cloud';
|
||||
if (['xml', 'svg'].includes(extension)) return 'i-ph:file-xml';
|
||||
if (['toml'].includes(extension)) return 'i-ph:file-text';
|
||||
if (['ini', 'conf', 'config'].includes(extension)) return 'i-ph:file-text';
|
||||
if (['env', 'env.local', 'env.development', 'env.production'].includes(extension)) return 'i-ph:file-lock';
|
||||
|
||||
if (['yml', 'yaml'].includes(extension)) {
|
||||
return 'i-ph:file-cloud';
|
||||
}
|
||||
|
||||
if (['xml', 'svg'].includes(extension)) {
|
||||
return 'i-ph:file-xml';
|
||||
}
|
||||
|
||||
if (['toml'].includes(extension)) {
|
||||
return 'i-ph:file-text';
|
||||
}
|
||||
|
||||
if (['ini', 'conf', 'config'].includes(extension)) {
|
||||
return 'i-ph:file-text';
|
||||
}
|
||||
|
||||
if (['env', 'env.local', 'env.development', 'env.production'].includes(extension)) {
|
||||
return 'i-ph:file-lock';
|
||||
}
|
||||
|
||||
// Document files
|
||||
if (['pdf'].includes(extension)) return 'i-ph:file-pdf';
|
||||
if (['doc', 'docx'].includes(extension)) return 'i-ph:file-doc';
|
||||
if (['xls', 'xlsx'].includes(extension)) return 'i-ph:file-xls';
|
||||
if (['ppt', 'pptx'].includes(extension)) return 'i-ph:file-ppt';
|
||||
if (['txt'].includes(extension)) return 'i-ph:file-text';
|
||||
|
||||
if (['pdf'].includes(extension)) {
|
||||
return 'i-ph:file-pdf';
|
||||
}
|
||||
|
||||
if (['doc', 'docx'].includes(extension)) {
|
||||
return 'i-ph:file-doc';
|
||||
}
|
||||
|
||||
if (['xls', 'xlsx'].includes(extension)) {
|
||||
return 'i-ph:file-xls';
|
||||
}
|
||||
|
||||
if (['ppt', 'pptx'].includes(extension)) {
|
||||
return 'i-ph:file-ppt';
|
||||
}
|
||||
|
||||
if (['txt'].includes(extension)) {
|
||||
return 'i-ph:file-text';
|
||||
}
|
||||
|
||||
// Image files
|
||||
if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'ico', 'tiff'].includes(extension)) return 'i-ph:file-image';
|
||||
|
||||
if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'ico', 'tiff'].includes(extension)) {
|
||||
return 'i-ph:file-image';
|
||||
}
|
||||
|
||||
// Audio/Video files
|
||||
if (['mp3', 'wav', 'ogg', 'flac', 'aac'].includes(extension)) return 'i-ph:file-audio';
|
||||
if (['mp4', 'avi', 'mov', 'wmv', 'flv', 'webm', 'mkv'].includes(extension)) return 'i-ph:file-video';
|
||||
|
||||
if (['mp3', 'wav', 'ogg', 'flac', 'aac'].includes(extension)) {
|
||||
return 'i-ph:file-audio';
|
||||
}
|
||||
|
||||
if (['mp4', 'avi', 'mov', 'wmv', 'flv', 'webm', 'mkv'].includes(extension)) {
|
||||
return 'i-ph:file-video';
|
||||
}
|
||||
|
||||
// Archive files
|
||||
if (['zip', 'rar', '7z', 'tar', 'gz', 'bz2'].includes(extension)) return 'i-ph:file-zip';
|
||||
|
||||
if (['zip', 'rar', '7z', 'tar', 'gz', 'bz2'].includes(extension)) {
|
||||
return 'i-ph:file-zip';
|
||||
}
|
||||
|
||||
// Special files
|
||||
if (filename === 'package.json') return 'i-ph:package';
|
||||
if (filename === 'tsconfig.json') return 'i-ph:file-ts';
|
||||
if (filename === 'README.md') return 'i-ph:book-open';
|
||||
if (filename === 'LICENSE') return 'i-ph:scales';
|
||||
if (filename === '.gitignore') return 'i-ph:git-branch';
|
||||
if (filename.startsWith('Dockerfile')) return 'i-ph:docker-logo';
|
||||
|
||||
if (filename === 'package.json') {
|
||||
return 'i-ph:package';
|
||||
}
|
||||
|
||||
if (filename === 'tsconfig.json') {
|
||||
return 'i-ph:file-ts';
|
||||
}
|
||||
|
||||
if (filename === 'README.md') {
|
||||
return 'i-ph:book-open';
|
||||
}
|
||||
|
||||
if (filename === 'LICENSE') {
|
||||
return 'i-ph:scales';
|
||||
}
|
||||
|
||||
if (filename === '.gitignore') {
|
||||
return 'i-ph:git-branch';
|
||||
}
|
||||
|
||||
if (filename.startsWith('Dockerfile')) {
|
||||
return 'i-ph:docker-logo';
|
||||
}
|
||||
|
||||
// Default
|
||||
return 'i-ph:file';
|
||||
};
|
||||
|
||||
const getIconColorForExtension = (extension: string): string => {
|
||||
// Code files
|
||||
if (['js', 'jsx'].includes(extension)) return 'text-yellow-500';
|
||||
if (['ts', 'tsx'].includes(extension)) return 'text-blue-500';
|
||||
if (['html', 'htm', 'xhtml'].includes(extension)) return 'text-orange-500';
|
||||
if (['css', 'scss', 'sass', 'less'].includes(extension)) return 'text-blue-400';
|
||||
if (['json', 'jsonc'].includes(extension)) return 'text-yellow-400';
|
||||
if (['md', 'markdown'].includes(extension)) return 'text-gray-500';
|
||||
if (['py', 'pyc', 'pyd', 'pyo'].includes(extension)) return 'text-green-500';
|
||||
if (['java', 'class', 'jar'].includes(extension)) return 'text-red-500';
|
||||
if (['php'].includes(extension)) return 'text-purple-500';
|
||||
if (['rb', 'ruby'].includes(extension)) return 'text-red-600';
|
||||
if (['c', 'cpp', 'h', 'hpp', 'cc'].includes(extension)) return 'text-blue-600';
|
||||
if (['go'].includes(extension)) return 'text-cyan-500';
|
||||
if (['rs', 'rust'].includes(extension)) return 'text-orange-600';
|
||||
if (['swift'].includes(extension)) return 'text-orange-500';
|
||||
if (['kt', 'kotlin'].includes(extension)) return 'text-purple-400';
|
||||
if (['dart'].includes(extension)) return 'text-cyan-400';
|
||||
|
||||
if (['js', 'jsx'].includes(extension)) {
|
||||
return 'text-yellow-500';
|
||||
}
|
||||
|
||||
if (['ts', 'tsx'].includes(extension)) {
|
||||
return 'text-blue-500';
|
||||
}
|
||||
|
||||
if (['html', 'htm', 'xhtml'].includes(extension)) {
|
||||
return 'text-orange-500';
|
||||
}
|
||||
|
||||
if (['css', 'scss', 'sass', 'less'].includes(extension)) {
|
||||
return 'text-blue-400';
|
||||
}
|
||||
|
||||
if (['json', 'jsonc'].includes(extension)) {
|
||||
return 'text-yellow-400';
|
||||
}
|
||||
|
||||
if (['md', 'markdown'].includes(extension)) {
|
||||
return 'text-gray-500';
|
||||
}
|
||||
|
||||
if (['py', 'pyc', 'pyd', 'pyo'].includes(extension)) {
|
||||
return 'text-green-500';
|
||||
}
|
||||
|
||||
if (['java', 'class', 'jar'].includes(extension)) {
|
||||
return 'text-red-500';
|
||||
}
|
||||
|
||||
if (['php'].includes(extension)) {
|
||||
return 'text-purple-500';
|
||||
}
|
||||
|
||||
if (['rb', 'ruby'].includes(extension)) {
|
||||
return 'text-red-600';
|
||||
}
|
||||
|
||||
if (['c', 'cpp', 'h', 'hpp', 'cc'].includes(extension)) {
|
||||
return 'text-blue-600';
|
||||
}
|
||||
|
||||
if (['go'].includes(extension)) {
|
||||
return 'text-cyan-500';
|
||||
}
|
||||
|
||||
if (['rs', 'rust'].includes(extension)) {
|
||||
return 'text-orange-600';
|
||||
}
|
||||
|
||||
if (['swift'].includes(extension)) {
|
||||
return 'text-orange-500';
|
||||
}
|
||||
|
||||
if (['kt', 'kotlin'].includes(extension)) {
|
||||
return 'text-purple-400';
|
||||
}
|
||||
|
||||
if (['dart'].includes(extension)) {
|
||||
return 'text-cyan-400';
|
||||
}
|
||||
|
||||
// Config files
|
||||
if (['yml', 'yaml'].includes(extension)) return 'text-purple-300';
|
||||
if (['xml'].includes(extension)) return 'text-orange-300';
|
||||
if (['svg'].includes(extension)) return 'text-green-400';
|
||||
if (['toml'].includes(extension)) return 'text-gray-500';
|
||||
if (['ini', 'conf', 'config'].includes(extension)) return 'text-gray-500';
|
||||
if (['env', 'env.local', 'env.development', 'env.production'].includes(extension)) return 'text-green-500';
|
||||
|
||||
if (['yml', 'yaml'].includes(extension)) {
|
||||
return 'text-purple-300';
|
||||
}
|
||||
|
||||
if (['xml'].includes(extension)) {
|
||||
return 'text-orange-300';
|
||||
}
|
||||
|
||||
if (['svg'].includes(extension)) {
|
||||
return 'text-green-400';
|
||||
}
|
||||
|
||||
if (['toml'].includes(extension)) {
|
||||
return 'text-gray-500';
|
||||
}
|
||||
|
||||
if (['ini', 'conf', 'config'].includes(extension)) {
|
||||
return 'text-gray-500';
|
||||
}
|
||||
|
||||
if (['env', 'env.local', 'env.development', 'env.production'].includes(extension)) {
|
||||
return 'text-green-500';
|
||||
}
|
||||
|
||||
// Document files
|
||||
if (['pdf'].includes(extension)) return 'text-red-500';
|
||||
if (['doc', 'docx'].includes(extension)) return 'text-blue-600';
|
||||
if (['xls', 'xlsx'].includes(extension)) return 'text-green-600';
|
||||
if (['ppt', 'pptx'].includes(extension)) return 'text-red-600';
|
||||
if (['txt'].includes(extension)) return 'text-gray-500';
|
||||
|
||||
if (['pdf'].includes(extension)) {
|
||||
return 'text-red-500';
|
||||
}
|
||||
|
||||
if (['doc', 'docx'].includes(extension)) {
|
||||
return 'text-blue-600';
|
||||
}
|
||||
|
||||
if (['xls', 'xlsx'].includes(extension)) {
|
||||
return 'text-green-600';
|
||||
}
|
||||
|
||||
if (['ppt', 'pptx'].includes(extension)) {
|
||||
return 'text-red-600';
|
||||
}
|
||||
|
||||
if (['txt'].includes(extension)) {
|
||||
return 'text-gray-500';
|
||||
}
|
||||
|
||||
// Image files
|
||||
if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'ico', 'tiff'].includes(extension)) return 'text-pink-500';
|
||||
|
||||
if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'ico', 'tiff'].includes(extension)) {
|
||||
return 'text-pink-500';
|
||||
}
|
||||
|
||||
// Audio/Video files
|
||||
if (['mp3', 'wav', 'ogg', 'flac', 'aac'].includes(extension)) return 'text-green-500';
|
||||
if (['mp4', 'avi', 'mov', 'wmv', 'flv', 'webm', 'mkv'].includes(extension)) return 'text-blue-500';
|
||||
|
||||
if (['mp3', 'wav', 'ogg', 'flac', 'aac'].includes(extension)) {
|
||||
return 'text-green-500';
|
||||
}
|
||||
|
||||
if (['mp4', 'avi', 'mov', 'wmv', 'flv', 'webm', 'mkv'].includes(extension)) {
|
||||
return 'text-blue-500';
|
||||
}
|
||||
|
||||
// Archive files
|
||||
if (['zip', 'rar', '7z', 'tar', 'gz', 'bz2'].includes(extension)) return 'text-yellow-600';
|
||||
|
||||
if (['zip', 'rar', '7z', 'tar', 'gz', 'bz2'].includes(extension)) {
|
||||
return 'text-yellow-600';
|
||||
}
|
||||
|
||||
// Special files
|
||||
if (filename === 'package.json') return 'text-red-400';
|
||||
if (filename === 'tsconfig.json') return 'text-blue-500';
|
||||
if (filename === 'README.md') return 'text-blue-400';
|
||||
if (filename === 'LICENSE') return 'text-gray-500';
|
||||
if (filename === '.gitignore') return 'text-orange-500';
|
||||
if (filename.startsWith('Dockerfile')) return 'text-blue-500';
|
||||
|
||||
if (filename === 'package.json') {
|
||||
return 'text-red-400';
|
||||
}
|
||||
|
||||
if (filename === 'tsconfig.json') {
|
||||
return 'text-blue-500';
|
||||
}
|
||||
|
||||
if (filename === 'README.md') {
|
||||
return 'text-blue-400';
|
||||
}
|
||||
|
||||
if (filename === 'LICENSE') {
|
||||
return 'text-gray-500';
|
||||
}
|
||||
|
||||
if (filename === '.gitignore') {
|
||||
return 'text-orange-500';
|
||||
}
|
||||
|
||||
if (filename.startsWith('Dockerfile')) {
|
||||
return 'text-blue-500';
|
||||
}
|
||||
|
||||
// Default
|
||||
return 'text-gray-400';
|
||||
};
|
||||
@ -140,7 +342,5 @@ export function FileIcon({ filename, size = 'md', className }: FileIconProps) {
|
||||
const color = getIconColorForExtension(extension);
|
||||
const sizeClass = getSizeClass(size);
|
||||
|
||||
return (
|
||||
<span className={classNames(icon, color, sizeClass, className)} />
|
||||
);
|
||||
return <span className={classNames(icon, color, sizeClass, className)} />;
|
||||
}
|
||||
|
@ -5,14 +5,19 @@ import { classNames } from '~/utils/classNames';
|
||||
interface FilterChipProps {
|
||||
/** The label text to display */
|
||||
label: string;
|
||||
|
||||
/** Optional value to display after the label */
|
||||
value?: string | number;
|
||||
|
||||
/** Function to call when the remove button is clicked */
|
||||
onRemove?: () => void;
|
||||
|
||||
/** Whether the chip is active/selected */
|
||||
active?: boolean;
|
||||
|
||||
/** Optional icon to display before the label */
|
||||
icon?: string;
|
||||
|
||||
/** Additional class name */
|
||||
className?: string;
|
||||
}
|
||||
|
@ -14,17 +14,27 @@ const GRADIENT_COLORS = [
|
||||
'from-pink-500/10 to-purple-500/5',
|
||||
];
|
||||
|
||||
interface GradientCardProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
interface GradientCardProps {
|
||||
/** Custom gradient class (overrides seed-based gradient) */
|
||||
gradient?: string;
|
||||
|
||||
/** Seed string to determine gradient color */
|
||||
seed?: string;
|
||||
|
||||
/** Whether to apply hover animation effect */
|
||||
hoverEffect?: boolean;
|
||||
|
||||
/** Whether to apply border effect */
|
||||
borderEffect?: boolean;
|
||||
|
||||
/** Card content */
|
||||
children: React.ReactNode;
|
||||
|
||||
/** Additional class name */
|
||||
className?: string;
|
||||
|
||||
/** Additional props */
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -54,7 +64,7 @@ export function GradientCard({
|
||||
},
|
||||
whileTap: { scale: 0.98 },
|
||||
}
|
||||
: {};
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
@ -80,8 +90,11 @@ export function GradientCard({
|
||||
* Calculate a gradient color based on the seed string for visual variety
|
||||
*/
|
||||
function getGradientColorFromSeed(seedString?: string): string {
|
||||
if (!seedString) return GRADIENT_COLORS[0];
|
||||
if (!seedString) {
|
||||
return GRADIENT_COLORS[0];
|
||||
}
|
||||
|
||||
const index = seedString.length % GRADIENT_COLORS.length;
|
||||
|
||||
return GRADIENT_COLORS[index];
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ export function RepositoryStats({ stats, className, compact = false }: Repositor
|
||||
Repository Statistics:
|
||||
</p>
|
||||
)}
|
||||
|
||||
|
||||
<div className={classNames('grid gap-3', compact ? 'grid-cols-2' : 'grid-cols-2 md:grid-cols-3')}>
|
||||
{totalFiles !== undefined && (
|
||||
<div className="flex items-center gap-2 text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary-dark">
|
||||
@ -33,7 +33,7 @@ export function RepositoryStats({ stats, className, compact = false }: Repositor
|
||||
<span className={compact ? 'text-xs' : 'text-sm'}>Total Files: {totalFiles.toLocaleString()}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
{totalSize !== undefined && (
|
||||
<div className="flex items-center gap-2 text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary-dark">
|
||||
<span className="i-ph:database text-purple-500 w-4 h-4" />
|
||||
@ -53,19 +53,12 @@ export function RepositoryStats({ stats, className, compact = false }: Repositor
|
||||
.sort(([, a], [, b]) => b - a)
|
||||
.slice(0, compact ? 3 : 5)
|
||||
.map(([lang, size]) => (
|
||||
<Badge
|
||||
key={lang}
|
||||
variant="subtle"
|
||||
size={compact ? 'sm' : 'md'}
|
||||
>
|
||||
<Badge key={lang} variant="subtle" size={compact ? 'sm' : 'md'}>
|
||||
{lang} ({formatSize(size)})
|
||||
</Badge>
|
||||
))}
|
||||
{Object.keys(languages).length > (compact ? 3 : 5) && (
|
||||
<Badge
|
||||
variant="subtle"
|
||||
size={compact ? 'sm' : 'md'}
|
||||
>
|
||||
<Badge variant="subtle" size={compact ? 'sm' : 'md'}>
|
||||
+{Object.keys(languages).length - (compact ? 3 : 5)} more
|
||||
</Badge>
|
||||
)}
|
||||
@ -77,20 +70,12 @@ export function RepositoryStats({ stats, className, compact = false }: Repositor
|
||||
<div className={compact ? 'pt-1' : 'pt-2'}>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{hasPackageJson && (
|
||||
<Badge
|
||||
variant="primary"
|
||||
size={compact ? 'sm' : 'md'}
|
||||
icon="i-ph:package w-3.5 h-3.5"
|
||||
>
|
||||
<Badge variant="primary" size={compact ? 'sm' : 'md'} icon="i-ph:package w-3.5 h-3.5">
|
||||
package.json
|
||||
</Badge>
|
||||
)}
|
||||
{hasDependencies && (
|
||||
<Badge
|
||||
variant="primary"
|
||||
size={compact ? 'sm' : 'md'}
|
||||
icon="i-ph:tree-structure w-3.5 h-3.5"
|
||||
>
|
||||
<Badge variant="primary" size={compact ? 'sm' : 'md'} icon="i-ph:tree-structure w-3.5 h-3.5">
|
||||
Dependencies
|
||||
</Badge>
|
||||
)}
|
||||
|
@ -6,12 +6,16 @@ import { motion, AnimatePresence } from 'framer-motion';
|
||||
interface SearchInputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
/** Function to call when the clear button is clicked */
|
||||
onClear?: () => void;
|
||||
|
||||
/** Whether to show the clear button when there is input */
|
||||
showClearButton?: boolean;
|
||||
|
||||
/** Additional class name for the search icon */
|
||||
iconClassName?: string;
|
||||
|
||||
/** Additional class name for the container */
|
||||
containerClassName?: string;
|
||||
|
||||
/** Whether the search is loading */
|
||||
loading?: boolean;
|
||||
}
|
||||
@ -47,7 +51,7 @@ export const SearchInput = forwardRef<HTMLInputElement, SearchInputProps>(
|
||||
{/* Input field */}
|
||||
<Input
|
||||
ref={ref}
|
||||
className={classNames('pl-10', hasValue && showClearButton && 'pr-10', className)}
|
||||
className={classNames('pl-10', hasValue && showClearButton ? 'pr-10' : '', className)}
|
||||
{...props}
|
||||
/>
|
||||
|
||||
|
@ -41,7 +41,7 @@ export function SearchResultItem({
|
||||
className={classNames(
|
||||
'p-5 rounded-xl border border-bolt-elements-borderColor dark:border-bolt-elements-borderColor-dark hover:border-purple-500/40 transition-all duration-300 shadow-sm hover:shadow-md bg-bolt-elements-background-depth-1/50 dark:bg-bolt-elements-background-depth-3/50',
|
||||
onClick ? 'cursor-pointer' : '',
|
||||
className
|
||||
className,
|
||||
)}
|
||||
whileHover={{
|
||||
scale: 1.01,
|
||||
@ -57,7 +57,12 @@ export function SearchResultItem({
|
||||
<div className="flex items-start justify-between mb-3 gap-3">
|
||||
<div className="flex items-start gap-3">
|
||||
{icon && (
|
||||
<div className={classNames('w-10 h-10 rounded-xl backdrop-blur-sm flex items-center justify-center shadow-sm', iconBackground)}>
|
||||
<div
|
||||
className={classNames(
|
||||
'w-10 h-10 rounded-xl backdrop-blur-sm flex items-center justify-center shadow-sm',
|
||||
iconBackground,
|
||||
)}
|
||||
>
|
||||
<span className={classNames(icon, 'w-5 h-5', iconColor)} />
|
||||
</div>
|
||||
)}
|
||||
@ -72,7 +77,7 @@ export function SearchResultItem({
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{actionLabel && onAction && (
|
||||
<motion.button
|
||||
onClick={(e) => {
|
||||
|
@ -37,12 +37,16 @@ const TEXT_SIZE_CLASSES: Record<SizeType, string> = {
|
||||
interface StatusIndicatorProps {
|
||||
/** The status to display */
|
||||
status: StatusType;
|
||||
|
||||
/** Size of the indicator */
|
||||
size?: SizeType;
|
||||
|
||||
/** Whether to show a pulsing animation */
|
||||
pulse?: boolean;
|
||||
|
||||
/** Optional label text */
|
||||
label?: string;
|
||||
|
||||
/** Additional class name */
|
||||
className?: string;
|
||||
}
|
||||
|
@ -5,8 +5,10 @@ import { classNames } from '~/utils/classNames';
|
||||
interface Tab {
|
||||
/** Unique identifier for the tab */
|
||||
id: string;
|
||||
|
||||
/** Content to display in the tab */
|
||||
label: React.ReactNode;
|
||||
|
||||
/** Optional icon to display before the label */
|
||||
icon?: string;
|
||||
}
|
||||
@ -14,16 +16,22 @@ interface Tab {
|
||||
interface TabsWithSliderProps {
|
||||
/** Array of tab objects */
|
||||
tabs: Tab[];
|
||||
|
||||
/** ID of the currently active tab */
|
||||
activeTab: string;
|
||||
|
||||
/** Function called when a tab is clicked */
|
||||
onChange: (tabId: string) => void;
|
||||
|
||||
/** Additional class name for the container */
|
||||
className?: string;
|
||||
|
||||
/** Additional class name for inactive tabs */
|
||||
tabClassName?: string;
|
||||
|
||||
/** Additional class name for the active tab */
|
||||
activeTabClassName?: string;
|
||||
|
||||
/** Additional class name for the slider */
|
||||
sliderClassName?: string;
|
||||
}
|
||||
@ -51,8 +59,10 @@ export function TabsWithSlider({
|
||||
// Update slider position when active tab changes
|
||||
useEffect(() => {
|
||||
const activeIndex = tabs.findIndex((tab) => tab.id === activeTab);
|
||||
|
||||
if (activeIndex !== -1 && tabsRef.current[activeIndex]) {
|
||||
const activeTabElement = tabsRef.current[activeIndex];
|
||||
|
||||
if (activeTabElement) {
|
||||
setSliderDimensions({
|
||||
width: activeTabElement.offsetWidth,
|
||||
|
Loading…
Reference in New Issue
Block a user