Skip to content

Commit 6e4a415

Browse files
wip
1 parent 355509b commit 6e4a415

25 files changed

Lines changed: 887 additions & 215 deletions

File tree

CLAUDE.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,18 @@ if (!value) { return; }
5757
if (condition) doSomething();
5858
```
5959

60+
## Conditional ClassNames
61+
62+
Use `cn()` from `@/lib/utils` for conditional classNames instead of template literal interpolation:
63+
64+
```tsx
65+
// Correct
66+
className={cn("border-b transition-colors", isActive ? "border-foreground" : "border-transparent")}
67+
68+
// Incorrect
69+
className={`border-b transition-colors ${isActive ? "border-foreground" : "border-transparent"}`}
70+
```
71+
6072
## Tailwind CSS
6173

6274
Use Tailwind color classes directly instead of CSS variable syntax:

packages/db/tools/scriptRunner.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { injectUserData } from "./scripts/inject-user-data";
77
import { confirmAction } from "./utils";
88
import { injectRepoData } from "./scripts/inject-repo-data";
99
import { testRepoQueryPerf } from "./scripts/test-repo-query-perf";
10+
import { injectChatData } from "./scripts/inject-chat-data";
1011

1112
export interface Script {
1213
run: (prisma: PrismaClient) => Promise<void>;
@@ -19,12 +20,13 @@ export const scripts: Record<string, Script> = {
1920
"inject-user-data": injectUserData,
2021
"inject-repo-data": injectRepoData,
2122
"test-repo-query-perf": testRepoQueryPerf,
23+
"inject-chat-data": injectChatData,
2224
}
2325

2426
const parser = new ArgumentParser();
2527
parser.add_argument("--url", { required: true, help: "Database URL" });
2628
parser.add_argument("--script", { required: true, help: "Script to run" });
27-
const args = parser.parse_args();
29+
const [args] = parser.parse_known_args();
2830

2931
(async () => {
3032
if (!(args.script in scripts)) {
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { Script } from "../scriptRunner";
2+
import { PrismaClient } from "../../dist";
3+
import { confirmAction } from "../utils";
4+
5+
const chatNames = [
6+
"How does the auth middleware work?",
7+
"Explain the search indexing pipeline",
8+
"Where are API routes defined?",
9+
"How to add a new database migration",
10+
"What is the repo sync process?",
11+
"Understanding the chat architecture",
12+
"How does SSO integration work?",
13+
"Explain the permission model",
14+
"Where is the webhook handler?",
15+
"How to configure environment variables",
16+
"Understanding the billing system",
17+
"How does the worker process jobs?",
18+
"Explain the caching strategy",
19+
"Where are the shared types defined?",
20+
"How does code search ranking work?",
21+
"Understanding the notification system",
22+
"How to add a new API endpoint",
23+
"Explain the deployment pipeline",
24+
"Where is error handling centralized?",
25+
"How does real-time updates work?",
26+
"Understanding the plugin system",
27+
"How to write integration tests",
28+
"Explain the file indexing process",
29+
"Where are the email templates?",
30+
"How does rate limiting work?",
31+
"Understanding the monorepo structure",
32+
"How to add a new feature flag",
33+
"Explain the logging setup",
34+
"Where is the GraphQL schema?",
35+
"How does the sidebar component work?",
36+
];
37+
38+
export const injectChatData: Script = {
39+
run: async (prisma: PrismaClient) => {
40+
const orgId = 1;
41+
42+
const org = await prisma.org.findUnique({
43+
where: { id: orgId }
44+
});
45+
46+
if (!org) {
47+
console.error(`Organization with id ${orgId} not found.`);
48+
return;
49+
}
50+
51+
const userIdArg = process.argv.find(arg => arg.startsWith("--user-id="))?.split("=")[1];
52+
53+
const user = userIdArg
54+
? await prisma.user.findUnique({ where: { id: userIdArg } })
55+
: await prisma.user.findFirst({
56+
where: {
57+
orgs: {
58+
some: { orgId }
59+
}
60+
}
61+
});
62+
63+
if (!user) {
64+
console.error(userIdArg
65+
? `User with id "${userIdArg}" not found.`
66+
: `No user found in org ${orgId}.`
67+
);
68+
return;
69+
}
70+
71+
await confirmAction(`This will create ${chatNames.length} chats for user "${user.name ?? user.email}" in org ${orgId}.`);
72+
73+
for (const name of chatNames) {
74+
await prisma.chat.create({
75+
data: {
76+
name,
77+
orgId,
78+
createdById: user.id,
79+
messages: [],
80+
}
81+
});
82+
}
83+
84+
console.log(`Created ${chatNames.length} chats.`);
85+
}
86+
};

packages/web/package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
"@radix-ui/react-avatar": "^1.1.2",
7171
"@radix-ui/react-checkbox": "^1.3.2",
7272
"@radix-ui/react-collapsible": "^1.1.11",
73-
"@radix-ui/react-dialog": "^1.1.4",
73+
"@radix-ui/react-dialog": "^1.1.15",
7474
"@radix-ui/react-dropdown-menu": "^2.1.1",
7575
"@radix-ui/react-hover-card": "^1.1.6",
7676
"@radix-ui/react-icons": "^1.3.0",
@@ -79,13 +79,13 @@
7979
"@radix-ui/react-popover": "^1.1.6",
8080
"@radix-ui/react-scroll-area": "^1.1.0",
8181
"@radix-ui/react-select": "^2.1.6",
82-
"@radix-ui/react-separator": "^1.1.0",
83-
"@radix-ui/react-slot": "^1.1.1",
82+
"@radix-ui/react-separator": "^1.1.8",
83+
"@radix-ui/react-slot": "^1.2.4",
8484
"@radix-ui/react-switch": "^1.2.4",
8585
"@radix-ui/react-tabs": "^1.1.2",
8686
"@radix-ui/react-toast": "^1.2.2",
8787
"@radix-ui/react-toggle": "^1.1.0",
88-
"@radix-ui/react-tooltip": "^1.1.4",
88+
"@radix-ui/react-tooltip": "^1.2.8",
8989
"@react-email/components": "^1.0.2",
9090
"@react-email/render": "^2.0.0",
9191
"@replit/codemirror-lang-csharp": "^6.2.0",
@@ -114,7 +114,7 @@
114114
"ai": "^6.0.105",
115115
"ajv": "^8.17.1",
116116
"bcryptjs": "^3.0.2",
117-
"class-variance-authority": "^0.7.0",
117+
"class-variance-authority": "^0.7.1",
118118
"client-only": "^0.0.1",
119119
"clsx": "^2.1.1",
120120
"cm6-graphql": "^0.2.0",
@@ -149,7 +149,7 @@
149149
"langfuse": "^3.38.4",
150150
"langfuse-vercel": "^3.38.4",
151151
"linguist-languages": "^9.3.1",
152-
"lucide-react": "^0.517.0",
152+
"lucide-react": "^1.7.0",
153153
"micromatch": "^4.0.8",
154154
"minidenticons": "^4.2.1",
155155
"next": "16.1.6",

packages/web/src/app/(app)/browse/layout.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { auth } from "@/auth";
21
import { LayoutClient } from "./layoutClient";
32
import { getConfiguredLanguageModelsInfo } from "@/features/chat/utils.server";
43

@@ -9,10 +8,9 @@ interface LayoutProps {
98
export default async function Layout({
109
children,
1110
}: LayoutProps) {
12-
const session = await auth();
1311
const languageModels = await getConfiguredLanguageModelsInfo();
1412
return (
15-
<LayoutClient session={session} isSearchAssistSupported={languageModels.length > 0}>
13+
<LayoutClient isSearchAssistSupported={languageModels.length > 0}>
1614
{children}
1715
</LayoutClient>
1816
)

packages/web/src/app/(app)/browse/layoutClient.tsx

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,40 +5,38 @@ import { BottomPanel } from "./components/bottomPanel";
55
import { AnimatedResizableHandle } from "@/components/ui/animatedResizableHandle";
66
import { BrowseStateProvider } from "./browseStateProvider";
77
import { FileTreePanel } from "./components/fileTreePanel";
8-
import { TopBar } from "@/app/(app)/components/topBar";
98
import { useBrowseParams } from "./hooks/useBrowseParams";
109
import { FileSearchCommandDialog } from "./components/fileSearchCommandDialog";
1110
import { SearchBar } from "../components/searchBar";
1211
import escapeStringRegexp from "escape-string-regexp";
13-
import { Session } from "next-auth";
12+
import { Separator } from "@/components/ui/separator";
1413

1514
interface LayoutProps {
1615
children: React.ReactNode;
17-
session: Session | null;
1816
isSearchAssistSupported: boolean;
1917
}
2018

2119
export function LayoutClient({
2220
children,
23-
session,
2421
isSearchAssistSupported,
2522
}: LayoutProps) {
2623
const { repoName, revisionName } = useBrowseParams();
2724
return (
2825
<BrowseStateProvider>
2926
<div className="flex flex-col h-screen">
30-
<TopBar
31-
session={session}
32-
>
33-
<SearchBar
34-
size="sm"
35-
defaults={{
36-
query: `repo:^${escapeStringRegexp(repoName)}$${revisionName ? ` rev:${revisionName}` : ''} `,
37-
}}
38-
className="w-full"
39-
isSearchAssistSupported={isSearchAssistSupported}
40-
/>
41-
</TopBar>
27+
<div className='sticky top-0 left-0 right-0 z-10'>
28+
<div className="py-1.5 px-3">
29+
<SearchBar
30+
size="sm"
31+
defaults={{
32+
query: `repo:^${escapeStringRegexp(repoName)}$${revisionName ? ` rev:${revisionName}` : ''} `,
33+
}}
34+
className="w-full"
35+
isSearchAssistSupported={isSearchAssistSupported}
36+
/>
37+
</div>
38+
<Separator />
39+
</div>
4240
<ResizablePanelGroup
4341
direction="horizontal"
4442
>

packages/web/src/app/(app)/chat/[id]/components/chatThreadPanel.tsx

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
'use client';
22

3-
import { ResizablePanel } from '@/components/ui/resizable';
43
import { ChatThread } from '@/features/chat/components/chatThread';
54
import { LanguageModelInfo, SBChatMessage, SearchScope, SetChatStatePayload } from '@/features/chat/types';
65
import { SELECTED_SEARCH_SCOPES_LOCAL_STORAGE_KEY, SET_CHAT_STATE_SESSION_STORAGE_KEY } from '@/features/chat/constants';
@@ -14,7 +13,6 @@ interface ChatThreadPanelProps {
1413
languageModels: LanguageModelInfo[];
1514
repos: RepositoryQuery[];
1615
searchContexts: SearchContextQuery[];
17-
order: number;
1816
messages: SBChatMessage[];
1917
isOwner: boolean;
2018
isAuthenticated: boolean;
@@ -25,7 +23,6 @@ export const ChatThreadPanel = ({
2523
languageModels,
2624
repos,
2725
searchContexts,
28-
order,
2926
messages,
3027
isOwner,
3128
isAuthenticated,
@@ -65,26 +62,20 @@ export const ChatThreadPanel = ({
6562
}, [chatState, setChatState]);
6663

6764
return (
68-
<ResizablePanel
69-
order={order}
70-
id="chat-thread-panel"
71-
defaultSize={85}
72-
>
73-
<div className="flex flex-col h-full w-full">
74-
<ChatThread
75-
id={chatId}
76-
initialMessages={messages}
77-
inputMessage={inputMessage}
78-
languageModels={languageModels}
79-
repos={repos}
80-
searchContexts={searchContexts}
81-
selectedSearchScopes={selectedSearchScopes}
82-
onSelectedSearchScopesChange={setSelectedSearchScopes}
83-
isOwner={isOwner}
84-
isAuthenticated={isAuthenticated}
85-
chatName={chatName}
86-
/>
87-
</div>
88-
</ResizablePanel>
65+
<div className="flex flex-col h-full w-full">
66+
<ChatThread
67+
id={chatId}
68+
initialMessages={messages}
69+
inputMessage={inputMessage}
70+
languageModels={languageModels}
71+
repos={repos}
72+
searchContexts={searchContexts}
73+
selectedSearchScopes={selectedSearchScopes}
74+
onSelectedSearchScopesChange={setSelectedSearchScopes}
75+
isOwner={isOwner}
76+
isAuthenticated={isAuthenticated}
77+
chatName={chatName}
78+
/>
79+
</div>
8980
)
9081
}

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

Lines changed: 11 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { getRepos, getSearchContexts } from '@/actions';
2-
import { getUserChatHistory, getChatInfo, claimAnonymousChats, getSharedWithUsersForChat } from '@/features/chat/actions';
2+
import { getChatInfo, claimAnonymousChats, getSharedWithUsersForChat } from '@/features/chat/actions';
33
import { getConfiguredLanguageModelsInfo } from "@/features/chat/utils.server";
44
import { ServiceErrorException } from '@/lib/serviceError';
55
import { isServiceError } from '@/lib/utils';
@@ -10,9 +10,6 @@ import { TopBar } from '../../components/topBar';
1010
import { ChatName } from '../components/chatName';
1111
import { ShareChatPopover } from '../components/shareChatPopover';
1212
import { auth } from '@/auth';
13-
import { AnimatedResizableHandle } from '@/components/ui/animatedResizableHandle';
14-
import { ChatSidePanel } from '../components/chatSidePanel';
15-
import { ResizablePanelGroup } from '@/components/ui/resizable';
1613
import { __unsafePrisma } from '@/prisma';
1714
import { ChatVisibility } from '@sourcebot/db';
1815
import { Metadata } from 'next';
@@ -96,12 +93,6 @@ export default async function Page(props: PageProps) {
9693
const repos = await getRepos();
9794
const searchContexts = await getSearchContexts();
9895
const chatInfo = await getChatInfo({ chatId: params.id });
99-
const chatHistory = session ? await getUserChatHistory() : [];
100-
101-
if (isServiceError(chatHistory)) {
102-
throw new ServiceErrorException(chatHistory);
103-
}
104-
10596
if (isServiceError(repos)) {
10697
throw new ServiceErrorException(repos);
10798
}
@@ -142,7 +133,7 @@ export default async function Page(props: PageProps) {
142133
const hasChatSharingEntitlement = hasEntitlement('chat-sharing');
143134

144135
return (
145-
<div className="flex flex-col h-screen w-screen">
136+
<div className="flex flex-col h-full">
146137
<TopBar
147138
homePath="/chat"
148139
session={session}
@@ -167,27 +158,15 @@ export default async function Page(props: PageProps) {
167158
/>
168159
) : undefined}
169160
/>
170-
<ResizablePanelGroup
171-
direction="horizontal"
172-
>
173-
<ChatSidePanel
174-
order={1}
175-
chatHistory={chatHistory}
176-
isAuthenticated={!!session}
177-
isCollapsedInitially={true}
178-
/>
179-
<AnimatedResizableHandle />
180-
<ChatThreadPanel
181-
languageModels={languageModels}
182-
repos={indexedRepos}
183-
searchContexts={searchContexts}
184-
messages={messages}
185-
order={2}
186-
isOwner={isOwner}
187-
isAuthenticated={!!session}
188-
chatName={name ?? undefined}
189-
/>
190-
</ResizablePanelGroup>
161+
<ChatThreadPanel
162+
languageModels={languageModels}
163+
repos={indexedRepos}
164+
searchContexts={searchContexts}
165+
messages={messages}
166+
isOwner={isOwner}
167+
isAuthenticated={!!session}
168+
chatName={name ?? undefined}
169+
/>
191170
</div>
192171
)
193172
}

packages/web/src/app/(app)/chat/components/chatActionsDropdown.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export const ChatActionsDropdown = ({
2121
align = "start",
2222
}: ChatActionsDropdownProps) => {
2323
return (
24-
<DropdownMenu>
24+
<DropdownMenu modal={false}>
2525
<DropdownMenuTrigger asChild>
2626
{children}
2727
</DropdownMenuTrigger>

0 commit comments

Comments
 (0)