summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-01-21 16:19:22 +0000
committersoryu <soryu@soryu.co>2026-01-21 16:19:22 +0000
commitf07f5f6bb2691cb62b0406a912e1d4962abee8d8 (patch)
tree823d5f33359252d6792ec1d96b75b69e00bd9189
parentef839683aed1f501e0de507e2ca72ccdc7b29666 (diff)
downloadsoryu-f07f5f6bb2691cb62b0406a912e1d4962abee8d8.tar.gz
soryu-f07f5f6bb2691cb62b0406a912e1d4962abee8d8.zip
feat(frontend): Add branch button and modal to TaskDetail
- Import BranchTaskModal component - Add onBranch prop to TaskDetailProps interface - Add showBranchModal state - Add canBranch check (task status !== "pending") - Add purple-themed Branch button next to Continue button - Render BranchTaskModal when showBranchModal is true Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
-rw-r--r--makima/frontend/src/components/mesh/TaskDetail.tsx26
1 files changed, 26 insertions, 0 deletions
diff --git a/makima/frontend/src/components/mesh/TaskDetail.tsx b/makima/frontend/src/components/mesh/TaskDetail.tsx
index efe26a8..a74f394 100644
--- a/makima/frontend/src/components/mesh/TaskDetail.tsx
+++ b/makima/frontend/src/components/mesh/TaskDetail.tsx
@@ -6,6 +6,7 @@ import { OverlayDiffViewer } from "./OverlayDiffViewer";
import { PRPreview } from "./PRPreview";
import { InlineSubtaskEditor } from "./InlineSubtaskEditor";
import { DirectoryInput } from "./DirectoryInput";
+import { BranchTaskModal } from "./BranchTaskModal";
interface TaskDetailProps {
task: TaskWithSubtasks;
@@ -25,6 +26,8 @@ interface TaskDetailProps {
viewingSubtaskId?: string | null;
/** Navigate to view the contract */
onViewContract?: (contractId: string) => void;
+ /** Branch the task to create a new task with same state */
+ onBranch?: (taskId: string, message: string, name?: string) => Promise<void>;
// Optional advanced features
overlayDiff?: string;
changedFiles?: string[];
@@ -110,6 +113,7 @@ export function TaskDetail({
onToggleSubtaskOutput,
viewingSubtaskId,
onViewContract,
+ onBranch,
overlayDiff,
changedFiles,
onRequestDiff,
@@ -142,6 +146,8 @@ export function TaskDetail({
const [isCloning, setIsCloning] = useState(false);
const [cloneError, setCloneError] = useState<string | null>(null);
const [cloneTargetDir, setCloneTargetDir] = useState("");
+ // Track branch modal state
+ const [showBranchModal, setShowBranchModal] = useState(false);
// Check if task is running
const isTaskRunning = task.status === "running" || task.status === "initializing" || task.status === "starting";
@@ -151,6 +157,8 @@ export function TaskDetail({
const isSupervisor = task.isSupervisor === true;
// Show continue for supervisors (always) or terminal states for other tasks
const canContinue = isSupervisor || isTaskTerminal;
+ // Show branch button when task has run at least once (not pending)
+ const canBranch = onBranch && task.status !== "pending";
// Determine which tasks to show: for supervisors, show contractTasks; for regular tasks, show subtasks
const displayTasks = useMemo(() => {
@@ -380,6 +388,15 @@ export function TaskDetail({
Continue
</button>
)}
+ {canBranch && (
+ <button
+ onClick={() => setShowBranchModal(true)}
+ className="px-3 py-1 font-mono text-xs text-purple-400 border border-purple-400/30 hover:border-purple-400/50 hover:bg-purple-400/10 transition-colors uppercase flex items-center gap-1"
+ >
+ <span className="w-1.5 h-1.5 bg-purple-400 rounded-full" />
+ Branch
+ </button>
+ )}
<button
onClick={() => setIsEditing(true)}
className="px-3 py-1 font-mono text-xs text-[#9bc3ff] border border-[rgba(117,170,252,0.25)] hover:border-[#3f6fb3] transition-colors uppercase"
@@ -908,6 +925,15 @@ export function TaskDetail({
</div>
</div>
)}
+
+ {/* Branch Task Modal */}
+ {showBranchModal && onBranch && (
+ <BranchTaskModal
+ task={task}
+ onBranch={onBranch}
+ onClose={() => setShowBranchModal(false)}
+ />
+ )}
</div>
);
}