Skip to content

Commit d2c59aa

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 d2c59aa

11 files changed

Lines changed: 1548 additions & 2571 deletions

File tree

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

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

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

Lines changed: 80 additions & 125 deletions
Large diffs are not rendered by default.

apps/web/src/app/chat/new/page.tsx

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

3-
import { Brain, CircleNotch, List, PaperPlaneRight, SidebarSimple } from '@phosphor-icons/react';
3+
import {
4+
Brain,
5+
CircleNotch,
6+
PaperPlaneRight,
7+
PlusCircle,
8+
SidebarSimple,
9+
} from '@phosphor-icons/react';
410
import { useRouter } from 'next/navigation';
511
import { useCallback, useEffect, useRef, useState } from 'react';
612
import { ChatSidebar } from '@/components/chat/chat-sidebar';
7-
import { MemoryPanel } from '@/components/chat/memory-panel';
8-
import { ModelSelector } from '@/components/chat/model-selector';
913
import { StreamingMessage } from '@/components/chat/StreamingMessage';
1014
import { VoiceButton } from '@/components/chat/voice-button';
1115
import { Button } from '@/components/ui/button';
@@ -29,17 +33,14 @@ export default function NewChatPage() {
2933
const { isLoaded, isSignedIn } = useAuth();
3034

3135
const [sidebarOpen, setSidebarOpen] = useState(true);
32-
const [memoryOpen, setMemoryOpen] = useState(true);
3336
const [chats, setChats] = useState<Chat[]>([]);
3437
const [selectedModel, setSelectedModel] = useState('openai/gpt-4o-mini');
3538
const [inputValue, setInputValue] = useState('');
3639
const textareaRef = useRef<HTMLTextAreaElement>(null);
3740
const messagesEndRef = useRef<HTMLDivElement>(null);
3841

39-
// Use "new" as a placeholder chatId - will be replaced on first message
4042
const { messages, isStreaming, sendMessage, error: streamError } = useStreamingChat('new');
4143

42-
// Load chat list for sidebar
4344
useEffect(() => {
4445
if (!isLoaded || !isSignedIn) return;
4546

@@ -58,7 +59,6 @@ export default function NewChatPage() {
5859
loadChats();
5960
}, [isLoaded, isSignedIn]);
6061

61-
// Auto-scroll to bottom
6262
useEffect(() => {
6363
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
6464
}, []);
@@ -70,7 +70,6 @@ export default function NewChatPage() {
7070
setInputValue('');
7171
if (textareaRef.current) textareaRef.current.style.height = 'auto';
7272

73-
// Send message - the hook will handle creating the chat on first message
7473
await sendMessage(content, { model: selectedModel });
7574
}, [inputValue, isStreaming, sendMessage, selectedModel]);
7675

@@ -105,14 +104,10 @@ export default function NewChatPage() {
105104
}
106105
};
107106

108-
const handleModelChange = useCallback(async (modelId: string) => {
109-
setSelectedModel(modelId);
110-
}, []);
111-
112107
if (!isLoaded) {
113108
return (
114109
<div className="h-screen flex items-center justify-center bg-background">
115-
<CircleNotch className="w-8 h-8 animate-spin text-zinc-400" />
110+
<CircleNotch className="w-8 h-8 animate-spin text-muted-foreground" />
116111
</div>
117112
);
118113
}
@@ -123,11 +118,11 @@ export default function NewChatPage() {
123118
}
124119

