Skip to content

feat(undo-redo): unify field, code, and loop config edits into one stack#4875

Open
waleedlatif1 wants to merge 3 commits into
stagingfrom
waleedlatif1/stack-integration-gaps
Open

feat(undo-redo): unify field, code, and loop config edits into one stack#4875
waleedlatif1 wants to merge 3 commits into
stagingfrom
waleedlatif1/stack-integration-gaps

Conversation

@waleedlatif1
Copy link
Copy Markdown
Collaborator

@waleedlatif1 waleedlatif1 commented Jun 3, 2026

Summary

  • Block-level field edits (agent tools, model, prompts, dropdowns, sliders, tag/reference insertion) now record on the workflow undo stack — previously only structural canvas ops were undoable, so e.g. deleting tools on an Agent block couldn't be undone
  • Consolidated the separate per-field code-editor undo stack into the single workflow stack and deleted it (use-code-undo-redo, code-store); every text editor now routes Cmd+Z/Cmd+Shift+Z to the workflow undo and suppresses native/editor-internal undo, so there is one source of truth
  • Persist the unified stack to IndexedDB (async, large quota) with throttled writes instead of synchronous localStorage — code-field frames are large and accumulate quickly, so this avoids main-thread jank per keystroke and the ~5MB localStorage quota (matches how the old code stack was stored)
  • Loop/parallel config values (iterations, collection, condition, batch size) are now undoable; consecutive same-field edits coalesce into one step (subblock and subflow)
  • The model→API-key clear now records as a single undo step
  • Undo/redo reveals the affected block (selects it + opens its editor panel) so a reverted field change is never off-screen
  • Also fixed a few pre-existing lint issues (useParseIntRadix, useIterableCallbackReturn) in the touched files to keep the pre-commit hook green

Type of Change

  • New feature

Testing

  • Added unit tests for store coalescing (subblock + subflow) and reveal targeting
  • Manual smoke-test recommended for editor interactions (delete a tool → undo, code edit → undo after blur, prompt + reference insert → undo, loop iterations → undo, model→apiKey → one undo, undo from canvas reveals the block)

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

- Record block-level field edits (tools, model, prompts, dropdowns, sliders,
  tag/reference insertion) on the workflow undo stack
- Consolidate the separate per-field code-editor stack into the workflow stack
  and delete it; route Cmd+Z/redo in every text editor to the workflow undo so
  native/editor undo is suppressed and there is one source of truth
- Make loop/parallel config values (iterations, collection, condition, batch
  size) undoable; coalesce consecutive same-field edits into one step
- Group the model->apiKey clear into a single undo step
- Reveal the affected block (select + open its editor panel) on undo/redo so a
  reverted field change is never off-screen
@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 3, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jun 4, 2026 12:12am

Request Review

@cursor
Copy link
Copy Markdown

cursor Bot commented Jun 3, 2026

PR Summary

Medium Risk
Touches collaborative persistence, undo recording for all subblock paths, and keyboard handling across many editors; regressions could affect undo scope, double-recording, or remote sync, though tests cover coalescing and reveal.

Overview
Unifies workflow undo/redo so panel field edits (prompts, selectors, code, loop/parallel config) share one stack with canvas structural changes, instead of a separate per-function code history.

Editors: New useEditorUndoRedo sends Cmd/Ctrl+Z and redo to the workflow stack and blocks native editor undo. The old use-code-undo-redo / IndexedDB code stack is removed.

Recording: collaborativeSetSubblockValue can recordUndo, group dependent clears and linked updates (e.g. model change + API key clear) into one step, and record loop/parallel config via recordSubflowFieldUpdate. useSubBlockValue and selector changes opt in with recordUndo: true. Search-replace applies subflow follow-ups under suspended recording to avoid duplicate undo entries.

Stack behavior: Persists to throttled IndexedDB; coalesces rapid single-field edits (~500ms); after undo/redo, reveals the affected block in the editor panel.

Reviewed by Cursor Bugbot for commit 9a24aa9. Configure here.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Jun 3, 2026

Greptile Summary

