feat: Add session management and global Claude terminal

Phase 1 - Accurate study status detection:
- Add is_optimization_running() to check for active processes
- Add get_accurate_study_status() with proper status logic
- Status now: not_started, running, paused, completed
- Add "paused" status styling (orange) to Home page

Phase 2 - Global Claude terminal:
- Create ClaudeTerminalContext for app-level state
- Create GlobalClaudeTerminal floating component
- Terminal persists across page navigation
- Shows green indicator when connected
- Remove inline terminal from Dashboard

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Antoine
2025-12-05 12:56:34 -05:00
parent fb2d06236a
commit 5c660ff270
8 changed files with 292 additions and 56 deletions

View File

@@ -13,6 +13,7 @@ import {
FolderOpen
} from 'lucide-react';
import { useStudy } from '../context/StudyContext';
import { useClaudeTerminal } from '../context/ClaudeTerminalContext';
interface ClaudeTerminalProps {
isExpanded?: boolean;
@@ -26,17 +27,24 @@ export const ClaudeTerminal: React.FC<ClaudeTerminalProps> = ({
onClose
}) => {
const { selectedStudy } = useStudy();
const { setIsConnected: setGlobalConnected } = useClaudeTerminal();
const terminalRef = useRef<HTMLDivElement>(null);
const xtermRef = useRef<Terminal | null>(null);
const fitAddonRef = useRef<FitAddon | null>(null);
const wsRef = useRef<WebSocket | null>(null);
const [isConnected, setIsConnected] = useState(false);
const [isConnected, setIsConnectedLocal] = useState(false);
const [isConnecting, setIsConnecting] = useState(false);
const [_error, setError] = useState<string | null>(null);
const [cliAvailable, setCliAvailable] = useState<boolean | null>(null);
const [contextSet, setContextSet] = useState(false);
const [settingContext, setSettingContext] = useState(false);
// Sync local connection state to global context
const setIsConnected = useCallback((connected: boolean) => {
setIsConnectedLocal(connected);
setGlobalConnected(connected);
}, [setGlobalConnected]);
// Check CLI availability
useEffect(() => {
fetch('/api/terminal/status')
@@ -251,9 +259,13 @@ export const ClaudeTerminal: React.FC<ClaudeTerminalProps> = ({
if (!wsRef.current || wsRef.current.readyState !== WebSocket.OPEN || !selectedStudy?.id) return;
setSettingContext(true);
// Send context message - Claude should use CLAUDE.md and .claude/skills/ for guidance
const contextMessage = `Context: Working on study "${selectedStudy.id}" at studies/${selectedStudy.id}/. ` +
`Read .claude/skills/ for task protocols. Use atomizer conda env. Acknowledge briefly.`;
// Send context message with POS bootstrap instructions and study context
const contextMessage =
`You are helping with Atomizer optimization. ` +
`First read: .claude/skills/00_BOOTSTRAP.md for task routing. ` +
`Then follow the Protocol Execution Framework. ` +
`Study context: Working on "${selectedStudy.id}" at studies/${selectedStudy.id}/. ` +
`Use atomizer conda env. Acknowledge briefly.`;
wsRef.current.send(JSON.stringify({ type: 'input', data: contextMessage + '\n' }));
// Mark as done after Claude has had time to process

View File

@@ -0,0 +1,50 @@
import React from 'react';
import { useClaudeTerminal } from '../context/ClaudeTerminalContext';
import { ClaudeTerminal } from './ClaudeTerminal';
import { Terminal } from 'lucide-react';
/**
* GlobalClaudeTerminal - A floating terminal that persists across page navigation
*
* This component renders at the App level and maintains the Claude Code session
* even when the user navigates between pages. It can be minimized to a floating
* button or expanded to a side panel.
*/
export const GlobalClaudeTerminal: React.FC = () => {
const { isOpen, setIsOpen, isExpanded, setIsExpanded, isConnected } = useClaudeTerminal();
// Floating button when terminal is closed
if (!isOpen) {
return (
<button
onClick={() => setIsOpen(true)}
className={`fixed bottom-6 right-6 p-4 rounded-full shadow-lg transition-all z-50 ${
isConnected
? 'bg-green-600 hover:bg-green-500'
: 'bg-primary-600 hover:bg-primary-500'
}`}
title={isConnected ? 'Claude Terminal (Connected)' : 'Open Claude Terminal'}
>
<Terminal className="w-6 h-6 text-white" />
{isConnected && (
<span className="absolute -top-1 -right-1 w-3 h-3 bg-green-400 rounded-full border-2 border-dark-900 animate-pulse" />
)}
</button>
);
}
// Terminal panel
return (
<div className={`fixed z-50 transition-all duration-200 ${
isExpanded
? 'inset-4'
: 'bottom-6 right-6 w-[650px] h-[500px]'
}`}>
<ClaudeTerminal
isExpanded={isExpanded}
onToggleExpand={() => setIsExpanded(!isExpanded)}
onClose={() => setIsOpen(false)}
/>
</div>
);
};