feat: Add ability to export workflow diagram as png - #373
Conversation
Signed-off-by: Cheryl Kong <cherylkong50@gmail.com>
Signed-off-by: Cheryl Kong <cherylkong50@gmail.com>
Signed-off-by: Cheryl Kong <cherylkong50@gmail.com>
Signed-off-by: Cheryl Kong <cherylkong50@gmail.com>
Signed-off-by: Cheryl Kong <cherylkong50@gmail.com>
Signed-off-by: Cheryl Kong <cherylkong50@gmail.com>
✅ Deploy Preview for openworkflow-editor ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
There was a problem hiding this comment.
Pull request overview
Adds a PNG export capability for the React Flow diagram in the Open Workflow diagram editor, integrating a new export utility and exposing the React Flow instance through shared editor context so the side panel can trigger an image download.
Changes:
- Add
html-to-imagedependency and a newexportDiagramAsPngutility for rendering the diagram to a high-resolution PNG. - Replace
MermaidActionswith a broaderExportActionscomponent and add a “Download as PNG” button in the side panel. - Extend
DiagramEditorContextto sharereactFlowInstance,isExporting, and a diagram container ref, and adjust Diagram rendering during export.
Reviewed changes
Copilot reviewed 16 out of 17 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-workspace.yaml | Adds html-to-image to the workspace catalog. |
| pnpm-lock.yaml | Locks html-to-image dependency version and metadata. |
| packages/open-workflow-diagram-editor/package.json | Adds html-to-image as a package dependency. |
| packages/open-workflow-diagram-editor/src/lib/exportPng.ts | New utility to compute bounds and export viewport as PNG via html-to-image. |
| packages/open-workflow-diagram-editor/src/side-panel/ExportActions.tsx | Renamed/expanded actions UI and adds “Download as PNG” behavior. |
| packages/open-workflow-diagram-editor/src/side-panel/SidePanel.tsx | Swaps MermaidActions for ExportActions in the footer. |
| packages/open-workflow-diagram-editor/src/store/DiagramEditorContext.tsx | Extends context type with react-flow instance, exporting state, and container ref. |
| packages/open-workflow-diagram-editor/src/store/DiagramEditorContextProvider.tsx | Implements new context state and exposes diagram container ref through provider. |
| packages/open-workflow-diagram-editor/src/react-flow/diagram/Diagram.tsx | Syncs React Flow instance into context and disables visible-only rendering during export. |
| packages/open-workflow-diagram-editor/src/react-flow/diagram/Diagram.css | Adjusts condition edge stroke styling to use a CSS variable. |
| packages/open-workflow-diagram-editor/src/i18n/locales/en.ts | Adds English string for “Download as PNG”. |
| packages/open-workflow-diagram-editor/src/diagram-editor/DiagramEditor.tsx | Passes diagram container ref into the context provider. |
| packages/open-workflow-diagram-editor/src/components/ui/sonner.css | Updates Sonner toast styling selectors to match portal behavior and enforce positioning. |
| packages/open-workflow-diagram-editor/tests/test-utils/render-helpers.tsx | Updates mocked context with new fields used by export flow. |
| packages/open-workflow-diagram-editor/tests/side-panel/ExportActions.test.tsx | Renames tests and adds coverage for PNG export button behavior. |
| packages/open-workflow-diagram-editor/stories/examples/workflows/listen-to-any-forever-foreach.yaml | Updates example workflow content. |
| .changeset/png-export.md | Adds a minor changeset entry for PNG export feature. |
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.
Signed-off-by: Cheryl Kong <cherylkong50@gmail.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 16 out of 17 changed files in this pull request and generated no new comments.
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
Suppressed comments (3)
Previously missed (3) — in code that hasn't changed since the last review.
packages/open-workflow-diagram-editor/src/side-panel/ExportActions.tsx:92
- The export flow uses a hard-coded
setTimeout(..., 50)to wait for React Flow to render non-visible nodes. This time-based delay can be flaky on slower/faster devices and adds unnecessary latency. Prefer waiting for one or two animation frames so the export reliably happens after the DOM has updated.
packages/open-workflow-diagram-editor/src/components/ui/sonner.css:42 - The Sonner styles are now applied to every
[data-sonner-*]toaster on the page. Since this package can be embedded in other apps, this can unintentionally restyle a host application's own Sonner toasts. You already setclassName="dec:toaster"on the editor’s<Sonner>(seesrc/components/ui/sonner.tsx:30-33), so these rules can be safely scoped to only that toaster.
[data-sonner-toaster] {
--width: 300px !important;
position: fixed !important;
top: var(--offset, 32px) !important;
bottom: auto !important;
.changeset/png-export.md:5
- Changeset summary text is inconsistent with the existing Changesets style (sentence case, proper casing for acronyms like PNG, and punctuation). This becomes user-facing release notes, so it should be polished.
add png export button for react flow diagram
| edges: RF.Edge[]; | ||
| taskReferences: Set<string>; | ||
| selectedNodeId: string | null; | ||
| reactFlowInstance: RF.ReactFlowInstance | null; |
There was a problem hiding this comment.
I dont fully understand why reactFlowInstance or diagramDivRef is needed here? You should be able to access in the Export file directly using something like
const reactFlowInstance = RF.useReactFlow() That would remove a lot of complexity
There was a problem hiding this comment.
useReactFlow() needs the calling component to be rendered inside the <ReactFlow> component itself. In our tree, <ReactFlow> lives inside Diagram, and ExportActions (via SidePanel) is not a descendant:
ReactFlowProvider
└── DiagramEditorContextProvider
├── Diagram
│ └── <ReactFlow> ← useReactFlow() only works inside here
└── SidePanel
└── ExportActions ← sibling, useReactFlow() would throw here
So ExportActions can't call useReactFlow() directly. The instance has to be captured inside Diagram (where <ReactFlow> renders) and shared via context. Same reasoning applies to diagramDivRef. ExportActions has no other way to reach it since it sits outside the canvas subtree
There was a problem hiding this comment.
useReactFlow() only needs to be inside <ReactFlowProvider>, not inside <ReactFlow> itself. That's the
whole point of the provider - it holds the store so components outside the canvas can still reach it.
Had a quick look and if you do the following in WorkflowActions.tsx and it should work
import { useReactFlow } from "@xyflow/react";
const { isExporting, setIsExporting, diagramDivRef } = useDiagramEditorContext();
// delete line
const { reactFlowInstance, setIsExporting, diagramDivRef } = useDiagramEditorContext();
// add
const reactFlowInstance = useReactFlow();
also delete
if (reactFlowInstance === null) return;
and change
disabled={reactFlowInstance === null} > disabled={isExporting}
Test and if that works you can remove it from context etc
There was a problem hiding this comment.
Likewise, you should be able to remove the diagramDivRef from context and have something like this in the file as well
const diagramDomNode = useStore((s) => s.domNode)
Signed-off-by: Cheryl Kong <cherylkong50@gmail.com>
Signed-off-by: Cheryl Kong <cherylkong50@gmail.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 18 out of 20 changed files in this pull request and generated 1 comment.
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
Suppressed comments (3)
Previously missed (2) — in code that hasn't changed since the last review.
packages/open-workflow-diagram-editor/tests/side-panel/WorkflowActions.test.tsx:18
vi.waitForisn’t used elsewhere in this test suite (other tests importwaitForfrom@testing-library/react) and may not exist in the Vitest API, which would break type-checking/runtime. ImportwaitForfrom@testing-library/reacthere as well.
packages/open-workflow-diagram-editor/tests/side-panel/WorkflowActions.test.tsx:141- Use
waitForfrom@testing-library/reactinstead ofvi.waitForso the assertion retry mechanism works consistently with the rest of the test suite.
This issue also appears on line 162 of the same file.
packages/open-workflow-diagram-editor/tests/side-panel/WorkflowActions.test.tsx:164
- Use
waitForfrom@testing-library/reactinstead ofvi.waitForso the assertion retry mechanism works consistently with the rest of the test suite.
Signed-off-by: Cheryl Kong <cherylkong50@gmail.com>
| throw new Error("No nodes to export"); | ||
| } | ||
|
|
||
| const { x: minX, y: minY, width, height } = getNodesBounds(nodes); |
There was a problem hiding this comment.
Would be safer here to have reactFlowInstance.getNodesBounds(nodes);. The instance passes nodeLookup, which has the already resolved positions, so nested workflows get the right box. It looks ok on the ones I tested but could be an issue in the future
Like we have it in const nodes above, you can see the doc here as well https://reactflow.dev/api-reference/types/react-flow-instance
| cursor: pointer; | ||
| } | ||
| } | ||
| } No newline at end of file |
There was a problem hiding this comment.
Just fix the newline here so file has no changes
Closes #341
Summary
This PR adds a
Download as PNGbutton to the side panel, which allows users to export the workflow diagram as a PNG file.Preview:

Key Changes
Preview of exported PNG from Managing Github Issues (under Use Cases):
