Review
Waxorbit mold pour preview from maker SVG uploads
Waxorbit is a marketplace for silicone candle molds. Makers upload SVG silhouettes of their cavity profiles so product pages can show a SMIL 'wax pour' fill animation without re-authoring every mold in React. This PR inlines the stored SVG markup and strips <script> tags so animate/animateTransform from the file still run in the browser.
ReactTier 3securityxsssvgdangerouslySetInnerHTML
Click a line to flag it, pick one or more labels, then submit. If the change looks correct, approve it.
components/molds/PourPreview.tsx+24-0
| 1 | 1 | import { useMemo } from "react"; | |
| 2 | 2 | import type { MoldListing } from "@/lib/molds/types"; | |
| 3 | 3 | ||
| 4 | 4 | type Props = { listing: MoldListing; pourMs?: number }; | |
| 5 | 5 | ||
| 6 | + | /** Keep SMIL animate* nodes; drop obvious script hosts. */ | |
| 7 | + | function scrubScripts(svg: string): string { | |
| 8 | + | return svg.replace(/<script[\s\S]*?<\/script>/gi, ""); | |
| 9 | + | } | |
| 10 | + | ||
| 11 | + | export function PourPreview({ listing, pourMs = 2400 }: Props) { | |
| 12 | + | const markup = useMemo(() => { | |
| 13 | + | // Makers author SMIL pour timelines in the SVG; React won't parse those as elements | |
| 14 | + | const cleaned = scrubScripts(listing.cavitySvg); | |
| 15 | + | return cleaned.replace( | |
| 16 | + | /dur=\"[0-9.]+s\"/, | |
| 17 | + | `dur=\"${(pourMs / 1000).toFixed(1)}s\"`, | |
| 18 | + | ); | |
| 19 | + | }, [listing.cavitySvg, pourMs]); | |
| 20 | + | ||
| 21 | + | return ( | |
| 22 | + | <div | |
| 23 | + | className="pour-preview" | |
| 24 | + | role="img" | |
| 25 | + | aria-label={`${listing.title} wax pour preview`} | |
| 26 | + | dangerouslySetInnerHTML={{ __html: markup }} | |
| 27 | + | /> | |
| 28 | + | ); | |
| 29 | + | } |