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
61 changes: 59 additions & 2 deletions desktop/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
"build:main": "tsc -p electron/tsconfig.json"
},
"dependencies": {
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@tanstack/react-query": "^5.90.12",
"axios": "^1.13.2",
"class-variance-authority": "^0.7.1",
Expand Down
89 changes: 25 additions & 64 deletions desktop/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ import { useState, useEffect } from 'react'
import { LayoutDashboard, Terminal, Activity, Map, Lightbulb, FileClock, Settings, Plus, Github, GitBranch } from 'lucide-react'
import { cn } from '@/lib/utils'
import { getSystemStatus, type SystemStatus } from '@/lib/api'
import { KanbanBoard } from '@/components/KanbanBoard'
import { TaskWizard } from '@/components/TaskWizard'

export default function App() {
const [activeTab, setActiveTab] = useState('kanban')
const [systemStatus, setSystemStatus] = useState<SystemStatus | null>(null)
const [isWizardOpen, setIsWizardOpen] = useState(false)
const [kanbanKey, setKanbanKey] = useState(0) // Used to force refresh Kanban after creation

useEffect(() => {
const fetchStatus = async () => {
Expand All @@ -18,6 +22,10 @@ export default function App() {
return () => clearInterval(interval)
}, [])

const handleTaskCreated = () => {
setKanbanKey(prev => prev + 1)
}

return (
<div className="flex h-screen bg-zinc-950 text-zinc-100 font-sans overflow-hidden">
{/* Sidebar */}
Expand Down Expand Up @@ -57,38 +65,45 @@ export default function App() {
{/* Bottom Actions */}
<div className="p-4 border-t border-zinc-800 space-y-2">
<SidebarItem icon={<Settings size={16} />} label="Settings" isActive={activeTab === 'settings'} onClick={() => setActiveTab('settings')} />
<button className="w-full flex items-center justify-center gap-2 bg-yellow-400 hover:bg-yellow-500 text-black font-semibold py-2 px-4 rounded-md transition-colors text-sm">
<button
onClick={() => setIsWizardOpen(true)}
className="w-full flex items-center justify-center gap-2 bg-yellow-400 hover:bg-yellow-500 text-black font-semibold py-2 px-4 rounded-md transition-colors text-sm shadow-lg shadow-yellow-400/10 active:scale-95 duration-100"
>
<Plus size={16} /> New Task
</button>
</div>
</aside>

{/* Main Content */}
<main className="flex-1 overflow-auto bg-zinc-900/50 p-6">
<main className="flex-1 overflow-auto bg-zinc-900/10 p-6">
<header className="flex justify-between items-center mb-8">
<div>
<h1 className="text-2xl font-bold">{activeTab.charAt(0).toUpperCase() + activeTab.slice(1).replace('-', ' ')}</h1>
<h1 className="text-2xl font-bold tracking-tight">{activeTab.charAt(0).toUpperCase() + activeTab.slice(1).replace('-', ' ')}</h1>
<p className="text-zinc-500 text-sm flex items-center gap-4 mt-1">
<span className={cn("flex items-center gap-1.5", systemStatus?.status === 'online' ? "text-green-500" : "text-red-500")}>
<span className={cn("w-2 h-2 rounded-full", systemStatus?.status === 'online' ? "bg-green-500" : "bg-red-500 animate-pulse")} />
{systemStatus?.status === 'online' ? 'System Online' : (systemStatus?.status || 'Connecting...')}
</span>
{systemStatus && (
<>
<span>•</span>
<span>{systemStatus.agents_online} Agents Online</span>
<span>•</span>
<span>{systemStatus.missions_active} Active Missions</span>
<span className="opacity-20">|</span>
<span className="flex items-center gap-1"><Terminal size={12} className="opacity-50" /> {systemStatus.agents_online} Agents Online</span>
<span className="opacity-20">|</span>
<span className="flex items-center gap-1"><Plus size={12} className="opacity-50" /> {systemStatus.missions_active} Active Missions</span>
</>
)}
</p>
</div>
</header>

{activeTab === 'kanban' && <KanbanView />}
{activeTab === 'terminals' && <div className="text-zinc-500">Agent Terminals Placeholder</div>}

{activeTab === 'kanban' && <KanbanBoard key={kanbanKey} />}
{activeTab === 'terminals' && <div className="text-zinc-500 bg-zinc-900/50 p-12 rounded-2xl border border-dashed border-zinc-800 text-center">Agent Terminals placeholder - Coming soon in Issue 7</div>}

<TaskWizard
isOpen={isWizardOpen}
onClose={() => setIsWizardOpen(false)}
onTaskCreated={handleTaskCreated}
/>
</main>
</div>
)
Expand All @@ -111,57 +126,3 @@ function SidebarItem({ icon, label, isActive, onClick }: { icon: any, label: str
)
}

function KanbanView() {
return (
<div className="grid grid-cols-4 gap-4 h-full">
<KanbanColumn title="Planning" count={1} status="neutral">
<KanbanCard title="Add Electron debugging" time="1h ago" tag="Pending" />
</KanbanColumn>
<KanbanColumn title="In Progress" count={0} status="blue" />
<KanbanColumn title="Review" count={0} status="yellow" />
<KanbanColumn title="Done" count={1} status="green">
<KanbanCard title="Implement virtualization" time="3h ago" tag="Complete" />
</KanbanColumn>
</div>
)
}

function KanbanColumn({ title, count, status, children }: { title: string, count: number, status: 'neutral' | 'blue' | 'yellow' | 'green', children?: React.ReactNode }) {
const statusColor = {
neutral: 'border-zinc-700',
blue: 'border-blue-500/50',
yellow: 'border-yellow-500/50',
green: 'border-green-500/50'
}[status]

return (
<div className={cn("flex flex-col h-full bg-zinc-900 rounded-xl border overflow-hidden", statusColor)}>
<div className={cn("p-3 border-b border-zinc-800 flex justify-between items-center")}>
<h4 className="font-semibold text-sm">{title} <span className="text-zinc-600 ml-1">{count}</span></h4>
{title === 'Planning' && <Plus size={14} className="text-zinc-500 cursor-pointer hover:text-white" />}
</div>
<div className="p-3 space-y-3 flex-1 bg-zinc-900/50">
{children ? children : (
<div className="h-full flex items-center justify-center border-2 border-dashed border-zinc-800 rounded-lg opacity-50">
<span className="text-xs text-zinc-600">Empty</span>
</div>
)}
</div>
</div>
)
}

function KanbanCard({ title, time, tag }: { title: string, time: string, tag: string }) {
return (
<div className="bg-zinc-950 p-4 rounded-lg border border-zinc-800 hover:border-zinc-600 cursor-pointer transition-colors group">
<div className="flex justify-between items-start mb-2">
<span className="text-xs font-medium text-zinc-400 group-hover:text-white transition-colors line-clamp-2 leading-snug">{title}</span>
<span className="text-[10px] uppercase font-bold tracking-wider bg-zinc-900 text-zinc-500 px-1.5 py-0.5 rounded">{tag}</span>
</div>
<div className="flex items-center gap-2 text-[10px] text-zinc-600 mt-3">
<FileClock size={10} />
{time}
</div>
</div>
)
}
Loading
Loading