style(studio): elevate CloudFileBrowser to SaaS Premium
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
8cf22aa90c
commit
cdc9890257
6 changed files with 72 additions and 72 deletions
|
|
@ -19,22 +19,22 @@ export function CloudFileBrowserSkeleton({
|
|||
return (
|
||||
<div className={cn('space-y-8 h-full flex flex-col', className)}>
|
||||
{/* Toolbar skeleton */}
|
||||
<div className="flex flex-col xl:flex-row gap-4 justify-between items-start xl:items-center bg-kodo-ink/50 p-4 rounded-xl border border-kodo-steel/50 animate-pulse">
|
||||
<div className="flex flex-col xl:flex-row gap-4 justify-between items-start xl:items-center bg-card/50 p-4 rounded-xl border border-border animate-pulse">
|
||||
<div className="flex flex-col sm:flex-row gap-4 w-full xl:w-auto">
|
||||
<div className="h-10 w-full sm:w-64 rounded-lg bg-kodo-steel/50" />
|
||||
<div className="h-10 w-full sm:w-64 rounded-lg bg-muted" />
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="h-4 w-4 rounded bg-kodo-steel/50 shrink-0" />
|
||||
<div className="h-4 w-4 rounded bg-muted shrink-0" />
|
||||
<div className="flex gap-2">
|
||||
{[1, 2, 3, 4, 5].map((i) => (
|
||||
<div key={i} className="h-6 w-16 rounded bg-kodo-steel/50" />
|
||||
<div key={i} className="h-6 w-16 rounded bg-muted" />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2 w-full xl:w-auto justify-end">
|
||||
<div className="h-10 w-24 rounded-lg bg-kodo-steel/50" />
|
||||
<div className="h-10 w-32 rounded-lg bg-kodo-steel/50" />
|
||||
<div className="h-10 w-20 rounded-lg bg-kodo-steel/50" />
|
||||
<div className="h-10 w-24 rounded-lg bg-muted" />
|
||||
<div className="h-10 w-32 rounded-lg bg-muted" />
|
||||
<div className="h-10 w-20 rounded-lg bg-muted" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -42,24 +42,24 @@ export function CloudFileBrowserSkeleton({
|
|||
<div className="flex-1 overflow-hidden min-h-layout-page-sm">
|
||||
{viewMode === 'list' ? (
|
||||
<div className="bg-kodo-graphite border border-kodo-steel rounded-xl overflow-hidden">
|
||||
<div className="border-b border-kodo-steel/30 p-4 flex gap-4">
|
||||
<div className="h-4 w-10 rounded bg-kodo-steel/50" />
|
||||
<div className="h-4 flex-1 rounded bg-kodo-steel/50" />
|
||||
<div className="h-4 w-16 rounded bg-kodo-steel/50" />
|
||||
<div className="h-4 w-12 rounded bg-kodo-steel/50" />
|
||||
<div className="h-4 w-20 rounded bg-kodo-steel/50" />
|
||||
<div className="border-b border-border p-4 flex gap-4">
|
||||
<div className="h-4 w-10 rounded bg-muted" />
|
||||
<div className="h-4 flex-1 rounded bg-muted" />
|
||||
<div className="h-4 w-16 rounded bg-muted" />
|
||||
<div className="h-4 w-12 rounded bg-muted" />
|
||||
<div className="h-4 w-20 rounded bg-muted" />
|
||||
</div>
|
||||
<div className="divide-y divide-kodo-steel/30">
|
||||
{Array.from({ length: ROW_COUNT }).map((_, i) => (
|
||||
<div key={i} className="p-4 flex gap-4 items-center">
|
||||
<div className="h-4 w-4 rounded bg-kodo-steel/50 shrink-0" />
|
||||
<div className="h-4 w-4 rounded bg-muted shrink-0" />
|
||||
<div className="flex items-center gap-4 flex-1 min-w-0">
|
||||
<div className="h-5 w-5 rounded bg-kodo-steel/50 shrink-0" />
|
||||
<div className="h-4 w-40 rounded bg-kodo-steel/50" />
|
||||
<div className="h-5 w-5 rounded bg-muted shrink-0" />
|
||||
<div className="h-4 w-40 rounded bg-muted" />
|
||||
</div>
|
||||
<div className="h-4 w-24 rounded bg-kodo-steel/50" />
|
||||
<div className="h-4 w-12 rounded bg-kodo-steel/50" />
|
||||
<div className="h-4 w-20 rounded bg-kodo-steel/50" />
|
||||
<div className="h-4 w-24 rounded bg-muted" />
|
||||
<div className="h-4 w-12 rounded bg-muted" />
|
||||
<div className="h-4 w-20 rounded bg-muted" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -69,14 +69,14 @@ export function CloudFileBrowserSkeleton({
|
|||
{Array.from({ length: 8 }).map((_, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="p-4 flex flex-col items-center gap-4 rounded-xl border border-kodo-steel bg-kodo-graphite animate-pulse"
|
||||
className="p-4 flex flex-col items-center gap-4 rounded-xl border border-border bg-card animate-pulse"
|
||||
>
|
||||
<div className="w-16 h-16 rounded-2xl bg-kodo-steel/50" />
|
||||
<div className="w-16 h-16 rounded-2xl bg-muted" />
|
||||
<div className="w-full space-y-2">
|
||||
<div className="h-4 w-full rounded bg-kodo-steel/50" />
|
||||
<div className="h-4 w-full rounded bg-muted" />
|
||||
<div className="flex justify-center gap-1">
|
||||
<div className="h-3 w-12 rounded bg-kodo-steel/50" />
|
||||
<div className="h-3 w-10 rounded bg-kodo-steel/50" />
|
||||
<div className="h-3 w-12 rounded bg-muted" />
|
||||
<div className="h-3 w-10 rounded bg-muted" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ export function FileGrid({
|
|||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'min-h-layout-page-sm flex items-center justify-center text-kodo-content-dim text-sm',
|
||||
'min-h-layout-page-sm flex items-center justify-center text-muted-foreground text-sm',
|
||||
className
|
||||
)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ export interface FileGridCardProps {
|
|||
|
||||
function FileTypeIcon({ type, size = 'md' }: { type: CloudFileNode['type']; size?: 'md' | 'lg' }) {
|
||||
const cl = size === 'lg' ? 'w-8 h-8' : 'w-5 h-5';
|
||||
if (type === 'folder') return <Folder className={cn(cl, 'text-kodo-gold')} />;
|
||||
if (type === 'folder') return <Folder className={cn(cl, 'text-warning')} />;
|
||||
if (type === 'audio') return <Music className={cn(cl, 'text-kodo-steel')} />;
|
||||
if (type === 'image') return <ImageIcon className={cn(cl, 'text-kodo-magenta')} />;
|
||||
if (type === 'image') return <ImageIcon className={cn(cl, 'text-primary')} />;
|
||||
if (['document', 'archive', 'project'].includes(type)) {
|
||||
return <File className={cn(cl, 'text-kodo-content-dim')} />;
|
||||
}
|
||||
|
|
@ -41,12 +41,12 @@ export function FileGridCard({
|
|||
className
|
||||
)}
|
||||
>
|
||||
<div className="w-16 h-16 rounded-2xl bg-kodo-steel/50" />
|
||||
<div className="w-16 h-16 rounded-2xl bg-muted" />
|
||||
<div className="w-full space-y-2">
|
||||
<div className="h-4 w-full rounded bg-kodo-steel/50" />
|
||||
<div className="h-4 w-full rounded bg-muted" />
|
||||
<div className="flex justify-center gap-1">
|
||||
<div className="h-3 w-12 rounded bg-kodo-steel/50" />
|
||||
<div className="h-3 w-10 rounded bg-kodo-steel/50" />
|
||||
<div className="h-3 w-12 rounded bg-muted" />
|
||||
<div className="h-3 w-10 rounded bg-muted" />
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
|
@ -57,8 +57,8 @@ export function FileGridCard({
|
|||
<Card
|
||||
variant="default"
|
||||
className={cn(
|
||||
'p-4 flex flex-col items-center text-center gap-4 cursor-pointer hover:border-kodo-steel/50 transition-all group relative',
|
||||
selected && 'border-kodo-cyan bg-kodo-cyan/5',
|
||||
'p-4 flex flex-col items-center text-center gap-4 cursor-pointer hover:border-primary/50 transition-all duration-[var(--duration-normal)] group relative',
|
||||
selected && 'border-primary bg-primary/5',
|
||||
className
|
||||
)}
|
||||
onClick={() => onClick(file)}
|
||||
|
|
@ -73,9 +73,9 @@ export function FileGridCard({
|
|||
aria-label={selected ? 'Désélectionner' : 'Sélectionner'}
|
||||
>
|
||||
{selected ? (
|
||||
<CheckSquare className="w-4 h-4 text-kodo-cyan" />
|
||||
<CheckSquare className="w-4 h-4 text-primary" />
|
||||
) : (
|
||||
<Square className="w-4 h-4 text-kodo-content-dim hover:text-white" />
|
||||
<Square className="w-4 h-4 text-muted-foreground hover:text-foreground" />
|
||||
)}
|
||||
</button>
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export function FileTable({
|
|||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'bg-kodo-graphite border border-kodo-steel rounded-xl overflow-hidden',
|
||||
'bg-card border border-border rounded-xl overflow-hidden',
|
||||
className
|
||||
)}
|
||||
>
|
||||
|
|
@ -45,31 +45,31 @@ export function FileTable({
|
|||
<button
|
||||
type="button"
|
||||
onClick={onSelectAll}
|
||||
className="cursor-pointer hover:text-white"
|
||||
className="cursor-pointer hover:text-foreground"
|
||||
aria-label={allSelected ? 'Désélectionner tout' : 'Tout sélectionner'}
|
||||
>
|
||||
{allSelected ? (
|
||||
<CheckSquare className="w-4 h-4 text-kodo-cyan" />
|
||||
<CheckSquare className="w-4 h-4 text-primary" />
|
||||
) : (
|
||||
<Square className="w-4 h-4" />
|
||||
)}
|
||||
</button>
|
||||
</th>
|
||||
<th
|
||||
className="p-4 cursor-pointer hover:text-white"
|
||||
className="p-4 cursor-pointer hover:text-foreground"
|
||||
onClick={() => onSort('name')}
|
||||
>
|
||||
Name
|
||||
</th>
|
||||
<th className="p-4">Tags</th>
|
||||
<th
|
||||
className="p-4 cursor-pointer hover:text-white"
|
||||
className="p-4 cursor-pointer hover:text-foreground"
|
||||
onClick={() => onSort('size')}
|
||||
>
|
||||
Size
|
||||
</th>
|
||||
<th
|
||||
className="p-4 cursor-pointer hover:text-white"
|
||||
className="p-4 cursor-pointer hover:text-foreground"
|
||||
onClick={() => onSort('modified')}
|
||||
>
|
||||
Modified
|
||||
|
|
@ -94,7 +94,7 @@ export function FileTable({
|
|||
<tr>
|
||||
<td
|
||||
colSpan={6}
|
||||
className="p-8 text-center text-kodo-content-dim text-sm"
|
||||
className="p-8 text-center text-muted-foreground text-sm"
|
||||
>
|
||||
No files match your filters.
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -24,13 +24,13 @@ export interface FileTableRowProps {
|
|||
}
|
||||
|
||||
function FileTypeIcon({ type }: { type: CloudFileNode['type'] }) {
|
||||
if (type === 'folder') return <Folder className="w-5 h-5 text-kodo-gold" />;
|
||||
if (type === 'audio') return <Music className="w-5 h-5 text-kodo-steel" />;
|
||||
if (type === 'image') return <ImageIcon className="w-5 h-5 text-kodo-magenta" />;
|
||||
if (type === 'folder') return <Folder className="w-5 h-5 text-warning" />;
|
||||
if (type === 'audio') return <Music className="w-5 h-5 text-muted-foreground" />;
|
||||
if (type === 'image') return <ImageIcon className="w-5 h-5 text-primary" />;
|
||||
if (['document', 'archive', 'project'].includes(type)) {
|
||||
return <File className="w-5 h-5 text-kodo-content-dim" />;
|
||||
return <File className="w-5 h-5 text-muted-foreground" />;
|
||||
}
|
||||
return <File className="w-5 h-5 text-kodo-content-dim" />;
|
||||
return <File className="w-5 h-5 text-muted-foreground" />;
|
||||
}
|
||||
|
||||
export function FileTableRow({
|
||||
|
|
@ -45,28 +45,28 @@ export function FileTableRow({
|
|||
return (
|
||||
<tr className="animate-pulse">
|
||||
<td className="p-4 w-10">
|
||||
<div className="h-4 w-4 rounded bg-kodo-steel/50" />
|
||||
<div className="h-4 w-4 rounded bg-muted" />
|
||||
</td>
|
||||
<td className="p-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="h-5 w-5 rounded bg-kodo-steel/50 shrink-0" />
|
||||
<div className="h-4 w-32 rounded bg-kodo-steel/50" />
|
||||
<div className="h-5 w-5 rounded bg-muted shrink-0" />
|
||||
<div className="h-4 w-32 rounded bg-muted" />
|
||||
</div>
|
||||
</td>
|
||||
<td className="p-4">
|
||||
<div className="flex gap-1">
|
||||
<div className="h-4 w-12 rounded bg-kodo-steel/50" />
|
||||
<div className="h-4 w-10 rounded bg-kodo-steel/50" />
|
||||
<div className="h-4 w-12 rounded bg-muted" />
|
||||
<div className="h-4 w-10 rounded bg-muted" />
|
||||
</div>
|
||||
</td>
|
||||
<td className="p-4">
|
||||
<div className="h-4 w-12 rounded bg-kodo-steel/50 font-mono text-xs" />
|
||||
<div className="h-4 w-12 rounded bg-muted font-mono text-xs" />
|
||||
</td>
|
||||
<td className="p-4">
|
||||
<div className="h-4 w-20 rounded bg-kodo-steel/50 text-xs" />
|
||||
<div className="h-4 w-20 rounded bg-muted text-xs" />
|
||||
</td>
|
||||
<td className="p-4 text-right">
|
||||
<div className="h-8 w-8 rounded bg-kodo-steel/50 ml-auto" />
|
||||
<div className="h-8 w-8 rounded bg-muted ml-auto" />
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
|
|
@ -76,18 +76,18 @@ export function FileTableRow({
|
|||
<tr
|
||||
className={cn(
|
||||
'group hover:bg-white/5 transition-colors',
|
||||
selected && 'bg-kodo-cyan/5'
|
||||
selected && 'bg-primary/5'
|
||||
)}
|
||||
>
|
||||
<td className="p-4">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onToggleSelect(file.id)}
|
||||
className="cursor-pointer text-kodo-content-dim hover:text-white"
|
||||
className="cursor-pointer text-muted-foreground hover:text-foreground"
|
||||
aria-label={selected ? 'Désélectionner' : 'Sélectionner'}
|
||||
>
|
||||
{selected ? (
|
||||
<CheckSquare className="w-4 h-4 text-kodo-cyan" />
|
||||
<CheckSquare className="w-4 h-4 text-primary" />
|
||||
) : (
|
||||
<Square className="w-4 h-4" />
|
||||
)}
|
||||
|
|
@ -100,7 +100,7 @@ export function FileTableRow({
|
|||
onClick={() => onFileClick(file)}
|
||||
>
|
||||
<FileTypeIcon type={file.type} />
|
||||
<span className="font-medium text-kodo-text-main group-hover:text-white transition-colors">
|
||||
<span className="font-medium text-foreground group-hover:text-foreground transition-colors">
|
||||
{file.name}
|
||||
</span>
|
||||
</button>
|
||||
|
|
@ -110,7 +110,7 @@ export function FileTableRow({
|
|||
{file.tags?.map((t) => (
|
||||
<span
|
||||
key={t}
|
||||
className="text-xs bg-kodo-slate px-1.5 py-0.5 rounded text-kodo-content-dim border border-kodo-steel"
|
||||
className="text-xs bg-muted px-1.5 py-0.5 rounded text-muted-foreground border border-border"
|
||||
>
|
||||
{t}
|
||||
</span>
|
||||
|
|
@ -125,7 +125,7 @@ export function FileTableRow({
|
|||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="p-1.5 text-kodo-steel"
|
||||
className="p-1.5 text-muted-foreground"
|
||||
title="Process with AI"
|
||||
onClick={() => onAction?.('ai', file)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ export function FileToolbar({
|
|||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-col xl:flex-row gap-4 justify-between items-start xl:items-center bg-kodo-ink/50 p-4 rounded-xl border border-kodo-steel/50 animate-pulse',
|
||||
'flex flex-col xl:flex-row gap-4 justify-between items-start xl:items-center bg-card/50 p-4 rounded-xl border border-border animate-pulse',
|
||||
className
|
||||
)}
|
||||
>
|
||||
|
|
@ -95,7 +95,7 @@ export function FileToolbar({
|
|||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-col xl:flex-row gap-4 justify-between items-start xl:items-center bg-kodo-ink/50 p-4 rounded-xl border border-kodo-steel/50',
|
||||
'flex flex-col xl:flex-row gap-4 justify-between items-start xl:items-center bg-card/50 p-4 rounded-xl border border-border',
|
||||
className
|
||||
)}
|
||||
>
|
||||
|
|
@ -108,7 +108,7 @@ export function FileToolbar({
|
|||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 overflow-x-auto no-scrollbar pb-2 sm:pb-0">
|
||||
<Filter className="w-4 h-4 text-kodo-content-dim shrink-0" />
|
||||
<Filter className="w-4 h-4 text-muted-foreground shrink-0" />
|
||||
{tagsToShow.map((tag) => (
|
||||
<button
|
||||
key={tag}
|
||||
|
|
@ -152,7 +152,7 @@ export function FileToolbar({
|
|||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={onBulkDelete}
|
||||
className="text-kodo-red hover:bg-kodo-red/10"
|
||||
className="text-destructive hover:bg-destructive/10"
|
||||
aria-label="Supprimer"
|
||||
>
|
||||
<Trash2 className="w-4 h-4" />
|
||||
|
|
@ -182,7 +182,7 @@ export function FileToolbar({
|
|||
Sort:
|
||||
</span>
|
||||
<select
|
||||
className="bg-transparent text-xs text-white outline-none"
|
||||
className="bg-transparent text-xs text-foreground outline-none"
|
||||
value={sortField}
|
||||
onChange={(e) => onSortFieldChange(e.target.value as SortField)}
|
||||
aria-label="Sort by"
|
||||
|
|
@ -195,7 +195,7 @@ export function FileToolbar({
|
|||
<button
|
||||
type="button"
|
||||
onClick={onSortOrderToggle}
|
||||
className="ml-2 p-1 hover:text-white text-kodo-content-dim"
|
||||
className="ml-2 p-1 hover:text-foreground text-muted-foreground"
|
||||
aria-label="Toggle sort order"
|
||||
>
|
||||
{sortOrder === 'asc' ? (
|
||||
|
|
@ -206,13 +206,13 @@ export function FileToolbar({
|
|||
</button>
|
||||
</div>
|
||||
|
||||
<div className="bg-kodo-void p-1 rounded-lg border border-kodo-steel flex">
|
||||
<div className="bg-muted p-1 rounded-xl border border-border flex">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onViewModeChange('list')}
|
||||
className={cn(
|
||||
'p-2 rounded',
|
||||
viewMode === 'list' ? 'bg-kodo-slate text-white' : 'text-kodo-content-dim hover:text-white'
|
||||
'p-2 rounded-lg transition-colors duration-[var(--duration-normal)]',
|
||||
viewMode === 'list' ? 'bg-primary text-primary-foreground' : 'text-muted-foreground hover:text-foreground'
|
||||
)}
|
||||
aria-label="List view"
|
||||
>
|
||||
|
|
@ -222,8 +222,8 @@ export function FileToolbar({
|
|||
type="button"
|
||||
onClick={() => onViewModeChange('grid')}
|
||||
className={cn(
|
||||
'p-2 rounded',
|
||||
viewMode === 'grid' ? 'bg-kodo-slate text-white' : 'text-kodo-content-dim hover:text-white'
|
||||
'p-2 rounded-lg transition-colors duration-[var(--duration-normal)]',
|
||||
viewMode === 'grid' ? 'bg-primary text-primary-foreground' : 'text-muted-foreground hover:text-foreground'
|
||||
)}
|
||||
aria-label="Grid view"
|
||||
>
|
||||
|
|
|
|||
Loading…
Reference in a new issue