From ec9738a069e61529be040eff065318972b8a11e2 Mon Sep 17 00:00:00 2001
From: soryu <soryu@soryu.co>
Date: Wed, 4 Mar 2026 16:47:12 +0000
Subject: feat: task slide-out panel, 3-way reconcile toggle, daemon reauth fix
 (#85)

* WIP: heartbeat checkpoint

* WIP: heartbeat checkpoint

* feat: soryu-co/soryu - makima: Fix daemon reauth flow for new claude setup-token output format

* feat: soryu-co/soryu - makima: Update frontend reconcile toggle to three-way switch

* feat: soryu-co/soryu - makima: Add task slide-out panel to directive page
---
 .../src/components/directives/DirectiveDetail.tsx  | 61 +++++++++++++++++-----
 1 file changed, 47 insertions(+), 14 deletions(-)

(limited to 'makima/frontend/src/components/directives/DirectiveDetail.tsx')

diff --git a/makima/frontend/src/components/directives/DirectiveDetail.tsx b/makima/frontend/src/components/directives/DirectiveDetail.tsx
index 8f39207..5f3489a 100644
--- a/makima/frontend/src/components/directives/DirectiveDetail.tsx
+++ b/makima/frontend/src/components/directives/DirectiveDetail.tsx
@@ -3,6 +3,7 @@ import type { DirectiveWithSteps, DirectiveStatus, UpdateDirectiveRequest } from
 import { DirectiveDAG } from "./DirectiveDAG";
 import type { SpecializedStep } from "./DirectiveDAG";
 import { DirectiveLogStream } from "./DirectiveLogStream";
+import { TaskSlideOutPanel } from "./TaskSlideOutPanel";
 import { useMultiTaskSubscription } from "../../hooks/useMultiTaskSubscription";
 import { useSupervisorQuestions } from "../../contexts/SupervisorQuestionsContext";
 
@@ -53,6 +54,11 @@ export function DirectiveDetail({
   const [pickingUpOrders, setPickingUpOrders] = useState(false);
   const [pickUpResult, setPickUpResult] = useState<string | null>(null);
   const [creatingPR, setCreatingPR] = useState(false);
+  const [slideOutTaskId, setSlideOutTaskId] = useState<string | null>(null);
+
+  const handleViewTask = (taskId: string) => {
+    setSlideOutTaskId(taskId);
+  };
 
   // Sync goalText and reset editing state when directive changes
   useEffect(() => {
@@ -178,7 +184,15 @@ export function DirectiveDetail({
   };
 
 
+  // Find the task name for the slide-out panel
+  const slideOutTaskName = slideOutTaskId
+    ? (directive.steps.find((s) => s.taskId === slideOutTaskId)?.name ??
+       taskMap.get(slideOutTaskId) ??
+       undefined)
+    : undefined;
+
   return (
+    <>
     <div className="flex flex-col h-full overflow-y-auto">
       {/* Header */}
       <div className="px-4 py-3 border-b border-dashed border-[rgba(117,170,252,0.2)]">
@@ -228,21 +242,31 @@ export function DirectiveDetail({
 
         {/* Reconcile mode toggle */}
         <div className="flex items-center gap-2 mb-2">
-          <button
-            type="button"
-            onClick={() => onUpdate({ reconcileMode: !directive.reconcileMode })}
-            className={`text-[10px] font-mono border rounded px-2 py-0.5 transition-colors ${
-              directive.reconcileMode
-                ? "text-amber-400 border-amber-800 bg-amber-900/20"
-                : "text-[#556677] border-[#2a3a5a] hover:text-[#7788aa]"
-            }`}
-          >
-            {directive.reconcileMode ? "Reconcile: ON" : "Reconcile: OFF"}
-          </button>
+          <div className="flex items-center border border-[#2a3a5a] rounded overflow-hidden">
+            {(["auto", "semi-auto", "manual"] as const).map((mode) => {
+              const isActive = directive.reconcileMode === mode;
+              const modeStyles: Record<string, string> = {
+                auto: isActive ? "text-[#9bc3ff] bg-[#1a2540]" : "text-[#445566] hover:text-[#7788aa]",
+                "semi-auto": isActive ? "text-amber-400 bg-amber-900/20" : "text-[#445566] hover:text-[#7788aa]",
+                manual: isActive ? "text-orange-400 bg-orange-900/20" : "text-[#445566] hover:text-[#7788aa]",
+              };
+              const labels: Record<string, string> = { auto: "Auto", "semi-auto": "Semi", manual: "Manual" };
+              return (
+                <button
+                  key={mode}
+                  type="button"
+                  onClick={() => onUpdate({ reconcileMode: mode })}
+                  className={`text-[10px] font-mono px-2 py-0.5 transition-colors border-r border-[#2a3a5a] last:border-r-0 ${modeStyles[mode]}`}
+                >
+                  {labels[mode]}
+                </button>
+              );
+            })}
+          </div>
           <span className="text-[9px] font-mono text-[#445566]">
-            {directive.reconcileMode
-              ? "Questions pause execution"
-              : "Questions timeout after 30s"}
+            {directive.reconcileMode === "auto" && "Questions timeout after 30s"}
+            {directive.reconcileMode === "semi-auto" && "Questions pause execution"}
+            {directive.reconcileMode === "manual" && "Tasks ask clarifying questions"}
           </span>
         </div>
 
@@ -424,6 +448,7 @@ export function DirectiveDetail({
           onComplete={onCompleteStep}
           onFail={onFailStep}
           onSkip={onSkipStep}
+          onViewTask={handleViewTask}
         />
       </div>
 
@@ -445,6 +470,14 @@ export function DirectiveDetail({
         </div>
       )}
     </div>
+
+    <TaskSlideOutPanel
+      taskId={slideOutTaskId || ""}
+      taskName={slideOutTaskName}
+      isOpen={!!slideOutTaskId}
+      onClose={() => setSlideOutTaskId(null)}
+    />
+    </>
   );
 }
 
-- 
cgit v1.2.3

