Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ pnpm-lock.yaml
poetry.lock
Pipfile.lock

# ===================
# TypeScript
# ===================
*.tsbuildinfo

# ===================
# Misc
# ===================
Expand Down
2 changes: 1 addition & 1 deletion ui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<title>AutoCoder</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=DM+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Archivo+Black&family=Work+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
</head>
<body>
<div id="root"></div>
Expand Down
50 changes: 41 additions & 9 deletions ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import { useProjects, useFeatures, useAgentStatus, useSettings } from './hooks/u
import { useProjectWebSocket } from './hooks/useWebSocket'
import { useFeatureSound } from './hooks/useFeatureSound'
import { useCelebration } from './hooks/useCelebration'

const STORAGE_KEY = 'autocoder-selected-project'
import { ProjectSelector } from './components/ProjectSelector'
import { KanbanBoard } from './components/KanbanBoard'
import { AgentControl } from './components/AgentControl'
Expand All @@ -20,9 +18,12 @@ import { AssistantPanel } from './components/AssistantPanel'
import { ExpandProjectModal } from './components/ExpandProjectModal'
import { SettingsModal } from './components/SettingsModal'
import { DevServerControl } from './components/DevServerControl'
import { Loader2, Settings } from 'lucide-react'
import { Loader2, Settings, Moon, Sun } from 'lucide-react'
import type { Feature } from './lib/types'

const STORAGE_KEY = 'autocoder-selected-project'
const DARK_MODE_KEY = 'autocoder-dark-mode'

function App() {
// Initialize selected project from localStorage
const [selectedProject, setSelectedProject] = useState<string | null>(() => {
Expand All @@ -42,6 +43,13 @@ function App() {
const [assistantOpen, setAssistantOpen] = useState(false)
const [showSettings, setShowSettings] = useState(false)
const [isSpecCreating, setIsSpecCreating] = useState(false)
const [darkMode, setDarkMode] = useState(() => {
try {
return localStorage.getItem(DARK_MODE_KEY) === 'true'
} catch {
return false
}
})

const queryClient = useQueryClient()
const { data: projects, isLoading: projectsLoading } = useProjects()
Expand All @@ -50,6 +58,20 @@ function App() {
useAgentStatus(selectedProject) // Keep polling for status updates
const wsState = useProjectWebSocket(selectedProject)

// Apply dark mode class to document
useEffect(() => {
if (darkMode) {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
try {
localStorage.setItem(DARK_MODE_KEY, String(darkMode))
} catch {
// localStorage not available
}
}, [darkMode])

// Play sounds when features move between columns
useFeatureSound(features)

Expand Down Expand Up @@ -170,9 +192,9 @@ function App() {
}

return (
<div className="min-h-screen bg-[var(--color-neo-bg)]">
<div className="min-h-screen bg-neo-bg">
{/* Header */}
<header className="bg-[var(--color-neo-text)] text-white border-b-4 border-[var(--color-neo-border)]">
<header className="bg-neo-card text-neo-text border-b-4 border-neo-border">
<div className="max-w-7xl mx-auto px-4 py-4">
<div className="flex items-center justify-between">
{/* Logo and Title */}
Expand Down Expand Up @@ -215,14 +237,24 @@ function App() {
{/* GLM Mode Badge */}
{settings?.glm_mode && (
<span
className="px-2 py-1 text-xs font-bold bg-purple-500 text-white rounded border-2 border-black shadow-neo-sm"
className="px-2 py-1 text-xs font-bold bg-[var(--color-neo-glm)] text-white rounded border-2 border-neo-border shadow-neo-sm"
title="Using GLM API (configured via .env)"
>
GLM
</span>
)}
</>
)}

{/* Dark mode toggle - always visible */}
<button
onClick={() => setDarkMode(!darkMode)}
className="neo-btn text-sm py-2 px-3"
title="Toggle dark mode"
aria-label="Toggle dark mode"
>
{darkMode ? <Sun size={18} /> : <Moon size={18} />}
</button>
</div>
</div>
</div>
Expand All @@ -238,7 +270,7 @@ function App() {
<h2 className="font-display text-2xl font-bold mb-2">
Welcome to AutoCoder
</h2>
<p className="text-[var(--color-neo-text-secondary)] mb-4">
<p className="text-neo-text-secondary mb-4">
Select a project from the dropdown above or create a new one to get started.
</p>
</div>
Expand All @@ -265,11 +297,11 @@ function App() {
features.done.length === 0 &&
wsState.agentStatus === 'running' && (
<div className="neo-card p-8 text-center">
<Loader2 size={32} className="animate-spin mx-auto mb-4 text-[var(--color-neo-progress)]" />
<Loader2 size={32} className="animate-spin mx-auto mb-4 text-neo-progress" />
<h3 className="font-display font-bold text-xl mb-2">
Initializing Features...
</h3>
<p className="text-[var(--color-neo-text-secondary)]">
<p className="text-neo-text-secondary">
The agent is reading your spec and creating features. This may take a moment.
</p>
</div>
Expand Down
11 changes: 7 additions & 4 deletions ui/src/components/AddFeatureForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion ui/src/components/AssistantChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
3 changes: 1 addition & 2 deletions ui/src/components/AssistantFAB.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 12 additions & 9 deletions ui/src/components/AssistantPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,36 +31,39 @@ 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
onClick={onClose}
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"
Expand Down
29 changes: 18 additions & 11 deletions ui/src/components/ChatMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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)',
},
}

Expand All @@ -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} />
Expand All @@ -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>
)}

Expand All @@ -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
Expand Down Expand Up @@ -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}
Expand Down Expand Up @@ -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>
Expand Down
8 changes: 4 additions & 4 deletions ui/src/components/ConfirmDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Loading