Skip to content

Commit cd6eabc

Browse files
committed
Redesign UI with dark Vercel shadcn system
1 parent 4f1524f commit cd6eabc

51 files changed

Lines changed: 3833 additions & 1579 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

bun.lock

Lines changed: 572 additions & 38 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"$schema": "https://ui.shadcn.com/schema.json",
3+
"style": "radix-nova",
4+
"rsc": true,
5+
"tsx": true,
6+
"tailwind": {
7+
"config": "",
8+
"css": "src/app/globals.css",
9+
"baseColor": "neutral",
10+
"cssVariables": true,
11+
"prefix": ""
12+
},
13+
"iconLibrary": "lucide",
14+
"rtl": false,
15+
"aliases": {
16+
"components": "@/components",
17+
"utils": "@/lib/utils",
18+
"ui": "@/components/ui",
19+
"lib": "@/lib",
20+
"hooks": "@/hooks"
21+
},
22+
"menuColor": "default",
23+
"menuAccent": "subtle",
24+
"registries": {}
25+
}

convex/chatSessions.ts

Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,35 @@ const messageValidator = v.object({
66
content: v.string(),
77
})
88

9+
type ChatSessionMessage = {
10+
role: "user" | "assistant" | "system"
11+
content: string
12+
}
13+
14+
function compactRepeatedTurns(messages: ChatSessionMessage[]): ChatSessionMessage[] {
15+
const compacted: ChatSessionMessage[] = []
16+
for (const message of messages) {
17+
compacted.push(message)
18+
const n = compacted.length
19+
if (n < 4) continue
20+
const firstUser = compacted[n - 4]
21+
const firstAssistant = compacted[n - 3]
22+
const secondUser = compacted[n - 2]
23+
const secondAssistant = compacted[n - 1]
24+
if (
25+
firstUser.role === "user" &&
26+
firstAssistant.role === "assistant" &&
27+
secondUser.role === "user" &&
28+
secondAssistant.role === "assistant" &&
29+
firstUser.content === secondUser.content &&
30+
firstAssistant.content === secondAssistant.content
31+
) {
32+
compacted.splice(n - 2, 2)
33+
}
34+
}
35+
return compacted
36+
}
37+
938
export const create = mutation({
1039
args: {
1140
skillPath: v.string(),
@@ -43,12 +72,19 @@ export const appendMessages = mutation({
4372
const identity = await ctx.auth.getUserIdentity()
4473
if (!identity) throw new Error("Not authenticated")
4574

46-
const session = await ctx.db.get(args.sessionId)
75+
const session = await ctx.db.get("chatSessions", args.sessionId)
4776
if (!session || session.tokenIdentifier !== identity.tokenIdentifier) {
4877
throw new Error("Session not found")
4978
}
5079

51-
const updated = [...session.messages, ...args.messages]
80+
const duplicateTail =
81+
args.messages.length > 0 &&
82+
session.messages.length >= args.messages.length &&
83+
args.messages.every((message, index) => {
84+
const existing = session.messages[session.messages.length - args.messages.length + index]
85+
return existing?.role === message.role && existing.content === message.content
86+
})
87+
const updated = compactRepeatedTurns(duplicateTail ? session.messages : [...session.messages, ...args.messages])
5288
const patch: Record<string, unknown> = {
5389
messages: updated,
5490
messageCount: updated.length,
@@ -57,7 +93,7 @@ export const appendMessages = mutation({
5793
if (args.title) {
5894
patch.title = args.title
5995
}
60-
await ctx.db.patch(args.sessionId, patch)
96+
await ctx.db.patch("chatSessions", args.sessionId, patch)
6197
return null
6298
},
6399
})
@@ -68,11 +104,12 @@ export const get = query({
68104
const identity = await ctx.auth.getUserIdentity()
69105
if (!identity) return null
70106

71-
const session = await ctx.db.get(args.sessionId)
107+
const session = await ctx.db.get("chatSessions", args.sessionId)
72108
if (!session || session.tokenIdentifier !== identity.tokenIdentifier) {
73109
return null
74110
}
75-
return session
111+
const messages = compactRepeatedTurns(session.messages)
112+
return { ...session, messages, messageCount: messages.length }
76113
},
77114
})
78115

@@ -108,16 +145,19 @@ export const list = query({
108145
.order("desc")
109146
.take(100)
110147

111-
return sessions.map((s) => ({
112-
_id: s._id,
113-
skillPath: s.skillPath,
114-
title: s.title,
115-
model: s.model,
116-
workspacePath: s.workspacePath,
117-
messageCount: s.messageCount,
118-
createdAt: s.createdAt,
119-
updatedAt: s.updatedAt,
120-
}))
148+
return sessions.map((s) => {
149+
const messages = compactRepeatedTurns(s.messages)
150+
return {
151+
_id: s._id,
152+
skillPath: s.skillPath,
153+
title: s.title,
154+
model: s.model,
155+
workspacePath: s.workspacePath,
156+
messageCount: messages.length,
157+
createdAt: s.createdAt,
158+
updatedAt: s.updatedAt,
159+
}
160+
})
121161
},
122162
})
123163

@@ -128,9 +168,9 @@ export const remove = mutation({
128168
const identity = await ctx.auth.getUserIdentity()
129169
if (!identity) throw new Error("Not authenticated")
130170

131-
const session = await ctx.db.get(args.sessionId)
171+
const session = await ctx.db.get("chatSessions", args.sessionId)
132172
if (session && session.tokenIdentifier === identity.tokenIdentifier) {
133-
await ctx.db.delete(args.sessionId)
173+
await ctx.db.delete("chatSessions", args.sessionId)
134174
}
135175
return null
136176
},

eslint.config.mjs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const eslintConfig = defineConfig([
1515
"build/**",
1616
"next-env.d.ts",
1717
"convex/_generated/**",
18+
".agents/**",
1819
]),
1920
// Convex recommended rules (includes no-old-registered-function-syntax,
2021
// require-args-validator, explicit-table-ids, no-filter-in-query,
@@ -38,7 +39,9 @@ const eslintConfig = defineConfig([
3839
languageOptions: {
3940
parser: tsParser,
4041
parserOptions: {
41-
projectService: true,
42+
projectService: {
43+
allowDefaultProject: ["vitest.config.ts"],
44+
},
4245
tsconfigRootDir: import.meta.dirname,
4346
},
4447
},

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,20 @@
1717
"@clerk/themes": "^2.4.57",
1818
"@daytona/sdk": "^0.167.0",
1919
"@lobehub/icons": "^5.4.0",
20+
"class-variance-authority": "^0.7.1",
21+
"clsx": "^2.1.1",
2022
"convex": "^1.35.1",
2123
"lucide-react": "^1.11.0",
2224
"next": "16.2.4",
2325
"node-polyfill-webpack-plugin": "^4.1.0",
26+
"radix-ui": "^1.4.3",
2427
"react": "19.2.4",
2528
"react-dom": "19.2.4",
2629
"react-markdown": "^10.1.0",
2730
"rehype-highlight": "^7.0.2",
31+
"shadcn": "^4.4.0",
32+
"tailwind-merge": "^3.5.0",
33+
"tw-animate-css": "^1.4.0",
2834
"unenv": "^1.10.0"
2935
},
3036
"devDependencies": {

src/__tests__/components/chat/use-chat.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ vi.mock("@/lib/providers/check-credit", () => ({
1818
}))
1919

2020
const mockCreateSession = vi.fn().mockResolvedValue("test-session-id")
21-
const mockAppendMessages = vi.fn().mockResolvedValue(null)
2221

2322
vi.mock("convex/react", () => ({
2423
useMutation: () => mockCreateSession,

0 commit comments

Comments
 (0)