Skip to content

Commit ac62f8f

Browse files
web(chore): Cleanup me control design (#874)
1 parent 290fc75 commit ac62f8f

File tree

13 files changed

+401
-295
lines changed

13 files changed

+401
-295
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Changed
11+
- Changed the me-control to render the user's avatar in the top-bar. [#874](https://github.com/sourcebot-dev/sourcebot/pull/874)
12+
- Moved the "current version" indicator into the "what's new" dropdown. [#874](https://github.com/sourcebot-dev/sourcebot/pull/874)
13+
14+
### Removed
15+
- Removed the Discord and GitHub buttons from the top corner. [#874](https://github.com/sourcebot-dev/sourcebot/pull/874)
16+
1017
## [4.10.29] - 2026-02-10
1118

1219
### Changed
Lines changed: 8 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,17 @@
1-
'use client';
2-
3-
import { ResizablePanel, ResizablePanelGroup } from "@/components/ui/resizable";
4-
import { BottomPanel } from "./components/bottomPanel";
5-
import { AnimatedResizableHandle } from "@/components/ui/animatedResizableHandle";
6-
import { BrowseStateProvider } from "./browseStateProvider";
7-
import { FileTreePanel } from "./components/fileTreePanel";
8-
import { TopBar } from "@/app/[domain]/components/topBar";
9-
import { useBrowseParams } from "./hooks/useBrowseParams";
10-
import { FileSearchCommandDialog } from "./components/fileSearchCommandDialog";
11-
import { useDomain } from "@/hooks/useDomain";
12-
import { SearchBar } from "../components/searchBar";
13-
import escapeStringRegexp from "escape-string-regexp";
1+
import { auth } from "@/auth";
2+
import { LayoutClient } from "./layoutClient";
143

154
interface LayoutProps {
165
children: React.ReactNode;
176
}
187

19-
export default function Layout({
8+
export default async function Layout({
209
children,
2110
}: LayoutProps) {
22-
const { repoName, revisionName } = useBrowseParams();
23-
const domain = useDomain();
24-
11+
const session = await auth();
2512
return (
26-
<BrowseStateProvider>
27-
<div className="flex flex-col h-screen">
28-
<TopBar
29-
domain={domain}
30-
>
31-
<SearchBar
32-
size="sm"
33-
defaults={{
34-
query: `repo:^${escapeStringRegexp(repoName)}$${revisionName ? ` rev:${revisionName}` : ''} `,
35-
}}
36-
className="w-full"
37-
/>
38-
</TopBar>
39-
<ResizablePanelGroup
40-
direction="horizontal"
41-
>
42-
<FileTreePanel order={1} />
43-
44-
<AnimatedResizableHandle />
45-
46-
<ResizablePanel
47-
order={2}
48-
minSize={10}
49-
defaultSize={80}
50-
id="code-preview-panel-container"
51-
>
52-
<ResizablePanelGroup
53-
direction="vertical"
54-
>
55-
<ResizablePanel
56-
order={1}
57-
id="code-preview-panel"
58-
>
59-
{children}
60-
</ResizablePanel>
61-
<AnimatedResizableHandle />
62-
<BottomPanel
63-
order={2}
64-
/>
65-
</ResizablePanelGroup>
66-
</ResizablePanel>
67-
</ResizablePanelGroup>
68-
</div>
69-
<FileSearchCommandDialog />
70-
</BrowseStateProvider>
71-
);
13+
<LayoutClient session={session}>
14+
{children}
15+
</LayoutClient>
16+
)
7217
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
'use client';
2+
3+
import { ResizablePanel, ResizablePanelGroup } from "@/components/ui/resizable";
4+
import { BottomPanel } from "./components/bottomPanel";
5+
import { AnimatedResizableHandle } from "@/components/ui/animatedResizableHandle";
6+
import { BrowseStateProvider } from "./browseStateProvider";
7+
import { FileTreePanel } from "./components/fileTreePanel";
8+
import { TopBar } from "@/app/[domain]/components/topBar";
9+
import { useBrowseParams } from "./hooks/useBrowseParams";
10+
import { FileSearchCommandDialog } from "./components/fileSearchCommandDialog";
11+
import { useDomain } from "@/hooks/useDomain";
12+
import { SearchBar } from "../components/searchBar";
13+
import escapeStringRegexp from "escape-string-regexp";
14+
import { Session } from "next-auth";
15+
16+
interface LayoutProps {
17+
children: React.ReactNode;
18+
session: Session | null;
19+
}
20+
21+
export function LayoutClient({
22+
children,
23+
session,
24+
}: LayoutProps) {
25+
const { repoName, revisionName } = useBrowseParams();
26+
const domain = useDomain();
27+
28+
return (
29+
<BrowseStateProvider>
30+
<div className="flex flex-col h-screen">
31+
<TopBar
32+
domain={domain}
33+
session={session}
34+
>
35+
<SearchBar
36+
size="sm"
37+
defaults={{
38+
query: `repo:^${escapeStringRegexp(repoName)}$${revisionName ? ` rev:${revisionName}` : ''} `,
39+
}}
40+
className="w-full"
41+
/>
42+
</TopBar>
43+
<ResizablePanelGroup
44+
direction="horizontal"
45+
>
46+
<FileTreePanel order={1} />
47+
48+
<AnimatedResizableHandle />
49+
50+
<ResizablePanel
51+
order={2}
52+
minSize={10}
53+
defaultSize={80}
54+
id="code-preview-panel-container"
55+
>
56+
<ResizablePanelGroup
57+
direction="vertical"
58+
>
59+
<ResizablePanel
60+
order={1}
61+
id="code-preview-panel"
62+
>
63+
{children}
64+
</ResizablePanel>
65+
<AnimatedResizableHandle />
66+
<BottomPanel
67+
order={2}
68+
/>
69+
</ResizablePanelGroup>
70+
</ResizablePanel>
71+
</ResizablePanelGroup>
72+
</div>
73+
<FileSearchCommandDialog />
74+
</BrowseStateProvider>
75+
);
76+
}

packages/web/src/app/[domain]/chat/[id]/page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export default async function Page(props: PageProps) {
5757
<TopBar
5858
domain={params.domain}
5959
homePath={`/${params.domain}/chat`}
60+
session={session}
6061
>
6162
<div className="flex flex-row gap-2 items-center">
6263
<span className="text-muted mx-2 select-none">/</span>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { Button } from "@/components/ui/button"
2+
import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
3+
import { Settings2Icon } from "lucide-react"
4+
import { AppearanceDropdownMenuGroup } from "./appearanceDropdownMenuGroup"
5+
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
6+
7+
interface AppearanceDropdownMenuProps {
8+
className?: string;
9+
}
10+
11+
export const AppearanceDropdownMenu = ({ className }: AppearanceDropdownMenuProps) => {
12+
return (
13+
<DropdownMenu>
14+
<Tooltip
15+
delayDuration={100}
16+
>
17+
<TooltipTrigger asChild>
18+
<DropdownMenuTrigger asChild>
19+
<Button variant="outline" size="icon" className={className}>
20+
<Settings2Icon className="h-4 w-4" />
21+
</Button>
22+
</DropdownMenuTrigger>
23+
</TooltipTrigger>
24+
<TooltipContent>
25+
Appearance settings
26+
</TooltipContent>
27+
</Tooltip>
28+
<DropdownMenuContent>
29+
<AppearanceDropdownMenuGroup />
30+
</DropdownMenuContent>
31+
</DropdownMenu>
32+
)
33+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
'use client';
2+
3+
import {
4+
DropdownMenuGroup,
5+
DropdownMenuPortal,
6+
DropdownMenuRadioGroup,
7+
DropdownMenuRadioItem,
8+
DropdownMenuSub,
9+
DropdownMenuSubContent,
10+
DropdownMenuSubTrigger
11+
} from "@/components/ui/dropdown-menu"
12+
import { useKeymapType } from "@/hooks/useKeymapType"
13+
import { KeymapType } from "@/lib/types"
14+
import {
15+
CodeIcon,
16+
Laptop,
17+
Moon,
18+
Sun
19+
} from "lucide-react"
20+
import { useTheme } from "next-themes"
21+
import { useMemo } from "react"
22+
23+
export const AppearanceDropdownMenuGroup = () => {
24+
const { theme: _theme, setTheme } = useTheme();
25+
const [keymapType, setKeymapType] = useKeymapType();
26+
27+
const theme = useMemo(() => {
28+
return _theme ?? "light";
29+
}, [_theme]);
30+
31+
const ThemeIcon = useMemo(() => {
32+
switch (theme) {
33+
case "light":
34+
return <Sun className="h-4 w-4 mr-2" />;
35+
case "dark":
36+
return <Moon className="h-4 w-4 mr-2" />;
37+
case "system":
38+
return <Laptop className="h-4 w-4 mr-2" />;
39+
default:
40+
return <Laptop className="h-4 w-4 mr-2" />;
41+
}
42+
}, [theme]);
43+
44+
return (
45+
<DropdownMenuGroup>
46+
<DropdownMenuSub>
47+
<DropdownMenuSubTrigger>
48+
{ThemeIcon}
49+
<span>Theme</span>
50+
</DropdownMenuSubTrigger>
51+
<DropdownMenuPortal>
52+
<DropdownMenuSubContent>
53+
<DropdownMenuRadioGroup value={theme} onValueChange={setTheme}>
54+
<DropdownMenuRadioItem value="light">
55+
Light
56+
</DropdownMenuRadioItem>
57+
<DropdownMenuRadioItem value="dark">
58+
Dark
59+
</DropdownMenuRadioItem>
60+
<DropdownMenuRadioItem value="system">
61+
System
62+
</DropdownMenuRadioItem>
63+
</DropdownMenuRadioGroup>
64+
</DropdownMenuSubContent>
65+
</DropdownMenuPortal>
66+
</DropdownMenuSub>
67+
<DropdownMenuSub>
68+
<DropdownMenuSubTrigger>
69+
<CodeIcon className="h-4 w-4 mr-2" />
70+
<span>Code navigation</span>
71+
</DropdownMenuSubTrigger>
72+
<DropdownMenuPortal>
73+
<DropdownMenuSubContent>
74+
<DropdownMenuRadioGroup value={keymapType} onValueChange={(value) => setKeymapType(value as KeymapType)}>
75+
<DropdownMenuRadioItem value="default">
76+
Default
77+
</DropdownMenuRadioItem>
78+
<DropdownMenuRadioItem value="vim">
79+
Vim
80+
</DropdownMenuRadioItem>
81+
</DropdownMenuRadioGroup>
82+
</DropdownMenuSubContent>
83+
</DropdownMenuPortal>
84+
</DropdownMenuSub>
85+
</DropdownMenuGroup>
86+
)
87+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
'use client';
2+
3+
import {
4+
LogOut,
5+
Settings,
6+
} from "lucide-react"
7+
import {
8+
DropdownMenu,
9+
DropdownMenuContent,
10+
DropdownMenuGroup,
11+
DropdownMenuItem,
12+
DropdownMenuSeparator,
13+
DropdownMenuTrigger,
14+
} from "@/components/ui/dropdown-menu"
15+
import { cn } from "@/lib/utils"
16+
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
17+
import { signOut } from "next-auth/react"
18+
import posthog from "posthog-js";
19+
import { useDomain } from "@/hooks/useDomain";
20+
import { Session } from "next-auth";
21+
import { AppearanceDropdownMenuGroup } from "./appearanceDropdownMenuGroup";
22+
import placeholderAvatar from "@/public/placeholder_avatar.png";
23+
24+
interface MeControlDropdownMenuProps {
25+
menuButtonClassName?: string;
26+
session: Session;
27+
}
28+
29+
export const MeControlDropdownMenu = ({
30+
menuButtonClassName,
31+
session,
32+
}: MeControlDropdownMenuProps) => {
33+
const domain = useDomain();
34+
35+
return (
36+
<DropdownMenu>
37+
<DropdownMenuTrigger asChild>
38+
<Avatar className={cn("h-8 w-8 cursor-pointer", menuButtonClassName)}>
39+
<AvatarImage src={session.user.image ?? placeholderAvatar.src} />
40+
<AvatarFallback className="bg-primary/10 text-primary font-semibold text-sm">
41+
{session.user.name && session.user.name.length > 0 ? session.user.name[0].toUpperCase() : 'U'}
42+
</AvatarFallback>
43+
</Avatar>
44+
</DropdownMenuTrigger>
45+
<DropdownMenuContent className="w-64" align="end" sideOffset={5}>
46+
<DropdownMenuGroup>
47+
<div className="flex flex-row items-center gap-3 px-3 py-3">
48+
<Avatar className="h-10 w-10 flex-shrink-0">
49+
<AvatarImage
50+
src={session.user.image ?? ""}
51+
/>
52+
<AvatarFallback className="bg-primary/10 text-primary font-semibold">
53+
{session.user.name && session.user.name.length > 0 ? session.user.name[0].toUpperCase() : 'U'}
54+
</AvatarFallback>
55+
</Avatar>
56+
<div className="flex flex-col flex-1 min-w-0">
57+
<p className="text-sm font-semibold truncate">{session.user.name ?? "User"}</p>
58+
{session.user.email && (
59+
<p className="text-xs text-muted-foreground truncate">{session.user.email}</p>
60+
)}
61+
</div>
62+
</div>
63+
</DropdownMenuGroup>
64+
<DropdownMenuSeparator />
65+
<AppearanceDropdownMenuGroup />
66+
<DropdownMenuItem asChild>
67+
<a href={`/${domain}/settings`}>
68+
<Settings className="h-4 w-4 mr-2" />
69+
<span>Settings</span>
70+
</a>
71+
</DropdownMenuItem>
72+
<DropdownMenuSeparator />
73+
<DropdownMenuGroup>
74+
<DropdownMenuItem
75+
onClick={() => {
76+
signOut({
77+
redirectTo: "/login",
78+
}).then(() => {
79+
posthog.reset();
80+
})
81+
}}
82+
>
83+
<LogOut className="mr-2 h-4 w-4" />
84+
<span>Log out</span>
85+
</DropdownMenuItem>
86+
</DropdownMenuGroup>
87+
</DropdownMenuContent>
88+
</DropdownMenu>
89+
)
90+
}

0 commit comments

Comments
 (0)