Skip to content

Render dashboard preview panes inside medium-matching chrome (browser for web, email for emails)#5888

Merged
gianfrancopiana merged 17 commits into
mainfrom
pages-preview-browser-chrome
Jul 20, 2026
Merged

Render dashboard preview panes inside medium-matching chrome (browser for web, email for emails)#5888
gianfrancopiana merged 17 commits into
mainfrom
pages-preview-browser-chrome

Conversation

@gumclaw

@gumclaw gumclaw commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

What

Wraps dashboard preview panes in shared chrome that matches the medium being previewed:

  • Web previews use a centered title, quiet inset URL field, and compact open-in-new-tab action with a 44×44px hit area.
  • Email previews use left-aligned From and Subject fields, with no URL or open action.
  • Unsaved pages omit the URL and action.
  • The chrome itself is not selectable; the actual preview content remains selectable.

The shared frame is used by Pages, product and bundle editors, the Receipt tab, Checkout, workflow emails, and profile settings. Preview frames no longer repeat a separate Preview heading or URL caption.

Checkout is intentionally different: it is labeled Checkout preview and has no URL or arrow because it renders a synthetic cart that does not exist at /checkout. Its layout now responds to the preview container, so the narrow embedded preview uses the real checkout's stacked layout while the full checkout still switches to two columns when there is enough room.

Receipt From, Subject, and body values come from the same Rails preview response, so unsaved client state cannot make the chrome disagree with the email body.

Per-surface chrome

Surface Chrome Identity Open action
Pages editor Web Page title and public URL Public page; omitted for unsaved pages
Product edit Web Product name and short URL Save then open
Bundle edit Web Bundle name and short URL Save then open
Receipt tab Email From and Subject None
Checkout dashboard Web Checkout preview None; the cart is synthetic
Workflow emails Email From; each email keeps its own subject heading None
Profile settings Web Seller name and profile URL Save then open

Why

This follows the first-class Pages work in #5812 and Sahil's direction to apply the same preview language to product pages and all other preview panes, while matching email previews to email rather than browser chrome.

The result keeps identity and actions in one place, aligns the preview with the edit form, and avoids false affordances. Only the arrow reacts on hover; the preview frame itself stays unchanged. No fake browser controls were added.

Screenshots

All after screenshots were recaptured locally at the current branch head in a consistent 1600×1000 viewport.

Rich text page

Before After (light) After (dark)
Before Rich text light Rich text dark

Home

Light Dark
Home light Home dark

New page

Light Dark
New page light New page dark

Product edit

Light Dark
Product edit light Product edit dark

Bundle edit

Light Dark
Bundle edit light Bundle edit dark

Receipt tab

Light Dark
Receipt light Receipt dark

Checkout form

Light Dark
Checkout light Checkout dark

Workflow emails

Light Dark
Workflow light Workflow dark

Profile settings

Light Dark
Profile light Profile dark

Test plan

  • Autoreview: clean, no findings, 0.94 confidence.
  • bin/test-confidence: 99% green; the tool classified and skipped 21 existing branch failures.
  • spec/requests/checkout/form_spec.rb: 8 examples, 0 failures.
  • Focused preview specs: 5 examples, 0 failures.
  • Prettier and ESLint: clean on the final TypeScript changes.
  • Manual in-app browser pass: all nine surfaces in light and dark mode; Checkout also verified at narrow preview and full desktop widths.
  • npm run typecheck currently stops before checking project source because this branch cannot resolve the configured vite/client type definition (TS2688).

Combined scope note: this PR carries the full stack from #5894, which was merged into this branch and closed.

The preview pane previously showed a bare iframe with the page URL as a
caption underneath and an open-in-new-tab arrow up in the sidebar header.
This wraps every preview state in a browser-style chrome bar: the page
title and URL centered like a browser tab and address bar, with the
open-in-new-tab arrow on the right of the bar itself. The sidebar-header
preview link and the separate URL caption are gone — the chrome carries
the page's identity in one place, matching the follow-up called out on
the first-class Pages PR.

