summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-02-12 23:38:26 +0000
committersoryu <soryu@soryu.co>2026-02-12 23:38:26 +0000
commit70bcc16ce1f58e81c01b34d647becc90e53d5833 (patch)
tree01c8c850766fb43959fa066abb03201b1184899f
parent355f10964c4dbec24a244a00caba5c17ed23fc65 (diff)
downloadsoryu-makima/makima-jp--fix-log-streaming-on-directive-page-d4c53b88.tar.gz
soryu-makima/makima-jp--fix-log-streaming-on-directive-page-d4c53b88.zip
feat: makima.jp: Fix log streaming on directive pagemakima/makima-jp--fix-log-streaming-on-directive-page-d4c53b88
-rw-r--r--makima/frontend/src/components/directives/DirectiveDetail.tsx40
-rw-r--r--makima/frontend/src/components/directives/DirectiveLogStream.tsx26
-rw-r--r--makima/frontend/src/hooks/useMultiTaskSubscription.ts26
3 files changed, 59 insertions, 33 deletions
diff --git a/makima/frontend/src/components/directives/DirectiveDetail.tsx b/makima/frontend/src/components/directives/DirectiveDetail.tsx
index ab6ddbb..69fadb6 100644
--- a/makima/frontend/src/components/directives/DirectiveDetail.tsx
+++ b/makima/frontend/src/components/directives/DirectiveDetail.tsx
@@ -52,7 +52,9 @@ export function DirectiveDetail({
const [goalText, setGoalText] = useState(directive.goal);
const [visibleTaskIds, setVisibleTaskIds] = useState<Set<string> | null>(null);
const [searchQuery, setSearchQuery] = useState("");
- const [isLogCollapsed, setIsLogCollapsed] = useState(true);
+ const [isLogCollapsed, setIsLogCollapsed] = useState(
+ directive.status !== "active"
+ );
const prevHadRunningRef = useRef(false);
const badge = STATUS_BADGE[directive.status] || STATUS_BADGE.draft;
@@ -97,10 +99,10 @@ export function DirectiveDetail({
return map;
}, [directive.orchestratorTaskId, directive.steps]);
- // Subscribe to all task outputs
+ // Subscribe to all task outputs (always enabled so we're ready when tasks appear)
const { connected, entries, clearEntries } = useMultiTaskSubscription({
taskMap,
- enabled: taskMap.size > 0,
+ enabled: true,
});
// Auto-expand log panel when tasks start running
@@ -558,23 +560,21 @@ export function DirectiveDetail({
/>
</div>
- {/* Log Stream */}
- {taskMap.size > 0 && (
- <div className="px-4 py-3 border-t border-[rgba(117,170,252,0.1)]">
- <DirectiveLogStream
- entries={entries}
- taskMap={taskMap}
- connected={connected}
- visibleTaskIds={visibleTaskIds}
- searchQuery={searchQuery}
- isCollapsed={isLogCollapsed}
- onToggleCollapse={() => setIsLogCollapsed((prev) => !prev)}
- onSetVisibleTaskIds={setVisibleTaskIds}
- onSetSearchQuery={setSearchQuery}
- onClear={clearEntries}
- />
- </div>
- )}
+ {/* Log Stream — always visible so users can see the stream area */}
+ <div className="px-4 py-3 border-t border-[rgba(117,170,252,0.1)]">
+ <DirectiveLogStream
+ entries={entries}
+ taskMap={taskMap}
+ connected={connected}
+ visibleTaskIds={visibleTaskIds}
+ searchQuery={searchQuery}
+ isCollapsed={isLogCollapsed}
+ onToggleCollapse={() => setIsLogCollapsed((prev) => !prev)}
+ onSetVisibleTaskIds={setVisibleTaskIds}
+ onSetSearchQuery={setSearchQuery}
+ onClear={clearEntries}
+ />
+ </div>
</div>
);
}
diff --git a/makima/frontend/src/components/directives/DirectiveLogStream.tsx b/makima/frontend/src/components/directives/DirectiveLogStream.tsx
index d457fe3..760a0ae 100644
--- a/makima/frontend/src/components/directives/DirectiveLogStream.tsx
+++ b/makima/frontend/src/components/directives/DirectiveLogStream.tsx
@@ -94,7 +94,7 @@ export function DirectiveLogStream({
}, [filteredEntries.length, autoScroll]);
// Count active (running) tasks
- const activeTaskCount = Array.from(taskMap.keys()).length;
+ const activeTaskCount = taskMap.size;
if (isCollapsed) {
return (
@@ -106,9 +106,15 @@ export function DirectiveLogStream({
<span className="text-[10px] font-mono text-[#9bc3ff] uppercase tracking-wide">
Log Stream
</span>
- <span className="text-[10px] font-mono text-[#556677]">
- [{activeTaskCount} task{activeTaskCount !== 1 ? "s" : ""}]
- </span>
+ {activeTaskCount > 0 ? (
+ <span className="text-[10px] font-mono text-[#556677]">
+ [{activeTaskCount} task{activeTaskCount !== 1 ? "s" : ""}]
+ </span>
+ ) : (
+ <span className="text-[10px] font-mono text-[#556677]">
+ [no active tasks]
+ </span>
+ )}
{connected && entries.length > 0 && (
<span className="flex items-center gap-1 px-1.5 py-0.5 bg-green-400/10 border border-green-400/20 rounded">
<span className="w-1.5 h-1.5 bg-green-400 rounded-full animate-pulse" />
@@ -258,11 +264,13 @@ export function DirectiveLogStream({
>
{filteredEntries.length === 0 ? (
<div className="text-[#555] italic text-[10px]">
- {entries.length === 0
- ? connected
- ? "Waiting for output..."
- : "No tasks subscribed"
- : "No entries match filter"}
+ {taskMap.size === 0
+ ? "No active tasks — logs will appear here when tasks start running"
+ : entries.length === 0
+ ? connected
+ ? "Waiting for output..."
+ : "Connecting..."
+ : "No entries match filter"}
</div>
) : (
<div className="space-y-1">
diff --git a/makima/frontend/src/hooks/useMultiTaskSubscription.ts b/makima/frontend/src/hooks/useMultiTaskSubscription.ts
index 19d6dea..1a43b7b 100644
--- a/makima/frontend/src/hooks/useMultiTaskSubscription.ts
+++ b/makima/frontend/src/hooks/useMultiTaskSubscription.ts
@@ -118,10 +118,12 @@ export function useMultiTaskSubscription(options: UseMultiTaskSubscriptionOption
setConnected(false);
wsRef.current = null;
- // Reconnect if we still have subscriptions
+ // Reconnect if enabled and we have active subscriptions
if (subscribedTasksRef.current.size > 0 && enabledRef.current) {
reconnectTimeoutRef.current = window.setTimeout(() => {
- connect();
+ if (enabledRef.current && subscribedTasksRef.current.size > 0) {
+ connect();
+ }
}, 3000);
}
};
@@ -132,8 +134,8 @@ export function useMultiTaskSubscription(options: UseMultiTaskSubscriptionOption
// Manage subscriptions when task IDs change
useEffect(() => {
- if (!enabled || taskIds.length === 0) {
- // Close connection if no tasks
+ if (!enabled) {
+ // Disabled — tear down connection
if (wsRef.current) {
subscribedTasksRef.current.clear();
wsRef.current.close();
@@ -142,6 +144,19 @@ export function useMultiTaskSubscription(options: UseMultiTaskSubscriptionOption
return;
}
+ if (taskIds.length === 0) {
+ // No tasks yet — keep the connection alive if it exists (ready for new tasks),
+ // but don't create a new one just for an empty set.
+ // Clear any stale subscriptions.
+ if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
+ for (const existingId of subscribedTasksRef.current) {
+ unsubscribeFromTask(wsRef.current, existingId);
+ }
+ }
+ subscribedTasksRef.current = new Set();
+ return;
+ }
+
const newTaskIds = new Set(taskIds);
const ws = wsRef.current;
@@ -165,6 +180,9 @@ export function useMultiTaskSubscription(options: UseMultiTaskSubscriptionOption
subscribeToTask(ws, newId);
}
}
+
+ // Update tracked subscriptions
+ subscribedTasksRef.current = newTaskIds;
}, [taskIds, enabled, connect, subscribeToTask, unsubscribeFromTask]);
// Cleanup on unmount