feat(ui): comprehensive design system improvements

This PR addresses 53 design issues identified in the UI codebase,
implementing a more consistent and polished neobrutalism design system.

Typography:
- Improved font stacks with proper fallbacks
- Added font smoothing for crisp text rendering

Color/Theme:
- Added neutral scale (50-900) for consistent grays
- Added semantic log level colors with dark mode variants
- Added category colors for feature cards
- Added GLM badge color variable
- Full dark mode support for all new variables

Design Tokens:
- Spacing scale (xs to 2xl)
- Z-index scale (dropdown to toast)
- Border radius tokens
- Inset shadow variants

Animations:
- New transition timing variables
- New easing curves (bounce, smooth, out-back)
- Slide-in animations (top/bottom/left)
- Bounce, shake, scale-pop animations
- Stagger delay utilities
- Enhanced YOLO fire effect with parallax layers

Components:
- Button size variants (sm/lg/icon) and loading state
- Input variants (error/disabled/textarea)
- Badge color and size variants
- Card elevation variants (elevated/flat/sunken)
- Progress bar shimmer animation
- Stronger modal backdrop with blur
- Neobrutalist tooltips
- Enhanced empty state with striped pattern

Component Fixes:
- Replaced hardcoded colors with CSS variables
- Fixed ProgressDashboard percentage alignment
- Improved ChatMessage role-specific styling
- Consistent category badge colors in FeatureModal
- Improved step input styling in forms

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
M Zubair
2026-01-14 22:14:29 +01:00
parent d1b8eb5f99
commit 501719f77a
26 changed files with 911 additions and 301 deletions

View File