This PR unifies block-level field edits, code editor keystrokes, and loop/parallel config changes into the single workflow undo stack, replaces per-field code undo and synchronous localStorage with a throttled IndexedDB adapter, and adds a "reveal" mechanism that opens the affected block's editor panel after an undo/redo.

  • Undo stack unification: BATCH_UPDATE_SUBBLOCKS now records subblock values, tag selections, model→API-key cascades, and loop/parallel config fields; consecutive same-field edits within 500 ms coalesce into one step via the new describeSingleField/buildSingleFieldData helpers.
  • Editor keyboard routing: useEditorUndoRedo intercepts Cmd+Z/Cmd+Shift+Z inside all text editors and routes them to the workflow stack, suppressing native editor undo; the old use-code-undo-redo hook and code-store are deleted.
  • IndexedDB persistence: The new createThrottledIndexedDbStorage adapter batches writes (1 s throttle) and flushes eagerly on pagehide/visibilitychange, avoiding the 5 MB localStorage quota and per-keystroke main-thread writes.

Confidence Score: 4/5

Safe to merge with one targeted fix: the condition-field undo writes to the wrong loop field when the user switches loop type between edit and undo.

The overall architecture is solid — coalescing, IndexedDB throttling, reveal targeting, and keyboard routing all work correctly. The one concrete defect is in applySubflowUndoRedoUpdate: both whileCondition and doWhileCondition share the subflowCondition fieldId, so undo/redo resolves the target field from the current loopType rather than the loopType at record time. Changing the loop mode between an edit and its undo silently writes the condition value to the wrong field.

apps/sim/hooks/use-undo-redo.ts — specifically applySubflowUndoRedoUpdate where condition undo/redo branches on current loopType.

Important Files Changed

Filename Overview
apps/sim/hooks/use-undo-redo.ts New applySubflowUndoRedoUpdate resolves the correct loop store method using the current loopType, not the loopType at record time — if the user switches whiledoWhile between edit and undo, the condition value is silently written to the wrong field.
apps/sim/stores/undo-redo/store.ts New subblock/subflow coalescing logic is well-structured; 500ms window, key-based deduplication, and no-op dropping are all correct.
apps/sim/stores/undo-redo/storage.ts New throttled IndexedDB adapter looks correct: pending-map read-before-write for stale reads, pagehide/visibilitychange flush guards, SSR protection.
apps/sim/hooks/use-collaborative-workflow.ts New collaborativeSetSubblockValue undo collector pattern, collaborativeSetTagSelection recording, and recordSubflowFieldUpdate are all consistent.
apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value.ts Model→API key linked-update grouping is cleanly integrated; the linkedUpdates mechanism passes down into the undo collector so the clear records as one step.
apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-input.ts Correctly routes Cmd+Z/Shift+Z to the workflow undo stack; handleEditorUndoRedo missing from dep arrays of handleKeyDown and createFieldHandlers.
apps/sim/stores/undo-redo/reveal.ts Clean utility; correctly returns null for structural operations and documents the intentional single-panel limitation.
apps/sim/stores/undo-redo/store.test.ts Good coverage of coalescing, suspension, pruning, and isolation; new subflow cases verify field-boundary guards.

Reviews (2): Last reviewed commit: "fix(undo-redo): address PR review feedba..." | Re-trigger Greptile

Comment thread apps/sim/stores/undo-redo/reveal.ts
Code-field undo frames are large and accumulate quickly; keeping them in the
synchronous localStorage-backed stack risked main-thread jank on every keystroke
and the ~5MB quota. Move the unified undo store to async IndexedDB (idb-keyval),
matching how the old separate code stack was persisted.

- Replace the localStorage persist adapter with a throttled IndexedDB adapter
  (coalesces a burst of writes into one transaction; flushes on tab hide)
- Delete the now-orphaned code-storage adapter
- collaborativeBatchSetSubblockValues: capture each field's real prior value
  before mutating instead of recording before: update.expectedValue, which was
  undefined when a caller omitted expectedValue (would undo a field to undefined)
- List the stable recordSubflowFieldUpdate helper in the subflow methods'
  dependency arrays
- Document that a multi-block batch undo reveals only its first block
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

Thanks for the review — addressed the two concerns from the summary in 9a24aa9:

  • collaborativeBatchSetSubblockValues undo-before could be undefined: this was real. It recorded before: update.expectedValue, which is undefined when a caller omits expectedValue — undo would set the field to undefined. Now it captures each field's actual prior value from the store before mutating, so undo always restores the real value regardless of expectedValue.
  • Subflow useCallback deps missing recordSubflowFieldUpdate: added it to the three subflow methods' dependency arrays. (The helper is intentionally stable — it reads the latest recorder via a ref and has [] deps — so this is purely for correctness/lint, no extra re-creation.)

The multi-block reveal note is addressed inline and resolved.

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 9a24aa9. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant