Skip to content

feat(workflows): show email previews in the workflow editor - #111731

Closed
Silthus wants to merge 5 commits into
PostHog:masterfrom
Silthus:t3code/embedded-email-previews
Closed

Silthus wants to merge 5 commits into
PostHog:masterfrom
Silthus:t3code/embedded-email-previews

Conversation

@Silthus

@Silthus Silthus commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Problem

  • People building a workflow see each email step as a small title card, so they cannot tell what an email says without opening it.
  • Emails are what most workflows send, so the editor should keep their content in view.

Note

🔵 Merge risk: low (merge-risk rubric)

  • Graph layout and edge handles now use each node's own size, so a sizing mistake would misplace edges in every workflow graph.
  • Nodes without email HTML keep their old size. Logic tests cover email sizing, sibling-branch alignment, and dropzone centering. The change is frontend-only and reverts cleanly.

Changes

Kind Files Lines
Production 9 +98 −38
Tests 2 +142 −1
Total 11 +240 −39
  • Email steps show a scaled-down preview of their HTML in the graph editor and in the list view.
  • In dark mode the preview is inverted, so a white email does not glare in a dark editor. Photos inside the email invert too.
  • The preview reuses EmailPreviewThumbnail, which is sandboxed, runs no scripts, and keeps the HTML out of session replay with ph-no-capture. Two sizes are added to it.
  • Previews appear for function_email steps and for generic steps that use the email template.
  • In the graph, email nodes are larger (182×234 instead of 120×34).
  • The graph layout and edge handles use each node's size. Sibling branches still start on one row, and the branch-join dropzone stays centered on the larger node.
  • In the list view, a 288px preview sits under the step title, and the icon and drag handle align to the top.
  • Liquid variables show as written, for example {{ person.properties.first_name }}.
  • Mechanical: the fixed handle-position constants move into a nodeSize helper, and the story fixtures get a realistic email template.
Light Dark
Graph graph light graph dark
List list light list dark

Before, an email step looked like every other step: a title card with no content. These screenshots replace the first upload, which showed an earlier iteration.

Note

Known limits:

  • The graph preview has a fixed height. A short email leaves empty space, and a long one is cut off.
  • In deeply nested list branches, the 288px preview can clip on the right.
  • Edges are straight vertical lines, so in branched graphs an edge can pass behind a tall email node. This existed before for regular nodes and is more frequent now.

How did you test this code?

Test rationale: hogFlowEditorLogic.test.ts gets four cases. The closest existing tests, "graph identity across rebuilds" and "showDropzones branch-join placement", do not check sizes.

  • Email layout, for function_email and for generic email-template steps: fails when the layout or the edge handles ignore the email node size, which makes the preview overlap the next step.
  • Sibling branch row: fails when ELK centers a layer again (before the fix the sibling sat 100px lower).
  • Branch-join dropzone: fails when the dropzone sits off-center on a wider node.

Verify with hogli test products/workflows/frontend/Workflows/hogflows/hogFlowEditorLogic.test.ts. Expect 31 passing tests, including the "email step layout" block.

  • The graph and list editor stories were rendered in light and dark mode through Storybook and Playwright.
  • The visual review baselines for the graph and list stories that contain email steps change.
  • Not checked: real customer templates, browsers other than Chromium, and the frontend CI jobs, which the draft matrix does not run.

👉 Stay up-to-date with PostHog coding conventions for a smoother review.

Release status

  • No feature flag controls this change
  • This change is behind a feature flag and is not available to users
  • This change makes a previously flagged feature available to everyone

Automatic notifications

  • Publish to changelog?

Docs update

None.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Agent: Claude Code, Claude Opus 5.5. Review panel: GPT-6.1 Sol through Codex, plus Claude Opus.

  • Skills invoked: /tdd, /writing-tests, /writing-ui-components, /reviewing-with-coderabbit, /writing-pr-descriptions, /share-preview, /qa-swarm, /merge-risk, /leaving-pr-comments.
  • CodeRabbit CLI: skipped. The CLI was signed out, and the developer chose to skip the local pass.
  • The work started as a prototype shared through a Storybook preview link. Dark mode and the tests came after review of that prototype.
  • A four-round review panel replaced a bespoke iframe with EmailPreviewThumbnail, which fixed session replay capture and email media queries. It also found the generic email-template steps, the dropzone offset, and the sibling-row offset.
  • Dark mode uses Tailwind dark: classes, not an inversion inside the email document. Storybook sets only body[theme], and color-scheme did not reach the iframe's media query in Chromium.
  • The CodeRabbit thread about external images was answered and withdrawn: the viewers are the email's own team, and the same HTML already loads in the email editor.
  • Duplicate search: no open PR shows email content in the editor graph or list. feat(workflows): preview a workflow email's merge tags before the send #105379 previews merge tags inside the email editor, which is a different surface.
  • All fixture and screenshot content is invented.

