feat: Improve dashboard layout and Claude terminal context
- Reorganize dashboard: control panel on top, charts stacked vertically - Add Set Context button to Claude terminal for study awareness - Add conda environment instructions to CLAUDE.md - Fix STUDY_REPORT.md location in generate-report.md skill - Claude terminal now sends study context with skills reminder 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -14,9 +14,10 @@ import { useStudy } from '../../context/StudyContext';
|
||||
|
||||
interface ControlPanelProps {
|
||||
onStatusChange?: () => void;
|
||||
horizontal?: boolean;
|
||||
}
|
||||
|
||||
export const ControlPanel: React.FC<ControlPanelProps> = ({ onStatusChange }) => {
|
||||
export const ControlPanel: React.FC<ControlPanelProps> = ({ onStatusChange, horizontal = false }) => {
|
||||
const { selectedStudy, refreshStudies } = useStudy();
|
||||
const [processStatus, setProcessStatus] = useState<ProcessStatus | null>(null);
|
||||
const [actionInProgress, setActionInProgress] = useState<string | null>(null);
|
||||
@@ -131,6 +132,177 @@ export const ControlPanel: React.FC<ControlPanelProps> = ({ onStatusChange }) =>
|
||||
|
||||
const isRunning = processStatus?.is_running || selectedStudy?.status === 'running';
|
||||
|
||||
// Horizontal layout for top of page
|
||||
if (horizontal) {
|
||||
return (
|
||||
<div className="bg-dark-800 rounded-xl border border-dark-600 overflow-hidden">
|
||||
<div className="px-4 py-3 flex items-center gap-6">
|
||||
{/* Status */}
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
{isRunning ? (
|
||||
<>
|
||||
<div className="w-3 h-3 bg-green-500 rounded-full animate-pulse" />
|
||||
<span className="text-green-400 font-medium text-sm">Running</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="w-3 h-3 bg-dark-500 rounded-full" />
|
||||
<span className="text-dark-400 text-sm">Stopped</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{processStatus?.fea_count && (
|
||||
<span className="text-xs text-dark-400">
|
||||
FEA: <span className="text-primary-400">{processStatus.fea_count}</span>
|
||||
{processStatus.nn_count && (
|
||||
<> | NN: <span className="text-orange-400">{processStatus.nn_count}</span></>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Main Actions */}
|
||||
<div className="flex items-center gap-2">
|
||||
{isRunning ? (
|
||||
<button
|
||||
onClick={handleStop}
|
||||
disabled={actionInProgress !== null}
|
||||
className="flex items-center gap-2 px-3 py-1.5 bg-red-600 hover:bg-red-500
|
||||
disabled:opacity-50 text-white rounded-lg text-sm font-medium"
|
||||
>
|
||||
{actionInProgress === 'stop' ? <Loader2 className="w-4 h-4 animate-spin" /> : <Skull className="w-4 h-4" />}
|
||||
Kill
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
onClick={handleStart}
|
||||
disabled={actionInProgress !== null}
|
||||
className="flex items-center gap-2 px-3 py-1.5 bg-green-600 hover:bg-green-500
|
||||
disabled:opacity-50 text-white rounded-lg text-sm font-medium"
|
||||
>
|
||||
{actionInProgress === 'start' ? <Loader2 className="w-4 h-4 animate-spin" /> : <Play className="w-4 h-4" />}
|
||||
Start
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={handleValidate}
|
||||
disabled={actionInProgress !== null || isRunning}
|
||||
className="flex items-center gap-2 px-3 py-1.5 bg-primary-600 hover:bg-primary-500
|
||||
disabled:opacity-50 text-white rounded-lg text-sm font-medium"
|
||||
>
|
||||
{actionInProgress === 'validate' ? <Loader2 className="w-4 h-4 animate-spin" /> : <CheckCircle className="w-4 h-4" />}
|
||||
Validate
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={handleLaunchOptuna}
|
||||
disabled={actionInProgress !== null}
|
||||
className="flex items-center gap-2 px-3 py-1.5 bg-dark-700 hover:bg-dark-600
|
||||
border border-dark-600 disabled:opacity-50 text-dark-300 hover:text-white rounded-lg text-sm"
|
||||
>
|
||||
<ExternalLink className="w-4 h-4" />
|
||||
Optuna
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Settings Toggle */}
|
||||
<button
|
||||
onClick={() => setShowSettings(!showSettings)}
|
||||
className={`p-1.5 rounded-lg transition-colors ${
|
||||
showSettings ? 'bg-primary-600 text-white' : 'bg-dark-700 text-dark-300 hover:text-white'
|
||||
}`}
|
||||
>
|
||||
<Settings className="w-4 h-4" />
|
||||
</button>
|
||||
|
||||
{/* Error */}
|
||||
{error && (
|
||||
<div className="flex items-center gap-2 text-red-400 text-sm">
|
||||
<AlertTriangle className="w-4 h-4" />
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Collapsible Settings */}
|
||||
{showSettings && (
|
||||
<div className="px-4 py-3 border-t border-dark-700 bg-dark-750">
|
||||
<div className="flex items-center gap-4 flex-wrap">
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="text-xs text-dark-400">Iterations:</label>
|
||||
<input
|
||||
type="number"
|
||||
value={settings.maxIterations}
|
||||
onChange={(e) => setSettings({ ...settings, maxIterations: parseInt(e.target.value) || 100 })}
|
||||
className="w-16 px-2 py-1 bg-dark-700 border border-dark-600 rounded text-white text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="text-xs text-dark-400">FEA Batch:</label>
|
||||
<input
|
||||
type="number"
|
||||
value={settings.feaBatchSize}
|
||||
onChange={(e) => setSettings({ ...settings, feaBatchSize: parseInt(e.target.value) || 5 })}
|
||||
className="w-12 px-2 py-1 bg-dark-700 border border-dark-600 rounded text-white text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="text-xs text-dark-400">Patience:</label>
|
||||
<input
|
||||
type="number"
|
||||
value={settings.patience}
|
||||
onChange={(e) => setSettings({ ...settings, patience: parseInt(e.target.value) || 5 })}
|
||||
className="w-12 px-2 py-1 bg-dark-700 border border-dark-600 rounded text-white text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="text-xs text-dark-400">Tune:</label>
|
||||
<input
|
||||
type="number"
|
||||
value={settings.tuneTrials}
|
||||
onChange={(e) => setSettings({ ...settings, tuneTrials: parseInt(e.target.value) || 30 })}
|
||||
className="w-12 px-2 py-1 bg-dark-700 border border-dark-600 rounded text-white text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="text-xs text-dark-400">Ensemble:</label>
|
||||
<input
|
||||
type="number"
|
||||
value={settings.ensembleSize}
|
||||
onChange={(e) => setSettings({ ...settings, ensembleSize: parseInt(e.target.value) || 3 })}
|
||||
className="w-12 px-2 py-1 bg-dark-700 border border-dark-600 rounded text-white text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="text-xs text-dark-400">Validate Top:</label>
|
||||
<input
|
||||
type="number"
|
||||
min={1}
|
||||
max={20}
|
||||
value={validateTopN}
|
||||
onChange={(e) => setValidateTopN(parseInt(e.target.value) || 5)}
|
||||
className="w-12 px-2 py-1 bg-dark-700 border border-dark-600 rounded text-white text-sm"
|
||||
/>
|
||||
</div>
|
||||
<label className="flex items-center gap-2 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={settings.freshStart}
|
||||
onChange={(e) => setSettings({ ...settings, freshStart: e.target.checked })}
|
||||
className="w-4 h-4 rounded border-dark-600 bg-dark-700 text-primary-600"
|
||||
/>
|
||||
<span className="text-xs text-dark-300">Fresh Start</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Vertical layout (original sidebar layout)
|
||||
return (
|
||||
<div className="bg-dark-800 rounded-xl border border-dark-600 overflow-hidden">
|
||||
{/* Header */}
|
||||
|
||||
Reference in New Issue
Block a user