Visual editor
Inspector + live preview. Edit copy, swap colors, reorder blocks. Auto-saves; explicit publish.
The visual editor lives at Paywalls → Editor → [paywall id] in your dashboard. It's a constrained editor: you can edit content / theme / policy, reorder + hide / delete blocks, change the render surface — but you can't add brand-new block types in v1. (That's unconstrained mode in v2.)
Layout
┌────────────────────────────────────────────────────────────┐
│ Top bar: name · status · publish · preview surface tabs │
├──────────────────────────┬─────────────────────────────────┤
│ │ │
│ Inspector (~360px) │ Live preview pane │
│ ┌────────────────────┐ │ (fluid) │
│ │ Blocks · Theme · │ │ │
│ │ Settings · Policy │ │ │
│ └────────────────────┘ │ │
│ │ │
└──────────────────────────┴─────────────────────────────────┘
The preview is a real <Paywall> rendered with a stubbed transport — clicks
inside it don't fire Stripe Checkout, so you can iterate without spawning
test sessions.
Inspector tabs
Blocks
The default tab. Lists every block in config.blocks. You can:
- Drag blocks to reorder (handle on the left of each row)
- Click a block to expand its per-type form
- Eye icon — hide a block without deleting it
- Trash — delete the block
Per-block forms cover the most-used fields:
| Block | Editable in form |
|---|---|
| Hero | eyebrow, headline, subheadline, image URL, useDisplayFont, badge |
| Plans | layout (cards / tier-ladder / compact-list), CTA label |
| Features | columns, items (icon + title + description) |
| FAQ | items (q + a) |
| Trust | badges (kind + label) |
| CTA | label, action, priceId |
| Comparison · Testimonials · CosmeticGrid | raw JSON edit (structured forms in v2) |
Theme
- Accent / Text / Surface / Border — color pickers + hex inputs
- Page background — CSS string (gradient or solid)
- Decoration —
none/scanlines/dot-grid/starfield/grain - Heading font — CSS font-family stack
- Display font — only used by blocks with
useDisplayFont: true - Radius — default border-radius
Changes apply instantly to the preview pane via CSS variables.
Settings
- Render surface —
extension-popup/extension-overlay/web. Affects max-width. - Modal title / subtitle — used when the paywall is opened via
usePaywall().open() - Success URL / Cancel URL — where Stripe Checkout redirects
Policy
- Refund policy —
standard/no-refunds/custom - Terms URL + Privacy URL — auto-rendered as footer links
- Enforce CWS compliance — flips on the strict footer template (refund line, Terms, Privacy, cancel-anytime)
Top-bar controls
| Control | What it does |
|---|---|
| Editable name | Renames the paywall (auto-saves) |
| Save indicator | "Saving" → "Saved" → idle. Errors surface here too. |
| Surface tabs (Popup / Overlay / Web) | Override surface in the preview without saving. Useful for proofing the same paywall across surfaces. |
| State tabs (Not paying / Trial / Paid) | Switch the preview's stub subscription state. Trial reveals <TrialBadge>; Paid reveals <ManageSubscriptionLink>. |
| Status pill | draft or published + version number |
| Publish button | Flips status: published, bumps version, purges KV cache. The button's wording changes to Re-publish once first published. |
Auto-save
Every keystroke marks the editor dirty. 800ms after the last edit the editor PATCHes the API. Errors surface in the save indicator + browser console; the editor doesn't roll back local state, so you don't lose your edits if a save fails — fix the network and the next change re-tries.
Publish
Auto-save never publishes. The publish button:
- Forces a final save (in case the 800ms hasn't elapsed)
- POSTs to
/v1/dashboard/paywalls/:id/publish - Bumps
versionfrom N to N+1 - Stamps
publishedAt - Purges
KV.activePaywall(extensionId)— next SDK fetch hits D1 + writes the new bytes back to KV
End-to-end "click publish → installed extension renders the new paywall": within ~5 seconds for popup-on-open. Existing open popups don't auto-refresh in v1; users see the new paywall on their next open.
Rollback
Use Re-publish on an older draft. The renderer always serves the latest published row, so rolling back is just publishing an earlier version.
(Version history UI lands in v2.)
Constraints + reasons
| Constraint | Why |
|---|---|
| Can't add new block types | Each new type = renderer + editor + tests. v1 ships the closed set; v2 lifts this. |
| Comparison / Testimonials / CosmeticGrid edited as JSON | Structured forms in v2 |
| Single paywall per surface published at a time | Targeting + A/B testing is a separate Phase 9 feature |
| Auto-save over every keystroke | Avoids the "I forgot to save" footgun. We never auto-publish. |
Next
- → Schema — what's actually being saved
- → Publishing — what happens after you click Publish
Was this page helpful?
Your feedback shapes what we document next.