Co-authored-by: Sahil Lavingia <sahil@gumroad.com>
@gumclaw gumclaw self-assigned this Jul 14, 2026
@gumclaw gumclaw added preview Has a deployed preview app / relates to preview environments working Someone is actively working this — don't duplicate effort labels Jul 14, 2026
@greptile-apps

greptile-apps Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR gives dashboard previews shared chrome that matches the previewed medium. The main changes are:

  • Web previews get title, URL, and open-in-new-tab chrome.
  • Email previews get From and Subject-style header chrome.
  • Receipt preview subject and body come from the same server response.
  • Checkout preview uses title-only chrome for its synthetic cart.
  • Checkout layout now responds to its preview container width.

Confidence Score: 5/5

This looks safe to merge.

No blocking issues found in the changed code.

T-Rex T-Rex Logs

What T-Rex did

  • The environment blocker in the Ruby/Bundler setup was identified before attempting UI tests.
  • A Playwright fallback path was attempted and visibility checks for the web title, URL, open action, and email header fields were performed, but the checks did not pass because the React harness did not render visible preview chrome.
  • Visual and video evidence were captured to document the fallback path, along with additional logs to support review of the run.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
app/javascript/components/PreviewSidebar.tsx Adds the shared preview chrome and updates the sidebar structure.
app/javascript/components/ProductEdit/ReceiptPreview.tsx Loads receipt preview subject and HTML together from the JSON response.
app/controllers/api/internal/receipt_previews_controller.rb Returns receipt preview subject and HTML as JSON.
app/javascript/components/Checkout/index.tsx Uses container-query classes for checkout layout and navigation placement.
app/javascript/components/CheckoutDashboard/CheckoutPreview.tsx Wraps the synthetic checkout preview in title-only preview chrome.
app/javascript/pages/Pages/Edit.tsx Wraps page previews in shared web preview chrome.
app/javascript/pages/Settings/Profile/Show.tsx Wraps profile previews in shared web preview chrome.
app/javascript/components/WorkflowsPage/Layout.tsx Wraps workflow email previews in shared email preview chrome.

Reviews (11): Last reviewed commit: "Fix preview framing and refresh screensh..." | Re-trigger Greptile

@slavingia

Copy link
Copy Markdown
Contributor

Ensure works both light and dark mode

The chrome container was hardcoded bg-white, which clashed with the
dashboard in dark mode. bg-background follows the theme; the bar and
placeholder already used theme tokens, and the framed page documents
handle their own prefers-color-scheme.

Co-authored-by: Sahil Lavingia <sahil@gumroad.com>
@gumclaw

gumclaw commented Jul 14, 2026

Copy link
Copy Markdown
Contributor Author

Done — the chrome container was hardcoded bg-white, which stuck out in dark mode. It now uses bg-background so the bar and rounded frame follow the dashboard theme (37c31c9). The framed page documents already handle their own prefers-color-scheme: the rich-text preview document flips to black/white in dark mode, the storefront frame is the live profile, and agent-built pages are prompted to support both modes. The iframe itself keeps a white backdrop pre-load, matching the existing product/profile preview components — that's what a page with no background of its own looks like in a real browser tab.

Will grab light + dark screenshots from the preview app for the Before/After table.

@slavingia

Copy link
Copy Markdown
Contributor

Screenshots

Light and dark captures of the three editor preview states (rich text
page, Home storefront frame, new-page placeholder) from the preview app.

Co-authored-by: Sahil Lavingia <sahil@gumroad.com>
@gumclaw

gumclaw commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

Added — light + dark captures of all three preview states (rich text page, Home storefront frame, new-page placeholder) are in the Before/After section now, taken from the preview app at the current head. The chrome bar, borders, and placeholder follow the dashboard theme in dark mode; the framed rich text page flips dark via its own prefers-color-scheme handling.

