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:
+3
-10
@@ -44,15 +44,8 @@ export default function App() {
|
||||
height: '40px',
|
||||
border: '3px solid var(--border)',
|
||||
borderTopColor: 'var(--accent)',
|
||||
borderRadius: '50%',
|
||||
animation: 'spin 1s linear infinite'
|
||||
borderRadius: '50%'
|
||||
}} />
|
||||
<style>{`
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
`}</style>
|
||||
|
||||
Loading...
|
||||
</div>
|
||||
</div>
|
||||
@@ -72,11 +65,11 @@ export default function App() {
|
||||
zIndex: 100,
|
||||
background: 'var(--bg-secondary)',
|
||||
border: '1px solid var(--border)',
|
||||
fontSize: '20px'
|
||||
fontSize: '18px'
|
||||
}}
|
||||
title="Open Menu"
|
||||
>
|
||||
☰
|
||||
Menu
|
||||
</button>
|
||||
|
||||
<ChatWindow
|
||||
|
||||
@@ -43,7 +43,7 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
|
||||
if (!input.trim() || isLoading) return
|
||||
|
||||
if (!config.endpoint) {
|
||||
setError('Please configure API endpoint in the drawer (☰)')
|
||||
setError('Please configure API endpoint in the drawer')
|
||||
return
|
||||
}
|
||||
|
||||
@@ -123,18 +123,18 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
|
||||
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
|
||||
let content = `# Conversation Export\n\n`
|
||||
content += `**Date:** ${new Date().toLocaleString()}\n`
|
||||
content += `**Endpoint:** ${config.endpoint}\n`
|
||||
content += `**Model:** ${config.model || 'local-swarm'}\n`
|
||||
content += `Date: ${new Date().toLocaleString()}\n`
|
||||
content += `Endpoint: ${config.endpoint}\n`
|
||||
content += `Model: ${config.model || 'local-swarm'}\n`
|
||||
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`
|
||||
|
||||
session.messages.forEach(msg => {
|
||||
const role = msg.role === 'user' ? '👤 User' : '🤖 Assistant'
|
||||
content += `### ${role}\n\n${msg.content}\n\n`
|
||||
const role = msg.role === 'user' ? 'User' : 'Assistant'
|
||||
content += `## ${role}\n\n${msg.content}\n\n`
|
||||
})
|
||||
|
||||
// Create and download file
|
||||
@@ -161,14 +161,15 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
|
||||
height: '100vh',
|
||||
backgroundColor: 'var(--bg-primary)'
|
||||
}}>
|
||||
{/* Header */}
|
||||
{/* Header - Added padding-left to avoid overlap with menu button */}
|
||||
<header style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
padding: '16px 20px',
|
||||
padding: '16px 20px 16px 80px',
|
||||
borderBottom: '1px solid var(--border)',
|
||||
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 }}>
|
||||
<h1 style={{
|
||||
@@ -210,7 +211,7 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
|
||||
className="secondary small"
|
||||
title="Export conversation"
|
||||
>
|
||||
📥 Export
|
||||
Export
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
@@ -235,11 +236,10 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
|
||||
textAlign: 'center',
|
||||
padding: '40px'
|
||||
}}>
|
||||
<div style={{ fontSize: '48px', marginBottom: '16px' }}>💬</div>
|
||||
<h2 style={{ fontSize: '20px', marginBottom: '8px', color: 'var(--text-primary)' }}>
|
||||
Welcome to Light Chat
|
||||
</h2>
|
||||
<p>Open the menu (☰) to create a session</p>
|
||||
<p>Open the menu to create a session</p>
|
||||
</div>
|
||||
) : session.messages.length === 0 ? (
|
||||
<div style={{
|
||||
@@ -252,7 +252,6 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
|
||||
textAlign: 'center',
|
||||
padding: '40px'
|
||||
}}>
|
||||
<div style={{ fontSize: '48px', marginBottom: '16px' }}>👋</div>
|
||||
<h2 style={{ fontSize: '20px', marginBottom: '8px', color: 'var(--text-primary)' }}>
|
||||
Start the conversation
|
||||
</h2>
|
||||
@@ -264,8 +263,7 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
|
||||
key={index}
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: msg.role === 'user' ? 'flex-end' : 'flex-start',
|
||||
animation: 'fadeIn 0.3s ease-out'
|
||||
justifyContent: msg.role === 'user' ? 'flex-end' : 'flex-start'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
@@ -280,20 +278,16 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
|
||||
: 'var(--bg-secondary)',
|
||||
color: msg.role === 'user' ? 'white' : 'var(--text-primary)',
|
||||
boxShadow: 'var(--shadow)',
|
||||
border: `1px solid ${msg.role === 'user' ? 'var(--accent)' : 'var(--border)'}`,
|
||||
position: 'relative'
|
||||
border: `1px solid ${msg.role === 'user' ? 'var(--accent)' : 'var(--border)'}`
|
||||
}}
|
||||
>
|
||||
<div style={{
|
||||
fontSize: '12px',
|
||||
fontWeight: 600,
|
||||
marginBottom: '6px',
|
||||
color: msg.role === 'user' ? 'rgba(255,255,255,0.8)' : 'var(--accent)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '6px'
|
||||
color: msg.role === 'user' ? 'rgba(255,255,255,0.8)' : 'var(--accent)'
|
||||
}}>
|
||||
{msg.role === 'user' ? '👤 You' : '🤖 Assistant'}
|
||||
{msg.role === 'user' ? 'You' : 'Assistant'}
|
||||
</div>
|
||||
<div style={{
|
||||
whiteSpace: 'pre-wrap',
|
||||
@@ -313,8 +307,7 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-start',
|
||||
animation: 'fadeIn 0.3s ease-out'
|
||||
justifyContent: 'flex-start'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
@@ -325,8 +318,7 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
|
||||
backgroundColor: 'var(--bg-secondary)',
|
||||
color: 'var(--text-primary)',
|
||||
boxShadow: 'var(--shadow)',
|
||||
border: '1px solid var(--accent)',
|
||||
position: 'relative'
|
||||
border: '1px solid var(--accent)'
|
||||
}}
|
||||
>
|
||||
<div style={{
|
||||
@@ -338,14 +330,13 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
|
||||
alignItems: 'center',
|
||||
gap: '6px'
|
||||
}}>
|
||||
🤖 Assistant
|
||||
Assistant
|
||||
<span style={{
|
||||
display: 'inline-block',
|
||||
width: '6px',
|
||||
height: '6px',
|
||||
backgroundColor: 'var(--accent)',
|
||||
borderRadius: '50%',
|
||||
animation: 'pulse 1s infinite'
|
||||
borderRadius: '50%'
|
||||
}} />
|
||||
</div>
|
||||
<div style={{
|
||||
@@ -370,14 +361,6 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
|
||||
gap: '8px',
|
||||
padding: '10px 0'
|
||||
}}>
|
||||
<span style={{
|
||||
display: 'inline-block',
|
||||
width: '8px',
|
||||
height: '8px',
|
||||
backgroundColor: 'var(--accent)',
|
||||
borderRadius: '50%',
|
||||
animation: 'pulse 1.5s infinite'
|
||||
}} />
|
||||
Thinking...
|
||||
</div>
|
||||
)}
|
||||
@@ -392,7 +375,7 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
|
||||
borderRadius: 'var(--radius-md)',
|
||||
border: '1px solid var(--error)'
|
||||
}}>
|
||||
⚠️ {error}
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
<div ref={messagesEndRef} />
|
||||
@@ -414,7 +397,7 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
|
||||
type="text"
|
||||
value={input}
|
||||
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}
|
||||
style={{
|
||||
flex: 1,
|
||||
@@ -430,7 +413,7 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
|
||||
className="danger"
|
||||
style={{ padding: '14px 20px' }}
|
||||
>
|
||||
⏹️ Stop
|
||||
Stop
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
@@ -438,7 +421,7 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
|
||||
disabled={!input.trim() || !session}
|
||||
style={{ padding: '14px 24px' }}
|
||||
>
|
||||
Send 📤
|
||||
Send
|
||||
</button>
|
||||
)}
|
||||
</form>
|
||||
|
||||
@@ -22,7 +22,7 @@ describe('SessionDrawer', () => {
|
||||
|
||||
it('should render when open', () => {
|
||||
render(<SessionDrawer {...defaultProps} />)
|
||||
expect(screen.getByText(/Your Sessions/)).toBeInTheDocument()
|
||||
expect(screen.getByText('Sessions')).toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: /New Session/i })).toBeInTheDocument()
|
||||
})
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ export function SessionDrawer({
|
||||
backgroundColor: 'var(--bg-secondary)',
|
||||
borderRight: '1px solid var(--border)',
|
||||
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,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
@@ -100,12 +100,9 @@ export function SessionDrawer({
|
||||
<h2 style={{
|
||||
fontSize: '20px',
|
||||
fontWeight: 600,
|
||||
marginBottom: '16px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px'
|
||||
marginBottom: '16px'
|
||||
}}>
|
||||
<span>💬</span> Sessions
|
||||
Sessions
|
||||
</h2>
|
||||
<button
|
||||
onClick={() => {
|
||||
@@ -263,7 +260,6 @@ export function SessionDrawer({
|
||||
padding: '40px 20px',
|
||||
color: 'var(--text-muted)'
|
||||
}}>
|
||||
<div style={{ fontSize: '32px', marginBottom: '12px' }}>📝</div>
|
||||
<p style={{ fontSize: '14px' }}>
|
||||
No sessions yet.<br />Create one to start chatting.
|
||||
</p>
|
||||
@@ -331,7 +327,7 @@ export function SessionDrawer({
|
||||
}}
|
||||
title="Delete session"
|
||||
>
|
||||
🗑️
|
||||
Del
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
|
||||
+13
-72
@@ -14,12 +14,12 @@
|
||||
--success: #10b981;
|
||||
--error: #ef4444;
|
||||
--warning: #f59e0b;
|
||||
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -1px rgba(0, 0, 0, 0.2);
|
||||
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.4), 0 4px 6px -2px rgba(0, 0, 0, 0.3);
|
||||
--shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
--shadow-lg: 0 4px 12px rgba(0, 0, 0, 0.4);
|
||||
--radius-sm: 6px;
|
||||
--radius-md: 10px;
|
||||
--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%;
|
||||
}
|
||||
|
||||
/* Modern Button Styles */
|
||||
/* Button Styles - No Animations */
|
||||
button {
|
||||
background: linear-gradient(135deg, var(--accent) 0%, var(--accent-hover) 100%);
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
@@ -55,48 +55,15 @@ button {
|
||||
letter-spacing: 0.3px;
|
||||
transition: var(--transition);
|
||||
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 {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: translateY(0);
|
||||
background: var(--accent-hover);
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
button:disabled::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Secondary Button Style */
|
||||
@@ -112,7 +79,11 @@ button.secondary:hover {
|
||||
|
||||
/* Danger Button Style */
|
||||
button.danger {
|
||||
background: linear-gradient(135deg, var(--error) 0%, #dc2626 100%);
|
||||
background: var(--error);
|
||||
}
|
||||
|
||||
button.danger:hover {
|
||||
background: #dc2626;
|
||||
}
|
||||
|
||||
/* Icon Button Style */
|
||||
@@ -123,7 +94,7 @@ button.icon-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 18px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
/* Small Button */
|
||||
@@ -137,11 +108,10 @@ button.small {
|
||||
button.toggle {
|
||||
background: var(--bg-tertiary);
|
||||
border: 2px solid var(--border);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
button.toggle.active {
|
||||
background: linear-gradient(135deg, var(--accent) 0%, var(--accent-hover) 100%);
|
||||
background: var(--accent);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
@@ -161,7 +131,6 @@ input, textarea {
|
||||
input:focus, textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.15);
|
||||
}
|
||||
|
||||
input::placeholder, textarea::placeholder {
|
||||
@@ -191,31 +160,3 @@ input:disabled, textarea:disabled {
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user