@@ -87,13 +87,13 @@ export function AddFeatureForm({ projectName, onClose }: AddFeatureFormProps) {
<form onSubmit={handleSubmit} className="p-6 space-y-4">
{/* Error Message */}
{error && (
<div className="flex items-center gap-3 p-4 bg-[var(--color-neo-danger)] text-white border-3 border-[var(--color-neo-border)]">
<div className="flex items-center gap-3 p-4 bg-[var(--color-neo-error-bg)] text-[var(--color-neo-error-text)] border-3 border-[var(--color-neo-error-border)]">
<AlertCircle size={20} />
<span>{error}</span>
<button
type="button"
onClick={() => setError(null)}
className="ml-auto"
className="ml-auto hover:opacity-70 transition-opacity"
>
<X size={16} />
</button>
@@ -166,8 +166,11 @@ export function AddFeatureForm({ projectName, onClose }: AddFeatureFormProps) {
</label>
<div className="space-y-2">
{steps.map((step, index) => (
<div key={step.id} className="flex gap-2">
<span className="neo-input w-12 text-center flex-shrink-0 flex items-center justify-center">
<div key={step.id} className="flex gap-2 items-center">
<span
className="w-10 h-10 flex-shrink-0 flex items-center justify-center font-mono font-bold text-sm border-3 border-[var(--color-neo-border)] bg-[var(--color-neo-bg)] text-[var(--color-neo-text-secondary)]"
style={{ boxShadow: 'var(--shadow-neo-sm)' }}
>
{index + 1}
</span>
<input

View File

@@ -131,7 +131,7 @@ export function AssistantChat({ projectName }: AssistantChatProps) {
)}
{/* Input area */}
<div className="border-t-3 border-[var(--color-neo-border)] p-4 bg-white">
<div className="border-t-3 border-[var(--color-neo-border)] p-4 bg-[var(--color-neo-card)]">
<div className="flex gap-2">
<textarea
ref={inputRef}

View File

@@ -17,9 +17,8 @@ export function AssistantFAB({ onClick, isOpen }: AssistantFABProps) {
fixed bottom-6 right-6 z-50
w-14 h-14
flex items-center justify-center
bg-[var(--color-neo-progress)] text-white
bg-[var(--color-neo-progress)] text-[var(--color-neo-text-on-bright)]
border-3 border-[var(--color-neo-border)]
rounded-full
shadow-neo-md
transition-all duration-200
hover:shadow-neo-lg hover:-translate-y-0.5

View File

@@ -31,26 +31,29 @@ export function AssistantPanel({ projectName, isOpen, onClose }: AssistantPanelP
className={`
fixed right-0 top-0 bottom-0 z-50
w-[400px] max-w-[90vw]
bg-white
bg-neo-card
border-l-4 border-[var(--color-neo-border)]
shadow-[-8px_0_0px_rgba(0,0,0,1)]
transform transition-transform duration-300 ease-out
flex flex-col
${isOpen ? 'translate-x-0' : 'translate-x-full'}
`}
style={{ boxShadow: 'var(--shadow-neo-left-lg)' }}
role="dialog"
aria-label="Project Assistant"
aria-hidden={!isOpen}
>
{/* Header */}
<div className="flex items-center justify-between px-4 py-3 border-b-3 border-[var(--color-neo-border)] bg-[var(--color-neo-progress)]">
<div className="flex items-center justify-between px-4 py-3 border-b-3 border-neo-border bg-neo-progress">
<div className="flex items-center gap-2">
<div className="bg-white border-2 border-[var(--color-neo-border)] p-1.5 shadow-[2px_2px_0px_rgba(0,0,0,1)]">
<div
className="bg-neo-card border-2 border-neo-border p-1.5"
style={{ boxShadow: 'var(--shadow-neo-sm)' }}
>
<Bot size={18} />
</div>
<div>
<h2 className="font-display font-bold text-white">Project Assistant</h2>
<p className="text-xs text-white/80 font-mono">{projectName}</p>
<h2 className="font-display font-bold text-neo-text-on-bright">Project Assistant</h2>
<p className="text-xs text-neo-text-on-bright opacity-80 font-mono">{projectName}</p>
</div>
</div>
<button
@@ -58,9 +61,9 @@ export function AssistantPanel({ projectName, isOpen, onClose }: AssistantPanelP
className="
neo-btn neo-btn-ghost
p-2
bg-white/20 border-white/40
hover:bg-white/30
text-white
bg-[var(--color-neo-card)] border-[var(--color-neo-border)]
hover:bg-[var(--color-neo-bg)]
text-[var(--color-neo-text)]
"
title="Close Assistant (Press A)"
aria-label="Close Assistant"

View File

@@ -21,31 +21,37 @@ export function ChatMessage({ message }: ChatMessageProps) {
minute: '2-digit',
})
// Role-specific styling
// Role-specific styling using CSS variables for theme consistency
const roleConfig = {
user: {
icon: User,
bgColor: 'bg-[var(--color-neo-pending)]',
textColor: 'text-[var(--color-neo-text-on-bright)]',
borderColor: 'border-[var(--color-neo-border)]',
align: 'justify-end',
bubbleAlign: 'items-end',
iconBg: 'bg-[var(--color-neo-pending)]',
shadow: 'var(--shadow-neo-md)',
},
assistant: {
icon: Bot,
bgColor: 'bg-white',
bgColor: 'bg-[var(--color-neo-card)]',
textColor: 'text-[var(--color-neo-text)]',
borderColor: 'border-[var(--color-neo-border)]',
align: 'justify-start',
bubbleAlign: 'items-start',
iconBg: 'bg-[var(--color-neo-progress)]',
shadow: 'var(--shadow-neo-md)',
},
system: {
icon: Info,
bgColor: 'bg-[var(--color-neo-done)]',
textColor: 'text-[var(--color-neo-text-on-bright)]',
borderColor: 'border-[var(--color-neo-border)]',
align: 'justify-center',
bubbleAlign: 'items-center',
iconBg: 'bg-[var(--color-neo-done)]',
shadow: 'var(--shadow-neo-sm)',
},
}
@@ -61,9 +67,9 @@ export function ChatMessage({ message }: ChatMessageProps) {
${config.bgColor}
border-2 ${config.borderColor}
px-4 py-2
text-sm font-mono
shadow-[2px_2px_0px_rgba(0,0,0,1)]
text-sm font-mono text-[var(--color-neo-text-on-bright)]
`}
style={{ boxShadow: 'var(--shadow-neo-sm)' }}
>
<span className="flex items-center gap-2">
<Icon size={14} />
@@ -85,11 +91,11 @@ export function ChatMessage({ message }: ChatMessageProps) {
${config.iconBg}
border-2 border-[var(--color-neo-border)]
p-1.5
shadow-[2px_2px_0px_rgba(0,0,0,1)]
flex-shrink-0
`}
style={{ boxShadow: 'var(--shadow-neo-sm)' }}
>
<Icon size={16} className="text-white" />
<Icon size={16} className="text-[var(--color-neo-text-on-bright)]" />
</div>
)}
@@ -98,13 +104,13 @@ export function ChatMessage({ message }: ChatMessageProps) {
${config.bgColor}
border-3 ${config.borderColor}
px-4 py-3
shadow-[4px_4px_0px_rgba(0,0,0,1)]
${isStreaming ? 'animate-pulse-neo' : ''}
`}
style={{ boxShadow: config.shadow }}
>
{/* Parse content for basic markdown-like formatting */}
{content && (
<div className="whitespace-pre-wrap text-sm leading-relaxed text-[#1a1a1a]">
<div className={`whitespace-pre-wrap text-sm leading-relaxed ${config.textColor}`}>
{content.split('\n').map((line, i) => {
// Bold text
const boldRegex = /\*\*(.*?)\*\*/g
@@ -144,7 +150,8 @@ export function ChatMessage({ message }: ChatMessageProps) {
{attachments.map((attachment) => (
<div
key={attachment.id}
className="border-2 border-[var(--color-neo-border)] p-1 bg-white shadow-[2px_2px_0px_rgba(0,0,0,1)]"
className="border-2 border-[var(--color-neo-border)] p-1 bg-[var(--color-neo-card)]"
style={{ boxShadow: 'var(--shadow-neo-sm)' }}
>
<img
src={attachment.previewUrl}
@@ -153,7 +160,7 @@ export function ChatMessage({ message }: ChatMessageProps) {
onClick={() => window.open(attachment.previewUrl, '_blank')}
title={`${attachment.filename} (click to enlarge)`}
/>
<span className="text-xs text-[var(--color-neo-text-secondary)] block mt-1 text-center">
<span className="text-xs text-neo-text-secondary block mt-1 text-center">
{attachment.filename}
</span>
</div>
@@ -163,7 +170,7 @@ export function ChatMessage({ message }: ChatMessageProps) {
{/* Streaming indicator */}
{isStreaming && (
<span className="inline-block w-2 h-4 bg-[var(--color-neo-accent)] ml-1 animate-pulse" />
<span className="inline-block w-2 h-4 bg-neo-accent ml-1 animate-pulse" />
)}
</div>
@@ -173,11 +180,11 @@ export function ChatMessage({ message }: ChatMessageProps) {
${config.iconBg}
border-2 border-[var(--color-neo-border)]
p-1.5
shadow-[2px_2px_0px_rgba(0,0,0,1)]
flex-shrink-0
`}
style={{ boxShadow: 'var(--shadow-neo-sm)' }}
>
<Icon size={16} />
<Icon size={16} className="text-[var(--color-neo-text-on-bright)]" />
</div>
)}
</div>

View File

@@ -55,12 +55,12 @@ export function ConfirmDialog({
<div className="flex items-center justify-between p-4 border-b-3 border-[var(--color-neo-border)]">
<div className="flex items-center gap-3">
<div
className="p-2 border-2 border-[var(--color-neo-border)] shadow-[2px_2px_0px_rgba(0,0,0,1)]"
style={{ backgroundColor: colors.icon }}
className="p-2 border-2 border-[var(--color-neo-border)]"
style={{ boxShadow: 'var(--shadow-neo-sm)', backgroundColor: colors.icon }}
>
<AlertTriangle size={20} className="text-white" />
<AlertTriangle size={20} className="text-[var(--color-neo-text-on-bright)]" />
</div>
<h2 className="font-display font-bold text-lg text-[#1a1a1a]">
<h2 className="font-display font-bold text-lg text-[var(--color-neo-text)]">
{title}
</h2>
</div>

View File

@@ -273,18 +273,18 @@ export function DebugLogViewer({
return 'info'
}
// Get color class for log level
// Get color class for log level using theme CSS variables
const getLogColor = (level: LogLevel): string => {
switch (level) {
case 'error':
return 'text-red-400'
return 'text-[var(--color-neo-log-error)]'
case 'warn':
return 'text-yellow-400'
return 'text-[var(--color-neo-log-warning)]'
case 'debug':
return 'text-gray-400'
return 'text-[var(--color-neo-log-debug)]'
case 'info':
default:
return 'text-green-400'
return 'text-[var(--color-neo-log-success)]'
}
}
@@ -316,27 +316,27 @@ export function DebugLogViewer({
className="absolute top-0 left-0 right-0 h-2 cursor-ns-resize group flex items-center justify-center -translate-y-1/2 z-50"
onMouseDown={handleResizeStart}
>
<div className="w-16 h-1.5 bg-[#333] rounded-full group-hover:bg-[#555] transition-colors flex items-center justify-center">
<GripHorizontal size={12} className="text-gray-500 group-hover:text-gray-400" />
<div className="w-16 h-1.5 bg-[var(--color-neo-border)] rounded-full group-hover:bg-[var(--color-neo-text-secondary)] transition-colors flex items-center justify-center">
<GripHorizontal size={12} className="text-[var(--color-neo-text-muted)] group-hover:text-[var(--color-neo-text-secondary)]" />
</div>
</div>
)}
{/* Header bar */}
<div
className="flex items-center justify-between h-10 px-4 bg-[#1a1a1a] border-t-3 border-black"
className="flex items-center justify-between h-10 px-4 bg-[var(--color-neo-border)] border-t-3 border-[var(--color-neo-text)]"
>
<div className="flex items-center gap-2">
{/* Collapse/expand toggle */}
<button
onClick={onToggle}
className="flex items-center gap-2 hover:bg-[#333] px-2 py-1 rounded transition-colors cursor-pointer"
className="flex items-center gap-2 hover:bg-[var(--color-neo-hover-subtle)] px-2 py-1 rounded transition-colors cursor-pointer"
>
<TerminalIcon size={16} className="text-green-400" />
<span className="font-mono text-sm text-white font-bold">
<TerminalIcon size={16} className="text-[var(--color-neo-done)]" />
<span className="font-mono text-sm text-[var(--color-neo-bg)] font-bold">
Debug
</span>
<span className="px-1.5 py-0.5 text-xs font-mono bg-[#333] text-gray-500 rounded" title="Toggle debug panel">
<span className="px-1.5 py-0.5 text-xs font-mono bg-[var(--color-neo-card)] text-[var(--color-neo-text-muted)] rounded" title="Toggle debug panel">
D
</span>
</button>
@@ -351,14 +351,14 @@ export function DebugLogViewer({
}}
className={`flex items-center gap-1.5 px-3 py-1 text-xs font-mono rounded transition-colors ${
activeTab === 'agent'
? 'bg-[#333] text-white'
: 'text-gray-400 hover:text-white hover:bg-[#2a2a2a]'
? 'bg-[var(--color-neo-card)] text-[var(--color-neo-text)]'
: 'text-[var(--color-neo-text-muted)] hover:text-[var(--color-neo-text)] hover:bg-[var(--color-neo-hover-subtle)]'
}`}
>
<Cpu size={12} />
Agent
{logs.length > 0 && (
<span className="px-1.5 py-0.5 text-[10px] bg-[#444] rounded">
<span className="px-1.5 py-0.5 text-[10px] bg-[var(--color-neo-text-secondary)] text-[var(--color-neo-bg)] rounded">
{logs.length}
</span>
)}
@@ -370,14 +370,14 @@ export function DebugLogViewer({
}}
className={`flex items-center gap-1.5 px-3 py-1 text-xs font-mono rounded transition-colors ${
activeTab === 'devserver'
? 'bg-[#333] text-white'
: 'text-gray-400 hover:text-white hover:bg-[#2a2a2a]'
? 'bg-[var(--color-neo-card)] text-[var(--color-neo-text)]'
: 'text-[var(--color-neo-text-muted)] hover:text-[var(--color-neo-text)] hover:bg-[var(--color-neo-hover-subtle)]'
}`}
>
<Server size={12} />
Dev Server
{devLogs.length > 0 && (
<span className="px-1.5 py-0.5 text-[10px] bg-[#444] rounded">
<span className="px-1.5 py-0.5 text-[10px] bg-[var(--color-neo-text-secondary)] text-[var(--color-neo-bg)] rounded">
{devLogs.length}
</span>
)}
@@ -389,13 +389,13 @@ export function DebugLogViewer({
}}
className={`flex items-center gap-1.5 px-3 py-1 text-xs font-mono rounded transition-colors ${
activeTab === 'terminal'
? 'bg-[#333] text-white'
: 'text-gray-400 hover:text-white hover:bg-[#2a2a2a]'
? 'bg-[var(--color-neo-card)] text-[var(--color-neo-text)]'
: 'text-[var(--color-neo-text-muted)] hover:text-[var(--color-neo-text)] hover:bg-[var(--color-neo-hover-subtle)]'
}`}
>
<TerminalIcon size={12} />
Terminal
<span className="px-1.5 py-0.5 text-[10px] bg-[#444] text-gray-500 rounded" title="Toggle terminal">
<span className="px-1.5 py-0.5 text-[10px] bg-[var(--color-neo-text-secondary)] text-[var(--color-neo-text-muted)] rounded" title="Toggle terminal">
T
</span>
</button>
@@ -406,12 +406,12 @@ export function DebugLogViewer({
{isOpen && activeTab !== 'terminal' && (
<>
{getCurrentLogCount() > 0 && (
<span className="px-2 py-0.5 text-xs font-mono bg-[#333] text-gray-300 rounded ml-2">
<span className="px-2 py-0.5 text-xs font-mono bg-[var(--color-neo-card)] text-[var(--color-neo-text-secondary)] rounded ml-2">
{getCurrentLogCount()}
</span>
)}
{isAutoScrollPaused() && (
<span className="px-2 py-0.5 text-xs font-mono bg-yellow-600 text-white rounded">
<span className="px-2 py-0.5 text-xs font-mono bg-[var(--color-neo-pending)] text-[var(--color-neo-text-on-bright)] rounded">
Paused
</span>
)}
@@ -427,17 +427,17 @@ export function DebugLogViewer({
e.stopPropagation()
handleClear()
}}
className="p-1.5 hover:bg-[#333] rounded transition-colors"
className="p-1.5 hover:bg-[var(--color-neo-hover-subtle)] rounded transition-colors"
title="Clear logs"
>
<Trash2 size={14} className="text-gray-400" />
<Trash2 size={14} className="text-[var(--color-neo-text-muted)]" />
</button>
)}
<div className="p-1">
{isOpen ? (
<ChevronDown size={16} className="text-gray-400" />
<ChevronDown size={16} className="text-[var(--color-neo-text-muted)]" />
) : (
<ChevronUp size={16} className="text-gray-400" />
<ChevronUp size={16} className="text-[var(--color-neo-text-muted)]" />
)}
</div>
</div>
@@ -445,7 +445,7 @@ export function DebugLogViewer({
{/* Content area */}
{isOpen && (
<div className="h-[calc(100%-2.5rem)] bg-[#1a1a1a]">
<div className="h-[calc(100%-2.5rem)] bg-[var(--color-neo-border)]">
{/* Agent Logs Tab */}
{activeTab === 'agent' && (
<div
@@ -454,7 +454,7 @@ export function DebugLogViewer({
className="h-full overflow-y-auto p-2 font-mono text-sm"
>
{logs.length === 0 ? (
<div className="flex items-center justify-center h-full text-gray-500">
<div className="flex items-center justify-center h-full text-[var(--color-neo-text-muted)]">
No logs yet. Start the agent to see output.
</div>
) : (
@@ -467,9 +467,9 @@ export function DebugLogViewer({
return (
<div
key={`${log.timestamp}-${index}`}
className="flex gap-2 hover:bg-[#2a2a2a] px-1 py-0.5 rounded"
className="flex gap-2 hover:bg-[var(--color-neo-hover-subtle)] px-1 py-0.5 rounded"
>
<span className="text-gray-500 select-none shrink-0">
<span className="text-[var(--color-neo-text-muted)] select-none shrink-0">
{timestamp}
</span>
<span className={`${colorClass} whitespace-pre-wrap break-all`}>
@@ -491,7 +491,7 @@ export function DebugLogViewer({
className="h-full overflow-y-auto p-2 font-mono text-sm"
>
{devLogs.length === 0 ? (
<div className="flex items-center justify-center h-full text-gray-500">
<div className="flex items-center justify-center h-full text-[var(--color-neo-text-muted)]">
No dev server logs yet.
</div>
) : (
@@ -504,9 +504,9 @@ export function DebugLogViewer({
return (
<div
key={`${log.timestamp}-${index}`}
className="flex gap-2 hover:bg-[#2a2a2a] px-1 py-0.5 rounded"
className="flex gap-2 hover:bg-[var(--color-neo-hover-subtle)] px-1 py-0.5 rounded"
>
<span className="text-gray-500 select-none shrink-0">
<span className="text-[var(--color-neo-text-muted)] select-none shrink-0">
{timestamp}
</span>
<span className={`${colorClass} whitespace-pre-wrap break-all`}>
@@ -538,11 +538,11 @@ export function DebugLogViewer({
{/* Terminal content - render all terminals and show/hide to preserve buffers */}
<div className="flex-1 min-h-0 relative">
{isLoadingTerminals ? (
<div className="h-full flex items-center justify-center text-gray-500 font-mono text-sm">
<div className="h-full flex items-center justify-center text-[var(--color-neo-text-muted)] font-mono text-sm">
Loading terminals...
</div>
) : terminals.length === 0 ? (
<div className="h-full flex items-center justify-center text-gray-500 font-mono text-sm">
<div className="h-full flex items-center justify-center text-[var(--color-neo-text-muted)] font-mono text-sm">
No terminal available
</div>
) : (

View File

@@ -92,7 +92,7 @@ export function DevServerControl({ projectName, status, url }: DevServerControlP
className="neo-btn text-sm py-2 px-3"
style={isCrashed ? {
backgroundColor: 'var(--color-neo-danger)',
color: '#ffffff',
color: 'var(--color-neo-text-on-bright)',
} : undefined}
title={isCrashed ? "Dev Server Crashed - Click to Restart" : "Start Dev Server"}
aria-label={isCrashed ? "Restart Dev Server (crashed)" : "Start Dev Server"}
@@ -112,7 +112,7 @@ export function DevServerControl({ projectName, status, url }: DevServerControlP
className="neo-btn text-sm py-2 px-3"
style={{
backgroundColor: 'var(--color-neo-progress)',
color: '#ffffff',
color: 'var(--color-neo-text-on-bright)',
}}
title="Stop Dev Server"
aria-label="Stop Dev Server"
@@ -134,7 +134,7 @@ export function DevServerControl({ projectName, status, url }: DevServerControlP
className="neo-btn text-sm py-2 px-3 gap-1"
style={{
backgroundColor: 'var(--color-neo-progress)',
color: '#ffffff',
color: 'var(--color-neo-text-on-bright)',
textDecoration: 'none',
}}
title={`Open ${url} in new tab`}

View File

@@ -105,13 +105,13 @@ export function EditFeatureForm({ feature, projectName, onClose, onSaved }: Edit
<form onSubmit={handleSubmit} className="p-6 space-y-4">
{/* Error Message */}
{error && (
<div className="flex items-center gap-3 p-4 bg-[var(--color-neo-danger)] text-white border-3 border-[var(--color-neo-border)]">
<div className="flex items-center gap-3 p-4 bg-[var(--color-neo-error-bg)] text-[var(--color-neo-error-text)] border-3 border-[var(--color-neo-error-border)]">
<AlertCircle size={20} />
<span>{error}</span>
<button
type="button"
onClick={() => setError(null)}
className="ml-auto"
className="ml-auto hover:opacity-70 transition-opacity"
>
<X size={16} />
</button>
@@ -184,8 +184,11 @@ export function EditFeatureForm({ feature, projectName, onClose, onSaved }: Edit
</label>
<div className="space-y-2">
{steps.map((step, index) => (
<div key={step.id} className="flex gap-2">
<span className="neo-input w-12 text-center flex-shrink-0 flex items-center justify-center">
<div key={step.id} className="flex gap-2 items-center">
<span
className="w-10 h-10 flex-shrink-0 flex items-center justify-center font-mono font-bold text-sm border-3 border-[var(--color-neo-border)] bg-[var(--color-neo-bg)] text-[var(--color-neo-text-secondary)]"
style={{ boxShadow: 'var(--shadow-neo-sm)' }}
>
{index + 1}
</span>
<input

View File

@@ -152,28 +152,28 @@ export function ExpandProjectChat({
switch (connectionStatus) {
case 'connected':
return (
<span className="flex items-center gap-1 text-xs text-[var(--color-neo-done)]">
<span className="flex items-center gap-1 text-xs text-neo-done">
<Wifi size={12} />
Connected
</span>
)
case 'connecting':
return (
<span className="flex items-center gap-1 text-xs text-[var(--color-neo-pending)]">
<span className="flex items-center gap-1 text-xs text-neo-pending">
<Wifi size={12} className="animate-pulse" />
Connecting...
</span>
)
case 'error':
return (
<span className="flex items-center gap-1 text-xs text-[var(--color-neo-danger)]">
<span className="flex items-center gap-1 text-xs text-neo-danger">
<WifiOff size={12} />
Error
</span>
)
default:
return (
<span className="flex items-center gap-1 text-xs text-[var(--color-neo-text-secondary)]">
<span className="flex items-center gap-1 text-xs text-neo-text-secondary">
<WifiOff size={12} />
Disconnected
</span>
@@ -182,16 +182,16 @@ export function ExpandProjectChat({
}
return (
<div className="flex flex-col h-full bg-[var(--color-neo-bg)]">
<div className="flex flex-col h-full bg-neo-bg">
{/* Header */}
<div className="flex items-center justify-between p-4 border-b-3 border-[var(--color-neo-border)] bg-white">
<div className="flex items-center justify-between p-4 border-b-3 border-neo-border bg-neo-card">
<div className="flex items-center gap-3">
<h2 className="font-display font-bold text-lg text-[#1a1a1a]">
<h2 className="font-display font-bold text-lg text-neo-text">
Expand Project: {projectName}
</h2>
<ConnectionIndicator />
{featuresCreated > 0 && (
<span className="flex items-center gap-1 text-sm text-[var(--color-neo-done)] font-bold">
<span className="flex items-center gap-1 text-sm text-neo-done font-bold">
<Plus size={14} />
{featuresCreated} added
</span>
@@ -200,7 +200,7 @@ export function ExpandProjectChat({
<div className="flex items-center gap-2">
{isComplete && (
<span className="flex items-center gap-1 text-sm text-[var(--color-neo-done)] font-bold">
<span className="flex items-center gap-1 text-sm text-neo-done font-bold">
<CheckCircle2 size={16} />
Complete
</span>
@@ -218,12 +218,12 @@ export function ExpandProjectChat({
{/* Error banner */}
{error && (
<div className="flex items-center gap-2 p-3 bg-[var(--color-neo-danger)] text-white border-b-3 border-[var(--color-neo-border)]">
<div className="flex items-center gap-2 p-3 bg-neo-error-bg text-neo-error-text border-b-3 border-neo-error-border">
<AlertCircle size={16} />
<span className="flex-1 text-sm">{error}</span>
<button
onClick={() => setError(null)}
className="p-1 hover:bg-white/20 rounded"
className="p-1 hover:opacity-70 transition-opacity rounded"
>
<X size={14} />
</button>
@@ -238,7 +238,7 @@ export function ExpandProjectChat({
<h3 className="font-display font-bold text-lg mb-2">
Starting Project Expansion
</h3>
<p className="text-sm text-[var(--color-neo-text-secondary)]">
<p className="text-sm text-neo-text-secondary">
Connecting to Claude to help you add new features to your project...
</p>
{connectionStatus === 'error' && (
@@ -268,7 +268,7 @@ export function ExpandProjectChat({
{/* Input area */}
{!isComplete && (
<div
className="p-4 border-t-3 border-[var(--color-neo-border)] bg-white"
className="p-4 border-t-3 border-neo-border bg-neo-card"
onDrop={handleDrop}
onDragOver={handleDragOver}
>
@@ -278,7 +278,8 @@ export function ExpandProjectChat({
{pendingAttachments.map((attachment) => (
<div
key={attachment.id}
className="relative group border-2 border-[var(--color-neo-border)] p-1 bg-white shadow-[2px_2px_0px_rgba(0,0,0,1)]"
className="relative group border-2 border-neo-border p-1 bg-neo-card"
style={{ boxShadow: 'var(--shadow-neo-sm)' }}
>
<img
src={attachment.previewUrl}
@@ -287,7 +288,7 @@ export function ExpandProjectChat({
/>
<button
onClick={() => handleRemoveAttachment(attachment.id)}
className="absolute -top-2 -right-2 bg-[var(--color-neo-danger)] text-white rounded-full p-0.5 border-2 border-[var(--color-neo-border)] hover:scale-110 transition-transform"
className="absolute -top-2 -right-2 bg-neo-danger text-neo-text-on-bright rounded-full p-0.5 border-2 border-neo-border hover:scale-110 transition-transform"
title="Remove attachment"
>
<X size={12} />
@@ -351,7 +352,7 @@ export function ExpandProjectChat({
</div>
{/* Help text */}
<p className="text-xs text-[var(--color-neo-text-secondary)] mt-2">
<p className="text-xs text-neo-text-secondary mt-2">
Press Enter to send. Drag & drop or click <Paperclip size={12} className="inline" /> to attach images.
</p>
</div>
@@ -359,7 +360,7 @@ export function ExpandProjectChat({
{/* Completion footer */}
{isComplete && (
<div className="p-4 border-t-3 border-[var(--color-neo-border)] bg-[var(--color-neo-done)]">
<div className="p-4 border-t-3 border-neo-border bg-neo-done text-neo-text-on-bright">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<CheckCircle2 size={20} />
@@ -369,7 +370,7 @@ export function ExpandProjectChat({
</div>
<button
onClick={() => onComplete(featuresCreated)}
className="neo-btn bg-white"
className="neo-btn bg-neo-card"
>
Close
</button>

View File

@@ -7,16 +7,17 @@ interface FeatureCardProps {
isInProgress?: boolean
}
// Generate consistent color for category
// Generate consistent color for category using CSS variable references
// These map to the --color-neo-category-* variables defined in globals.css
function getCategoryColor(category: string): string {
const colors = [
'#ff006e', // pink
'#00b4d8', // cyan
'#70e000', // green
'#ffd60a', // yellow
'#ff5400', // orange
'#8338ec', // purple
'#3a86ff', // blue
'var(--color-neo-category-pink)',
'var(--color-neo-category-cyan)',
'var(--color-neo-category-green)',
'var(--color-neo-category-yellow)',
'var(--color-neo-category-orange)',
'var(--color-neo-category-purple)',
'var(--color-neo-category-blue)',
]
let hash = 0
@@ -36,18 +37,18 @@ export function FeatureCard({ feature, onClick, isInProgress }: FeatureCardProps
className={`
w-full text-left neo-card p-4 cursor-pointer
${isInProgress ? 'animate-pulse-neo' : ''}
${feature.passes ? 'border-[var(--color-neo-done)]' : ''}
${feature.passes ? 'border-neo-done' : ''}
`}
>
{/* Header */}
<div className="flex items-start justify-between gap-2 mb-2">
<span
className="neo-badge"
style={{ backgroundColor: categoryColor, color: 'white' }}
style={{ backgroundColor: categoryColor, color: 'var(--color-neo-text-on-bright)' }}
>
{feature.category}
</span>
<span className="font-mono text-sm text-[var(--color-neo-text-secondary)]">
<span className="font-mono text-sm text-neo-text-secondary">
#{feature.priority}
</span>
</div>
@@ -58,7 +59,7 @@ export function FeatureCard({ feature, onClick, isInProgress }: FeatureCardProps
</h3>
{/* Description */}
<p className="text-sm text-[var(--color-neo-text-secondary)] line-clamp-2 mb-3">
<p className="text-sm text-neo-text-secondary line-clamp-2 mb-3">
{feature.description}
</p>
@@ -66,18 +67,18 @@ export function FeatureCard({ feature, onClick, isInProgress }: FeatureCardProps
<div className="flex items-center gap-2 text-sm">
{isInProgress ? (
<>
<Loader2 size={16} className="animate-spin text-[var(--color-neo-progress)]" />
<span className="text-[var(--color-neo-progress)] font-bold">Processing...</span>
<Loader2 size={16} className="animate-spin text-neo-progress" />
<span className="text-neo-progress font-bold">Processing...</span>
</>
) : feature.passes ? (
<>
<CheckCircle2 size={16} className="text-[var(--color-neo-done)]" />
<span className="text-[var(--color-neo-done)] font-bold">Complete</span>
<CheckCircle2 size={16} className="text-neo-done" />
<span className="text-neo-done font-bold">Complete</span>
</>
) : (
<>
<Circle size={16} className="text-[var(--color-neo-text-secondary)]" />
<span className="text-[var(--color-neo-text-secondary)]">Pending</span>
<Circle size={16} className="text-neo-text-secondary" />
<span className="text-neo-text-secondary">Pending</span>
</>
)}
</div>

View File

@@ -4,6 +4,26 @@ import { useSkipFeature, useDeleteFeature } from '../hooks/useProjects'
import { EditFeatureForm } from './EditFeatureForm'
import type { Feature } from '../lib/types'
// Generate consistent color for category (matches FeatureCard pattern)
function getCategoryColor(category: string): string {
const colors = [
'#ff006e', // pink (accent)
'#00b4d8', // cyan (progress)
'#70e000', // green (done)
'#ffd60a', // yellow (pending)
'#ff5400', // orange (danger)
'#8338ec', // purple
'#3a86ff', // blue
]
let hash = 0
for (let i = 0; i < category.length; i++) {
hash = category.charCodeAt(i) + ((hash << 5) - hash)
}
return colors[Math.abs(hash) % colors.length]
}
interface FeatureModalProps {
feature: Feature
projectName: string
@@ -59,7 +79,10 @@ export function FeatureModal({ feature, projectName, onClose }: FeatureModalProp
{/* Header */}
<div className="flex items-start justify-between p-6 border-b-3 border-[var(--color-neo-border)]">
<div>
<span className="neo-badge bg-[var(--color-neo-accent)] text-white mb-2">
<span
className="neo-badge mb-2"
style={{ backgroundColor: getCategoryColor(feature.category), color: 'var(--color-neo-text-on-bright)' }}
>
{feature.category}
</span>
<h2 className="font-display text-2xl font-bold">
@@ -78,12 +101,12 @@ export function FeatureModal({ feature, projectName, onClose }: FeatureModalProp
<div className="p-6 space-y-6">
{/* Error Message */}
{error && (
<div className="flex items-center gap-3 p-4 bg-[var(--color-neo-danger)] text-white border-3 border-[var(--color-neo-border)]">
<div className="flex items-center gap-3 p-4 bg-[var(--color-neo-error-bg)] text-[var(--color-neo-error-text)] border-3 border-[var(--color-neo-error-border)]">
<AlertCircle size={20} />
<span>{error}</span>
<button
onClick={() => setError(null)}
className="ml-auto"
className="ml-auto hover:opacity-70 transition-opacity"
>
<X size={16} />
</button>

View File

@@ -139,10 +139,10 @@ export function FolderBrowser({ onSelect, onCancel, initialPath }: FolderBrowser
return (
<div className="flex flex-col h-full max-h-[70vh]">
{/* Header with breadcrumb navigation */}
<div className="flex-shrink-0 p-4 border-b-3 border-[var(--color-neo-border)] bg-white">
<div className="flex-shrink-0 p-4 border-b-3 border-[var(--color-neo-border)] bg-[var(--color-neo-card)]">
<div className="flex items-center gap-2 mb-3">
<Folder size={20} className="text-[var(--color-neo-progress)]" />
<span className="font-bold text-[#1a1a1a]">Select Project Folder</span>
<span className="font-bold text-[var(--color-neo-text)]">Select Project Folder</span>
</div>
{/* Breadcrumb navigation */}
@@ -159,11 +159,11 @@ export function FolderBrowser({ onSelect, onCancel, initialPath }: FolderBrowser
{breadcrumbs.map((crumb, index) => (
<div key={crumb.path} className="flex items-center">
{index > 0 && <ChevronRight size={14} className="text-gray-400 mx-1" />}
{index > 0 && <ChevronRight size={14} className="text-[var(--color-neo-text-muted)] mx-1" />}
<button
onClick={() => handleNavigate(crumb.path)}
className={`
px-2 py-1 rounded text-[#1a1a1a]
px-2 py-1 rounded text-[var(--color-neo-text)]
hover:bg-[var(--color-neo-bg)]
${index === breadcrumbs.length - 1 ? 'font-bold' : ''}
`}
@@ -187,7 +187,7 @@ export function FolderBrowser({ onSelect, onCancel, initialPath }: FolderBrowser
className={`
neo-btn neo-btn-ghost py-1 px-2 text-sm
flex items-center gap-1
${currentPath?.startsWith(drive.letter) ? 'bg-[var(--color-neo-progress)] text-white' : ''}
${currentPath?.startsWith(drive.letter) ? 'bg-[var(--color-neo-progress)] text-[var(--color-neo-text-on-bright)]' : ''}
`}
>
<HardDrive size={14} />
@@ -199,7 +199,7 @@ export function FolderBrowser({ onSelect, onCancel, initialPath }: FolderBrowser
)}
{/* Directory listing */}
<div className="flex-1 overflow-y-auto p-2 bg-white">
<div className="flex-1 overflow-y-auto p-2 bg-[var(--color-neo-card)]">
{isLoading ? (
<div className="flex items-center justify-center p-8">
<Loader2 size={24} className="animate-spin text-[var(--color-neo-progress)]" />
@@ -238,9 +238,9 @@ export function FolderBrowser({ onSelect, onCancel, initialPath }: FolderBrowser
) : (
<Folder size={18} className="text-[var(--color-neo-pending)] flex-shrink-0" />
)}
<span className="truncate flex-1 text-[#1a1a1a]">{entry.name}</span>
<span className="truncate flex-1 text-[var(--color-neo-text)]">{entry.name}</span>
{entry.has_children && (
<ChevronRight size={14} className="ml-auto text-gray-400 flex-shrink-0" />
<ChevronRight size={14} className="ml-auto text-[var(--color-neo-text-muted)] flex-shrink-0" />
)}
</button>
))}
@@ -299,11 +299,11 @@ export function FolderBrowser({ onSelect, onCancel, initialPath }: FolderBrowser
</div>
{/* Footer with selected path and actions */}
<div className="flex-shrink-0 p-4 border-t-3 border-[var(--color-neo-border)] bg-white">
<div className="flex-shrink-0 p-4 border-t-3 border-[var(--color-neo-border)] bg-[var(--color-neo-card)]">
{/* Selected path display */}
<div className="mb-3 p-2 bg-[var(--color-neo-bg)] rounded border-2 border-[var(--color-neo-border)]">
<div className="text-xs text-[#4a4a4a] mb-1">Selected path:</div>
<div className="font-mono text-sm truncate text-[#1a1a1a]">{selectedPath || 'No folder selected'}</div>
<div className="text-xs text-[var(--color-neo-text-secondary)] mb-1">Selected path:</div>
<div className="font-mono text-sm truncate text-[var(--color-neo-text)]">{selectedPath || 'No folder selected'}</div>
{selectedPath && (
<div className="text-xs text-[var(--color-neo-text-secondary)] mt-2 italic">
This folder will contain all project files

View File

@@ -40,9 +40,9 @@ export function KanbanColumn({
style={{ backgroundColor: colorMap[color] }}
>
<div className="flex items-center justify-between">
<h2 className="font-display text-lg font-bold uppercase flex items-center gap-2 text-[var(--color-neo-text)]">
<h2 className="font-display text-lg font-bold uppercase flex items-center gap-2 text-[var(--color-neo-text-on-bright)]">
{title}
<span className="neo-badge bg-white text-[var(--color-neo-text)]">{count}</span>
<span className="neo-badge bg-[var(--color-neo-card)] text-[var(--color-neo-text)]">{count}</span>
</h2>
{(onAddFeature || onExpandProject) && (
<div className="flex items-center gap-2">
@@ -58,7 +58,7 @@ export function KanbanColumn({
{onExpandProject && showExpandButton && (
<button
onClick={onExpandProject}
className="neo-btn bg-[var(--color-neo-progress)] text-black text-sm py-1.5 px-2"
className="neo-btn bg-[var(--color-neo-progress)] text-[var(--color-neo-text-on-bright)] text-sm py-1.5 px-2"
title="Expand project with AI (E)"
>
<Sparkles size={16} />

View File

@@ -212,10 +212,10 @@ export function NewProjectModal({
<div className="flex items-center gap-3">
<Folder size={24} className="text-[var(--color-neo-progress)]" />
<div>
<h2 className="font-display font-bold text-xl text-[#1a1a1a]">
<h2 className="font-display font-bold text-xl text-[var(--color-neo-text)]">
Select Project Location
</h2>
<p className="text-sm text-[#4a4a4a]">
<p className="text-sm text-[var(--color-neo-text-secondary)]">
Select the folder to use for project <span className="font-bold font-mono">{projectName}</span>. Create a new folder or choose an existing one.
</p>
</div>
@@ -248,7 +248,7 @@ export function NewProjectModal({
>
{/* Header */}
<div className="flex items-center justify-between p-4 border-b-3 border-[var(--color-neo-border)]">
<h2 className="font-display font-bold text-xl text-[#1a1a1a]">
<h2 className="font-display font-bold text-xl text-[var(--color-neo-text)]">
{step === 'name' && 'Create New Project'}
{step === 'method' && 'Choose Setup Method'}
{step === 'complete' && 'Project Created!'}
@@ -267,7 +267,7 @@ export function NewProjectModal({
{step === 'name' && (
<form onSubmit={handleNameSubmit}>
<div className="mb-6">
<label className="block font-bold mb-2 text-[#1a1a1a]">
<label className="block font-bold mb-2 text-[var(--color-neo-text)]">
Project Name
</label>
<input
@@ -285,7 +285,7 @@ export function NewProjectModal({
</div>
{error && (
<div className="mb-4 p-3 bg-[var(--color-neo-danger)] text-white text-sm border-2 border-[var(--color-neo-border)]">
<div className="mb-4 p-3 bg-[var(--color-neo-error-bg)] text-[var(--color-neo-error-text)] text-sm border-3 border-[var(--color-neo-error-border)]">
{error}
</div>
)}
@@ -315,25 +315,27 @@ export function NewProjectModal({
<button
onClick={() => handleMethodSelect('claude')}
disabled={createProject.isPending}
className={`
className="
w-full text-left p-4
border-3 border-[var(--color-neo-border)]
bg-white
shadow-[4px_4px_0px_rgba(0,0,0,1)]
bg-[var(--color-neo-card)]
hover:translate-x-[-2px] hover:translate-y-[-2px]
hover:shadow-[6px_6px_0px_rgba(0,0,0,1)]
transition-all duration-150
disabled:opacity-50 disabled:cursor-not-allowed
`}
neo-card
"
>
<div className="flex items-start gap-4">
<div className="p-2 bg-[var(--color-neo-progress)] border-2 border-[var(--color-neo-border)] shadow-[2px_2px_0px_rgba(0,0,0,1)]">
<Bot size={24} className="text-white" />
<div
className="p-2 bg-[var(--color-neo-progress)] border-2 border-[var(--color-neo-border)]"
style={{ boxShadow: 'var(--shadow-neo-sm)' }}
>
<Bot size={24} className="text-[var(--color-neo-text-on-bright)]" />
</div>
<div className="flex-1">
<div className="flex items-center gap-2">
<span className="font-bold text-lg text-[#1a1a1a]">Create with Claude</span>
<span className="neo-badge bg-[var(--color-neo-done)] text-xs">
<span className="font-bold text-lg text-[var(--color-neo-text)]">Create with Claude</span>
<span className="neo-badge bg-[var(--color-neo-done)] text-[var(--color-neo-text-on-bright)] text-xs">
Recommended
</span>
</div>
@@ -348,23 +350,25 @@ export function NewProjectModal({
<button
onClick={() => handleMethodSelect('manual')}
disabled={createProject.isPending}
className={`
className="
w-full text-left p-4
border-3 border-[var(--color-neo-border)]
bg-white
shadow-[4px_4px_0px_rgba(0,0,0,1)]
bg-[var(--color-neo-card)]
hover:translate-x-[-2px] hover:translate-y-[-2px]
hover:shadow-[6px_6px_0px_rgba(0,0,0,1)]
transition-all duration-150
disabled:opacity-50 disabled:cursor-not-allowed
`}
neo-card
"
>
<div className="flex items-start gap-4">
<div className="p-2 bg-[var(--color-neo-pending)] border-2 border-[var(--color-neo-border)] shadow-[2px_2px_0px_rgba(0,0,0,1)]">
<FileEdit size={24} />
<div
className="p-2 bg-[var(--color-neo-pending)] border-2 border-[var(--color-neo-border)]"
style={{ boxShadow: 'var(--shadow-neo-sm)' }}
>
<FileEdit size={24} className="text-[var(--color-neo-text-on-bright)]" />
</div>
<div className="flex-1">
<span className="font-bold text-lg text-[#1a1a1a]">Edit Templates Manually</span>
<span className="font-bold text-lg text-[var(--color-neo-text)]">Edit Templates Manually</span>
<p className="text-sm text-[var(--color-neo-text-secondary)] mt-1">
Edit the template files directly. Best for developers who want full control.
</p>
@@ -374,7 +378,7 @@ export function NewProjectModal({
</div>
{error && (
<div className="mt-4 p-3 bg-[var(--color-neo-danger)] text-white text-sm border-2 border-[var(--color-neo-border)]">
<div className="mt-4 p-3 bg-[var(--color-neo-error-bg)] text-[var(--color-neo-error-text)] text-sm border-3 border-[var(--color-neo-error-border)]">
{error}
</div>
)}
@@ -402,8 +406,11 @@ export function NewProjectModal({
{/* Step 3: Complete */}
{step === 'complete' && (
<div className="text-center py-8">
<div className="inline-flex items-center justify-center w-16 h-16 bg-[var(--color-neo-done)] border-3 border-[var(--color-neo-border)] shadow-[4px_4px_0px_rgba(0,0,0,1)] mb-4">
<CheckCircle2 size={32} />
<div
className="inline-flex items-center justify-center w-16 h-16 bg-[var(--color-neo-done)] border-3 border-[var(--color-neo-border)] mb-4"
style={{ boxShadow: 'var(--shadow-neo-md)' }}
>
<CheckCircle2 size={32} className="text-[var(--color-neo-text-on-bright)]" />
</div>
<h3 className="font-display font-bold text-xl mb-2">
{projectName}

View File

@@ -36,11 +36,13 @@ export function ProgressDashboard({
{/* Large Percentage */}
<div className="text-center mb-6">
<span className="font-display text-6xl font-bold">
{percentage.toFixed(1)}
</span>
<span className="font-display text-3xl font-bold text-[var(--color-neo-text-secondary)]">
%
<span className="inline-flex items-baseline">
<span className="font-display text-6xl font-bold">
{percentage.toFixed(1)}
</span>
<span className="font-display text-3xl font-bold text-[var(--color-neo-text-secondary)]">
%
</span>
</span>
</div>

View File

@@ -65,7 +65,7 @@ export function ProjectSelector({
{/* Dropdown Trigger */}
<button
onClick={() => setIsOpen(!isOpen)}
className="neo-btn bg-white text-[var(--color-neo-text)] min-w-[200px] justify-between"
className="neo-btn bg-[var(--color-neo-card)] text-[var(--color-neo-text)] min-w-[200px] justify-between"
disabled={isLoading}
>
{isLoading ? (
@@ -108,7 +108,7 @@ export function ProjectSelector({
key={project.name}
className={`flex items-center ${
project.name === selectedProject
? 'bg-[var(--color-neo-pending)]'
? 'bg-[var(--color-neo-pending)] text-[var(--color-neo-text-on-bright)]'
: ''
}`}
>

View File

@@ -93,11 +93,11 @@ export function QuestionOptions({
{questions.map((q, questionIdx) => (
<div
key={questionIdx}
className="neo-card p-4 bg-white"
className="neo-card p-4 bg-[var(--color-neo-card)]"
>
{/* Question header */}
<div className="flex items-center gap-3 mb-4">
<span className="neo-badge bg-[var(--color-neo-accent)] text-white">
<span className="neo-badge bg-[var(--color-neo-accent)] text-[var(--color-neo-text-on-bright)]">
{q.header}
</span>
<span className="font-bold text-[var(--color-neo-text)]">
@@ -126,11 +126,24 @@ export function QuestionOptions({
transition-all duration-150
${
isSelected
? 'bg-[var(--color-neo-pending)] shadow-[2px_2px_0px_rgba(0,0,0,1)] translate-x-[1px] translate-y-[1px]'
: 'bg-white shadow-[4px_4px_0px_rgba(0,0,0,1)] hover:translate-x-[-1px] hover:translate-y-[-1px] hover:shadow-[5px_5px_0px_rgba(0,0,0,1)]'
? 'bg-[var(--color-neo-pending)] translate-x-[1px] translate-y-[1px]'
: 'bg-[var(--color-neo-card)] hover:translate-x-[-1px] hover:translate-y-[-1px]'
}
disabled:opacity-50 disabled:cursor-not-allowed
`}
style={{
boxShadow: isSelected ? 'var(--shadow-neo-sm)' : 'var(--shadow-neo-md)',
}}
onMouseEnter={(e) => {
if (!isSelected && !disabled) {
e.currentTarget.style.boxShadow = 'var(--shadow-neo-lg)'
}
}}
onMouseLeave={(e) => {
if (!isSelected && !disabled) {
e.currentTarget.style.boxShadow = 'var(--shadow-neo-md)'
}
}}
>
<div className="flex items-start gap-2">
{/* Checkbox/Radio indicator */}
@@ -140,7 +153,7 @@ export function QuestionOptions({
border-2 border-[var(--color-neo-border)]
flex items-center justify-center
${q.multiSelect ? '' : 'rounded-full'}
${isSelected ? 'bg-[var(--color-neo-done)]' : 'bg-white'}
${isSelected ? 'bg-[var(--color-neo-done)]' : 'bg-[var(--color-neo-card)]'}
`}
>
{isSelected && <Check size={12} strokeWidth={3} />}
@@ -169,11 +182,24 @@ export function QuestionOptions({
transition-all duration-150
${
showCustomInput[String(questionIdx)]
? 'bg-[var(--color-neo-pending)] shadow-[2px_2px_0px_rgba(0,0,0,1)] translate-x-[1px] translate-y-[1px]'
: 'bg-white shadow-[4px_4px_0px_rgba(0,0,0,1)] hover:translate-x-[-1px] hover:translate-y-[-1px] hover:shadow-[5px_5px_0px_rgba(0,0,0,1)]'
? 'bg-[var(--color-neo-pending)] translate-x-[1px] translate-y-[1px]'
: 'bg-[var(--color-neo-card)] hover:translate-x-[-1px] hover:translate-y-[-1px]'
}
disabled:opacity-50 disabled:cursor-not-allowed
`}
style={{
boxShadow: showCustomInput[String(questionIdx)] ? 'var(--shadow-neo-sm)' : 'var(--shadow-neo-md)',
}}
onMouseEnter={(e) => {
if (!showCustomInput[String(questionIdx)] && !disabled) {
e.currentTarget.style.boxShadow = 'var(--shadow-neo-lg)'
}
}}
onMouseLeave={(e) => {
if (!showCustomInput[String(questionIdx)] && !disabled) {
e.currentTarget.style.boxShadow = 'var(--shadow-neo-md)'
}
}}
>
<div className="flex items-start gap-2">
<div
@@ -182,7 +208,7 @@ export function QuestionOptions({
border-2 border-[var(--color-neo-border)]
flex items-center justify-center
${q.multiSelect ? '' : 'rounded-full'}
${showCustomInput[String(questionIdx)] ? 'bg-[var(--color-neo-done)]' : 'bg-white'}
${showCustomInput[String(questionIdx)] ? 'bg-[var(--color-neo-done)]' : 'bg-[var(--color-neo-card)]'}
`}
>
{showCustomInput[String(questionIdx)] && <Check size={12} strokeWidth={3} />}

View File

@@ -115,14 +115,14 @@ export function SettingsModal({ onClose }: SettingsModalProps) {
{/* Error State */}
{isError && (
<div className="p-4 bg-[var(--color-neo-danger)] text-white border-3 border-[var(--color-neo-border)] mb-4">
<div className="p-4 bg-[var(--color-neo-error-bg)] text-[var(--color-neo-error-text)] border-3 border-[var(--color-neo-error-border)] mb-4">
<div className="flex items-center gap-2">
<AlertCircle size={18} />
<span>Failed to load settings</span>
</div>
<button
onClick={() => refetch()}
className="mt-2 underline text-sm"
className="mt-2 underline text-sm hover:opacity-70 transition-opacity"
>
Retry
</button>
@@ -152,7 +152,7 @@ export function SettingsModal({ onClose }: SettingsModalProps) {
className={`relative w-14 h-8 rounded-none border-3 border-[var(--color-neo-border)] transition-colors ${
settings.yolo_mode
? 'bg-[var(--color-neo-pending)]'
: 'bg-white'
: 'bg-[var(--color-neo-card)]'
} ${isSaving ? 'opacity-50 cursor-not-allowed' : ''}`}
role="switch"
aria-checked={settings.yolo_mode}
@@ -189,8 +189,8 @@ export function SettingsModal({ onClose }: SettingsModalProps) {
aria-checked={settings.model === model.id}
className={`flex-1 py-3 px-4 font-display font-bold text-sm transition-colors ${
settings.model === model.id
? 'bg-[var(--color-neo-accent)] text-white'
: 'bg-white text-[var(--color-neo-text)] hover:bg-gray-100'
? 'bg-[var(--color-neo-accent)] text-[var(--color-neo-text-on-bright)]'
: 'bg-[var(--color-neo-card)] text-[var(--color-neo-text)] hover:bg-[var(--color-neo-hover-subtle)]'
} ${isSaving ? 'opacity-50 cursor-not-allowed' : ''}`}
>
{model.name}
@@ -201,7 +201,7 @@ export function SettingsModal({ onClose }: SettingsModalProps) {
{/* Update Error */}
{updateSettings.isError && (
<div className="p-3 bg-red-50 border-3 border-red-200 text-red-700 text-sm">
<div className="p-3 bg-[var(--color-neo-error-bg)] border-3 border-[var(--color-neo-error-border)] text-[var(--color-neo-error-text)] text-sm">
Failed to save settings. Please try again.
</div>
)}

View File

@@ -108,7 +108,7 @@ export function SetupWizard({ onComplete }: SetupWizardProps) {
{/* Error Message */}
{(healthError || setupError) && (
<div className="mt-6 p-4 bg-[var(--color-neo-danger)] text-white border-3 border-[var(--color-neo-border)]">
<div className="mt-6 p-4 bg-[var(--color-neo-error-bg)] text-[var(--color-neo-error-text)] border-3 border-[var(--color-neo-error-border)]">
<p className="font-bold mb-2">Setup Error</p>
<p className="text-sm">
{healthError

View File

@@ -207,9 +207,9 @@ export function SpecCreationChat({
return (
<div className="flex flex-col h-full bg-[var(--color-neo-bg)]">
{/* Header */}
<div className="flex items-center justify-between p-4 border-b-3 border-[var(--color-neo-border)] bg-white">
<div className="flex items-center justify-between p-4 border-b-3 border-[var(--color-neo-border)] bg-[var(--color-neo-card)]">
<div className="flex items-center gap-3">
<h2 className="font-display font-bold text-lg text-[#1a1a1a]">
<h2 className="font-display font-bold text-lg text-[var(--color-neo-text)]">
Create Spec: {projectName}
</h2>
<ConnectionIndicator />
@@ -245,12 +245,12 @@ export function SpecCreationChat({
{/* Error banner */}
{error && (
<div className="flex items-center gap-2 p-3 bg-[var(--color-neo-danger)] text-white border-b-3 border-[var(--color-neo-border)]">
<div className="flex items-center gap-2 p-3 bg-[var(--color-neo-error-bg)] text-[var(--color-neo-error-text)] border-b-3 border-[var(--color-neo-error-border)]">
<AlertCircle size={16} />
<span className="flex-1 text-sm">{error}</span>
<button
onClick={() => setError(null)}
className="p-1 hover:bg-white/20 rounded"
className="p-1 hover:opacity-70 transition-opacity rounded"
>
<X size={14} />
</button>
@@ -304,7 +304,7 @@ export function SpecCreationChat({
{/* Input area */}
{!isComplete && (
<div
className="p-4 border-t-3 border-[var(--color-neo-border)] bg-white"
className="p-4 border-t-3 border-[var(--color-neo-border)] bg-[var(--color-neo-card)]"
onDrop={handleDrop}
onDragOver={handleDragOver}
>
@@ -314,7 +314,8 @@ export function SpecCreationChat({
{pendingAttachments.map((attachment) => (
<div
key={attachment.id}
className="relative group border-2 border-[var(--color-neo-border)] p-1 bg-white shadow-[2px_2px_0px_rgba(0,0,0,1)]"
className="relative group border-2 border-[var(--color-neo-border)] p-1 bg-[var(--color-neo-card)]"
style={{ boxShadow: 'var(--shadow-neo-sm)' }}
>
<img
src={attachment.previewUrl}
@@ -323,7 +324,7 @@ export function SpecCreationChat({
/>
<button
onClick={() => handleRemoveAttachment(attachment.id)}
className="absolute -top-2 -right-2 bg-[var(--color-neo-danger)] text-white rounded-full p-0.5 border-2 border-[var(--color-neo-border)] hover:scale-110 transition-transform"
className="absolute -top-2 -right-2 bg-[var(--color-neo-danger)] text-[var(--color-neo-text-on-bright)] rounded-full p-0.5 border-2 border-[var(--color-neo-border)] hover:scale-110 transition-transform"
title="Remove attachment"
>
<X size={12} />
@@ -409,22 +410,22 @@ export function SpecCreationChat({
<div className="flex items-center gap-2">
{initializerStatus === 'starting' ? (
<>
<Loader2 size={20} className="animate-spin" />
<span className="font-bold">
<Loader2 size={20} className="animate-spin text-[var(--color-neo-text-on-bright)]" />
<span className="font-bold text-[var(--color-neo-text-on-bright)]">
Starting agent{yoloEnabled ? ' (YOLO mode)' : ''}...
</span>
</>
) : initializerStatus === 'error' ? (
<>
<AlertCircle size={20} className="text-white" />
<span className="font-bold text-white">
<AlertCircle size={20} className="text-[var(--color-neo-text-on-bright)]" />
<span className="font-bold text-[var(--color-neo-text-on-bright)]">
{initializerError || 'Failed to start agent'}
</span>
</>
) : (
<>
<CheckCircle2 size={20} />
<span className="font-bold">Specification created successfully!</span>
<CheckCircle2 size={20} className="text-[var(--color-neo-text-on-bright)]" />
<span className="font-bold text-[var(--color-neo-text-on-bright)]">Specification created successfully!</span>
</>
)}
</div>
@@ -432,7 +433,7 @@ export function SpecCreationChat({
{initializerStatus === 'error' && onRetryInitializer && (
<button
onClick={onRetryInitializer}
className="neo-btn bg-white"
className="neo-btn bg-[var(--color-neo-card)]"
>
<RotateCcw size={14} />
Retry
@@ -444,7 +445,7 @@ export function SpecCreationChat({
<button
onClick={() => setYoloEnabled(!yoloEnabled)}
className={`neo-btn text-sm py-2 px-3 ${
yoloEnabled ? 'neo-btn-warning' : 'bg-white'
yoloEnabled ? 'neo-btn-warning' : 'bg-[var(--color-neo-card)]'
}`}
title="YOLO Mode: Skip testing for rapid prototyping"
>

View File

@@ -165,7 +165,7 @@ export function TerminalTabs({
${
activeTerminalId === terminal.id
? 'bg-neo-progress text-black'
: 'bg-[#3a3a3a] text-white hover:bg-[#4a4a4a]'
: 'bg-[#3a3a3a] text-white hover:bg-neo-text-secondary'
}
`}
onClick={() => onSelect(terminal.id)}
@@ -180,7 +180,7 @@ export function TerminalTabs({
onChange={(e) => setEditValue(e.target.value)}
onBlur={submitEdit}
onKeyDown={handleKeyDown}
className="bg-white text-black px-1 py-0 text-sm font-mono border-2 border-black w-24 outline-none"
className="bg-neo-card text-neo-text px-1 py-0 text-sm font-mono border-2 border-black w-24 outline-none"
onClick={(e) => e.stopPropagation()}
/>
) : (
@@ -212,7 +212,7 @@ export function TerminalTabs({
{/* Add new terminal button */}
<button
onClick={onCreate}
className="flex items-center justify-center w-8 h-8 border-2 border-black bg-[#3a3a3a] text-white hover:bg-[#4a4a4a] transition-colors"
className="flex items-center justify-center w-8 h-8 border-2 border-black bg-[#3a3a3a] text-white hover:bg-neo-text-secondary transition-colors"
title="New terminal"
>
<Plus className="w-4 h-4" />
@@ -222,8 +222,8 @@ export function TerminalTabs({
{contextMenu.visible && (
<div
ref={contextMenuRef}
className="fixed z-50 bg-white border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] py-1 min-w-[120px]"
style={{ left: contextMenu.x, top: contextMenu.y }}
className="fixed z-50 bg-neo-card border-2 border-[var(--color-neo-border)] py-1 min-w-[120px]"
style={{ left: contextMenu.x, top: contextMenu.y, boxShadow: 'var(--shadow-neo-md)' }}
>
<button
onClick={handleContextMenuRename}