mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-07-12 21:02:17 +02:00
style(x): unify tab headers and clean up Brain/Home UI
- Shared header treatment across Apps, Background tasks, Workspace, Meetings, Brain: 24px/650 heading, no leading icons, no divider line, centered 1120px column with fixed 34px/30px padding - Apps-style page background on BG tasks, Workspace, Meetings - Brain files view: monochrome then icon-free rows, inline note-count meta line instead of pill chips, semibold item names, uniform row heights, breadcrumb-only navigation, ghost quick actions - Bases toolbar flush with table edge - Meetings: full-width Coming up section - Home: drop Connections/Ask-anything icon tiles, real Slack logo - Sidebar: Apps above Background agents
This commit is contained in:
parent
248bb0d1a1
commit
602889e340
9 changed files with 86 additions and 132 deletions
|
|
@ -58,8 +58,8 @@ const CARD_CSS = `
|
|||
--ma-pat-opacity:0.05; --ma-glow-opacity:0.10; --ma-glow-hover-opacity:0.16;
|
||||
--ma-badge-mix:15%; --ma-pill-mix:13%; --ma-tint:20%; --ma-tint-hover:26%;
|
||||
}
|
||||
.ma-inner { max-width:1120px; margin:0 auto; padding:clamp(20px,3.5cqw,34px) clamp(16px,3cqw,30px) 48px; }
|
||||
.ma-h1 { font-size:clamp(19px,2.6cqw,24px); font-weight:650; letter-spacing:-0.02em; color:var(--ma-h1); margin:0 0 4px; }
|
||||
.ma-inner { max-width:1120px; margin:0 auto; padding:34px 30px 48px; }
|
||||
.ma-h1 { font-size:24px; font-weight:650; letter-spacing:-0.02em; color:var(--ma-h1); margin:0 0 4px; }
|
||||
.ma-sub { font-size:clamp(13px,1.5cqw,14px); color:var(--ma-sub); margin:0 0 clamp(14px,2cqw,20px); }
|
||||
.ma-tabs { display:flex; gap:6px; margin-bottom:clamp(14px,2cqw,22px); }
|
||||
.ma-tab { border:1px solid var(--ma-border); background:transparent; color:var(--ma-sub); border-radius:999px; padding:5px 14px; font-size:13px; font-weight:600; cursor:pointer; }
|
||||
|
|
|
|||
|
|
@ -466,7 +466,7 @@ export function BasesView({ tree, onSelectNote, config, onConfigChange, isDefaul
|
|||
return (
|
||||
<div className="flex-1 flex flex-col overflow-hidden">
|
||||
{/* Toolbar */}
|
||||
<div className="shrink-0 border-b border-border px-4 py-2 flex items-center gap-3">
|
||||
<div className="shrink-0 border-b border-border pr-4 py-2 flex items-center gap-3">
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<button className="inline-flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground">
|
||||
|
|
|
|||
|
|
@ -1733,22 +1733,20 @@ export function BgTasksView({ onCreateWithCopilot, onEditWithCopilot, initialSlu
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col overflow-hidden">
|
||||
<div className="shrink-0 border-b border-border px-6 py-5">
|
||||
<div className="flex h-full flex-col overflow-hidden bg-[#f8f8f9] dark:bg-[#0b0b0d]">
|
||||
<div className="mx-auto w-full max-w-[1120px] shrink-0 px-[30px] pt-[34px] pb-5">
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<ListChecks className="size-5 text-primary" />
|
||||
<h2 className="text-base font-semibold text-foreground">Background tasks</h2>
|
||||
</div>
|
||||
<h2 className="text-[24px] font-[650] tracking-[-0.02em] text-[#0d0e11] dark:text-[#f4f5f7]">Background tasks</h2>
|
||||
<Button size="sm" onClick={() => setShowNewDialog(true)}>
|
||||
New task
|
||||
</Button>
|
||||
</div>
|
||||
<p className="mt-1 text-xs text-muted-foreground">
|
||||
<p className="mt-1 text-[14px] text-black/50 dark:text-white/[0.52]">
|
||||
Persistent agents that fire on a schedule or in response to events. Toggle a task inactive to pause it.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex-1 overflow-auto p-6">
|
||||
<div className="flex-1 overflow-auto">
|
||||
<div className="mx-auto h-full w-full max-w-[1120px] px-[30px] pb-12">
|
||||
{loading ? (
|
||||
<div className="flex h-full items-center justify-center">
|
||||
<Loader2 className="size-5 animate-spin text-muted-foreground" />
|
||||
|
|
@ -1885,6 +1883,7 @@ export function BgTasksView({ onCreateWithCopilot, onEditWithCopilot, initialSlu
|
|||
</table>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<NewTaskDialog
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react'
|
|||
import { ArrowRight, Bot, Calendar, Clock, ExternalLink, FileText, Mail, MessageSquare, Mic, Plus, Video } from 'lucide-react'
|
||||
import { extractConferenceLink } from '@/lib/calendar-event'
|
||||
import { ToolConnectionsCard } from '@/components/tool-connections-card'
|
||||
import { SlackIcon } from '@/components/onboarding/provider-icons'
|
||||
|
||||
interface TreeNode {
|
||||
path: string
|
||||
|
|
@ -432,7 +433,7 @@ export function HomeView({
|
|||
{slackEnabled && (
|
||||
<div className={CARD}>
|
||||
<div className="mb-3 flex items-center gap-2">
|
||||
<MessageSquare className="size-[15px]" />
|
||||
<SlackIcon className="size-[15px]" />
|
||||
<span className="text-sm font-medium">Slack</span>
|
||||
<span className="flex-1" />
|
||||
<span className="text-xs text-muted-foreground">Latest messages</span>
|
||||
|
|
@ -548,9 +549,6 @@ export function HomeView({
|
|||
onClick={onOpenChat}
|
||||
className="flex items-center gap-3.5 rounded-xl border border-border bg-card p-4 text-left transition-colors hover:bg-accent"
|
||||
>
|
||||
<div className="flex size-9 shrink-0 items-center justify-center rounded-lg border border-border bg-card text-muted-foreground">
|
||||
<MessageSquare className="size-[15px]" />
|
||||
</div>
|
||||
<div className="min-w-0 flex-1 text-[13.5px] leading-snug">
|
||||
<span className="font-medium">Ask anything</span>
|
||||
<span className="text-muted-foreground"> — create presentations, do research, collaborate on docs.</span>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from 'react'
|
||||
import {
|
||||
ArrowLeft,
|
||||
ChevronRight,
|
||||
Copy,
|
||||
ExternalLink,
|
||||
|
|
@ -70,20 +69,6 @@ const HIDDEN_PATHS = new Set(['knowledge/Meetings', 'knowledge/Workspace'])
|
|||
|
||||
// Theme-aware accent palette for folder avatars — colored letter on a faint
|
||||
// tint of the same hue. Mirrors the design's six-colour rotation.
|
||||
const AVATAR_PALETTE = [
|
||||
'bg-indigo-500/10 text-indigo-600 dark:text-indigo-400',
|
||||
'bg-violet-500/10 text-violet-600 dark:text-violet-400',
|
||||
'bg-amber-500/10 text-amber-600 dark:text-amber-400',
|
||||
'bg-rose-500/10 text-rose-600 dark:text-rose-400',
|
||||
'bg-emerald-500/10 text-emerald-600 dark:text-emerald-400',
|
||||
'bg-sky-500/10 text-sky-600 dark:text-sky-400',
|
||||
] as const
|
||||
|
||||
function avatarClass(name: string): string {
|
||||
let hash = 0
|
||||
for (let i = 0; i < name.length; i++) hash = (hash * 31 + name.charCodeAt(i)) >>> 0
|
||||
return AVATAR_PALETTE[hash % AVATAR_PALETTE.length]
|
||||
}
|
||||
|
||||
function isMarkdown(node: TreeNode): boolean {
|
||||
return node.kind === 'file' && node.name.toLowerCase().endsWith('.md')
|
||||
|
|
@ -204,10 +189,10 @@ export function KnowledgeView({
|
|||
|
||||
return (
|
||||
<div className="flex h-full flex-col overflow-hidden">
|
||||
<div className="shrink-0 flex items-start justify-between gap-4 border-b border-border px-8 py-6">
|
||||
<div className="mx-auto w-full max-w-[1120px] shrink-0 flex items-start justify-between gap-4 px-[30px] pt-[34px] pb-5">
|
||||
<div className="min-w-0">
|
||||
<h1 className="text-2xl font-bold tracking-tight">Brain</h1>
|
||||
<p className="mt-1 text-sm text-muted-foreground">
|
||||
<h1 className="text-[24px] font-[650] tracking-[-0.02em] text-[#0d0e11] dark:text-[#f4f5f7]">Brain</h1>
|
||||
<p className="mt-1 text-[14px] text-black/50 dark:text-white/[0.52]">
|
||||
{totalNotes} {totalNotes === 1 ? 'note' : 'notes'} across {folders.length}{' '}
|
||||
{folders.length === 1 ? 'folder' : 'folders'}
|
||||
</p>
|
||||
|
|
@ -242,12 +227,12 @@ export function KnowledgeView({
|
|||
{graphContent}
|
||||
</div>
|
||||
) : mode === 'basis' ? (
|
||||
<div className="flex-1 min-h-0 overflow-hidden">
|
||||
<div className="mx-auto flex w-full max-w-[1120px] flex-1 min-h-0 flex-col overflow-hidden px-[30px] pb-6">
|
||||
{basisContent}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
<div className="mx-auto w-full max-w-3xl px-8 py-6">
|
||||
<div className="mx-auto w-full max-w-[1120px] px-[30px] py-6">
|
||||
{currentFolder ? (
|
||||
<FolderDetail
|
||||
folder={currentFolder}
|
||||
|
|
@ -261,7 +246,7 @@ export function KnowledgeView({
|
|||
/>
|
||||
) : (
|
||||
<>
|
||||
<SectionHeader label={`Folders · ${folders.length}`} aside="Sorted by name" />
|
||||
<SectionHeader label={`Folders · ${folders.length}`} />
|
||||
{folders.length === 0 ? (
|
||||
<EmptyState text="No folders yet." />
|
||||
) : (
|
||||
|
|
@ -398,9 +383,9 @@ function QuickAction({
|
|||
<button
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
className="inline-flex items-center gap-2 rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground transition-colors hover:bg-accent"
|
||||
className="inline-flex items-center gap-2 rounded-md px-2.5 py-1.5 text-sm text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
|
||||
>
|
||||
<Icon className="size-4 text-muted-foreground" />
|
||||
<Icon className="size-4" />
|
||||
<span>{label}</span>
|
||||
</button>
|
||||
)
|
||||
|
|
@ -425,20 +410,6 @@ function EmptyState({ text }: { text: string }) {
|
|||
)
|
||||
}
|
||||
|
||||
function FolderAvatar({ name, className }: { name: string; className?: string }) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'flex size-8 shrink-0 items-center justify-center rounded-md text-[13px] font-bold',
|
||||
avatarClass(name),
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{name.charAt(0).toUpperCase() || '?'}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function FolderCard({
|
||||
node,
|
||||
actions,
|
||||
|
|
@ -472,9 +443,8 @@ function FolderCard({
|
|||
onOpenFolder(node.path)
|
||||
}
|
||||
}}
|
||||
className="group flex w-full cursor-pointer items-start gap-3 px-4 py-3 text-left transition-colors hover:bg-accent/50"
|
||||
className="group flex w-full cursor-pointer items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-accent/50"
|
||||
>
|
||||
<FolderAvatar name={node.name} className="mt-0.5" />
|
||||
<div className="min-w-0 flex-1">
|
||||
{renameActive ? (
|
||||
<RenameField
|
||||
|
|
@ -489,28 +459,32 @@ function FolderCard({
|
|||
{node.name}
|
||||
</span>
|
||||
)}
|
||||
<div className="mt-0.5 text-xs text-muted-foreground">
|
||||
{count} {count === 1 ? 'note' : 'notes'}
|
||||
<div className="mt-0.5 flex min-w-0 items-baseline gap-1.5 text-xs text-muted-foreground">
|
||||
<span className="shrink-0">
|
||||
{count} {count === 1 ? 'note' : 'notes'}
|
||||
</span>
|
||||
{peek.length > 0 && (
|
||||
<span className="truncate text-muted-foreground/70">
|
||||
{peek.map((n) => (
|
||||
<span key={n.path}>
|
||||
<span className="text-muted-foreground/40">{' · '}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
onOpenNote(n.path)
|
||||
}}
|
||||
className="transition-colors hover:text-foreground hover:underline"
|
||||
>
|
||||
{displayName(n)}
|
||||
</button>
|
||||
</span>
|
||||
))}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{peek.length > 0 && (
|
||||
<div className="mt-2 flex flex-wrap gap-1.5">
|
||||
{peek.map((n) => (
|
||||
<button
|
||||
key={n.path}
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
onOpenNote(n.path)
|
||||
}}
|
||||
className="max-w-[200px] truncate rounded-full border border-border/60 bg-muted px-2.5 py-0.5 text-xs text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
|
||||
>
|
||||
{displayName(n)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center gap-2 pt-1">
|
||||
<div className="flex shrink-0 items-center gap-2">
|
||||
<span className="text-xs text-muted-foreground tabular-nums whitespace-nowrap">
|
||||
{modified}
|
||||
</span>
|
||||
|
|
@ -565,17 +539,6 @@ function FolderDetail({
|
|||
return (
|
||||
<>
|
||||
<div className="mb-4 flex min-w-0 items-center gap-1.5 text-sm">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
const parent = crumbs.length >= 2 ? crumbs[crumbs.length - 2].path : null
|
||||
onNavigate(parent)
|
||||
}}
|
||||
className="inline-flex items-center gap-1 rounded-md px-1.5 py-1 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
|
||||
aria-label="Back"
|
||||
>
|
||||
<ArrowLeft className="size-4" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onNavigate(null)}
|
||||
|
|
@ -665,13 +628,6 @@ function ItemRow({
|
|||
}}
|
||||
className="group flex w-full cursor-pointer items-center gap-3 px-4 py-2.5 text-left transition-colors hover:bg-accent/50"
|
||||
>
|
||||
{isDir ? (
|
||||
<FolderAvatar name={node.name} />
|
||||
) : (
|
||||
<div className="flex size-8 shrink-0 items-center justify-center rounded-md bg-muted text-muted-foreground">
|
||||
<FileText className="size-4" />
|
||||
</div>
|
||||
)}
|
||||
<div className="min-w-0 flex-1">
|
||||
{renameActive ? (
|
||||
<RenameField
|
||||
|
|
@ -682,16 +638,21 @@ function ItemRow({
|
|||
onDone={onClearRename}
|
||||
/>
|
||||
) : (
|
||||
<span className="block truncate text-sm text-foreground">{displayName(node)}</span>
|
||||
)}
|
||||
{isDir && (
|
||||
<div className="mt-0.5 text-xs text-muted-foreground">
|
||||
{count} {count === 1 ? 'note' : 'notes'}
|
||||
</div>
|
||||
<span className="block truncate text-sm font-semibold text-foreground">
|
||||
{displayName(node)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center gap-2">
|
||||
<span className="text-xs text-muted-foreground tabular-nums whitespace-nowrap">
|
||||
<div className="flex shrink-0 items-center gap-2 text-xs text-muted-foreground">
|
||||
{isDir && (
|
||||
<>
|
||||
<span className="whitespace-nowrap">
|
||||
{count} {count === 1 ? 'note' : 'notes'}
|
||||
</span>
|
||||
<span className="text-muted-foreground/40">·</span>
|
||||
</>
|
||||
)}
|
||||
<span className="tabular-nums whitespace-nowrap">
|
||||
{modified}
|
||||
</span>
|
||||
{isDir && (
|
||||
|
|
|
|||
|
|
@ -711,8 +711,8 @@ function UpcomingEvents({ onOpenNote }: { onOpenNote: (path: string) => void })
|
|||
const todayKey = localDateKey(now)
|
||||
|
||||
return (
|
||||
<section className="border-b border-border/60 px-6 pb-6 pt-5">
|
||||
<div className="mx-auto w-full max-w-[760px]">
|
||||
<section className="border-b border-border/60 pb-6 pt-5">
|
||||
<div className="w-full">
|
||||
<div className="mb-3 flex items-baseline justify-between">
|
||||
<h3 className="text-sm font-semibold text-foreground flex items-center gap-2">
|
||||
<Calendar className="size-4 text-muted-foreground" />
|
||||
|
|
@ -1243,13 +1243,10 @@ export function MeetingsView({ onOpenNote, onTakeMeetingNotes, meetingState, mee
|
|||
const isRecording = meetingState === 'recording'
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col overflow-hidden">
|
||||
<div className="shrink-0 border-b border-border px-6 py-5">
|
||||
<div className="flex h-full flex-col overflow-hidden bg-[#f8f8f9] dark:bg-[#0b0b0d]">
|
||||
<div className="mx-auto w-full max-w-[1120px] shrink-0 px-[30px] pt-[34px] pb-5">
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Mic className="size-5 text-primary" />
|
||||
<h2 className="text-base font-semibold text-foreground">Meetings</h2>
|
||||
</div>
|
||||
<h2 className="text-[24px] font-[650] tracking-[-0.02em] text-[#0d0e11] dark:text-[#f4f5f7]">Meetings</h2>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
|
|
@ -1267,13 +1264,14 @@ export function MeetingsView({ onOpenNote, onTakeMeetingNotes, meetingState, mee
|
|||
{meetingSummarizing ? 'Generating notes...' : getMeetingButtonLabel(meetingState)}
|
||||
</Button>
|
||||
</div>
|
||||
<p className="mt-1 text-xs text-muted-foreground">
|
||||
<p className="mt-1 text-[14px] text-black/50 dark:text-white/[0.52]">
|
||||
Upcoming events and meeting notes.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex-1 overflow-auto">
|
||||
<div className="mx-auto w-full max-w-[1120px] px-[30px] pb-12">
|
||||
<UpcomingEvents onOpenNote={onOpenNote} />
|
||||
<div className="p-6">
|
||||
<div className="pt-6">
|
||||
{loading ? (
|
||||
<div className="flex items-center justify-center py-10">
|
||||
<Loader2 className="size-5 animate-spin text-muted-foreground" />
|
||||
|
|
@ -1329,6 +1327,7 @@ export function MeetingsView({ onOpenNote, onTakeMeetingNotes, meetingState, mee
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -890,6 +890,16 @@ export function SidebarContentPanel({
|
|||
<div className="mx-3 my-2 border-t border-sidebar-border" />
|
||||
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton
|
||||
data-tour-id="nav-apps"
|
||||
isActive={activeNav === 'apps'}
|
||||
onClick={onOpenApps}
|
||||
>
|
||||
<LayoutGrid className="size-4 shrink-0 text-muted-foreground" />
|
||||
<span className="flex-1 truncate text-muted-foreground">Apps</span>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton
|
||||
data-tour-id="nav-agents"
|
||||
|
|
@ -911,16 +921,6 @@ export function SidebarContentPanel({
|
|||
</div>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton
|
||||
data-tour-id="nav-apps"
|
||||
isActive={activeNav === 'apps'}
|
||||
onClick={onOpenApps}
|
||||
>
|
||||
<LayoutGrid className="size-4 shrink-0 text-muted-foreground" />
|
||||
<span className="flex-1 truncate text-muted-foreground">Apps</span>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton
|
||||
data-tour-id="nav-workspaces"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { ArrowRight, Plug } from 'lucide-react'
|
||||
import { ArrowRight } from 'lucide-react'
|
||||
|
||||
import { SettingsDialog } from '@/components/settings-dialog'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
|
@ -101,9 +101,6 @@ export function ToolConnectionsCard({ className }: { className?: string }) {
|
|||
<>
|
||||
<div className={cn('rounded-xl border border-border bg-card p-4', className)}>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="flex size-8 shrink-0 items-center justify-center rounded-lg border border-border bg-muted text-muted-foreground">
|
||||
<Plug className="size-[14px]" />
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="text-[13.5px] leading-snug">
|
||||
<span className="text-muted-foreground">Bring context from and take action in the apps you already use.</span>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import {
|
|||
Folder as FolderIcon,
|
||||
FolderOpen,
|
||||
FolderPlus,
|
||||
Home,
|
||||
Loader2,
|
||||
MessageSquare,
|
||||
Pencil,
|
||||
|
|
@ -365,19 +364,18 @@ export function WorkspaceView({ tree, initialPath, actions, onNavigate, onOpenNo
|
|||
}, [newName, onCreateWorkspace, resetAddDialog])
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col overflow-hidden">
|
||||
<div className="flex shrink-0 items-center justify-between gap-3 border-b border-border px-6 py-4">
|
||||
<div className="flex h-full flex-col overflow-hidden bg-[#f8f8f9] dark:bg-[#0b0b0d]">
|
||||
<div className="mx-auto flex w-full max-w-[1120px] shrink-0 items-center justify-between gap-3 pl-[22px] pr-[30px] pt-[30px] pb-4">
|
||||
<div className="flex min-w-0 items-center gap-1 text-sm">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onNavigate(WORKSPACE_ROOT)}
|
||||
className={cn(
|
||||
'inline-flex items-center gap-1.5 rounded-md px-2 py-1 transition-colors',
|
||||
isRoot ? 'text-foreground' : 'text-muted-foreground hover:text-foreground hover:bg-accent',
|
||||
'inline-flex items-center rounded-md px-2 py-1 transition-colors',
|
||||
isRoot ? 'text-[#0d0e11] dark:text-[#f4f5f7]' : 'text-muted-foreground hover:text-foreground hover:bg-accent',
|
||||
)}
|
||||
>
|
||||
<Home className="size-4" />
|
||||
<span className="font-medium">Workspace</span>
|
||||
<span className="text-[24px] font-[650] tracking-[-0.02em]">Workspace</span>
|
||||
</button>
|
||||
{breadcrumbs.map((crumb, idx) => {
|
||||
const isLast = idx === breadcrumbs.length - 1
|
||||
|
|
@ -482,12 +480,13 @@ export function WorkspaceView({ tree, initialPath, actions, onNavigate, onOpenNo
|
|||
|
||||
<div className="flex flex-1 overflow-hidden">
|
||||
<div
|
||||
className="relative flex-1 overflow-y-auto px-6 py-6"
|
||||
className="relative flex-1 overflow-y-auto"
|
||||
onDragEnter={handleDragEnter}
|
||||
onDragOver={handleDragOver}
|
||||
onDragLeave={handleDragLeave}
|
||||
onDrop={handleDrop}
|
||||
>
|
||||
<div className="mx-auto h-full w-full max-w-[1120px] px-[30px] py-6">
|
||||
{items.length === 0 ? (
|
||||
<div className="flex h-full flex-col items-center justify-center gap-3 text-center text-muted-foreground">
|
||||
<FolderIcon className="size-10 opacity-50" />
|
||||
|
|
@ -594,6 +593,7 @@ export function WorkspaceView({ tree, initialPath, actions, onNavigate, onOpenNo
|
|||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{dropEnabled && isDraggingOver && (
|
||||
<div className="pointer-events-none absolute inset-3 z-10 flex flex-col items-center justify-center gap-2 rounded-xl border-2 border-dashed border-primary/60 bg-primary/5 text-primary">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue