From 93632240b6f850669851376a46640216b106c276 Mon Sep 17 00:00:00 2001 From: Matt Toohey Date: Tue, 3 Mar 2026 16:06:52 +1100 Subject: [PATCH 1/2] fix(mark): restrict sine wave animation to run-type actions only Pre-run actions (e.g. npm install) incorrectly displayed the sine wave animation meant for run actions. This adds actionType to RunningActionInfo so the frontend can distinguish action types, guards the default RunPhase fallback on reload to only apply to run actions, and adds an action type check to the secondary actions sine wave rendering. Co-Authored-By: Claude Opus 4.6 --- apps/mark/src-tauri/src/actions/commands.rs | 2 ++ apps/mark/src-tauri/src/actions/events.rs | 4 ++++ apps/mark/src-tauri/src/actions/registry.rs | 3 +++ apps/mark/src-tauri/src/branches.rs | 1 + apps/mark/src/lib/features/actions/actions.ts | 1 + .../mark/src/lib/features/branches/BranchCard.svelte | 12 ++++++------ 6 files changed, 17 insertions(+), 6 deletions(-) diff --git a/apps/mark/src-tauri/src/actions/commands.rs b/apps/mark/src-tauri/src/actions/commands.rs index 4430cf1b..bb4cbe61 100644 --- a/apps/mark/src-tauri/src/actions/commands.rs +++ b/apps/mark/src-tauri/src/actions/commands.rs @@ -191,6 +191,7 @@ pub async fn run_branch_action( branch_id.clone(), action_id.clone(), action.name.clone(), + action.action_type.as_str().to_string(), reg.clone(), )); @@ -603,6 +604,7 @@ pub async fn run_prerun_actions( branch_id.clone(), action.id.clone(), action.name.clone(), + action.action_type.as_str().to_string(), registry.inner().clone(), )); diff --git a/apps/mark/src-tauri/src/actions/events.rs b/apps/mark/src-tauri/src/actions/events.rs index 58601adb..155e60c2 100644 --- a/apps/mark/src-tauri/src/actions/events.rs +++ b/apps/mark/src-tauri/src/actions/events.rs @@ -39,6 +39,7 @@ pub struct TauriExecutionListener { branch_id: String, action_id: String, action_name: String, + action_type: String, registry: Arc, } @@ -48,6 +49,7 @@ impl TauriExecutionListener { branch_id: String, action_id: String, action_name: String, + action_type: String, registry: Arc, ) -> Self { Self { @@ -55,6 +57,7 @@ impl TauriExecutionListener { branch_id, action_id, action_name, + action_type, registry, } } @@ -74,6 +77,7 @@ impl ExecutionListener for TauriExecutionListener { self.branch_id.clone(), self.action_id.clone(), self.action_name.clone(), + self.action_type.clone(), started_at, ); diff --git a/apps/mark/src-tauri/src/actions/registry.rs b/apps/mark/src-tauri/src/actions/registry.rs index cd08aae0..a84ff692 100644 --- a/apps/mark/src-tauri/src/actions/registry.rs +++ b/apps/mark/src-tauri/src/actions/registry.rs @@ -17,6 +17,7 @@ pub struct RunningActionInfo { pub branch_id: String, pub action_id: String, pub action_name: String, + pub action_type: String, pub started_at: i64, } @@ -63,6 +64,7 @@ impl ActionRegistry { branch_id: String, action_id: String, action_name: String, + action_type: String, started_at: i64, ) { let info = RunningActionInfo { @@ -70,6 +72,7 @@ impl ActionRegistry { branch_id, action_id, action_name, + action_type, started_at, }; diff --git a/apps/mark/src-tauri/src/branches.rs b/apps/mark/src-tauri/src/branches.rs index f223e8b7..6945c663 100644 --- a/apps/mark/src-tauri/src/branches.rs +++ b/apps/mark/src-tauri/src/branches.rs @@ -1744,6 +1744,7 @@ pub(crate) async fn run_prerun_actions_for_branch( branch_id.to_string(), action.id.clone(), action.name.clone(), + action.action_type.as_str().to_string(), Arc::clone(act_registry), )); diff --git a/apps/mark/src/lib/features/actions/actions.ts b/apps/mark/src/lib/features/actions/actions.ts index 1a20aaab..4eb3fe86 100644 --- a/apps/mark/src/lib/features/actions/actions.ts +++ b/apps/mark/src/lib/features/actions/actions.ts @@ -106,6 +106,7 @@ export interface RunningActionInfo { branchId: string; actionId: string; actionName: string; + actionType: string; startedAt: number; } diff --git a/apps/mark/src/lib/features/branches/BranchCard.svelte b/apps/mark/src/lib/features/branches/BranchCard.svelte index cc46ca39..4c9d7099 100644 --- a/apps/mark/src/lib/features/branches/BranchCard.svelte +++ b/apps/mark/src/lib/features/branches/BranchCard.svelte @@ -882,11 +882,10 @@ const phase = await getRunPhase(info.executionId); if (phase) { runPhases.set(info.executionId, phase); - } else { - // No persisted phase — the action is running but detection state - // was lost (app restart). Default to Running with no endpoint so - // the sine wave is shown. This is safe for non-Run actions too - // since phases are only rendered for Run-type action buttons. + } else if (info.actionType === 'run') { + // No persisted phase — the run action is running but detection + // state was lost (app restart). Default to Running with no + // endpoint so the sine wave is shown. runPhases.set(info.executionId, { type: 'running', endpoint: null }); } } catch { @@ -1794,6 +1793,7 @@ {@const isStopping = stoppingExecutions.has(execution.executionId)} {@const showStopIcon = altHeld && isRunning && !isStopping} {@const phase = runPhases.get(execution.executionId)} + {@const actionDef = actions.find((a) => a.id === execution.actionId)}
{:else if showStopIcon} - {:else if isRunning && phase && phase.type !== 'building'} + {:else if isRunning && phase && phase.type !== 'building' && actionDef?.actionType === 'run'} {:else if isRunning} From c56fcc4bfb96a6f1534546a6c64d1c1e7b79dd7c Mon Sep 17 00:00:00 2001 From: Matt Toohey Date: Thu, 5 Mar 2026 16:31:15 +1100 Subject: [PATCH 2/2] fix(mark): store actionType on RunningAction to avoid race condition Address code review feedback from the previous commit: - Add action_type to Rust ActionStatusEvent so the frontend receives it with status change events - Change RunningActionInfo.actionType from string to ActionType for type safety - Add actionType field to the RunningAction type in BranchCard and populate it at both push sites (status event handler and loadRunningActions) - Replace the actions.find() lookup in the template with execution.actionType, eliminating the race condition where the actions array may not be loaded yet Co-Authored-By: Claude Opus 4.6 --- apps/mark/src-tauri/src/actions/events.rs | 3 +++ apps/mark/src/lib/features/actions/actions.ts | 3 ++- apps/mark/src/lib/features/branches/BranchCard.svelte | 7 +++++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/apps/mark/src-tauri/src/actions/events.rs b/apps/mark/src-tauri/src/actions/events.rs index 155e60c2..bb3758c0 100644 --- a/apps/mark/src-tauri/src/actions/events.rs +++ b/apps/mark/src-tauri/src/actions/events.rs @@ -27,6 +27,7 @@ pub struct ActionStatusEvent { pub branch_id: String, pub action_id: String, pub action_name: String, + pub action_type: String, pub status: String, // "running", "completed", "failed", "stopped" pub exit_code: Option, pub started_at: Option, @@ -89,6 +90,7 @@ impl ExecutionListener for TauriExecutionListener { branch_id: self.branch_id.clone(), action_id: self.action_id.clone(), action_name: self.action_name.clone(), + action_type: self.action_type.clone(), status: "running".to_string(), exit_code: None, started_at: Some(started_at), @@ -142,6 +144,7 @@ impl ExecutionListener for TauriExecutionListener { branch_id: self.branch_id.clone(), action_id: self.action_id.clone(), action_name: self.action_name.clone(), + action_type: self.action_type.clone(), status: status_str.to_string(), exit_code, started_at, diff --git a/apps/mark/src/lib/features/actions/actions.ts b/apps/mark/src/lib/features/actions/actions.ts index 4eb3fe86..3c0b0f74 100644 --- a/apps/mark/src/lib/features/actions/actions.ts +++ b/apps/mark/src/lib/features/actions/actions.ts @@ -80,6 +80,7 @@ export interface ActionStatusEvent { branchId: string; actionId: string; actionName: string; + actionType: ActionType; status: ActionStatus; exitCode?: number; startedAt?: number; @@ -106,7 +107,7 @@ export interface RunningActionInfo { branchId: string; actionId: string; actionName: string; - actionType: string; + actionType: ActionType; startedAt: number; } diff --git a/apps/mark/src/lib/features/branches/BranchCard.svelte b/apps/mark/src/lib/features/branches/BranchCard.svelte index 4c9d7099..92c6ad91 100644 --- a/apps/mark/src/lib/features/branches/BranchCard.svelte +++ b/apps/mark/src/lib/features/branches/BranchCard.svelte @@ -71,6 +71,7 @@ listenToRunPhaseChanged, listenToRepoActionsDetection, type ActionStatusEvent, + type ActionType, type RunPhase, } from '../actions/actions'; import { getAvailableOpeners, openInApp, copyPathToClipboard, type OpenerApp } from './branch'; @@ -324,6 +325,7 @@ executionId: string; actionId: string; actionName: string; + actionType: ActionType; status: 'running' | 'completed' | 'failed' | 'stopped'; exitCode?: number | null; startedAt?: number; @@ -443,6 +445,7 @@ executionId: payload.executionId, actionId: payload.actionId, actionName: payload.actionName, + actionType: payload.actionType, status: 'running', startedAt: payload.startedAt ?? Date.now(), }); @@ -869,6 +872,7 @@ executionId: info.executionId, actionId: info.actionId, actionName: info.actionName, + actionType: info.actionType, status: 'running', startedAt: info.startedAt, }); @@ -1793,7 +1797,6 @@ {@const isStopping = stoppingExecutions.has(execution.executionId)} {@const showStopIcon = altHeld && isRunning && !isStopping} {@const phase = runPhases.get(execution.executionId)} - {@const actionDef = actions.find((a) => a.id === execution.actionId)}
{:else if showStopIcon} - {:else if isRunning && phase && phase.type !== 'building' && actionDef?.actionType === 'run'} + {:else if isRunning && phase && phase.type !== 'building' && execution.actionType === 'run'} {:else if isRunning}