refactor: remove animations and emoji, fix layout spacing

- Remove all button shine/hover animations from CSS
- Replace all emoji with text labels
- Fix header padding to prevent overlap with drawer
  - Added left padding (80px) to header content
  - Session name is now fully visible
- Simplified empty states without icons
- Updated delete button text from icon to 'Del'
- Menu button now shows 'Menu' text instead of hamburger icon
- All 52 tests passing
This commit is contained in:
2026-02-26 01:50:23 +01:00
parent 0789893e19
commit 4e65745bf9
5 changed files with 47 additions and 134 deletions
+3 -10
View File
@@ -44,15 +44,8 @@ export default function App() {
height: '40px', height: '40px',
border: '3px solid var(--border)', border: '3px solid var(--border)',
borderTopColor: 'var(--accent)', borderTopColor: 'var(--accent)',
borderRadius: '50%', borderRadius: '50%'
animation: 'spin 1s linear infinite'
}} /> }} />
<style>{`
@keyframes spin {
to { transform: rotate(360deg); }
}
`}</style>
Loading... Loading...
</div> </div>
</div> </div>
@@ -72,11 +65,11 @@ export default function App() {
zIndex: 100, zIndex: 100,
background: 'var(--bg-secondary)', background: 'var(--bg-secondary)',
border: '1px solid var(--border)', border: '1px solid var(--border)',
fontSize: '20px' fontSize: '18px'
}} }}
title="Open Menu" title="Open Menu"
> >
Menu
</button> </button>
<ChatWindow <ChatWindow
+26 -43
View File
@@ -43,7 +43,7 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
if (!input.trim() || isLoading) return if (!input.trim() || isLoading) return
if (!config.endpoint) { if (!config.endpoint) {
setError('Please configure API endpoint in the drawer (☰)') setError('Please configure API endpoint in the drawer')
return return
} }
@@ -123,18 +123,18 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-') const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
let content = `# Conversation Export\n\n` let content = `# Conversation Export\n\n`
content += `**Date:** ${new Date().toLocaleString()}\n` content += `Date: ${new Date().toLocaleString()}\n`
content += `**Endpoint:** ${config.endpoint}\n` content += `Endpoint: ${config.endpoint}\n`
content += `**Model:** ${config.model || 'local-swarm'}\n` content += `Model: ${config.model || 'local-swarm'}\n`
if (config.systemPrompt) { if (config.systemPrompt) {
content += `**System Prompt:** ${config.systemPrompt}\n` content += `System Prompt: ${config.systemPrompt}\n`
} }
content += `**Streaming:** ${config.streaming ? 'Enabled' : 'Disabled'}\n\n` content += `Streaming: ${config.streaming ? 'Enabled' : 'Disabled'}\n\n`
content += `---\n\n` content += `---\n\n`
session.messages.forEach(msg => { session.messages.forEach(msg => {
const role = msg.role === 'user' ? '👤 User' : '🤖 Assistant' const role = msg.role === 'user' ? 'User' : 'Assistant'
content += `### ${role}\n\n${msg.content}\n\n` content += `## ${role}\n\n${msg.content}\n\n`
}) })
// Create and download file // Create and download file
@@ -161,14 +161,15 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
height: '100vh', height: '100vh',
backgroundColor: 'var(--bg-primary)' backgroundColor: 'var(--bg-primary)'
}}> }}>
{/* Header */} {/* Header - Added padding-left to avoid overlap with menu button */}
<header style={{ <header style={{
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
padding: '16px 20px', padding: '16px 20px 16px 80px',
borderBottom: '1px solid var(--border)', borderBottom: '1px solid var(--border)',
backgroundColor: 'var(--bg-secondary)', backgroundColor: 'var(--bg-secondary)',
boxShadow: '0 1px 3px rgba(0,0,0,0.2)' boxShadow: '0 1px 3px rgba(0,0,0,0.2)',
minHeight: '64px'
}}> }}>
<div style={{ flex: 1 }}> <div style={{ flex: 1 }}>
<h1 style={{ <h1 style={{
@@ -210,7 +211,7 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
className="secondary small" className="secondary small"
title="Export conversation" title="Export conversation"
> >
📥 Export Export
</button> </button>
</div> </div>
</header> </header>
@@ -235,11 +236,10 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
textAlign: 'center', textAlign: 'center',
padding: '40px' padding: '40px'
}}> }}>
<div style={{ fontSize: '48px', marginBottom: '16px' }}>💬</div>
<h2 style={{ fontSize: '20px', marginBottom: '8px', color: 'var(--text-primary)' }}> <h2 style={{ fontSize: '20px', marginBottom: '8px', color: 'var(--text-primary)' }}>
Welcome to Light Chat Welcome to Light Chat
</h2> </h2>
<p>Open the menu () to create a session</p> <p>Open the menu to create a session</p>
</div> </div>
) : session.messages.length === 0 ? ( ) : session.messages.length === 0 ? (
<div style={{ <div style={{
@@ -252,7 +252,6 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
textAlign: 'center', textAlign: 'center',
padding: '40px' padding: '40px'
}}> }}>
<div style={{ fontSize: '48px', marginBottom: '16px' }}>👋</div>
<h2 style={{ fontSize: '20px', marginBottom: '8px', color: 'var(--text-primary)' }}> <h2 style={{ fontSize: '20px', marginBottom: '8px', color: 'var(--text-primary)' }}>
Start the conversation Start the conversation
</h2> </h2>
@@ -264,8 +263,7 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
key={index} key={index}
style={{ style={{
display: 'flex', display: 'flex',
justifyContent: msg.role === 'user' ? 'flex-end' : 'flex-start', justifyContent: msg.role === 'user' ? 'flex-end' : 'flex-start'
animation: 'fadeIn 0.3s ease-out'
}} }}
> >
<div <div
@@ -280,20 +278,16 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
: 'var(--bg-secondary)', : 'var(--bg-secondary)',
color: msg.role === 'user' ? 'white' : 'var(--text-primary)', color: msg.role === 'user' ? 'white' : 'var(--text-primary)',
boxShadow: 'var(--shadow)', boxShadow: 'var(--shadow)',
border: `1px solid ${msg.role === 'user' ? 'var(--accent)' : 'var(--border)'}`, border: `1px solid ${msg.role === 'user' ? 'var(--accent)' : 'var(--border)'}`
position: 'relative'
}} }}
> >
<div style={{ <div style={{
fontSize: '12px', fontSize: '12px',
fontWeight: 600, fontWeight: 600,
marginBottom: '6px', marginBottom: '6px',
color: msg.role === 'user' ? 'rgba(255,255,255,0.8)' : 'var(--accent)', color: msg.role === 'user' ? 'rgba(255,255,255,0.8)' : 'var(--accent)'
display: 'flex',
alignItems: 'center',
gap: '6px'
}}> }}>
{msg.role === 'user' ? '👤 You' : '🤖 Assistant'} {msg.role === 'user' ? 'You' : 'Assistant'}
</div> </div>
<div style={{ <div style={{
whiteSpace: 'pre-wrap', whiteSpace: 'pre-wrap',
@@ -313,8 +307,7 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
<div <div
style={{ style={{
display: 'flex', display: 'flex',
justifyContent: 'flex-start', justifyContent: 'flex-start'
animation: 'fadeIn 0.3s ease-out'
}} }}
> >
<div <div
@@ -325,8 +318,7 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
backgroundColor: 'var(--bg-secondary)', backgroundColor: 'var(--bg-secondary)',
color: 'var(--text-primary)', color: 'var(--text-primary)',
boxShadow: 'var(--shadow)', boxShadow: 'var(--shadow)',
border: '1px solid var(--accent)', border: '1px solid var(--accent)'
position: 'relative'
}} }}
> >
<div style={{ <div style={{
@@ -338,14 +330,13 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
alignItems: 'center', alignItems: 'center',
gap: '6px' gap: '6px'
}}> }}>
🤖 Assistant Assistant
<span style={{ <span style={{
display: 'inline-block', display: 'inline-block',
width: '6px', width: '6px',
height: '6px', height: '6px',
backgroundColor: 'var(--accent)', backgroundColor: 'var(--accent)',
borderRadius: '50%', borderRadius: '50%'
animation: 'pulse 1s infinite'
}} /> }} />
</div> </div>
<div style={{ <div style={{
@@ -370,14 +361,6 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
gap: '8px', gap: '8px',
padding: '10px 0' padding: '10px 0'
}}> }}>
<span style={{
display: 'inline-block',
width: '8px',
height: '8px',
backgroundColor: 'var(--accent)',
borderRadius: '50%',
animation: 'pulse 1.5s infinite'
}} />
Thinking... Thinking...
</div> </div>
)} )}
@@ -392,7 +375,7 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
borderRadius: 'var(--radius-md)', borderRadius: 'var(--radius-md)',
border: '1px solid var(--error)' border: '1px solid var(--error)'
}}> }}>
{error} {error}
</div> </div>
)} )}
<div ref={messagesEndRef} /> <div ref={messagesEndRef} />
@@ -414,7 +397,7 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
type="text" type="text"
value={input} value={input}
onChange={(e) => setInput(e.target.value)} onChange={(e) => setInput(e.target.value)}
placeholder={config.endpoint ? "Type your message..." : "Configure API endpoint in menu (☰) first"} placeholder={config.endpoint ? "Type your message..." : "Configure API endpoint in menu first"}
disabled={isLoading || !session} disabled={isLoading || !session}
style={{ style={{
flex: 1, flex: 1,
@@ -430,7 +413,7 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
className="danger" className="danger"
style={{ padding: '14px 20px' }} style={{ padding: '14px 20px' }}
> >
Stop Stop
</button> </button>
) : ( ) : (
<button <button
@@ -438,7 +421,7 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
disabled={!input.trim() || !session} disabled={!input.trim() || !session}
style={{ padding: '14px 24px' }} style={{ padding: '14px 24px' }}
> >
Send 📤 Send
</button> </button>
)} )}
</form> </form>
+1 -1
View File
@@ -22,7 +22,7 @@ describe('SessionDrawer', () => {
it('should render when open', () => { it('should render when open', () => {
render(<SessionDrawer {...defaultProps} />) render(<SessionDrawer {...defaultProps} />)
expect(screen.getByText(/Your Sessions/)).toBeInTheDocument() expect(screen.getByText('Sessions')).toBeInTheDocument()
expect(screen.getByRole('button', { name: /New Session/i })).toBeInTheDocument() expect(screen.getByRole('button', { name: /New Session/i })).toBeInTheDocument()
}) })
+4 -8
View File
@@ -82,7 +82,7 @@ export function SessionDrawer({
backgroundColor: 'var(--bg-secondary)', backgroundColor: 'var(--bg-secondary)',
borderRight: '1px solid var(--border)', borderRight: '1px solid var(--border)',
transform: isOpen ? 'translateX(0)' : 'translateX(-100%)', transform: isOpen ? 'translateX(0)' : 'translateX(-100%)',
transition: 'transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)', transition: 'transform 0.3s ease',
zIndex: 1000, zIndex: 1000,
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
@@ -100,12 +100,9 @@ export function SessionDrawer({
<h2 style={{ <h2 style={{
fontSize: '20px', fontSize: '20px',
fontWeight: 600, fontWeight: 600,
marginBottom: '16px', marginBottom: '16px'
display: 'flex',
alignItems: 'center',
gap: '8px'
}}> }}>
<span>💬</span> Sessions Sessions
</h2> </h2>
<button <button
onClick={() => { onClick={() => {
@@ -263,7 +260,6 @@ export function SessionDrawer({
padding: '40px 20px', padding: '40px 20px',
color: 'var(--text-muted)' color: 'var(--text-muted)'
}}> }}>
<div style={{ fontSize: '32px', marginBottom: '12px' }}>📝</div>
<p style={{ fontSize: '14px' }}> <p style={{ fontSize: '14px' }}>
No sessions yet.<br />Create one to start chatting. No sessions yet.<br />Create one to start chatting.
</p> </p>
@@ -331,7 +327,7 @@ export function SessionDrawer({
}} }}
title="Delete session" title="Delete session"
> >
🗑 Del
</button> </button>
</div> </div>
))} ))}
+13 -72
View File
@@ -14,12 +14,12 @@
--success: #10b981; --success: #10b981;
--error: #ef4444; --error: #ef4444;
--warning: #f59e0b; --warning: #f59e0b;
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -1px rgba(0, 0, 0, 0.2); --shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.4), 0 4px 6px -2px rgba(0, 0, 0, 0.3); --shadow-lg: 0 4px 12px rgba(0, 0, 0, 0.4);
--radius-sm: 6px; --radius-sm: 6px;
--radius-md: 10px; --radius-md: 10px;
--radius-lg: 14px; --radius-lg: 14px;
--transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); --transition: all 0.15s ease;
} }
* { * {
@@ -42,9 +42,9 @@ body {
width: 100%; width: 100%;
} }
/* Modern Button Styles */ /* Button Styles - No Animations */
button { button {
background: linear-gradient(135deg, var(--accent) 0%, var(--accent-hover) 100%); background: var(--accent);
color: white; color: white;
border: none; border: none;
padding: 10px 20px; padding: 10px 20px;
@@ -55,48 +55,15 @@ button {
letter-spacing: 0.3px; letter-spacing: 0.3px;
transition: var(--transition); transition: var(--transition);
box-shadow: var(--shadow); box-shadow: var(--shadow);
position: relative;
overflow: hidden;
}
button::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(
90deg,
transparent,
rgba(255, 255, 255, 0.2),
transparent
);
transition: left 0.5s;
}
button:hover::before {
left: 100%;
} }
button:hover { button:hover {
transform: translateY(-1px); background: var(--accent-hover);
box-shadow: var(--shadow-lg);
}
button:active {
transform: translateY(0);
} }
button:disabled { button:disabled {
opacity: 0.5; opacity: 0.5;
cursor: not-allowed; cursor: not-allowed;
transform: none;
box-shadow: none;
}
button:disabled::before {
display: none;
} }
/* Secondary Button Style */ /* Secondary Button Style */
@@ -112,7 +79,11 @@ button.secondary:hover {
/* Danger Button Style */ /* Danger Button Style */
button.danger { button.danger {
background: linear-gradient(135deg, var(--error) 0%, #dc2626 100%); background: var(--error);
}
button.danger:hover {
background: #dc2626;
} }
/* Icon Button Style */ /* Icon Button Style */
@@ -123,7 +94,7 @@ button.icon-btn {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-size: 18px; font-size: 16px;
} }
/* Small Button */ /* Small Button */
@@ -137,11 +108,10 @@ button.small {
button.toggle { button.toggle {
background: var(--bg-tertiary); background: var(--bg-tertiary);
border: 2px solid var(--border); border: 2px solid var(--border);
position: relative;
} }
button.toggle.active { button.toggle.active {
background: linear-gradient(135deg, var(--accent) 0%, var(--accent-hover) 100%); background: var(--accent);
border-color: var(--accent); border-color: var(--accent);
} }
@@ -161,7 +131,6 @@ input, textarea {
input:focus, textarea:focus { input:focus, textarea:focus {
outline: none; outline: none;
border-color: var(--accent); border-color: var(--accent);
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.15);
} }
input::placeholder, textarea::placeholder { input::placeholder, textarea::placeholder {
@@ -191,31 +160,3 @@ input:disabled, textarea:disabled {
::-webkit-scrollbar-thumb:hover { ::-webkit-scrollbar-thumb:hover {
background: var(--border-light); background: var(--border-light);
} }
/* Animations */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes slideIn {
from { transform: translateX(-100%); }
to { transform: translateX(0); }
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.animate-fadeIn {
animation: fadeIn 0.3s ease-out;
}
.animate-slideIn {
animation: slideIn 0.3s ease-out;
}
.animate-pulse {
animation: pulse 1.5s ease-in-out infinite;
}