import React, { useState, useEffect, useRef } from 'react';
import {
Command,
Shield,
Zap,
Layout,
Settings,
ChevronRight,
Play,
CheckCircle2,
Circle,
AlertCircle,
FileText,
X,
Terminal,
Smartphone,
HardDrive,
Cpu,
MoreHorizontal,
ArrowRight,
Clock,
Menu,
Download,
Folder,
Plus,
Trash2,
LogOut,
RefreshCcw,
Package,
Globe,
ChevronDown,
FolderPlus,
Check,
Search,
Box,
BrainCircuit,
Wrench,
History,
Sparkles,
LayoutTemplate,
Loader2,
MessageSquare,
ArrowUp
} from 'lucide-react';
// --- Mock Data & Types ---
const MOCK_TEMPLATES = [
{ id: 't1', title: "Understand this workspace", description: "Explains local vs global tools", icon: "help-circle", prompt: "Explain how this workspace is configured and what tools are available locally." },
{ id: 't2', title: "Create a new skill", description: "Guide to adding capabilities", icon: "sparkles", prompt: "I want to create a new skill for this workspace. Guide me through it." },
{ id: 't3', title: "Run a scheduled task", description: "Demo of the scheduler plugin", icon: "clock", prompt: "Show me how to schedule a task to run every morning." },
{ id: 't4', title: "Turn task into template", description: "Save workflow for later", icon: "save", prompt: "Help me turn the last task into a reusable template." },
];
const MOCK_SESSIONS = [
{ id: 104, title: "Create 'Data Cleaner' Skill", status: "running", date: "2 mins ago", workspaceId: 'starter' },
{ id: 101, title: "Update Dependencies", status: "completed", date: "1 hour ago", workspaceId: 'starter' },
{ id: 102, title: "Fix CSS Bug", status: "failed", date: "Yesterday", workspaceId: 'proj_alpha' },
{ id: 103, title: "Deploy to Prod", status: "waiting", date: "Yesterday", workspaceId: 'personal_blog' },
];
const MOCK_WORKSPACES = [
{ id: 'starter', name: 'My First Workspace', path: '~/Documents/OpenWork/MyWorkspace', type: 'starter', icon: Zap },
{ id: 'proj_alpha', name: 'Project Alpha', path: '~/Documents/Code/alpha', type: 'custom', icon: Folder },
{ id: 'personal_blog', name: 'Personal Blog', path: '~/Github/blog', type: 'custom', icon: Globe },
];
const MOCK_EXTENSIONS = {
plugins: [
{ id: 'p1', name: 'OpenCode Scheduler', version: '0.9.0', description: 'Run tasks on a cron schedule.', scope: 'workspace', status: 'active' },
{ id: 'p2', name: 'Browser Automation', version: '1.2.0', description: 'Headless browser control.', scope: 'global', status: 'active' },
{ id: 'p3', name: 'Python Runtime', version: '3.11.0', description: 'Execute Python scripts locally.', scope: 'workspace', status: 'active' },
],
skills: [
{ id: 's1', name: 'Workspace Guide', description: 'Teaches you how to use this workspace.', scope: 'workspace', status: 'active' },
{ id: 's2', name: 'Meeting Summarizer', description: 'Extracts action items from transcripts.', scope: 'global', status: 'active' },
]
};
// --- Components ---
const OpenWorkLogo = ({ size = 24, className = "" }) => (
);
const Button = ({ children, variant = "primary", className = "", onClick, disabled }) => {
const baseStyle = "px-4 py-2.5 rounded-xl font-medium transition-all duration-200 flex items-center justify-center gap-2 active:scale-95 text-sm";
const variants = {
primary: "bg-white text-black hover:bg-gray-100 shadow-lg shadow-white/5",
secondary: "bg-zinc-800 text-zinc-100 hover:bg-zinc-700 border border-zinc-700/50",
ghost: "bg-transparent text-zinc-400 hover:text-white hover:bg-zinc-800/50",
danger: "bg-red-500/10 text-red-400 hover:bg-red-500/20 border border-red-500/20",
outline: "border border-zinc-700 text-zinc-300 hover:border-zinc-500 bg-transparent",
};
return (
);
};
const StatusBadge = ({ status }) => {
const styles = {
completed: "bg-emerald-500/10 text-emerald-400 border-emerald-500/20",
running: "bg-blue-500/10 text-blue-400 border-blue-500/20 animate-pulse",
waiting: "bg-amber-500/10 text-amber-400 border-amber-500/20",
failed: "bg-red-500/10 text-red-400 border-red-500/20",
stopped: "bg-zinc-500/10 text-zinc-400 border-zinc-500/20",
};
return (
{status.charAt(0).toUpperCase() + status.slice(1)}
);
};
// --- Workspace Components ---
const WorkspaceChip = ({ activeWorkspace, onClick }) => {
const Icon = activeWorkspace.icon || Folder;
return (
);
};
const WorkspacePicker = ({ isOpen, onClose, workspaces, activeWorkspaceId, onSelect, onCreateNew }) => {
if (!isOpen) return null;
return (
e.stopPropagation()}>
Active
{workspaces.map(ws => {
const Icon = ws.icon;
return (
);
})}
);
};
const CreateWorkspaceModal = ({ isOpen, onClose, onConfirm }) => {
const [step, setStep] = useState(1);
const [template, setTemplate] = useState('starter');
if (!isOpen) return null;
return (
Create Workspace
Initialize a new folder-based workspace.
= 1 ? 'opacity-100' : 'opacity-40'}`}>
{[
{ id: 'starter', name: 'Starter', desc: 'Pre-configured with Scheduler & Core Tools. Best for general use.' },
{ id: 'automation', name: 'Automation', desc: 'Optimized for background tasks and scripting.' },
{ id: 'minimal', name: 'Minimal', desc: 'Empty project. Connects only to core engine.' }
].map(t => (
setTemplate(t.id)}
className={`p-4 rounded-xl border cursor-pointer transition-all ${template === t.id ? 'bg-indigo-500/10 border-indigo-500/50' : 'bg-zinc-900 border-zinc-800 hover:border-zinc-700'}`}
>
))}
);
};
const SettingsModal = ({ isOpen, onClose, currentMode, onSwitchMode, onClearPreference }) => {
if (!isOpen) return null;
return (
Settings
{currentMode === 'host' ? : }
{currentMode} Mode
);
};
// --- Main Views ---
const ExtensionsView = ({ activeWorkspace, onCreateSkill }) => {
const [tab, setTab] = useState('skills'); // 'plugins' | 'skills'
return (
Extensions
Capabilities available to your sessions.
{/* Skills Tab */}
{tab === 'skills' && (
Create a Custom Skill
Teaches OpenCode how to perform a specific workflow in plain English.
{MOCK_EXTENSIONS.skills.map(skill => (
{skill.name}
{skill.description}
{skill.scope === 'workspace' && (
WORKSPACE
)}
))}
)}
{/* Plugins Tab */}
{tab === 'plugins' && (
Plugins provide low-level capabilities (engine primitives). They are configured in opencode.json.
{MOCK_EXTENSIONS.plugins.map(plugin => (
{plugin.name}
{plugin.description}
{plugin.scope === 'workspace' && (
WORKSPACE
)}
{plugin.scope === 'global' && (
GLOBAL
)}
))}
)}
);
};
const SessionsListView = ({ onOpenSession, sessions = MOCK_SESSIONS }) => {
return (
{sessions.map((s, i) => (
onOpenSession(s)} key={s.id} className={`p-4 flex items-center justify-between hover:bg-zinc-800/50 transition-colors cursor-pointer ${i !== sessions.length - 1 ? 'border-b border-zinc-800/50' : ''}`}>
#{s.id}
{s.title}
{s.date}
•
{MOCK_WORKSPACES.find(w => w.id === s.workspaceId)?.name}
))}
)
}
const OnboardingView = ({ onComplete }) => {
const [step, setStep] = useState('mode-select'); // mode-select | create-workspace | initializing
const [mode, setMode] = useState(null);
const [initStage, setInitStage] = useState(0);
const handleModeSelect = (selectedMode) => {
setMode(selectedMode);
if (selectedMode === 'host') {
setStep('create-workspace');
} else {
onComplete(selectedMode);
}
};
const handleCreateWorkspace = () => {
setStep('initializing');
// Simulate staging
setTimeout(() => setInitStage(1), 800);
setTimeout(() => setInitStage(2), 1600);
setTimeout(() => setInitStage(3), 2400);
setTimeout(() => setInitStage(4), 3200);
};
if (step === 'create-workspace') {
return (
Create your first workspace
A workspace is just a folder with its own skills, plugins, and tasks.
);
}
if (step === 'initializing') {
return (
Setting up workspace...
Populating your folder with superpowers
= 1 ? 'bg-zinc-900/50 border-zinc-800 opacity-100' : 'border-transparent opacity-50'}`}>
{initStage >= 1 ? : }
Installing Scheduler Plugin (Workspace-local)
= 2 ? 'bg-zinc-900/50 border-zinc-800 opacity-100' : 'border-transparent opacity-50'}`}>
{initStage >= 2 ? : (initStage === 1 ? : )}
Adding "Workspace Guide" Skill
= 3 ? 'bg-zinc-900/50 border-zinc-800 opacity-100' : 'border-transparent opacity-50'}`}>
{initStage >= 3 ? : (initStage === 2 ? : )}
Generating Starter Templates
= 4 ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-4'}`}>
);
}
return (
How would you like to run OpenWork today?
);
};
const SessionView = ({ initialSession, onBack, activeWorkspace, injectedPrompt }) => {
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');
const [formRequest, setFormRequest] = useState(null); // Special modal state
const messagesEndRef = useRef(null);
useEffect(() => {
if (injectedPrompt) {
// Auto-send the template prompt if provided
setMessages([{ id: Date.now(), role: 'user', content: injectedPrompt }]);
// Mock Response logic based on prompt
setTimeout(() => {
let response = "I can help with that.";
if (injectedPrompt.includes("create a new skill")) {
response = "I'll help you create a new skill for this workspace. I need a few details to get started.";
setTimeout(() => {
setFormRequest({
id: 'form_1',
title: 'New Skill Details',
fields: [
{ name: 'name', label: 'Skill Name', placeholder: 'e.g. daily-summarizer' },
{ name: 'description', label: 'What does it do?', placeholder: 'Summarizes daily logs...' }
]
});
}, 1500);
} else if (injectedPrompt.includes("configured")) {
response = `I'm running inside **${activeWorkspace.name}**. This workspace is a folder located at \`${activeWorkspace.path}\`\n\n**Locally Installed:**\n- Scheduler Plugin\n- Python Runtime\n- Workspace Guide Skill\n\nSessions here are part of your global history but tagged to this workspace.`;
}
setMessages(prev => [...prev, { id: Date.now() + 1, role: 'assistant', content: response }]);
}, 800);
} else if (initialSession) {
// Existing session loaded
if (initialSession.id === 'onboarding-1') {
// ... existing onboarding logic if needed ...
}
}
}, [initialSession, activeWorkspace, injectedPrompt]);
const handleSend = () => {
if (!input.trim()) return;
setMessages(prev => [...prev, { id: Date.now(), role: 'user', content: input }]);
setInput('');
// Mock response
setTimeout(() => {
setMessages(prev => [...prev, { id: Date.now()+1, role: 'assistant', content: "I'm working on that..." }]);
}, 1000);
};
const handleFormSubmit = (data) => {
setFormRequest(null);
setMessages(prev => [
...prev,
{ id: Date.now(), role: 'system', content: `Tool Output: ${JSON.stringify(data)}`, type: 'audit' },
{ id: Date.now()+1, role: 'assistant', content: `Great. I'm creating the skill "${data.name}". I've generated the SKILL.md and config files.` }
]);
};
return (
{initialSession?.title || 'New Session'}
{activeWorkspace.name}
{messages.map((msg) => (
{msg.type === 'audit' ? (
{msg.content}
) : (
<>
{msg.content.split('\n').map((line, i) => (
0 ? "mt-3" : ""}`}>{line}
))}
>
)}
))}
OpenWork can make mistakes. Check important info.
{/* Tool Form Modal */}
{formRequest && (
{formRequest.fields.map(field => (
))}
)}
);
};
const DashboardView = ({ onStartTask, isHost, onOpenSettings, activeWorkspace, onChangeWorkspace, onNavigate, onViewChange, sessions, onTemplateClick }) => {
const [showWorkspacePicker, setShowWorkspacePicker] = useState(false);
const [showCreateWorkspace, setShowCreateWorkspace] = useState(false);
const [workspaces, setWorkspaces] = useState(MOCK_WORKSPACES);
return (
setShowWorkspacePicker(true)}
/>
How can I help you today?
{MOCK_TEMPLATES.slice(0, 4).map((t) => (
))}
setShowWorkspacePicker(false)}
workspaces={workspaces}
activeWorkspaceId={activeWorkspace.id}
onSelect={onChangeWorkspace}
onCreateNew={() => setShowCreateWorkspace(true)}
/>
setShowCreateWorkspace(false)}
onConfirm={() => {}}
/>
);
};
// --- Main App Controller ---
export default function App() {
const [view, setView] = useState('loading');
const [mode, setMode] = useState(null);
const [activeWorkspaceId, setActiveWorkspaceId] = useState('starter');
const [workspaces, setWorkspaces] = useState(MOCK_WORKSPACES);
const [currentSession, setCurrentSession] = useState(null);
const [sessions, setSessions] = useState(MOCK_SESSIONS);
const [injectedPrompt, setInjectedPrompt] = useState(null);
const activeWorkspace = workspaces.find(w => w.id === activeWorkspaceId) || workspaces[0];
useEffect(() => {
const pref = localStorage.getItem('openwork_mode_pref');
if (pref) {
setTimeout(() => {
setMode(pref);
setView('dashboard');
}, 800);
} else {
setView('onboarding');
}
}, []);
const handleOnboardingComplete = (selectedMode) => {
setMode(selectedMode);
setView('dashboard'); // Go to dashboard, don't auto-start session
};
const handleStartTask = (prompt = null) => {
const newSession = { id: Date.now(), title: prompt ? prompt.slice(0, 20) + '...' : 'New Task', workspaceId: activeWorkspaceId };
setCurrentSession(newSession);
setSessions([newSession, ...sessions]);
setInjectedPrompt(prompt); // Pass prompt to session view
setView('session');
};
const handleCreateSkill = (title, prompt) => {
handleStartTask(prompt);
};
const handleOpenSession = (s) => {
setCurrentSession(s);
setInjectedPrompt(null);
setView('session');
};
const handleTemplateClick = (template) => {
handleStartTask(template.prompt);
};
return (
{view === 'onboarding' &&
}
{view === 'dashboard' && (
handleStartTask()}
isHost={mode === 'host'}
activeWorkspace={activeWorkspace}
onChangeWorkspace={setActiveWorkspaceId}
onViewChange={setView}
sessions={sessions}
onNavigate={handleOpenSession}
onTemplateClick={handleTemplateClick}
/>
)}
{/* Global Sidebar Wrapper for Non-Dashboard Views */}
{['skills', 'plugins', 'sessions'].includes(view) && (
{view === 'skills' && }
{view === 'plugins' && }
{view === 'sessions' && }
)}
{view === 'session' && (
setView('dashboard')}
activeWorkspace={activeWorkspace}
injectedPrompt={injectedPrompt}
/>
)}
);
}