@slavingia

Copy link
Copy Markdown
Contributor

That button padding way too much.

The default icon button size (48px) dwarfed the slim chrome bar and
read as oversized padding. Use a compact 32px button with a smaller
arrow icon, and tighten the bar padding to match.

Co-authored-by: Sahil Lavingia <sahil@gumroad.com>
@gumclaw

gumclaw commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

Fixed — the arrow was using the stock icon-button size (48px), which is why it looked so padded inside the slim chrome bar. It's now a compact 32px button with a smaller arrow, and the bar's side padding is tightened to match (fefd462). Refreshed screenshots coming once the preview app picks up the new commit.

@gumclaw

gumclaw commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

Screenshots refreshed from the preview app at the new head — the compact button is in all six captures (light + dark for rich text, Home, and new page), same filenames so the Before/After tables in the description show the current state.

@slavingia

Copy link
Copy Markdown
Contributor

We can remove the "Preview" header as it's implied by the chrome and vertically aligns both sides.

The browser-style chrome already identifies the pane as a preview, so the
sidebar heading duplicated it and pushed the chrome below the top of the
edit form. PreviewSidebar gains a hideHeader option (keeping an aria-label
for the region); other PreviewSidebar users are unchanged.

Co-authored-by: Sahil Lavingia <sahil@gumroad.com>
@gumclaw

gumclaw commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

Done — removed the "Preview" heading from the sidebar, so the chrome bar now sits at the top of the pane, aligned with the edit form. PreviewSidebar gets a hideHeader option (the pane keeps an aria-label for screen readers); the other editors that use the sidebar are unchanged. Pushed in 9fd413c.

Extract the Pages editor's browser-style preview chrome into a shared
PreviewChrome component and render every PreviewSidebar consumer inside
it: product edit, bundle edit, the three checkout dashboard previews,
workflow emails, and profile settings. The redundant "Preview" heading
(and the separate open-in-new-tab arrow next to it) is gone everywhere;
the chrome bar carries each surface's title, public URL, and open link
instead. Surfaces without a meaningful public URL (the receipt preview,
workflow emails) render the chrome with a title only.
gumclaw added 3 commits July 14, 2026 22:12
The chrome's overflow-hidden (needed for the rounded-corner clipping)
zeroes its automatic minimum flex size, so inside the sidebar's
fixed-height flex column it compressed to the viewport and clipped tall
previews — the workflow email list's 'View content' button became
invisible (CI: workflows_spec 'allows creating a new email'). shrink-0
restores the sidebar as the scroll container.
Per Sahil, the preview chrome should match the medium being previewed:
web pages keep the browser-style title + URL bar, but emails land in
inboxes, so the two email surfaces now render an email-client header
instead. PreviewChrome gains a discriminated variant prop:
variant="email" takes a From line and an optional Subject and drops
the URL bar and open-in-new-tab arrow (an email has no URL to open),
while the default web variant is unchanged.

Both surfaces show honest values sourced from the real mailer config:

- Workflow emails: From is the exact sender PostEmailApi/PostResendApi
  build (creator name <username@creators domain>), passed through a new
  email_from field in the workflow form context. No Subject line — the
  pane stacks every email in the workflow, each with its own subject as
  its heading, so a single chrome-level Subject would be ambiguous.
