Skip to content

Add state management, undo / redo capabilities and component API - #324

Open
handreyrc wants to merge 3 commits into
open-workflow-specification:mainfrom
handreyrc:add-undo-redo
Open

Add state management, undo / redo capabilities and component API #324
handreyrc wants to merge 3 commits into
open-workflow-specification:mainfrom
handreyrc:add-undo-redo

Conversation

@handreyrc

Copy link
Copy Markdown
Contributor

Closes #318

Summary

This PR adds state management as the model changes, undo/redo capabilities by handling a stack of states, and implements an API to expose those features.

Changes

  • Added state management integrated to the store, driven by model changes.
  • Added a stack of states where new states are pushed on top.
  • Added undo/redo capabilities and an API to trigger them and cause the diagram to load the stored states.
  • Added means to store and restore viewport state (zoom and pan) along with the model state.
  • Added means to restore selection if the name of the task has not changed.
  • Added an API to expose getContent, setContent, undo, redo, canUndo, canRedo, and colorMode, so it is possible to interact with the editor component by exposing the editor's ref and calling those functions from the browser console, making it easier to integrate with external components.
  • DiagramEditor, store, diagram error handling, and I18n were refactored and optimized to accommodate state management and the changes in the contextProvider.
  • Added the fast-equals library to detect structural changes between the current model and the new model.
    • Added a custom comparison function to ignore class-based internals, handle circular references, and treat field order as insignificant, so objects with the same fields and values in any order are considered equal.
  • Added an Undo/Redo story under features with a Docs section detailing the features and a story where all implemented features can be tested.

Signed-off-by: handreyrc <handrey.cunha@gmail.com>
Signed-off-by: handreyrc <handrey.cunha@gmail.com>
Copilot AI lite review requested due to automatic review settings August 11, 2026 20:51
@handreyrc handreyrc self-assigned this Aug 11, 2026
@netlify

netlify Bot commented Aug 11, 2026

Copy link
Copy Markdown

Deploy Preview for openworkflow-editor ready!

Name Link
🔨 Latest commit 012357b
🔍 Latest deploy log https://app.netlify.com/projects/openworkflow-editor/deploys/6a7b9cb57a91150008304f8f
😎 Deploy Preview https://deploy-preview-324--openworkflow-editor.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Adds editor state/history management to support undo/redo (including viewport + selection restore) and exposes a new imperative API on the editor ref for integration/testing.

Changes:

  • Introduces generic useHistory and workflow-specific useWorkflowHistory hooks, plus structural equality comparison to avoid redundant history entries.
  • Refactors Diagram and store context provider to seed/history-track models and restore viewport/selection during undo/redo.
  • Adds Storybook feature story + tests for history behavior and ref API; adds fast-equals dependency.

Reviewed changes

Copilot reviewed 18 out of 20 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
pnpm-workspace.yaml Adds fast-equals to the workspace catalog.
packages/open-workflow-diagram-editor/package.json Adds fast-equals dependency for structural comparisons.
packages/open-workflow-diagram-editor/src/core/hooks/structuralEqual.ts Implements constructor-agnostic deep structural equality with circular handling.
packages/open-workflow-diagram-editor/src/react-flow/hooks/useHistory.ts Adds generic past/present/future history reducer + hook with stack cap.
packages/open-workflow-diagram-editor/src/react-flow/hooks/useWorkflowHistory.ts Adds workflow-aware history snapshots (model/viewport/selection) + undo/redo behavior.
packages/open-workflow-diagram-editor/src/store/DiagramEditorContext.tsx Extends context type with history + content-format APIs.
packages/open-workflow-diagram-editor/src/store/DiagramEditorContextProvider.tsx Seeds history from content, exposes imperative API, and wires history into context.
packages/open-workflow-diagram-editor/src/react-flow/diagram/Diagram.tsx Gates ReactFlow mount until first layout, submits snapshots, and restores viewport on undo/redo.
packages/open-workflow-diagram-editor/src/diagram-editor/DiagramEditor.tsx Refactors editor shell + docs and attempts to expose imperative API via context provider.
packages/open-workflow-diagram-editor/src/styles.css Removes stray trailing whitespace.
packages/open-workflow-diagram-editor/stories/features/UndoRedoEditor.tsx Adds a Storybook wrapper with undo/redo toolbar + window-exposed ref.
packages/open-workflow-diagram-editor/stories/features/UndoRedo.stories.tsx Adds Storybook docs + interactive story for undo/redo and ref API.
packages/open-workflow-diagram-editor/tests/react-flow/hooks/useHistory.test.ts Adds unit tests for generic history reducer/hook behavior.
packages/open-workflow-diagram-editor/tests/react-flow/hooks/useWorkflowHistory.test.ts Adds tests for workflow history snapshots, equality behavior, and viewport restore.
packages/open-workflow-diagram-editor/tests/core/hooks/structuralEqual.test.ts Adds comprehensive tests for structural equality across class/plain + circular refs.
packages/open-workflow-diagram-editor/tests/react-flow/diagram/Diagram.test.tsx Updates tests to wait for delayed ReactFlow mount after layout gating.
packages/open-workflow-diagram-editor/tests/store/DiagramEditorContextProvider.test.tsx Updates expected render cycles due to history seeding effect.
packages/open-workflow-diagram-editor/tests/diagram-editor/DiagramEditor.test.tsx Expands tests for ref API (undo/redo/getContent/setContent) and async canvas-dependent UI.
.changeset/state-management.md Publishes a minor version bump describing new state/history + API.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/open-workflow-diagram-editor/src/store/DiagramEditorContextProvider.tsx Outdated
Comment thread packages/open-workflow-diagram-editor/src/diagram-editor/DiagramEditor.tsx Outdated
Comment on lines +193 to +195
// setIsReadOnly is intentionally inoperative: isReadOnly is driven by
// props, not internal state, so there is no local setter to dispatch to.
setIsReadOnly: () => {},

