@@ -7,192 +7,33 @@ import { usePreventZoom } from '@/app/workspace/[workspaceId]/w/[workflowId]/hoo
77import { useCopilotStore , usePanelStore } from '@/stores/panel'
88import { useTerminalStore } from '@/stores/terminal'
99import { useWorkflowDiffStore } from '@/stores/workflow-diff'
10- import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
11- import { mergeSubblockState } from '@/stores/workflows/utils'
12- import { useWorkflowStore } from '@/stores/workflows/workflow/store'
1310
1411const logger = createLogger ( 'DiffControls' )
1512
1613export const DiffControls = memo ( function DiffControls ( ) {
1714 const isTerminalResizing = useTerminalStore ( ( state ) => state . isResizing )
1815 const isPanelResizing = usePanelStore ( ( state ) => state . isResizing )
19- const { isDiffReady, hasActiveDiff, acceptChanges, rejectChanges, baselineWorkflow } =
20- useWorkflowDiffStore (
21- useCallback (
22- ( state ) => ( {
23- isDiffReady : state . isDiffReady ,
24- hasActiveDiff : state . hasActiveDiff ,
25- acceptChanges : state . acceptChanges ,
26- rejectChanges : state . rejectChanges ,
27- baselineWorkflow : state . baselineWorkflow ,
28- } ) ,
29- [ ]
30- )
16+ const { isDiffReady, hasActiveDiff, acceptChanges, rejectChanges } = useWorkflowDiffStore (
17+ useCallback (
18+ ( state ) => ( {
19+ isDiffReady : state . isDiffReady ,
20+ hasActiveDiff : state . hasActiveDiff ,
21+ acceptChanges : state . acceptChanges ,
22+ rejectChanges : state . rejectChanges ,
23+ } ) ,
24+ [ ]
3125 )
26+ )
3227
33- const { updatePreviewToolCallState, currentChat , messages } = useCopilotStore (
28+ const { updatePreviewToolCallState } = useCopilotStore (
3429 useCallback (
3530 ( state ) => ( {
3631 updatePreviewToolCallState : state . updatePreviewToolCallState ,
37- currentChat : state . currentChat ,
38- messages : state . messages ,
3932 } ) ,
4033 [ ]
4134 )
4235 )
4336
44- const { activeWorkflowId } = useWorkflowRegistry (
45- useCallback ( ( state ) => ( { activeWorkflowId : state . activeWorkflowId } ) , [ ] )
46- )
47-
48- const createCheckpoint = useCallback ( async ( ) => {
49- if ( ! activeWorkflowId || ! currentChat ?. id ) {
50- logger . warn ( 'Cannot create checkpoint: missing workflowId or chatId' , {
51- workflowId : activeWorkflowId ,
52- chatId : currentChat ?. id ,
53- } )
54- return false
55- }
56-
57- try {
58- logger . info ( 'Creating checkpoint before accepting changes' )
59-
60- // Use the baseline workflow (state before diff) instead of current state
61- // This ensures reverting to the checkpoint restores the pre-diff state
62- const rawState = baselineWorkflow || useWorkflowStore . getState ( ) . getWorkflowState ( )
63-
64- // The baseline already has merged subblock values, but we'll merge again to be safe
65- // This ensures all user inputs and subblock data are captured
66- const blocksWithSubblockValues = mergeSubblockState ( rawState . blocks , activeWorkflowId )
67-
68- // Filter and complete blocks to ensure all required fields are present
69- // This matches the validation logic from /api/workflows/[id]/state
70- const filteredBlocks = Object . entries ( blocksWithSubblockValues ) . reduce (
71- ( acc , [ blockId , block ] ) => {
72- if ( block . type && block . name ) {
73- // Ensure all required fields are present
74- acc [ blockId ] = {
75- ...block ,
76- id : block . id || blockId , // Ensure id field is set
77- enabled : block . enabled !== undefined ? block . enabled : true ,
78- horizontalHandles :
79- block . horizontalHandles !== undefined ? block . horizontalHandles : true ,
80- height : block . height !== undefined ? block . height : 90 ,
81- subBlocks : block . subBlocks || { } ,
82- outputs : block . outputs || { } ,
83- data : block . data || { } ,
84- position : block . position || { x : 0 , y : 0 } , // Ensure position exists
85- }
86- }
87- return acc
88- } ,
89- { } as typeof rawState . blocks
90- )
91-
92- // Clean the workflow state - only include valid fields, exclude null/undefined values
93- const workflowState = {
94- blocks : filteredBlocks ,
95- edges : rawState . edges || [ ] ,
96- loops : rawState . loops || { } ,
97- parallels : rawState . parallels || { } ,
98- lastSaved : rawState . lastSaved || Date . now ( ) ,
99- deploymentStatuses : rawState . deploymentStatuses || { } ,
100- }
101-
102- logger . info ( 'Prepared complete workflow state for checkpoint' , {
103- blocksCount : Object . keys ( workflowState . blocks ) . length ,
104- edgesCount : workflowState . edges . length ,
105- loopsCount : Object . keys ( workflowState . loops ) . length ,
106- parallelsCount : Object . keys ( workflowState . parallels ) . length ,
107- hasRequiredFields : Object . values ( workflowState . blocks ) . every (
108- ( block ) => block . id && block . type && block . name && block . position
109- ) ,
110- hasSubblockValues : Object . values ( workflowState . blocks ) . some ( ( block ) =>
111- Object . values ( block . subBlocks || { } ) . some (
112- ( subblock ) => subblock . value !== null && subblock . value !== undefined
113- )
114- ) ,
115- sampleBlock : Object . values ( workflowState . blocks ) [ 0 ] ,
116- } )
117-
118- // Find the most recent user message ID from the current chat
119- const userMessages = messages . filter ( ( msg ) => msg . role === 'user' )
120- const lastUserMessage = userMessages [ userMessages . length - 1 ]
121- const messageId = lastUserMessage ?. id
122-
123- logger . info ( 'Creating checkpoint with message association' , {
124- totalMessages : messages . length ,
125- userMessageCount : userMessages . length ,
126- lastUserMessageId : messageId ,
127- chatId : currentChat . id ,
128- entireMessageArray : messages ,
129- allMessageIds : messages . map ( ( m ) => ( {
130- id : m . id ,
131- role : m . role ,
132- content : m . content . substring ( 0 , 50 ) ,
133- } ) ) ,
134- selectedUserMessages : userMessages . map ( ( m ) => ( {
135- id : m . id ,
136- content : m . content . substring ( 0 , 100 ) ,
137- } ) ) ,
138- allRawMessageIds : messages . map ( ( m ) => m . id ) ,
139- userMessageIds : userMessages . map ( ( m ) => m . id ) ,
140- checkpointData : {
141- workflowId : activeWorkflowId ,
142- chatId : currentChat . id ,
143- messageId : messageId ,
144- messageFound : ! ! lastUserMessage ,
145- } ,
146- } )
147-
148- const response = await fetch ( '/api/copilot/checkpoints' , {
149- method : 'POST' ,
150- headers : { 'Content-Type' : 'application/json' } ,
151- body : JSON . stringify ( {
152- workflowId : activeWorkflowId ,
153- chatId : currentChat . id ,
154- messageId,
155- workflowState : JSON . stringify ( workflowState ) ,
156- } ) ,
157- } )
158-
159- if ( ! response . ok ) {
160- throw new Error ( `Failed to create checkpoint: ${ response . statusText } ` )
161- }
162-
163- const result = await response . json ( )
164- const newCheckpoint = result . checkpoint
165-
166- logger . info ( 'Checkpoint created successfully' , {
167- messageId,
168- chatId : currentChat . id ,
169- checkpointId : newCheckpoint ?. id ,
170- } )
171-
172- // Update the copilot store immediately to show the checkpoint icon
173- if ( newCheckpoint && messageId ) {
174- const { messageCheckpoints : currentCheckpoints } = useCopilotStore . getState ( )
175- const existingCheckpoints = currentCheckpoints [ messageId ] || [ ]
176-
177- const updatedCheckpoints = {
178- ...currentCheckpoints ,
179- [ messageId ] : [ newCheckpoint , ...existingCheckpoints ] ,
180- }
181-
182- useCopilotStore . setState ( { messageCheckpoints : updatedCheckpoints } )
183- logger . info ( 'Updated copilot store with new checkpoint' , {
184- messageId,
185- checkpointId : newCheckpoint . id ,
186- } )
187- }
188-
189- return true
190- } catch ( error ) {
191- logger . error ( 'Failed to create checkpoint:' , error )
192- return false
193- }
194- } , [ activeWorkflowId , currentChat , messages , baselineWorkflow ] )
195-
19637 const handleAccept = useCallback ( ( ) => {
19738 logger . info ( 'Accepting proposed changes with backup protection' )
19839
@@ -229,12 +70,8 @@ export const DiffControls = memo(function DiffControls() {
22970 } )
23071
23172 // Create checkpoint in the background (fire-and-forget) so it doesn't block UI
232- createCheckpoint ( ) . catch ( ( error ) => {
233- logger . warn ( 'Failed to create checkpoint after accept:' , error )
234- } )
235-
23673 logger . info ( 'Accept triggered; UI will update optimistically' )
237- } , [ createCheckpoint , updatePreviewToolCallState , acceptChanges ] )
74+ } , [ updatePreviewToolCallState , acceptChanges ] )
23875
23976 const handleReject = useCallback ( ( ) => {
24077 logger . info ( 'Rejecting proposed changes (optimistic)' )
0 commit comments