cflx 0.6.11

Conflux – a spec-driven parallel coding orchestrator that runs AI agents on git worktrees
import React, { useEffect, useState } from 'react';
import { Play, Square } from 'lucide-react';
import { ConnectionStatus } from '../api/wsClient';
import { OrchestrationStatus } from '../api/types';
import { fetchVersion } from '../api/restClient';

interface HeaderProps {
  connectionStatus: ConnectionStatus;
  orchestrationStatus: OrchestrationStatus;
  onRun: () => void;
  onStop: () => void;
  isLoading: boolean;
}

const statusConfig: Record<ConnectionStatus, { color: string; label: string }> = {
  connected: { color: 'bg-[#22c55e]', label: 'Connected' },
  reconnecting: { color: 'bg-[#f59e0b] animate-pulse', label: 'Reconnecting' },
  disconnected: { color: 'bg-[#ef4444]', label: 'Disconnected' },
};

export function Header({ connectionStatus, orchestrationStatus, onRun, onStop, isLoading }: HeaderProps) {
  const { color, label } = statusConfig[connectionStatus];
  const [version, setVersion] = useState<string | null>(null);

  useEffect(() => {
    fetchVersion()
      .then((data) => setVersion(data.version))
      .catch(() => {
        // Silently ignore fetch failures — version is not displayed
      });
  }, []);

  const isRunning = orchestrationStatus === 'running';

  return (
    <header className="border-b border-[#27272a] bg-[#111113] px-5 py-3">
      <div className="flex items-center justify-between">
        <div className="flex items-center gap-2">
          <div className="size-5 rounded bg-[#6366f1] flex items-center justify-center">
            <svg width="12" height="12" viewBox="0 0 12 12" fill="none">
              <path d="M6 1L11 4V8L6 11L1 8V4L6 1Z" fill="white" fillOpacity="0.9" />
            </svg>
          </div>
          <span className="text-sm font-semibold tracking-tight text-[#fafafa]">Conflux</span>
          {version && (
            <span className="text-xs text-[#52525b]">{version}</span>
          )}
        </div>
        <div className="flex items-center gap-3">
          {/* Global Run/Stop buttons */}
          {isRunning ? (
            <button
              onClick={onStop}
              disabled={isLoading}
              className="flex items-center gap-1.5 rounded-md bg-[#450a0a]/60 px-3 py-1.5 text-xs font-medium text-[#ef4444] transition-colors hover:bg-[#450a0a]/80 disabled:cursor-not-allowed disabled:opacity-40"
              aria-label="Stop orchestration"
            >
              <Square className="size-3" />
              Stop
            </button>
          ) : (
            <button
              onClick={onRun}
              disabled={isLoading}
              className="flex items-center gap-1.5 rounded-md bg-[#166534]/60 px-3 py-1.5 text-xs font-medium text-[#22c55e] transition-colors hover:bg-[#166534]/80 disabled:cursor-not-allowed disabled:opacity-40"
              aria-label="Run orchestration"
            >
              <Play className="size-3" />
              Run
            </button>
          )}

          {/* Connection status */}
          <div className="flex items-center gap-1.5">
            <div className={`size-1.5 rounded-full ${color}`} aria-hidden="true" />
            <span className="text-xs text-[#71717a]">{label}</span>
          </div>
        </div>
      </div>
    </header>
  );
}