From 0ba5ec65f78e3fd74bc5807fd7d2f64333ea5388 Mon Sep 17 00:00:00 2001 From: Siddharth Ganesan Date: Fri, 9 Jan 2026 18:26:29 -0800 Subject: [PATCH] Diff view --- .../diff-controls/diff-controls.tsx | 71 +++++++--------- apps/sim/stores/workflow-diff/store.ts | 83 +++++++++++-------- 2 files changed, 81 insertions(+), 73 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/diff-controls/diff-controls.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/diff-controls/diff-controls.tsx index 5664c769c2..d8424fbfa9 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/diff-controls/diff-controls.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/diff-controls/diff-controls.tsx @@ -206,54 +206,47 @@ export const DiffControls = memo(function DiffControls() { } }, [activeWorkflowId, currentChat, messages, baselineWorkflow]) - const handleAccept = useCallback(async () => { + const handleAccept = useCallback(() => { logger.info('Accepting proposed changes with backup protection') + // Resolve target toolCallId for build/edit and update to terminal success state in the copilot store + // This happens synchronously first for instant UI feedback try { - // Create a checkpoint before applying changes so it appears under the triggering user message - await createCheckpoint().catch((error) => { - logger.warn('Failed to create checkpoint before accept:', error) - }) - - // Resolve target toolCallId for build/edit and update to terminal success state in the copilot store - try { - const { toolCallsById, messages } = useCopilotStore.getState() - let id: string | undefined - outer: for (let mi = messages.length - 1; mi >= 0; mi--) { - const m = messages[mi] - if (m.role !== 'assistant' || !m.contentBlocks) continue - const blocks = m.contentBlocks as any[] - for (let bi = blocks.length - 1; bi >= 0; bi--) { - const b = blocks[bi] - if (b?.type === 'tool_call') { - const tn = b.toolCall?.name - if (tn === 'edit_workflow') { - id = b.toolCall?.id - break outer - } + const { toolCallsById, messages } = useCopilotStore.getState() + let id: string | undefined + outer: for (let mi = messages.length - 1; mi >= 0; mi--) { + const m = messages[mi] + if (m.role !== 'assistant' || !m.contentBlocks) continue + const blocks = m.contentBlocks as any[] + for (let bi = blocks.length - 1; bi >= 0; bi--) { + const b = blocks[bi] + if (b?.type === 'tool_call') { + const tn = b.toolCall?.name + if (tn === 'edit_workflow') { + id = b.toolCall?.id + break outer } } } - if (!id) { - const candidates = Object.values(toolCallsById).filter((t) => t.name === 'edit_workflow') - id = candidates.length ? candidates[candidates.length - 1].id : undefined - } - if (id) updatePreviewToolCallState('accepted', id) - } catch {} + } + if (!id) { + const candidates = Object.values(toolCallsById).filter((t) => t.name === 'edit_workflow') + id = candidates.length ? candidates[candidates.length - 1].id : undefined + } + if (id) updatePreviewToolCallState('accepted', id) + } catch {} - // Accept changes without blocking the UI; errors will be logged by the store handler - acceptChanges().catch((error) => { - logger.error('Failed to accept changes (background):', error) - }) + // Accept changes without blocking the UI; errors will be logged by the store handler + acceptChanges().catch((error) => { + logger.error('Failed to accept changes (background):', error) + }) - logger.info('Accept triggered; UI will update optimistically') - } catch (error) { - logger.error('Failed to accept changes:', error) + // Create checkpoint in the background (fire-and-forget) so it doesn't block UI + createCheckpoint().catch((error) => { + logger.warn('Failed to create checkpoint after accept:', error) + }) - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' - logger.error('Workflow update failed:', errorMessage) - alert(`Failed to save workflow changes: ${errorMessage}`) - } + logger.info('Accept triggered; UI will update optimistically') }, [createCheckpoint, updatePreviewToolCallState, acceptChanges]) const handleReject = useCallback(() => { diff --git a/apps/sim/stores/workflow-diff/store.ts b/apps/sim/stores/workflow-diff/store.ts index 2fb8fe65b5..54ee8b69d9 100644 --- a/apps/sim/stores/workflow-diff/store.ts +++ b/apps/sim/stores/workflow-diff/store.ts @@ -433,6 +433,7 @@ export const useWorkflowDiffStore = create {}) } - const toolCallId = await findLatestEditWorkflowToolCallId() - if (toolCallId) { - try { - await getClientTool(toolCallId)?.handleAccept?.() - } catch (error) { - logger.warn('Failed to notify tool accept state', { error }) + findLatestEditWorkflowToolCallId().then((toolCallId) => { + if (toolCallId) { + getClientTool(toolCallId)?.handleAccept?.()?.catch?.((error: Error) => { + logger.warn('Failed to notify tool accept state', { error }) + }) } - } + }) }, rejectChanges: async () => { @@ -487,27 +487,26 @@ export const useWorkflowDiffStore = create { + logger.error('Failed to broadcast reject to other users:', error) + }) + + // Persist to database in background + persistWorkflowStateToServer(baselineWorkflowId, baselineWorkflow).catch((error) => { + logger.error('Failed to persist baseline workflow state:', error) + }) + if (_triggerMessageId) { fetch('/api/copilot/stats', { method: 'POST', @@ -534,16 +552,13 @@ export const useWorkflowDiffStore = create {}) } - const toolCallId = await findLatestEditWorkflowToolCallId() - if (toolCallId) { - try { - await getClientTool(toolCallId)?.handleReject?.() - } catch (error) { - logger.warn('Failed to notify tool reject state', { error }) + findLatestEditWorkflowToolCallId().then((toolCallId) => { + if (toolCallId) { + getClientTool(toolCallId)?.handleReject?.()?.catch?.((error: Error) => { + logger.warn('Failed to notify tool reject state', { error }) + }) } - } - - get().clearDiff({ restoreBaseline: false }) + }) }, reapplyDiffMarkers: () => {