@handreyrc handreyrc Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed! I opted for removing setIsReadOnly from DiagramEditorContextType.
@lornakelly @fantonangeli @kumaradityaraj, lets be careful with this one. I couldn't find any side effect but it is good to double check it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@handreyrc opening the preview and settings isReadOnly to false, I could not move the nodes in the diagram. Am I missing somenthing?

Comment thread packages/open-workflow-diagram-editor/stories/features/UndoRedoEditor.tsx Outdated
Signed-off-by: handreyrc <handrey.cunha@gmail.com>
Copilot AI review requested due to automatic review settings August 11, 2026 22:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 19 out of 21 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file
Suppressed comments (8)

packages/open-workflow-diagram-editor/src/store/DiagramEditorContextProvider.tsx:51

  • The comment says the content format is fixed at mount time, but setContent() can update contentFormat.current. This is misleading documentation and makes it harder to reason about getContent() behavior.
  // Detect the serialization format once from the initial content prop.
  // JSON content starts with `{` (after trimming); everything else is YAML.
  // We use a ref so the format is fixed at mount time and never flips mid-session
  // (an undo/redo should round-trip back in the same format the host provided).

packages/open-workflow-diagram-editor/src/store/DiagramEditorContextProvider.tsx:89

  • Undo/redo changes the model from history, but errors are currently tied to props.content. Deriving errors from the current model keeps validation/error-highlighting consistent across undo/redo snapshots, while still using parse errors when no model is available.
  // parseWorkflow drives both errors and the external-content model source.
  // errors are never part of a snapshot — always recomputed from current content.
  const { model: parsedModel, errors } = React.useMemo(
    () => parseWorkflow(props.content),
    [props.content],

packages/open-workflow-diagram-editor/src/react-flow/diagram/Diagram.tsx:131

  • If applyAutoLayout throws on the initial render, layoutReady stays false and the ReactFlow canvas never mounts, leaving the editor blank. Consider falling back to rendering the un-laid-out graph (or at least setting layoutReady to true) on non-abort errors.
        .catch((error) => {
          if (error.name === "AbortError") {
            return;
          }
          console.error("Failed to apply auto-layout:", error);

packages/open-workflow-diagram-editor/src/diagram-editor/DiagramEditor.tsx:86

  • This prop doc says the serialization format is preserved for the lifetime of the component, but the ref API docs (and tests) indicate the format can change after a successful setContent() call. Please align the documentation with the actual behavior.
   * The serialisation format is auto-detected on first load and preserved for
   * the lifetime of the component — see `getContent()` on `DiagramEditorRef`.

packages/open-workflow-diagram-editor/src/diagram-editor/DiagramEditor.tsx:183

  • DiagramEditor computes a fallback locale (and uses it for <I18nProvider> and the lang attribute), but DiagramEditorBody passes the raw props.locale down into DiagramEditorContextProvider. If locale is omitted at runtime, the context provider can receive undefined and diverge from the I18n provider.
            props={props}

packages/open-workflow-diagram-editor/src/store/DiagramEditorContextProvider.tsx:19

  • Undo/redo changes the model from history, but errors are derived from parseWorkflow(props.content) and therefore won’t match the restored snapshot. This can make error highlighting inconsistent after undo/redo or after imperative setContent() (which doesn’t change props.content).

This issue also appears on line 85 of the same file.

import { buildFlatGraph, getTaskReferences, parseWorkflow } from "../core";

packages/open-workflow-diagram-editor/stories/features/UndoRedo.stories.tsx:101

  • The Storybook docs state that getContent() format is fixed at mount time, but the implementation/tests describe format switching after a successful setContent() call. This section is internally inconsistent (it later says the format becomes the new format) — please make the docs unambiguous.
Serialises the current model back to the **same format the editor received
on first load**: if the initial \`content\` prop was JSON it returns JSON; if
it was YAML it returns YAML. The format is fixed at mount time and preserved
for the lifetime of the component, so undo/redo always round-trips in the
original format. Returns \`""\` when no valid model has been loaded yet.

packages/open-workflow-diagram-editor/tests/test-utils/render-helpers.tsx:45

  • DiagramEditorContextType now requires contentFormat and the history API members, but the test mock context value doesn’t provide them. This should be a type error and may also cause runtime issues in tests that rely on these fields.
  edges: [],
  taskReferences: new Set(),
  selectedNodeId: null,
  setLocale: noop,
  setEdges: noop,
  setNodes: noop,

@lornakelly

Copy link
Copy Markdown
Collaborator

Thanks for PR @handreyrc, looks really good, have just tested the story so far but noticed a couple of things:

  • setContent doesnt seem to be parsing the model fully as its not validating, the validation gets triggered when you undo/redo
  • Also, we should hide the toolbar when isReadOnly is true as currently it allows you to edit when it is true
Screen.Recording.2026-08-12.at.10.46.38.mov

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.

feat: Implement state management, undo / redo capabilities and component API

4 participants