125120
return (
126-
<div className="h-screen bg-zinc-50 dark:bg-zinc-950 overflow-hidden font-sans flex">
121+
<div className="h-screen bg-background overflow-hidden font-sans flex">
127122
{/* Sidebar */}
128123
<div
129124
className={cn(
130-
'h-full bg-zinc-50 dark:bg-zinc-950 border-r border-zinc-200 dark:border-zinc-900 transition-all duration-300',
125+
'h-full bg-muted border-r border-border transition-all duration-300',
131126
sidebarOpen ? 'w-64' : 'w-0 overflow-hidden'
132127
)}
133128
>
@@ -140,38 +135,24 @@ export default function NewChatPage() {
140135
</div>
141136

142137
{/* Main Chat */}
143-
<div className="flex-1 h-full flex flex-col relative bg-white dark:bg-zinc-950 dark:text-zinc-100">
144-
{/* YULA Monolith Background - Amber glow only */}
145-
<div className="absolute inset-0 pointer-events-none -z-10">
146-
<div className="absolute top-1/4 right-1/4 w-[500px] h-[500px] bg-gradient-to-br from-amber-100/10 to-transparent dark:from-amber-900/10 rounded-full blur-3xl" />
147-
<div className="absolute bottom-1/3 left-1/3 w-[600px] h-[600px] bg-gradient-to-bl from-amber-50/8 to-transparent dark:from-amber-800/8 rounded-full blur-3xl" />
148-
</div>
138+
<div className="flex-1 h-full flex flex-col relative">
149139
{/* Header */}
150-
<div className="flex items-center justify-between p-4 border-b border-zinc-200 dark:border-zinc-800 bg-white/50 dark:bg-zinc-950/50 backdrop-blur-md z-10 relative">
140+
<div className="flex items-center justify-between p-3 border-b border-border flex-none bg-background z-10">
151141
<Button
152142
variant="ghost"
153143
size="icon"
154144
onClick={() => setSidebarOpen(!sidebarOpen)}
155-
className="h-9 w-9 text-zinc-500 hover:text-zinc-900 dark:hover:text-zinc-50 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-lg transition-all"
156-
title="Toggle Sidebar"
145+
className="h-8 w-8 text-muted-foreground hover:text-foreground"
157146
>
158-
<SidebarSimple weight="duotone" className="w-5 h-5" />
147+
<SidebarSimple className="w-4 h-4" />
159148
</Button>
160-
<div className="flex-1 flex justify-center">
161-
<ModelSelector
162-
selectedModel={selectedModel}
163-
onModelChange={handleModelChange}
164-
disabled={isStreaming}
165-
/>
166-
</div>
167149
<Button
168150
variant="ghost"
169151
size="icon"
170-
onClick={() => setMemoryOpen(!memoryOpen)}
171-
className="h-9 w-9 text-zinc-500 hover:text-zinc-900 dark:hover:text-zinc-50 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-lg transition-all"
172-
title="Toggle Memory"
152+
onClick={handleNewChat}
153+
className="h-8 w-8 text-muted-foreground hover:text-foreground"
173154
>
174-
<List weight="duotone" className="w-5 h-5" />
155+
<PlusCircle className="w-4 h-4" />
175156
</Button>
176157
</div>
177158

@@ -180,17 +161,14 @@ export default function NewChatPage() {
180161
<div className="max-w-3xl mx-auto space-y-4 pb-32">
181162
{messages.length === 0 ? (
182163
<div className="text-center py-20 animate-fade-up">
183-
<div className="inline-flex items-center justify-center w-16 h-16 rounded-2xl bg-gradient-to-br from-zinc-100 to-zinc-200 dark:from-zinc-800 dark:to-zinc-900 mb-6 shadow-lg">
184-
<Brain
185-
className="w-8 h-8 text-zinc-600 dark:text-zinc-400"
186-
weight="duotone"
187-
/>
164+
<div className="inline-flex items-center justify-center w-16 h-16 rounded-2xl bg-muted mb-6 border border-border">
165+
<Brain className="w-8 h-8 text-foreground" weight="duotone" />
188166
</div>
189-
<p className="text-xl font-serif font-semibold text-zinc-700 dark:text-zinc-300 mb-2">
167+
<p className="text-xl font-semibold text-foreground mb-2">
190168
Start a conversation
191169
</p>
192-
<p className="text-sm text-zinc-500">
193-
Ask anything or share what's on your mind...
170+
<p className="text-sm text-muted-foreground">
171+
Ask anything or share what&apos;s on your mind...
194172
</p>
195173
</div>
196174
) : (
@@ -218,18 +196,18 @@ export default function NewChatPage() {
218196

219197
{/* Input */}
220198
<div className="max-w-3xl mx-auto w-full px-4 mb-6 relative z-10">
221-
<div className="relative flex flex-col bg-white/80 dark:bg-zinc-900/80 backdrop-blur-lg border border-zinc-200 dark:border-zinc-800 shadow-lg rounded-2xl overflow-hidden focus-within:ring-2 focus-within:ring-emerald-500/30 dark:focus-within:ring-emerald-500/20 focus-within:border-emerald-500/50 dark:focus-within:border-emerald-500/30 transition-all">
199+
<div className="relative flex flex-col bg-background border border-border rounded-2xl overflow-hidden focus-within:ring-2 focus-within:ring-ring/30 focus-within:border-ring/50 transition-colors">
222200
<textarea
223201
ref={textareaRef}
224202
value={inputValue}
225203
onChange={handleInputChange}
226204
onKeyDown={handleKeyDown}
227-
className="w-full min-h-[60px] max-h-[200px] p-4 bg-transparent resize-none outline-none text-[15px] placeholder:text-zinc-400 text-zinc-900 dark:text-zinc-50"
205+
className="w-full min-h-[60px] max-h-[200px] p-4 bg-transparent resize-none outline-none text-[15px] placeholder:text-muted-foreground text-foreground"
228206
placeholder="Ask anything..."
229207
disabled={isStreaming}
230208
/>
231209
<div className="flex items-center justify-between px-4 pb-3 pt-1">
232-
<span className="text-xs font-mono text-zinc-500">
210+
<span className="text-xs font-mono text-muted-foreground">
233211
{inputValue.length} / 4000
234212
</span>
235213
<div className="flex items-center gap-2">
@@ -241,7 +219,7 @@ export default function NewChatPage() {
241219
size="icon"
242220
onClick={handleSend}
243221
disabled={!inputValue.trim() || isStreaming}
244-
className="h-9 w-9 rounded-xl bg-gradient-to-br from-emerald-600 to-emerald-500 hover:from-emerald-700 hover:to-emerald-600 text-white shadow-md hover:shadow-lg disabled:opacity-50 disabled:hover:shadow-md transition-all"
222+
className="h-9 w-9 rounded-xl bg-foreground hover:bg-foreground/90 text-background disabled:opacity-50 transition-colors"
245223
>
246224
{isStreaming ? (
247225
<CircleNotch
@@ -255,21 +233,11 @@ export default function NewChatPage() {
255233
</div>
256234
</div>
257235
</div>
258-
<p className="text-center text-[11px] text-zinc-500 mt-3">
259-
Aspendos memories are private. AI-generated content may be inaccurate.
236+
<p className="text-center text-[11px] text-muted-foreground mt-3">
237+
YULA memories are private. AI-generated content may be inaccurate.
260238
</p>
261239
</div>
262240
</div>
263-
264-
{/* Memory Panel */}
265-
<div
266-
className={cn(
267-
'h-full border-l border-zinc-200 dark:border-zinc-900 transition-all duration-300',
268-
memoryOpen ? 'w-80' : 'w-0 overflow-hidden'
269-
)}
270-
>
271-
<MemoryPanel />
272-
</div>
273241
</div>
274242
);
275243
}

apps/web/src/app/chat/page.tsx

Lines changed: 27 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@
33
import { Brain, CircleNotch } from '@phosphor-icons/react';
44
import { useRouter } from 'next/navigation';
55
import { useCallback, useEffect, useState } from 'react';
6-
import { useAuth } from '@/hooks/use-auth';
76
import { PromptInputBox } from '@/components/chat/prompt-input-box';
8-
import { ShortcutsDock } from '@/components/chat/shortcuts-dock';
9-
import { SkyToggle } from '@/components/ui/sky-toggle';
107
import { WelcomeGuide } from '@/components/onboarding';
8+
import { useAuth } from '@/hooks/use-auth';
119

1210
const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8080';
1311

@@ -60,24 +58,26 @@ export default function ChatIndexPage() {
6058
}, [isLoaded, isSignedIn, router]);
6159

6260
const handleNewChat = useCallback(() => {
63-
// Navigate immediately to /chat/new without creating chat ID
64-
// Chat will be created when user sends first message
6561
router.push('/chat/new');
6662
}, [router]);
6763

6864
// Keyboard shortcuts
6965
useEffect(() => {
7066
const handleKeyDown = (e: KeyboardEvent) => {
71-
// New Chat: Cmd+N
7267
if (e.metaKey && e.key === 'n') {
7368
e.preventDefault();
7469
handleNewChat();
7570
}
7671

77-
// Focus Input: /
78-
if (e.key === '/' && document.activeElement?.tagName !== 'TEXTAREA' && document.activeElement?.tagName !== 'INPUT') {
72+
if (
73+
e.key === '/' &&
74+
document.activeElement?.tagName !== 'TEXTAREA' &&
75+
document.activeElement?.tagName !== 'INPUT'
76+
) {
7977
e.preventDefault();
80-
const input = document.querySelector('textarea, input') as HTMLTextAreaElement | HTMLInputElement;
78+
const input = document.querySelector('textarea, input') as
79+
| HTMLTextAreaElement
80+
| HTMLInputElement;
8181
input?.focus();
8282
}
8383
};
@@ -107,33 +107,25 @@ export default function ChatIndexPage() {
107107
}
108108

109109
return (
110-
<div className="min-h-screen bg-gradient-to-b from-white to-zinc-50 dark:from-zinc-950 dark:to-black relative overflow-hidden flex flex-col items-center justify-center p-4">
110+
<div className="min-h-screen bg-background flex flex-col items-center justify-center p-4">
111111
{/* Onboarding for new users */}
112112
<WelcomeGuide />
113-
{/* YULA Monolith Background - Amber glow only */}
114-
<div className="absolute inset-0 pointer-events-none -z-10">
115-
<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/10 rounded-full blur-3xl" />
116-
<div className="absolute top-1/3 right-1/4 w-[600px] h-[600px] bg-gradient-to-bl from-amber-50/8 to-transparent dark:from-amber-800/8 rounded-full blur-3xl" />
117-
<div className="absolute bottom-0 left-1/2 w-[700px] h-[700px] bg-gradient-to-t from-emerald-100/10 to-transparent dark:from-emerald-900/5 rounded-full blur-3xl" />
118-
</div>
119113

120-
<div className="absolute top-6 right-6 z-50">
121-
<SkyToggle />
122-
</div>
123-
124-
<div className="max-w-2xl w-full text-center space-y-10 relative z-10">
114+
<div className="max-w-2xl w-full text-center space-y-10">
125115
{/* Header */}
126116
<div className="space-y-4 animate-fade-up opacity-0 animation-delay-100">
127-
<div className="inline-flex items-center justify-center w-16 h-16 rounded-2xl bg-white dark:bg-zinc-900 mb-2 shadow-sm border border-zinc-200 dark:border-zinc-800">
128-
<Brain
129-
className="w-8 h-8 text-zinc-900 dark:text-zinc-100"
130-
weight="regular"
131-
/>
117+
<div className="inline-flex items-center justify-center w-16 h-16 rounded-2xl bg-muted mb-2 border border-border">
118+
<Brain className="w-8 h-8 text-foreground" weight="regular" />
132119
</div>
133120
<h1 className="text-3xl font-semibold tracking-tight text-foreground">
134121
{(() => {
135122
const hour = new Date().getHours();
136-
const greeting = hour < 12 ? 'Good morning' : hour < 18 ? 'Good afternoon' : 'Good evening';
123+
const greeting =
124+
hour < 12
125+
? 'Good morning'
126+
: hour < 18
127+
? 'Good afternoon'
128+
: 'Good evening';
137129
return greeting;
138130
})()}
139131
</h1>
@@ -143,28 +135,29 @@ export default function ChatIndexPage() {
143135
<div className="max-w-xl mx-auto w-full animate-fade-up opacity-0 animation-delay-200">
144136
<PromptInputBox
145137
onSubmit={(text) => {
146-
// In a real app, this would start a chat with the initial message
147138
void text;
148139
handleNewChat();
149140
}}
150141
/>
151142
</div>
152143

153-
{/* Capability Hints */}
144+
{/* Suggestion Chips */}
154145
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3 max-w-3xl mx-auto pt-8 animate-fade-up opacity-0 animation-delay-300">
155-
{['Analyze a complex PDF', 'Plan a weekend trip', 'Debug a React component'].map((hint) => (
146+
{[
147+
'Analyze a complex PDF',
148+
'Plan a weekend trip',
149+
'Debug a React component',
150+
].map((hint) => (
156151
<button
157152
key={hint}
158153
onClick={handleNewChat}
159-
className="text-sm text-muted-foreground hover:text-foreground hover:bg-white/50 dark:hover:bg-zinc-800/50 py-3 px-4 rounded-xl transition-all text-center border border-transparent hover:border-zinc-200 dark:hover:border-zinc-800 hover:shadow-sm"
154+
className="text-sm text-muted-foreground hover:text-foreground hover:bg-muted py-3 px-4 rounded-xl transition-all text-center border border-transparent hover:border-border"
160155
>
161-
"{hint}"
156+
&ldquo;{hint}&rdquo;
162157
</button>
163158
))}
164159
</div>
165160
</div>
166-
<ShortcutsDock />
167161
</div>
168-
169162
);
170163
}

0 commit comments

Comments
 (0)