🤖 Generated with Claude Code

@trunk-io

trunk-io Bot commented Oct 5, 2026

Copy link
Copy Markdown

Merging to master in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here

@coderabbitai

coderabbitai Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Important

Review skipped

We couldn't safely recover the incremental review. No full review was started, and the last reviewed checkpoint was preserved. Retry later, or explicitly request a full review by commenting @coderabbitai full review.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: PostHog/posthog/.coderabbit.yaml
  • Review profile: QUIET
  • Plan: Enterprise
  • Run ID: e0073ff7-aeee-474b-9652-d627c9b66f1f
📥 Commits

Reviewing files that changed from the base of the PR and between bae437d and 5af3b90.

📒 Files selected for processing (7)
  • frontend/src/lib/components/EmailPreviewThumbnail/EmailPreviewThumbnail.tsx
  • products/workflows/frontend/Workflows/hogflows/hogFlowEditorLogic.test.ts
  • products/workflows/frontend/Workflows/hogflows/react_flow_utils/autolayout.ts
  • products/workflows/frontend/Workflows/hogflows/react_flow_utils/nodeSize.ts
  • products/workflows/frontend/Workflows/hogflows/steps/components/StepView.tsx
  • products/workflows/frontend/Workflows/hogflows/steps/emailStepHtml.ts
  • products/workflows/frontend/Workflows/hogflows/tree/HogFlowTreeStep.tsx

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

Workflow email actions with configured HTML now display previews in the editor and tree view. Node dimensions, layout, and edge handle offsets use action-specific sizes. Branch-join dropzones are centered using the target node width. The example email action now uses a styled HTML template, and tests cover email-node layout and dropzone centering.

Priority: ➖ Normal

Merge Risk: 🟡 Moderate · up to 5af3b

Opening a workflow can now request externally hosted images in its email previews, exposing the viewer’s IP address to those hosts. Restrict those requests or explicitly accept that exposure before merging.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 5af3b

The previews retain script and same-origin isolation. However, opening a workflow can now load remote images embedded in its email content without opening the email editor. This creates a limited privacy risk; access to application credentials or other workflows has not been demonstrated.

Retained concerns

  • Low · security · inferred: An actor able to influence configured workflow email HTML can embed a remote image that may receive a request when another user opens the workflow graph or list. Previously, the workflow email editor already permitted this rendering capability, but the PR removes the need to open that editor. The empty iframe sandbox isolates execution and origin access, not image requests. This can expose viewing activity and network metadata; application-secret exfiltration and an authorization bypass are not established.
Security review details

Security Blast Radius

  • inferred — The supported exposure is client-side: browsers rendering affected workflow email actions may contact author-selected image hosts. Exploitation requires influence over the configured HTML and a user viewing the affected workflow. Exact author/viewer role boundaries remain unverified; no server-side fetch or cross-tenant access was established.

Security Findings and Attack Paths

  • inferred — A configured remote image provides a passive request path from opening the workflow to an external host, without script execution or opening the email editor. The prior editor already allowed equivalent HTML rendering; the PR expands automatic reachability. No browser trace or application-secret disclosure was observed.

Trust Boundaries and Controls

  • observed — Configured HTML is isolated in an iframe without script or same-origin allowances. The source-level application policy nevertheless admits HTTPS images from any host, and its narrower image policy is monitoring-only. Deployed headers and additional deployment restrictions were not inspected.

Hardening Proposals

  • proposed — Define an explicit remote-resource policy for automatic workflow previews, such as opt-in external image loading or network-inert previews, while retaining the existing sandbox. Validate the intended policy against deployed headers and supported browsers.
🚥 Pre-merge checks | ✅ 1
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The description explains the user problem, visible changes, tests and test rationale, release status, docs status, and agent context. It includes frontend screenshots and known limits. The release sta…
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

🟡 Other comments (1)
products/workflows/frontend/Workflows/hogflows/steps/components/EmailStepPreview.tsx-9-9 (1)

9-9: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep each email’s doctype ahead of the preview style.

scaleToWidth puts <style> before the configured HTML. When that HTML begins with a doctype, the iframe parser has already entered quirks mode before it reaches the doctype. Quirks-mode table sizing can change these table-based previews. The proposed <!doctype html> prefix would take precedence over an authored doctype. Preserve an existing doctype, and add an HTML5 doctype only when the input has none.

