cflx 0.6.11

Conflux – a spec-driven parallel coding orchestrator that runs AI agents on git worktrees
import React from 'react';
import { GitBranch, GitMerge, Loader2, Trash2 } from 'lucide-react';
import { WorktreeInfo, ActiveCommand } from '../api/types';

interface WorktreeRowProps {
  worktree: WorktreeInfo;
  onMerge?: (branchName: string) => void;
  onDelete?: (branchName: string) => void;
  onClickWorktree?: (branch: string) => void;
  isSelected?: boolean;
  isLoading?: boolean;
  /** Active command for this worktree root, if any */
  activeCommand?: ActiveCommand;
}

export function WorktreeRow({ worktree, onMerge, onDelete, onClickWorktree, isSelected, isLoading, activeCommand }: WorktreeRowProps) {
  const isBusy = !!activeCommand;

  const canMerge =
    !worktree.is_main &&
    !worktree.is_detached &&
    worktree.has_commits_ahead &&
    !worktree.merge_conflict &&
    !worktree.is_merging &&
    !isBusy;

  const canDelete = !worktree.is_main && !isBusy;

  const label = worktree.branch || worktree.head;

  const handleRowClick = () => {
    onClickWorktree?.(worktree.branch);
  };

  return (
    <div
      onClick={handleRowClick}
      className={`flex items-center justify-between gap-2 rounded-md border p-3 transition-colors ${
        isSelected
          ? 'border-[#6366f1] bg-[#1e1b4b]/30'
          : 'border-[#27272a] bg-[#111113] hover:border-[#3f3f46]'
      } cursor-pointer`}
    >
      <div className="flex min-w-0 flex-1 items-center gap-2">
        <GitBranch className="size-3.5 shrink-0 text-[#52525b]" />
        <span className="truncate font-mono text-xs text-[#a1a1aa]">{label}</span>

        <div className="flex shrink-0 items-center gap-1">
          {worktree.is_main && (
            <span className="rounded px-1.5 py-0.5 text-xs font-medium text-[#6366f1] bg-[#1e1b4b]/50">
              MAIN
            </span>
          )}
          {worktree.is_detached && (
            <span className="rounded px-1.5 py-0.5 text-xs font-medium text-[#71717a] bg-[#27272a]">
              DETACHED
            </span>
          )}
          {worktree.is_merging && (
            <span className="rounded px-1.5 py-0.5 text-xs font-medium text-[#f59e0b] bg-[#451a03]/50">
              merging
            </span>
          )}
          {worktree.has_commits_ahead && !worktree.is_merging && !worktree.merge_conflict && (
            <span className="rounded px-1.5 py-0.5 text-xs font-medium text-[#22c55e] bg-[#052e16]/50">
              ahead
            </span>
          )}
          {worktree.merge_conflict && (
            <span className="rounded px-1.5 py-0.5 text-xs font-medium text-[#ef4444] bg-[#450a0a]/50">
              {worktree.merge_conflict.conflict_files.length} conflict{worktree.merge_conflict.conflict_files.length !== 1 ? 's' : ''}
            </span>
          )}
          {isBusy && (
            <span className="flex items-center gap-1 rounded px-1.5 py-0.5 text-xs font-medium text-[#f59e0b] bg-[#451a03]/50">
              <Loader2 className="size-3 animate-spin" />
              {activeCommand.operation}
            </span>
          )}
        </div>
      </div>

      <div className="flex shrink-0 items-center gap-1">
        {canMerge && onMerge && (
          <button
            onClick={() => onMerge(worktree.branch)}
            disabled={isLoading || isBusy}
            title={isBusy ? `Busy: ${activeCommand.operation}` : 'Merge branch'}
            className="rounded p-1.5 text-[#52525b] transition-colors hover:bg-[#27272a] hover:text-[#22c55e] disabled:opacity-50"
          >
            <GitMerge className="size-3.5" />
          </button>
        )}
        {canDelete && onDelete && (
          <button
            onClick={() => onDelete(worktree.branch)}
            disabled={isLoading || isBusy}
            title={isBusy ? `Busy: ${activeCommand.operation}` : 'Delete worktree'}
            className="rounded p-1.5 text-[#52525b] transition-colors hover:bg-[#27272a] hover:text-[#ef4444] disabled:opacity-50"
          >
            <Trash2 className="size-3.5" />
          </button>
        )}
      </div>
    </div>
  );
}