Skip to content

Commit 82bcdd2

Browse files
EfeDurmaz16claude
andcommitted
feat(ui): clean minimal redesign inspired by ChatGPT/Claude
Replace navy/terracotta/cream color scheme with neutral white/dark palette, swap Playfair Display serif fonts for Geist sans-serif, remove glassmorphism, gradient mesh, glow effects, and backdrop-blur throughout chat UI. Home page now redirects authenticated users to /chat, landing page moved to (marketing) route group. Sidebar rebranded from Aspendos to YULA with nav links for Council and Memory. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 4fe61da commit 82bcdd2

11 files changed

Lines changed: 1299 additions & 2414 deletions

File tree

apps/web/src/app/(marketing)/landing/page.tsx

Lines changed: 994 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { SiteDock } from '@/components/layout/site-dock';
2+
3+
export default function MarketingLayout({
4+
children,
5+
}: {
6+
children: React.ReactNode;
7+
}) {
8+
return (
9+
<>
10+
{children}
11+
<SiteDock />
12+
</>
13+
);
14+
}

apps/web/src/app/chat/[id]/page.tsx

Lines changed: 29 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
'use client';
22

3-
import { Brain, CircleNotch, List, SidebarSimple, GlobeIcon } from '@phosphor-icons/react';
3+
import { Brain, CircleNotch, SidebarSimple, GlobeIcon, PlusCircle } from '@phosphor-icons/react';
44
import { useParams, useRouter } from 'next/navigation';
55
import { useCallback, useEffect, useState } from 'react';
66
import { ChatSidebar } from '@/components/chat/chat-sidebar';
7-
import { MemoryPanel } from '@/components/chat/memory-panel';
8-
import { ModelPicker } from '@/components/chat/model-picker';
97
import { AddModelsModal } from '@/components/chat/add-models-modal';
108
import { KeyboardShortcuts } from '@/components/chat/keyboard-shortcuts';
119
import { ContextMenuMessage } from '@/components/chat/context-menu-message';
@@ -87,7 +85,6 @@ export default function ChatPage() {
8785
const chatId = params.id as string;
8886

8987
const [sidebarOpen, setSidebarOpen] = useState(true);
90-
const [memoryOpen, setMemoryOpen] = useState(false);
9188
const [chat, setChat] = useState<Chat | null>(null);
9289
const [chats, setChats] = useState<Chat[]>([]);
9390
const [isLoadingChat, setIsLoadingChat] = useState(true);
@@ -140,16 +137,6 @@ export default function ChatPage() {
140137

141138
const data = await res.json();
142139
setChat(data);
143-
// Initial messages are loaded by useStreamingChat via its own logic if we were using useChat fully,
144-
// but here useStreamingChat manages messages state.
145-
// The original code setInitialMessages separately.
146-
// Assuming useStreamingChat handles the message list now or we load it here?
147-
// The original code loaded initialMessages and combined them: const allMessages = [...initialMessages, ...messages];
148-
// Wait, useStreamingChat in original code didn't load initial messages. It just exposed the new ones.
149-
// So I need to keep the "load initial messages" logic, but merge it effectively.
150-
// Actually, useStreamingChat hook in Step 16 has a "loadMessages" function.
151-
// But typically useChat stores all messages.
152-
// Let's stick to the previous pattern: "allMessages".
153140
} catch (err) {
154141
setError(err instanceof Error ? err.message : 'Failed to load chat');
155142
} finally {
@@ -179,12 +166,11 @@ export default function ChatPage() {
179166
loadChats();
180167
}, [isLoaded, isSignedIn]);
181168

182-
// Initial messages from chat load (re-implementing original logic because useStreamingChat is a wrapper)
169+
// Initial messages from chat load
183170
const [initialMessages, setInitialMessages] = useState<ChatMessage[]>([]);
184171

185172
useEffect(() => {
186173
if (chat?.messages) {
187-
// Map chat.messages to the format we need
188174
const converted: ChatMessage[] = chat.messages.map((m) => ({
189175
id: m.id,
190176
role: m.role,
@@ -196,11 +182,6 @@ export default function ChatPage() {
196182
}
197183
}, [chat]);
198184

199-
// Combine messages.
200-
// Note: useStreamingChat messages are cumulative for the session.
201-
// If we load old messages, we should probably initialize useStreamingChat with them if possible,
202-
// or just display them.
203-
// The previous code did: const allMessages = [...initialMessages, ...messages];
204185
const allMessages: ChatMessage[] = [...initialMessages, ...messages];
205186

206187
const handleNewChat = useCallback(async () => {
@@ -269,7 +250,7 @@ export default function ChatPage() {
269250
if (!isLoaded) {
270251
return (
271252
<div className="h-screen flex items-center justify-center bg-background">
272-
<CircleNotch className="w-8 h-8 animate-spin text-zinc-400" />
253+
<CircleNotch className="w-8 h-8 animate-spin text-muted-foreground" />
273254
</div>
274255
);
275256
}
@@ -280,17 +261,11 @@ export default function ChatPage() {
280261
}
281262

282263
return (
283-
<div className="h-screen bg-gradient-to-b from-white to-zinc-50 dark:from-zinc-950 dark:to-black overflow-hidden font-sans flex relative">
284-
{/* YULA Monolith Background - Amber glow only */}
285-
<div className="absolute inset-0 pointer-events-none z-0">
286-
<div className="absolute top-0 left-1/4 w-[500px] h-[500px] bg-gradient-to-br from-amber-100/10 to-transparent dark:from-amber-900/8 rounded-full blur-3xl opacity-50" />
287-
<div className="absolute bottom-0 right-1/4 w-[500px] h-[500px] bg-gradient-to-bl from-emerald-100/15 to-transparent dark:from-emerald-900/10 rounded-full blur-3xl opacity-50" />
288-
</div>
289-
264+
<div className="h-screen bg-background overflow-hidden font-sans flex">
290265
{/* Sidebar */}
291266
<div
292267
className={cn(
293-
'h-full bg-white/50 dark:bg-zinc-950/50 backdrop-blur border-r border-zinc-200 dark:border-zinc-900 transition-all duration-300 z-20',
268+
'h-full bg-muted border-r border-border transition-all duration-300 z-20',
294269
sidebarOpen ? 'w-64' : 'w-0 overflow-hidden'
295270
)}
296271
>
@@ -303,38 +278,26 @@ export default function ChatPage() {
303278
</div>
304279

305280
{/* Main Chat Area */}
306-
<div className="flex-1 h-full flex flex-col relative bg-transparent z-10 w-full min-w-0">
307-
{/* Header */}
308-
<div className="flex items-center justify-between p-3 border-b border-zinc-200 dark:border-zinc-800 flex-none bg-white/50 dark:bg-zinc-950/50 backdrop-blur z-20 w-full">
309-
<div className="flex items-center">
310-
<Button
311-
variant="ghost"
312-
size="icon"
313-
onClick={() => setSidebarOpen(!sidebarOpen)}
314-
className="h-8 w-8 text-muted-foreground hover:text-foreground mr-2"
315-
>
316-
<SidebarSimple className="w-4 h-4" />
317-
</Button>
318-
<div className="hidden sm:block">
319-
<ModelPicker
320-
selectedModel={chat?.modelPreference || 'openai/gpt-5.2'}
321-
onSelectModel={handleModelChange}
322-
onOpenAddModels={() => setIsAddModelsOpen(true)}
323-
enabledModels={enabledModels}
324-
/>
325-
</div>
326-
</div>
327-
328-
<div className="flex items-center gap-2">
329-
<Button
330-
variant="ghost"
331-
size="icon"
332-
onClick={() => setMemoryOpen(!memoryOpen)}
333-
className="h-8 w-8 text-muted-foreground hover:text-foreground"
334-
>
335-
<List className="w-4 h-4" />
336-
</Button>
337-
</div>
281+
<div className="flex-1 h-full flex flex-col relative z-10 w-full min-w-0">
282+
{/* Header - simplified: sidebar toggle + new chat */}
283+
<div className="flex items-center justify-between p-3 border-b border-border flex-none bg-background z-20 w-full">
284+
<Button
285+
variant="ghost"
286+
size="icon"
287+
onClick={() => setSidebarOpen(!sidebarOpen)}
288+
className="h-8 w-8 text-muted-foreground hover:text-foreground"
289+
>
290+
<SidebarSimple className="w-4 h-4" />
291+
</Button>
292+
293+
<Button
294+
variant="ghost"
295+
size="icon"
296+
onClick={handleNewChat}
297+
className="h-8 w-8 text-muted-foreground hover:text-foreground"
298+
>
299+
<PlusCircle className="w-4 h-4" />
300+
</Button>
338301
</div>
339302

340303
{/* Conversation Area */}
@@ -345,7 +308,7 @@ export default function ChatPage() {
345308
<ConversationEmptyState
346309
icon={<Brain className="w-12 h-12 text-muted-foreground/50" weight="duotone" />}
347310
title="Start a conversation"
348-
description="Ask anything, analyze data, or generate ideas. Aspendos is here to help."
311+
description="Ask anything, analyze data, or generate ideas. YULA is here to help."
349312
/>
350313
) : (
351314
allMessages.map((msg, index) => (
@@ -354,7 +317,6 @@ export default function ChatPage() {
354317
message={{ id: msg.id, content: msg.content, role: msg.role }}
355318
>
356319
<Message from={msg.role}>
357-
{/* Show reasoning for assistant messages if available */}
358320
{msg.role === 'assistant' && msg.decision?.reasoning && (
359321
<Reasoning
360322
isStreaming={
@@ -395,7 +357,7 @@ export default function ChatPage() {
395357
<div className="p-4 flex-none z-20 max-w-3xl mx-auto w-full">
396358
<PromptInput
397359
onSubmit={handleSubmit}
398-
className="shadow-xl"
360+
className="border border-border rounded-2xl"
399361
>
400362
<PromptInputHeader>
401363
<PromptInputAttachments>
@@ -411,7 +373,6 @@ export default function ChatPage() {
411373
<PromptInputActionMenuTrigger />
412374
<PromptInputActionMenuContent>
413375
<PromptInputActionAddAttachments />
414-
{/* Add more actions like shortcuts later */}
415376
</PromptInputActionMenuContent>
416377
</PromptInputActionMenu>
417378
<PromptInputButton
@@ -423,7 +384,7 @@ export default function ChatPage() {
423384
<GlobeIcon size={16} />
424385
<span className="text-xs">Search</span>
425386
</PromptInputButton>
426-
{/* Model Selector in input */}
387+
{/* Model Selector in input footer */}
427388
<PromptInputSelect
428389
value={model}
429390
onValueChange={handleModelChange}
@@ -444,21 +405,11 @@ export default function ChatPage() {
444405
</PromptInputFooter>
445406
</PromptInput>
446407
<div className="text-center mt-2 text-xs text-muted-foreground">
447-
Aspendos can make mistakes. Check important info.
408+
YULA can make mistakes. Check important info.
448409
</div>
449410
</div>
450411
</div>
451412

452-
{/* Memory Panel */}
453-
<div
454-
className={cn(
455-
'h-full border-l border-zinc-200 dark:border-zinc-900 bg-white/50 dark:bg-zinc-950/50 backdrop-blur transition-all duration-300 z-20',
456-
memoryOpen ? 'w-80' : 'w-0 overflow-hidden'
457-
)}
458-
>
459-
<MemoryPanel />
460-
</div>
461-
462413
{/* Keyboard Shortcuts Panel */}
463414
<KeyboardShortcuts />
464415

0 commit comments

Comments
 (0)