Suggested fix
 function scaleToWidth(html: string, width: number): string {
-    return `<style>html{zoom:${width / EMAIL_LAYOUT_WIDTH};overflow:hidden}</style>${html}`
+    const style = `<style>html{zoom:${width / EMAIL_LAYOUT_WIDTH};overflow:hidden}</style>`
+    const doctype = html.match(/^\s*<!doctype\b[^>]*>/i)?.[0]
+    if (doctype) {
+        return `${doctype}${style}${html.slice(doctype.length)}`
+    }
+    return `<!doctype html>${style}${html}`
 }

ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository: PostHog/posthog/.coderabbit.yaml
  • Review profile: QUIET
  • Plan: Enterprise
  • Run ID: 660dec19-01a2-4981-a48f-94327559abb3
📥 Commits

Reviewing files that changed from the base of the PR and between d463d51 and 121f703.

📒 Files selected for processing (11)
  • products/workflows/frontend/Workflows/hogflows/hogFlowEditorLogic.test.ts
  • products/workflows/frontend/Workflows/hogflows/hogFlowEditorLogic.tsx
  • products/workflows/frontend/Workflows/hogflows/react_flow_utils/autolayout.ts
  • products/workflows/frontend/Workflows/hogflows/react_flow_utils/constants.ts
  • products/workflows/frontend/Workflows/hogflows/react_flow_utils/nodeSize.ts
  • products/workflows/frontend/Workflows/hogflows/steps/Nodes.tsx
  • products/workflows/frontend/Workflows/hogflows/steps/components/EmailStepPreview.tsx
  • products/workflows/frontend/Workflows/hogflows/steps/components/StepView.tsx
  • products/workflows/frontend/Workflows/hogflows/steps/emailStepHtml.ts
  • products/workflows/frontend/Workflows/hogflows/tree/HogFlowTreeStep.tsx
  • products/workflows/frontend/Workflows/hogflows/tree/exampleWorkflows.ts
💤 Files with no reviewable changes (1)
  • products/workflows/frontend/Workflows/hogflows/react_flow_utils/constants.ts

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 4 remain after this review.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository: PostHog/posthog/.coderabbit.yaml
  • Review profile: QUIET
  • Plan: Enterprise
  • Run ID: d7b8a1e5-2887-4425-bb34-766ddd5269bd
📥 Commits

Reviewing files that changed from the base of the PR and between 121f703 and bae437d.

📒 Files selected for processing (7)
  • frontend/src/lib/components/EmailPreviewThumbnail/EmailPreviewThumbnail.tsx
  • products/workflows/frontend/Workflows/hogflows/hogFlowEditorLogic.test.ts
  • products/workflows/frontend/Workflows/hogflows/hogFlowEditorLogic.tsx
  • products/workflows/frontend/Workflows/hogflows/react_flow_utils/nodeSize.ts
  • products/workflows/frontend/Workflows/hogflows/steps/components/StepView.tsx
  • products/workflows/frontend/Workflows/hogflows/tree/HogFlowTreeStep.tsx
  • products/workflows/frontend/Workflows/hogflows/tree/exampleWorkflows.ts

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 4 remain after this review.

@Silthus
Silthus marked this pull request as ready for review October 5, 2026 16:55
@parameterai

parameterai Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Risk: No findings

The increment since the last review only tightens the workflow editor's fitView math — it derives a clamped fit zoom (bounded by the new MIN_ZOOM/MAX_ZOOM constants) from the node bounds and wrapper dimensions, and widens two generated-style type declarations with utm_params fields. All of it is pure client-side layout computation: no new network calls, no injection sinks, no auth or tenant-scoping changes, and no attacker-controlled data reaching a dangerous sink. I checked the division-by-zero path (a zero-width/height node bounds falls back to the current zoom at hogFlowEditorLogic.tsx:2926-2929), so no crash or DoS surface is introduced.

Sentinel reviewed e8f6965 · Review settings

@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested a review from a team October 5, 2026 16:56
Silthus and others added 5 commits October 7, 2026 07:59
Email steps render a scaled preview of their HTML in the graph editor and
the list view. Graph nodes for email steps are taller, so the layout and
edge handles use each node's size. In dark mode the preview is inverted.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Reuse EmailPreviewThumbnail so previews stay out of session replay and
render the email at its 600px layout. Center the branch-join dropzone on
wider email nodes, and invert the clipped thumbnail in dark mode so no
white corners show.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@Silthus

Silthus commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

Note

🤖 GPT-6.1 Sol responding on behalf of Michael

Replaced by the draft fork PR #356 in its native fork stack. @meikelmosby will start migrating the stack from the fork into PostHog/posthog for upstream review and CI. Closing this master-targeted copy to keep the handoff clear.

@Silthus Silthus closed this Oct 7, 2026
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