import React, { useMemo } from 'react'; import { markdownToHtml } from './MarkdownView'; /** One generated image to place on the preview page. */ export interface PreviewImage { url: string; alt?: string; placement?: string; } /** Props for {@link PagePreview}. */ interface PagePreviewProps { /** Article body in markdown. */ markdown: string; /** Page H1 / title rendered at the top of the mock page. */ title: string; /** Slug used to fake a real URL in the browser bar. */ slug?: string | null; /** * Generated images (from the article's image suggestions) to render on the * page. The ``hero`` one sits under the H1; the rest follow the body. Empty * until images have been generated. */ images?: PreviewImage[]; } /** * Prefix each heading in the generated HTML with a small ``H1``..``H6`` badge so * the merchant can see the AEO heading structure inline on the preview. Operates * on our own escaped output from {@link markdownToHtml}, so no user HTML is * injected here. * * @param {string} html - HTML produced by {@link markdownToHtml}. * @returns {string} HTML with a level badge prepended inside each heading. */ const annotateHeadings = (html: string): string => html.replace( /]*)>/g, (_match, level: string, attrs: string) => `H${level}` ); /** * A "real page" preview of a generated content: a light article page inside a * mock browser frame, with each heading tagged by its level (H1/H2/H3...). Shown * on the right of the content modal so the merchant sees how the page reads and * how its heading structure is organized for AI extraction. * * @param {PagePreviewProps} props - Preview props. * @returns {JSX.Element} The preview. */ const PagePreview = ({ markdown, title, slug, images = [], }: PagePreviewProps): JSX.Element => { const html = useMemo( () => annotateHeadings(markdownToHtml(markdown || '')), [markdown] ); const fakeUrl = `yourstore.com/${(slug || 'page').replace(/^\/+/, '')}`; const withUrl = images.filter(image => Boolean(image.url)); const hero = withUrl.find(image => image.placement === 'hero') ?? withUrl[0] ?? null; const rest = withUrl.filter(image => image !== hero); return (
{/* Mock browser chrome so it reads as a real page, not a dashboard panel. */}
{fakeUrl}
{/* The page itself: a white, article-typography column independent of the dashboard theme, so it approximates a real published page. */}

H1 {title}

{hero ? ( {hero.alt ) : null}
{rest.map((image, index) => ( {image.alt ))}
); }; /** Scoped, theme-independent typography for the mock page + heading badges. */ const PREVIEW_STYLES = ` .rm-page { color: #1f2933; font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif; line-height: 1.7; font-size: 16px; } .rm-page .rm-page-title { font-size: 32px; line-height: 1.2; font-weight: 800; color: #0f172a; margin: 0 0 20px; } .rm-page h1 { font-size: 28px; font-weight: 800; color: #0f172a; margin: 32px 0 12px; } .rm-page h2 { font-size: 23px; font-weight: 700; color: #111827; margin: 28px 0 10px; } .rm-page h3 { font-size: 19px; font-weight: 700; color: #1f2933; margin: 22px 0 8px; } .rm-page h4, .rm-page h5, .rm-page h6 { font-size: 16px; font-weight: 700; color: #1f2933; margin: 18px 0 6px; } .rm-page p { margin: 0 0 14px; } .rm-page ul, .rm-page ol { margin: 0 0 14px; padding-left: 22px; } .rm-page li { margin: 4px 0; } .rm-page a { color: #B7007C; text-decoration: underline; } .rm-page blockquote { margin: 0 0 14px; padding: 6px 14px; border-left: 3px solid #e2e8f0; color: #475569; } .rm-page pre { background: #f1f5f9; border-radius: 8px; padding: 12px; overflow-x: auto; margin: 0 0 14px; } .rm-page code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13px; } .rm-page hr { border: 0; border-top: 1px solid #e2e8f0; margin: 24px 0; } .rm-page .rm-page-hero { width: 100%; border-radius: 10px; margin: 0 0 22px; display: block; } .rm-page .rm-page-img { width: 100%; border-radius: 10px; margin: 18px 0; display: block; } .rm-page img { max-width: 100%; height: auto; border-radius: 8px; } .rm-page table { width: 100%; border-collapse: collapse; margin: 0 0 14px; font-size: 14px; } .rm-page th, .rm-page td { border: 1px solid #e2e8f0; padding: 6px 10px; text-align: left; } .rm-hbadge { display: inline-block; margin-right: 8px; padding: 0 6px; border-radius: 4px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11px; font-weight: 700; vertical-align: middle; } .rm-h1 { background: #B7007C1a; color: #B7007C; } .rm-h2 { background: #6366f11a; color: #6366f1; } .rm-h3 { background: #0891b21a; color: #0891b2; } .rm-h4, .rm-h5, .rm-h6 { background: #64748b1a; color: #64748b; } `; export default PagePreview;