- Product edit Receipt tab: From mirrors CustomerMailer#receipt
  (creator name <noreply@customers domain>) via a new receipt_email_from
  edit prop, and Subject mirrors ReceiptPresenter::MailSubject for the
  single-purchase preview the receipt endpoint renders (You bought /
  You got / You've subscribed to {product}!).
…ctation

The full-hash equality spec for a brand-new product's edit_props was not
updated when the Receipt preview gained its email-style chrome, which
added receipt_email_from to the presenter output.
gumclaw added 2 commits July 18, 2026 17:05
…ct source

- Mobile: restore a full-size labeled 'Open in new tab' button above the frame
  (compact 32px icon arrow stays desktop-only inside the chrome bar).
- Checkout: drop the open-in-new-tab arrow entirely — the pane shows a synthetic
  sample cart while /checkout would render the seller's persisted cart, so the
  destination could never match the preview.
- Receipt: subject and body now come from the same Rails preview response
  (ReceiptPresenter#mail_subject rides along with the rendered HTML as JSON), so
  unsaved client state can no longer disagree with the rendered receipt.
  PurchasePreview gains explicit link_name/price_cents so MailSubject sees real
  values instead of method_missing nils.
…utton, checkout sans arrow, server-sourced receipt subject)
@gumclaw

gumclaw commented Jul 18, 2026

Copy link
Copy Markdown
Contributor Author

@gianfrancopiana All four addressed (code in 95d548d, media refreshed after it):

  1. Stale Pages screenshots — all six recaptured against the current branch (no "Preview" heading, current offsets), desktop light/dark for rich-text/Home/new, committed under v2 names and re-linked in the body.
  2. Mobile action — mobile now gets a full labeled "Open in new tab" button in its own row above the frame (measured 180×48 target on a 390px viewport: qa-media/pr-5888-v2-after-richtext-mobile-light.png); desktop keeps the compact in-bar arrow, which is now hidden below lg.
  3. Checkout arrow — removed. The pane renders a synthetic sample cart while /checkout would load the seller's persisted cart, so no destination can match the preview; the chrome keeps the URL as identity only. Verified zero open-in-new-tab targets on the page.
  4. Receipt subject — the preview endpoint now returns {subject, html} from one Rails render; the chrome's Subject line is ReceiptPresenter#mail_subject for the exact PurchasePreview the body was rendered from, so they can't disagree (PurchasePreview gained explicit link_name/price_cents so MailSubject doesn't see method_missing nils). Controller spec covers paid/free subjects and the JSON error shape; verified live that the chrome shows the server subject ("You got …!" for a $0 product) sourced from the same 200 response as the rendered body.

@gianfrancopiana

Copy link
Copy Markdown
Member

@gumclaw See below.

CleanShot 2026-07-18 at 20 42 12@2x

The tooltip gets cut off on hover. We should fix this.

The chrome still reads as a generic header rather than browser chrome. Please make these changes:

  • Give the chrome bar a subtle surface fill that separates it from the preview.
  • Place the URL inside a quiet inset address field. Use middle truncation so the page path remains visible.
  • Keep the title centered and the arrow on the right.
  • Keep the arrow visually compact, but extend its hit area to at least 44×44px.
  • Only the arrow should react on hover; the preview frame should remain unchanged.
  • Do not add traffic-light dots, tabs, back buttons, or other fake controls.

Please refresh the light and dark screenshots after these changes. And make sure everything you add is consistent with Gumroad's design. Avoid new patterns.

…ddress field, fixed tooltip, bigger arrow hit area

- Chrome bar gets a subtle bg-body surface fill so it separates from the preview frame (both variants)
- URL moves into a quiet inset address field (bg-active-bg pill) with middle truncation so the page's own path segment stays visible; full URL kept in the accessibility tree
- Open-in-new-tab tooltip positioned bottom-end so it no longer clips against the sidebar edge
- Arrow stays visually compact (32px) but an invisible ::after pad extends its hit area to 44x44px
- Only the arrow reacts on hover (unchanged Button hover); no fake browser controls added

Co-authored-by: Gianfranco <gianfranco@gumroad.com>
@gumclaw

gumclaw commented Jul 19, 2026

Copy link
Copy Markdown
Contributor Author

@gianfrancopiana Got it — all of this is on me and I'll take it in one pass:

  • Tooltip clipping on hover — will fix so it renders fully (likely an overflow/stacking issue on the chrome bar container).
  • Chrome bar surface fill — subtle fill so the bar reads as chrome, visually separated from the preview beneath it.
  • URL in a quiet inset address field with middle truncation so the path stays visible on long URLs.
  • Title centered, arrow on the right, arrow visually compact but with a ≥44×44px hit area.
  • Hover feedback on the arrow only — no hover reaction on the preview frame.
  • No fake controls — no traffic lights, tabs, or back buttons.

I'll build this from existing Gumroad design primitives (no new patterns), verify it live, and refresh the light and dark screenshots after the changes. ETA: updated code + screenshots within ~12 hours (by ~13:00 UTC today).

@gumclaw

gumclaw commented Jul 19, 2026

Copy link
Copy Markdown
Contributor Author

Status update on the round-2 items: all six code changes are on the branch in c106154 (surface fill, inset address field with middle truncation, fixed tooltip clipping, centered title + right arrow, 44×44px arrow hit area with hover on the arrow only, no fake controls) and CI is fully green.

The one thing still outstanding is the light/dark screenshot refresh against the new chrome — I missed my ~13:00 UTC estimate on that. Refreshed screenshots will be up by ~20:00 UTC today, and I'll ping here when the body is updated so it's ready for another look.

@gumclaw

gumclaw commented Jul 19, 2026

Copy link
Copy Markdown
Contributor Author

Preview screenshots @ c106154 (hosted preview)

Captured on https://pages-preview-browser-chrome.apps.staging.gumroad.org (x-revision c106154c5823) — re-capture for the new head commit (browser-chrome restyle: surface fill, inset address field, bigger arrow hit area). Light + dark.

@slavingia

Copy link
Copy Markdown
Contributor

@gianfrancopiana handle locally, feedback loop is too slow for the remaining changes

@gumclaw

gumclaw commented Jul 19, 2026

Copy link
Copy Markdown
Contributor Author

Standing down — it's all yours locally, @gianfrancopiana. I won't push to this branch anymore.

Where the branch is at head 58f2519:

  • All six round-2 items landed in c106154c5 (surface fill, inset address field with middle truncation, tooltip clipping fix, centered title + right-aligned arrow, etc.).
  • 58f2519 only adds the hosted-preview screenshots to qa-media/ — no code changes after c106154c5.
  • CI is green at head; the hosted preview at https://pages-preview-browser-chrome.apps.staging.gumroad.org was last verified at revision c106154c5823.

The PR body's table maps every surface to its chrome variant if you need a reference while iterating.

@gumclaw gumclaw added the awaiting-human Waiting on a human response/decision; assignee is that human label Jul 19, 2026
@gianfrancopiana
gianfrancopiana enabled auto-merge (squash) July 20, 2026 01:52
@gianfrancopiana

Copy link
Copy Markdown
Member

Updated the preview chrome and checkout layout, refreshed all light/dark screenshots, and enabled auto-merge so this will merge once CI is green.

@slavingia

Copy link
Copy Markdown
Contributor

Seems like light mode is rendering in dark mode within the preview

pr-5888-after-bundle-edit-dark.png

@slavingia

Copy link
Copy Markdown
Contributor

Get CI green too

@gianfrancopiana
gianfrancopiana merged commit aa4ec14 into main Jul 20, 2026
158 of 159 checks passed
@gianfrancopiana
gianfrancopiana deleted the pages-preview-browser-chrome branch July 20, 2026 04:06
@gumclaw

gumclaw commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

CI is green now. Test Slow 22 failed on a single spec (spec/requests/subscription/non_tiered_membership_spec.rb:304) with a Stripe::RateLimitError from the live Stripe API — external rate limiting, not related to this PR's changes. Reran the failed shard and the full run completed successfully at the current head.

@slavingia

Copy link
Copy Markdown
Contributor

And the light mode within dark mode fix? Fix the tool bar too should be dark bg too or just slightly lighter than the text field bg

@gumclaw

gumclaw commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Checked the pixels in pr-5888-after-bundle-edit-dark.png — the chrome itself is following dark mode correctly: the browser-chrome bar samples at rgb(32, 32, 30), right next to the dashboard's rgb(36, 36, 34). What's white is only the page inside the frame (rgb 252, 252, 252), and that's the seller's profile background color rather than a color-scheme miss.

The editor preview pins --filled, --body-bg, and the rest of the palette to the seller's profile colors (default #ffffff) so it shows what buyers actually see. The live product page does the same thing — the seller profile's custom styles override the dark-mode variables on :root, so a buyer browsing in dark mode still gets the seller's chosen page colors, white by default. This behavior predates this PR: the same style override exists on main in BundleEdit/Layout.tsx and ProductEdit/Layout.tsx; this branch only moved it inside the new chrome frame.

So the screenshot is faithful: dark dashboard, dark chrome, seller-colored page in the frame — the same contrast a real browser window shows when a dark-mode user opens a white website.

If we'd rather have the preview adopt the dashboard's scheme when the seller hasn't customized their colors, the override to change is the profileColors style block on Preview in those two layouts — but that would make the preview diverge from the page buyers actually get. Leaving that call to you and @gianfrancopiana since this branch is being handled locally.

@gumclaw

gumclaw commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Fix is up: #5992.

Two changes, both from this comment:

  • Preview pane — the white pane was the seller's default profile background (#ffffff) being pinned onto the preview even for sellers who never customized it. Now the palette is only pinned when the profile background differs from the default white, so default-theme previews follow the dashboard's dark scheme, while sellers with a custom background still see exactly what buyers see.
  • Toolbar — the description toolbar's inverted black bar flipped to near-white in dark mode. In dark mode it now uses the page background (#242423), slightly lighter than the black text field beneath it; light mode is unchanged.

The PR is draft until I get dark/light screenshots from its hosted preview — will un-draft and ping once they're in the body.

gianfrancopiana pushed a commit that referenced this pull request Jul 20, 2026
## What

The rich text editor toolbar now follows dark mode. The toolbar uses an
inverted `bg-primary` bar, which is black in light mode but flips to
near-white in dark mode — a bright strip across an otherwise dark
editor. In dark mode it now uses the page background (`bg-body`,
`#242423`), which sits slightly lighter than the black text field
beneath it, with regular foreground text. Light mode is unchanged. The
`--color-muted` override for toolbar icons now derives from
`currentColor` so muted icons stay legible on both bars.

Re-opened from #5992 after review: the preview-palette portion of that
PR was dropped (`#ffffff` is the seller's actual storefront background,
so making the preview inherit dark mode would stop it matching what
buyers see; scheme-aware default pages need a proper theme model first).
This PR is the surviving toolbar-only fix on a clean branch.

## Why

Sahil flagged the toolbar on #5888 after merge: the description toolbar
stayed light in dark mode ("Fix the tool bar too — should be dark bg too
or just slightly lighter than the text field bg").

## Before / After

**Before (dark mode):** light toolbar strip across the dark editor —
visible in the top area of
![before-dark](https://raw.githubusercontent.com/antiwork/gumroad/main/qa-media/pr-5888-after-bundle-edit-dark.png)

**After:** capturing from the preview app once the deploy is up; PR
stays draft until attached.

## QA steps

1. Set the OS/browser to dark mode.
2. Open a product or bundle editor — the description toolbar should
render dark, slightly lighter than the text field beneath it, with
legible icons.
3. Switch to light mode — the toolbar is unchanged from before (inverted
black bar).

## Test plan

- TSX-only change to `RichTextEditor.tsx`; no Ruby touched.
- Prettier + ESLint clean on the changed file.
- CI on this PR builds fresh assets and runs the full suite.

---

## AI disclosure

Authored by Claude Fable 5 (Hermes agent) from Sahil's review comment on
#5888. Carried over from #5992 with the preview-palette change removed
per gianfrancopiana's review.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting-human Waiting on a human response/decision; assignee is that human preview Has a deployed preview app / relates to preview environments

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants