feat: update shell components (rail, settings-sidebar, workspace-sidebar)
This commit is contained in:
parent
db5b54025b
commit
9b1f764ab8
@ -18,21 +18,21 @@ function WorkspaceIcon({
|
|||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
aria-label={workspace.name}
|
aria-label={workspace.name}
|
||||||
className="group relative grid size-11 place-items-center"
|
className="group relative grid size-10 place-items-center"
|
||||||
title={workspace.name}
|
title={workspace.name}
|
||||||
to={`/${workspace.name}/repos`}
|
to={`/${workspace.name}/repos`}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
"absolute -left-3 h-2 w-1 rounded-r-full bg-foreground opacity-0 transition-all group-hover:h-5 group-hover:opacity-100",
|
"absolute -left-[11px] h-2 w-[3px] rounded-r-full bg-foreground opacity-0 transition-all group-hover:h-4 group-hover:opacity-60",
|
||||||
active && "h-8 opacity-100",
|
active && "h-6 opacity-100",
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
"grid size-10 place-items-center overflow-hidden rounded-2xl bg-gradient-to-br text-sm font-semibold text-white shadow-sm ring-1 ring-black/5 transition-all group-hover:rounded-xl",
|
"grid size-9 place-items-center overflow-hidden rounded-xl bg-gradient-to-br text-[12px] font-bold text-white ring-1 ring-black/[0.06] transition-all",
|
||||||
workspaceColor(workspace.name),
|
workspaceColor(workspace.name),
|
||||||
active && "rounded-xl ring-2 ring-primary/30",
|
active && "rounded-lg ring-2 ring-primary/20",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{workspace.avatar_url ? (
|
{workspace.avatar_url ? (
|
||||||
@ -61,7 +61,7 @@ function RailButton({
|
|||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
aria-label={label}
|
aria-label={label}
|
||||||
className="inline-flex size-10 shrink-0 items-center justify-center rounded-2xl border border-border bg-card text-muted-foreground shadow-sm transition-all hover:rounded-xl hover:bg-accent hover:text-primary"
|
className="inline-flex size-9 shrink-0 items-center justify-center rounded-xl border border-border bg-card text-muted-foreground transition-all hover:bg-accent hover:text-foreground"
|
||||||
title={label}
|
title={label}
|
||||||
to={to}
|
to={to}
|
||||||
>
|
>
|
||||||
@ -72,10 +72,10 @@ function RailButton({
|
|||||||
|
|
||||||
function WorkspaceSkeleton() {
|
function WorkspaceSkeleton() {
|
||||||
return (
|
return (
|
||||||
<div className="grid gap-3">
|
<div className="grid gap-2">
|
||||||
{Array.from({ length: 4 }).map((_, index) => (
|
{Array.from({ length: 4 }).map((_, index) => (
|
||||||
<div
|
<div
|
||||||
className="size-10 animate-pulse rounded-2xl bg-muted"
|
className="size-9 animate-pulse rounded-xl bg-muted"
|
||||||
key={index}
|
key={index}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
@ -95,16 +95,16 @@ export function WorkspaceRail() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside className="flex h-svh w-[72px] shrink-0 flex-col items-center bg-muted py-4">
|
<aside className="flex h-svh w-[72px] shrink-0 flex-col items-center border-r border-border bg-background py-5">
|
||||||
<RailButton label="Home" to="/">
|
<RailButton label="Home" to="/">
|
||||||
<Home className="size-5" />
|
<Home className="size-[18px]" />
|
||||||
</RailButton>
|
</RailButton>
|
||||||
|
|
||||||
<div className="my-4 h-px w-8 bg-border" />
|
<div className="my-3 h-px w-7 bg-border" />
|
||||||
|
|
||||||
<nav
|
<nav
|
||||||
aria-label="Workspaces"
|
aria-label="Workspaces"
|
||||||
className="flex min-h-0 flex-1 flex-col items-center gap-3 overflow-y-auto overscroll-contain px-3"
|
className="flex min-h-0 flex-1 flex-col items-center gap-2.5 overflow-y-auto overscroll-contain px-2"
|
||||||
>
|
>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<WorkspaceSkeleton />
|
<WorkspaceSkeleton />
|
||||||
@ -121,18 +121,18 @@ export function WorkspaceRail() {
|
|||||||
<CreateWorkspaceDialog>
|
<CreateWorkspaceDialog>
|
||||||
<Button
|
<Button
|
||||||
aria-label="Create workspace"
|
aria-label="Create workspace"
|
||||||
className="size-10 rounded-2xl border-border bg-card text-muted-foreground shadow-sm transition-all hover:rounded-xl hover:bg-accent hover:text-primary"
|
className="size-9 rounded-xl border-border bg-card text-muted-foreground transition-all hover:bg-accent hover:text-foreground"
|
||||||
size="icon"
|
size="icon"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
>
|
>
|
||||||
<Plus className="size-5" />
|
<Plus className="size-[18px]" />
|
||||||
</Button>
|
</Button>
|
||||||
</CreateWorkspaceDialog>
|
</CreateWorkspaceDialog>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div className="mt-4">
|
<div className="mt-3">
|
||||||
<RailButton label="Settings" to="/settings">
|
<RailButton label="Settings" to="/settings">
|
||||||
<Settings className="size-5" />
|
<Settings className="size-[18px]" />
|
||||||
</RailButton>
|
</RailButton>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
@ -146,4 +146,4 @@ export default function NavShell({ children }: { children?: React.ReactNode }) {
|
|||||||
<div className="min-w-0 flex-1">{children}</div>
|
<div className="min-w-0 flex-1">{children}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -73,8 +73,8 @@ function SettingsSidebar() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside className="flex h-full w-[248px] shrink-0 flex-col overflow-hidden bg-card">
|
<aside className="flex h-full w-[240px] shrink-0 flex-col overflow-hidden border-r border-border bg-sidebar">
|
||||||
<header className="flex h-14 shrink-0 items-center gap-3 border-b border-border px-4 shadow-sm">
|
<header className="flex h-[48px] shrink-0 items-center gap-3 border-b border-border px-4">
|
||||||
<SettingsAvatar />
|
<SettingsAvatar />
|
||||||
<button className="flex min-w-0 flex-1 items-center gap-2 text-left text-base font-semibold text-foreground">
|
<button className="flex min-w-0 flex-1 items-center gap-2 text-left text-base font-semibold text-foreground">
|
||||||
<span className="truncate">Settings</span>
|
<span className="truncate">Settings</span>
|
||||||
@ -141,7 +141,7 @@ export function SettingsShell() {
|
|||||||
<div className="flex h-full overflow-hidden">
|
<div className="flex h-full overflow-hidden">
|
||||||
<SettingsSidebar />
|
<SettingsSidebar />
|
||||||
<main className="flex min-w-0 flex-1 flex-col overflow-hidden bg-background">
|
<main className="flex min-w-0 flex-1 flex-col overflow-hidden bg-background">
|
||||||
<div className="flex h-14 shrink-0 items-center border-b border-border px-6" />
|
<div className="flex h-[48px] shrink-0 items-center border-b border-border px-6" />
|
||||||
<div className="min-h-0 flex-1 overflow-y-auto overscroll-contain">
|
<div className="min-h-0 flex-1 overflow-y-auto overscroll-contain">
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -101,24 +101,24 @@ function ChannelLink({
|
|||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-[30px] items-center gap-2 rounded-md px-2 text-[13px] transition-all duration-150",
|
"group flex h-[32px] items-center gap-2 rounded-lg px-2.5 text-[13px] transition-all duration-150",
|
||||||
active
|
active
|
||||||
? "bg-primary/[0.06] font-medium text-foreground"
|
? "bg-primary/[0.08] font-semibold text-foreground shadow-[inset_0_0_0_1px_rgba(var(--color-primary),0.06)]"
|
||||||
: "text-muted-foreground/70 hover:bg-accent/[0.35] hover:text-foreground",
|
: "text-muted-foreground/70 hover:bg-accent/50 hover:text-foreground",
|
||||||
)}
|
)}
|
||||||
to={to}
|
to={to}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
"shrink-0 transition-colors duration-150",
|
"shrink-0 transition-colors duration-150",
|
||||||
active ? "text-primary/60" : "text-muted-foreground/40",
|
active ? "text-primary/70" : "text-muted-foreground/50 group-hover:text-muted-foreground/70",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Hash className="size-[15px]" />
|
<Hash className="size-[15px]" />
|
||||||
</span>
|
</span>
|
||||||
<span className="min-w-0 flex-1 truncate">{channel.name}</span>
|
<span className="min-w-0 flex-1 truncate">{channel.name}</span>
|
||||||
{channel.is_private && (
|
{channel.is_private && (
|
||||||
<Lock className="size-3 shrink-0 text-muted-foreground/25" />
|
<Lock className="size-3 shrink-0 text-muted-foreground/30" />
|
||||||
)}
|
)}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
@ -144,7 +144,7 @@ function ChannelCategoryGroup({
|
|||||||
return (
|
return (
|
||||||
<div className="mb-0.5">
|
<div className="mb-0.5">
|
||||||
<button
|
<button
|
||||||
className="flex h-7 w-full items-center gap-1.5 rounded-md px-1.5 text-[11px] font-semibold uppercase tracking-[0.06em] text-muted-foreground/35 transition-colors hover:text-muted-foreground/65"
|
className="flex h-7 w-full items-center gap-1.5 rounded-md px-1.5 text-[11px] font-semibold uppercase tracking-[0.06em] text-muted-foreground/50 transition-colors hover:text-muted-foreground/80 hover:bg-accent/40"
|
||||||
onClick={() => setOpen((v) => !v)}
|
onClick={() => setOpen((v) => !v)}
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
@ -155,7 +155,7 @@ function ChannelCategoryGroup({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<span className="flex-1 truncate text-left">{name}</span>
|
<span className="flex-1 truncate text-left">{name}</span>
|
||||||
<span className="text-[10px] tabular-nums text-muted-foreground/25">
|
<span className="text-[10px] tabular-nums text-muted-foreground/40">
|
||||||
{channels.length}
|
{channels.length}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
@ -252,9 +252,9 @@ function WorkspaceSidebar() {
|
|||||||
location.pathname.startsWith(`/${projectName}/chat/`);
|
location.pathname.startsWith(`/${projectName}/chat/`);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside className="flex h-full w-[252px] shrink-0 flex-col overflow-hidden border-r border-border/30 bg-card">
|
<aside className="flex h-full w-[240px] shrink-0 flex-col overflow-hidden border-r border-border bg-sidebar">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<header className="flex h-[52px] shrink-0 items-center gap-2.5 border-b border-border/40 px-4">
|
<header className="flex h-[48px] shrink-0 items-center gap-2.5 border-b border-border px-4">
|
||||||
<WorkspaceAvatar workspace={currentWorkspace} />
|
<WorkspaceAvatar workspace={currentWorkspace} />
|
||||||
<button className="flex min-w-0 flex-1 items-center gap-1.5 text-left">
|
<button className="flex min-w-0 flex-1 items-center gap-1.5 text-left">
|
||||||
<span className="truncate text-sm font-semibold text-foreground">
|
<span className="truncate text-sm font-semibold text-foreground">
|
||||||
@ -279,8 +279,8 @@ function WorkspaceSidebar() {
|
|||||||
{showChannel ? (
|
{showChannel ? (
|
||||||
<section className="mb-5">
|
<section className="mb-5">
|
||||||
{/* Channel list header */}
|
{/* Channel list header */}
|
||||||
<div className="mb-1 flex items-center justify-between px-1.5">
|
<div className="mb-2 flex items-center justify-between px-1.5">
|
||||||
<span className="text-[11px] font-semibold uppercase tracking-[0.08em] text-muted-foreground/35">
|
<span className="text-[11px] font-semibold uppercase tracking-[0.08em] text-muted-foreground/50">
|
||||||
Channels
|
Channels
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -315,7 +315,7 @@ function WorkspaceSidebar() {
|
|||||||
onCreated={refreshChannels}
|
onCreated={refreshChannels}
|
||||||
workspaceId={workspaceId}
|
workspaceId={workspaceId}
|
||||||
>
|
>
|
||||||
<div className="mt-1 flex h-[30px] w-full cursor-pointer items-center gap-2 rounded-md px-2 text-[13px] text-muted-foreground/40 transition-all duration-150 hover:bg-accent/[0.35] hover:text-muted-foreground/70">
|
<div className="mt-1.5 flex h-[32px] w-full cursor-pointer items-center gap-2 rounded-md px-2 text-[13px] text-muted-foreground/60 transition-all duration-150 hover:bg-accent/50 hover:text-muted-foreground">
|
||||||
<Plus className="size-[15px] shrink-0" />
|
<Plus className="size-[15px] shrink-0" />
|
||||||
<span>Add channel</span>
|
<span>Add channel</span>
|
||||||
</div>
|
</div>
|
||||||
@ -414,62 +414,62 @@ function TopBar() {
|
|||||||
const repoName = isRepo ? segments[2] : null;
|
const repoName = isRepo ? segments[2] : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="flex h-[52px] shrink-0 items-center justify-between border-b border-border/40 bg-background/70 px-5 backdrop-blur-sm">
|
<header className="flex h-[44px] shrink-0 items-center justify-between border-b border-border bg-background px-5">
|
||||||
<div className="flex min-w-0 items-center gap-2 text-sm">
|
<div className="flex min-w-0 items-center gap-1.5 text-[13px]">
|
||||||
<Link
|
<Link
|
||||||
className="shrink-0 text-muted-foreground/50 transition-colors hover:text-foreground"
|
className="shrink-0 text-muted-foreground/40 transition-colors hover:text-foreground"
|
||||||
to="/"
|
to="/"
|
||||||
>
|
>
|
||||||
<Home className="size-[18px]" />
|
<Home className="size-[16px]" />
|
||||||
</Link>
|
</Link>
|
||||||
<span className="shrink-0 text-muted-foreground/20">/</span>
|
<span className="shrink-0 text-muted-foreground/15">/</span>
|
||||||
<Link
|
<Link
|
||||||
className="shrink-0 truncate text-muted-foreground/60 transition-colors hover:text-foreground"
|
className="shrink-0 truncate text-muted-foreground/50 transition-colors hover:text-foreground"
|
||||||
to={`/${projectName}/repos`}
|
to={`/${projectName}/repos`}
|
||||||
>
|
>
|
||||||
{projectName}
|
{projectName}
|
||||||
</Link>
|
</Link>
|
||||||
{repoName && (
|
{repoName && (
|
||||||
<>
|
<>
|
||||||
<span className="shrink-0 text-muted-foreground/20">/</span>
|
<span className="shrink-0 text-muted-foreground/15">/</span>
|
||||||
<span className="shrink-0 truncate text-foreground/50">
|
<span className="shrink-0 truncate text-muted-foreground/40">
|
||||||
{repoName}
|
{repoName}
|
||||||
</span>
|
</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<span className="shrink-0 text-muted-foreground/20">/</span>
|
<span className="shrink-0 text-muted-foreground/15">/</span>
|
||||||
<span className="shrink-0 font-semibold text-foreground">
|
<span className="shrink-0 font-semibold text-foreground">
|
||||||
{title}
|
{title}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-0.5 text-muted-foreground/40">
|
<div className="flex items-center gap-1 text-muted-foreground/30">
|
||||||
<CommandPalette />
|
<CommandPalette />
|
||||||
<button
|
<button
|
||||||
className={cn(
|
className={cn(
|
||||||
"grid size-8 place-items-center rounded-lg transition-all duration-150 hover:bg-accent/50 hover:text-muted-foreground",
|
"grid size-8 place-items-center rounded-xl transition-all duration-150 hover:bg-accent/50 hover:text-muted-foreground/70",
|
||||||
panel.showSearch && "bg-accent/50 text-foreground",
|
panel.showSearch && "bg-accent/50 text-foreground",
|
||||||
)}
|
)}
|
||||||
onClick={panel.toggleSearch}
|
onClick={panel.toggleSearch}
|
||||||
title="Search"
|
title="Search"
|
||||||
>
|
>
|
||||||
<Search className="size-[15px]" />
|
<Search className="size-[14px]" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className={cn(
|
className={cn(
|
||||||
"grid size-8 place-items-center rounded-lg transition-all duration-150 hover:bg-accent/50 hover:text-muted-foreground",
|
"grid size-8 place-items-center rounded-xl transition-all duration-150 hover:bg-accent/50 hover:text-muted-foreground/70",
|
||||||
panel.showMembers && "bg-accent/50 text-foreground",
|
panel.showMembers && "bg-accent/50 text-foreground",
|
||||||
)}
|
)}
|
||||||
onClick={panel.toggleMembers}
|
onClick={panel.toggleMembers}
|
||||||
title="Members"
|
title="Members"
|
||||||
>
|
>
|
||||||
<Users className="size-[15px]" />
|
<Users className="size-[14px]" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="grid size-8 place-items-center rounded-lg transition-all duration-150 hover:bg-accent/50 hover:text-muted-foreground"
|
className="grid size-8 place-items-center rounded-xl transition-all duration-150 hover:bg-accent/50 hover:text-muted-foreground/70"
|
||||||
title="Notifications"
|
title="Notifications"
|
||||||
>
|
>
|
||||||
<Bell className="size-[15px]" />
|
<Bell className="size-[14px]" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
@ -524,7 +524,7 @@ function MembersPanel() {
|
|||||||
<div className="space-y-[1px]">
|
<div className="space-y-[1px]">
|
||||||
{panel.members.map((member) => (
|
{panel.members.map((member) => (
|
||||||
<div
|
<div
|
||||||
className="flex items-center gap-2.5 rounded-md px-2 py-1.5 transition-colors hover:bg-accent/[0.03]"
|
className="flex items-center gap-2.5 rounded-xl px-3 py-2 transition-colors hover:bg-accent/[0.04]"
|
||||||
key={member.id}
|
key={member.id}
|
||||||
>
|
>
|
||||||
<span className="grid size-8 place-items-center rounded-full bg-gradient-to-br from-muted-foreground/10 to-muted-foreground/5 text-xs font-semibold text-muted-foreground/60">
|
<span className="grid size-8 place-items-center rounded-full bg-gradient-to-br from-muted-foreground/10 to-muted-foreground/5 text-xs font-semibold text-muted-foreground/60">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user