{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "react-email/product-list",
  "title": "Product List",
  "description": "Product List email component",
  "dependencies": [
    "react-email"
  ],
  "registryDependencies": [
    "https://emailcn.run/r/react-email/default-fonts.json",
    "https://emailcn.run/r/react-email/theme-default.json"
  ],
  "files": [
    {
      "path": "registry/bases/react-email/components/ecommerce/product-lists/product-list.tsx",
      "content": "import { Fragment } from \"react\";\nimport type { ReactNode } from \"react\";\nimport {\n  Section,\n  Row,\n  Column,\n  Text,\n  Heading,\n  Link,\n  Img,\n  Body,\n  Head as EmailHead,\n  Html,\n  Preview,\n  Tailwind,\n} from \"react-email\";\n\nimport { DefaultFonts } from \"@/components/email/font-default\";\nimport { createEmailTailwindConfig } from \"@/components/email/email-theme\";\nimport type { EmailTheme } from \"@/components/email/email-theme\";\nimport { emailAsset } from \"@/components/email/email-assets\";\nimport { defaultTheme } from \"@/components/email/theme-default\";\n\nexport type ProductListVariant =\n  | \"basic\"\n  | \"reviews-top\"\n  | \"with-description\"\n  | \"with-intro\"\n  | \"with-details\"\n  | \"details-reviews-top\"\n  | \"full-details\"\n  | \"full-reviews-top\";\n\nexport interface ProductListItem {\n  imageUrl?: string;\n  name: string;\n  price: string;\n  quantity?: number;\n  description?: string;\n  details?: string;\n  reviewCount?: number;\n  href?: string;\n}\n\nconst fontFamily =\n  'Inter, ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", sans-serif';\n\nconst productListResponsiveStyles = `\n  @media only screen and (max-width: 599px) {\n    .product-list-column { display: block !important; width: 100% !important; }\n    .product-list-image { width: 100% !important; }\n    .product-list-gap { line-height: 24px !important; }\n  }\n  @media only screen and (max-width: 430px) {\n    .product-list-option { display: inline-block !important; }\n  }\n`;\n\nconst textStyle = { fontFamily, margin: 0 } as const;\n\nconst Spacer = ({ height }: { height: number }) => (\n  <Section style={{ lineHeight: `${height}px` }}>&zwj;</Section>\n);\n\nconst EmailShell = ({ children }: { children: ReactNode }) => (\n  <>\n    <style>{productListResponsiveStyles}</style>\n    <Section style={{ backgroundColor: \"#f1f5f9\", width: \"100%\" }}>\n      <Fragment>\n        <Row>\n          <Column>&zwj;</Column>\n          <Column\n            style={{\n              backgroundColor: \"#fffffe\",\n              maxWidth: \"100%\",\n              paddingBottom: \"44px\",\n              width: \"600px\",\n            }}\n          >\n            <Section style={{ width: \"100%\" }}>\n              <Fragment>\n                <Row>\n                  <Column style={{ padding: \"0 24px\" }}>\n                    <Spacer height={44} />\n                    {children}\n                  </Column>\n                </Row>\n              </Fragment>\n            </Section>\n          </Column>\n          <Column>&zwj;</Column>\n        </Row>\n      </Fragment>\n    </Section>\n  </>\n);\n\nconst Rating = ({\n  count,\n  allSolid = false,\n}: {\n  count: number;\n  allSolid?: boolean;\n}) => (\n  <Section align=\"left\" style={{ width: \"184px\" }}>\n    <Fragment>\n      <Row>\n        <Column>\n          <Section style={{ width: \"100px\" }}>\n            <Fragment>\n              <Row>\n                {[0, 1, 2, 3, 4].map((index) => (\n                  <Column key={index} style={{ paddingRight: \"4px\" }}>\n                    <Img\n                      alt=\"\"\n                      src={emailAsset(\n                        `icon-star-${index === 4 && !allSolid ? \"half\" : \"solid\"}.png`\n                      )}\n                      style={{ display: \"block\" }}\n                      width=\"16\"\n                    />\n                  </Column>\n                ))}\n              </Row>\n            </Fragment>\n          </Section>\n        </Column>\n        <Column style={{ paddingLeft: \"8px\" }}>\n          <Text\n            style={{\n              ...textStyle,\n              color: \"#4b5563\",\n              fontSize: \"12px\",\n              lineHeight: \"16px\",\n              marginTop: \"1px\",\n            }}\n          >\n            {`(${count} reviews)`}\n          </Text>\n        </Column>\n      </Row>\n    </Fragment>\n  </Section>\n);\n\nconst Copy = ({ children }: { children: ReactNode }) => (\n  <Text\n    style={{\n      ...textStyle,\n      color: \"#4b5563\",\n      fontSize: \"16px\",\n      fontWeight: 300,\n      lineHeight: \"24px\",\n    }}\n  >\n    {children}\n  </Text>\n);\n\nconst ProductOptions = () => (\n  <Section>\n    <Fragment>\n      <Row>\n        <Column\n          className=\"product-list-option\"\n          style={{ paddingRight: \"36px\" }}\n        >\n          <Section>\n            <Fragment>\n              <Row>\n                <Column\n                  style={{\n                    color: \"#4b5563\",\n                    fontFamily,\n                    fontSize: \"14px\",\n                    lineHeight: \"20px\",\n                    paddingRight: \"8px\",\n                  }}\n                >\n                  Colors:\n                </Column>\n                <Column>\n                  <Section style={{ fontSize: 0 }}>\n                    {[\"#030712\", \"#fffffe\", \"#E5E7EB\"].map((color) => (\n                      <span\n                        key={color}\n                        style={{ display: \"inline-block\", maxWidth: \"12px\" }}\n                      >\n                        <span\n                          style={{\n                            backgroundColor: color,\n                            borderRadius: \"9999px\",\n                            display: \"inline-block\",\n                            height: \"16px\",\n                            width: \"16px\",\n                          }}\n                        />\n                      </span>\n                    ))}\n                  </Section>\n                </Column>\n              </Row>\n            </Fragment>\n          </Section>\n        </Column>\n        <Column className=\"product-list-option\">\n          <Section>\n            <Fragment>\n              <Row>\n                <Column\n                  style={{\n                    color: \"#4b5563\",\n                    fontFamily,\n                    fontSize: \"14px\",\n                    lineHeight: \"20px\",\n                    paddingRight: \"8px\",\n                  }}\n                >\n                  Sizes:\n                </Column>\n                <Column\n                  style={{\n                    color: \"#4b5563\",\n                    fontFamily,\n                    fontSize: \"14px\",\n                    lineHeight: \"20px\",\n                  }}\n                >\n                  {[\n                    [\"S\", true],\n                    [\"M\", true],\n                    [\"L\", true],\n                    [\"XL\", false],\n                  ].map(([size, comma]) => (\n                    <span key={String(size)}>\n                      {`${size}${comma ? \", \" : \"\"}`}\n                    </span>\n                  ))}\n                </Column>\n              </Row>\n            </Fragment>\n          </Section>\n        </Column>\n      </Row>\n    </Fragment>\n  </Section>\n);\n\nconst Divider = ({ bottom, top }: { bottom: number; top: number }) => (\n  <Section\n    style={{\n      backgroundColor: \"#d1d5db\",\n      height: \"1px\",\n      lineHeight: \"1px\",\n      margin: `${top}px 0 ${bottom}px`,\n    }}\n  >\n    &zwj;\n  </Section>\n);\n\nconst Discover = ({ href }: { href: string }) => (\n  <Section>\n    <Link\n      href={href}\n      style={{\n        backgroundColor: \"#fffffe\",\n        color: \"#4f46e5\",\n        display: \"inline-block\",\n        fontFamily,\n        fontSize: \"16px\",\n        fontWeight: 500,\n        lineHeight: 1,\n        padding: \"4px 0\",\n        textDecoration: \"none\",\n      }}\n    >\n      <span style={{ marginRight: \"8px\" }}>Discover</span>\n      <span>\n        <Img\n          alt=\"\"\n          src={emailAsset(`icon-arrow-right-indigo.png`)}\n          style={{\n            display: \"inline-block\",\n            maxWidth: \"100%\",\n            verticalAlign: \"baseline\",\n          }}\n          width=\"16\"\n        />\n      </span>\n    </Link>\n  </Section>\n);\n\nconst defaultProducts: ProductListItem[] = [\n  {\n    description:\n      \"Crafted from a soft bamboo-linen blend, this shirt combines breathability, comfort...\",\n    details: \"Black, White, Gray +5 more | S,M,L,XL\",\n    href: \"https://example.com/thsirts\",\n    imageUrl: emailAsset(`product-lists/product-list-1.jpg`),\n    name: \"Stark Graphic T\",\n    price: \"$9.99\",\n    reviewCount: 18,\n  },\n  {\n    details: \"Black, White, Gray +5 more | S,M,L,XL\",\n    href: \"https://example.com\",\n    imageUrl: emailAsset(`product-lists/product-list-2.jpg`),\n    name: \"Intricate Lifestyle Cotton T\",\n    price: \"$9.99\",\n    reviewCount: 42,\n  },\n];\n\nconst Header = ({ item }: { item: ProductListItem }) => (\n  <Section style={{ width: \"100%\" }}>\n    <Fragment>\n      <Row>\n        <Column>\n          <Heading\n            style={{\n              ...textStyle,\n              color: \"#030712\",\n              fontSize: \"20px\",\n              fontWeight: 600,\n              lineHeight: \"28px\",\n            }}\n            as=\"h3\"\n          >\n            {item.name}\n          </Heading>\n        </Column>\n        <Column\n          style={{ textAlign: \"right\", verticalAlign: \"top\", width: \"80px\" }}\n        >\n          <Text\n            style={{\n              ...textStyle,\n              color: \"#030712\",\n              fontSize: \"20px\",\n              fontWeight: 500,\n              lineHeight: \"28px\",\n            }}\n          >\n            {item.price}\n          </Text>\n        </Column>\n      </Row>\n    </Fragment>\n  </Section>\n);\n\nconst ProductContent = ({\n  item,\n  variant,\n}: {\n  item: ProductListItem;\n  variant: ProductListVariant;\n}) => {\n  const detailsVariant = [\n    \"with-details\",\n    \"details-reviews-top\",\n    \"full-details\",\n    \"full-reviews-top\",\n  ].includes(variant);\n  const topReview = [\n    \"reviews-top\",\n    \"details-reviews-top\",\n    \"full-reviews-top\",\n  ].includes(variant);\n  const fullDetails = [\"full-details\", \"full-reviews-top\"].includes(variant);\n  const descriptionOnly = variant === \"with-description\";\n  let optionsBlock: ReactNode = null;\n  if (fullDetails) {\n    optionsBlock = (\n      <>\n        <Copy>{item.description}</Copy>\n        <Divider bottom={8} top={24} />\n        <ProductOptions />\n        <Divider bottom={6} top={8} />\n      </>\n    );\n  } else if (detailsVariant) {\n    optionsBlock = <ProductOptions />;\n  }\n  return (\n    <>\n      {topReview ? (\n        <>\n          <Rating\n            allSolid={(item.reviewCount ?? 18) === 42}\n            count={item.reviewCount ?? 18}\n          />\n          <Spacer height={18} />\n        </>\n      ) : null}\n      <Header item={item} />\n      <Spacer height={24} />\n      {descriptionOnly ? <Copy>{item.description}</Copy> : null}\n      {!descriptionOnly && !detailsVariant ? <Copy>{item.details}</Copy> : null}\n      {optionsBlock}\n      {!topReview && !descriptionOnly ? (\n        <>\n          <Spacer height={18} />\n          <Rating\n            allSolid={(item.reviewCount ?? 18) === 42}\n            count={item.reviewCount ?? 18}\n          />\n        </>\n      ) : null}\n      <Spacer height={18} />\n      <Discover href={item.href ?? \"https://example.com/thsirts\"} />\n    </>\n  );\n};\n\nconst ProductRow = ({\n  item,\n  variant,\n}: {\n  item: ProductListItem;\n  variant: ProductListVariant;\n}) => (\n  <Section style={{ width: \"100%\" }}>\n    <Fragment>\n      <Row>\n        <Column\n          className=\"product-list-column\"\n          style={{ verticalAlign: \"top\", width: \"144px\" }}\n        >\n          <Section>\n            <Img\n              alt=\"\"\n              className=\"product-list-image\"\n              src={item.imageUrl}\n              style={{\n                borderRadius: \"8px\",\n                maxWidth: \"100%\",\n                verticalAlign: \"middle\",\n                width: \"100%\",\n              }}\n              width=\"144\"\n            />\n          </Section>\n        </Column>\n        <Column\n          className=\"product-list-column product-list-gap\"\n          style={{ lineHeight: \"24px\", width: \"24px\" }}\n        >\n          &zwj;\n        </Column>\n        <Column\n          className=\"product-list-column\"\n          style={{ verticalAlign: \"top\" }}\n        >\n          <ProductContent item={item} variant={variant} />\n        </Column>\n      </Row>\n    </Fragment>\n  </Section>\n);\n\nconst ProductListWithRowsSection = ({\n  products,\n  variant = \"basic\",\n}: {\n  products?: ProductListItem[];\n  variant?: ProductListVariant;\n}) => {\n  const list = products ?? defaultProducts;\n  const visible =\n    variant === \"with-intro\" ? list.slice(0, 2) : list.slice(0, 1);\n  const rowVariant = variant === \"with-intro\" ? \"basic\" : variant;\n  return (\n    <EmailShell>\n      {variant === \"with-intro\" ? (\n        <>\n          <Heading\n            style={{\n              ...textStyle,\n              color: \"#030712\",\n              fontSize: \"30px\",\n              fontWeight: 600,\n              lineHeight: \"36px\",\n              textAlign: \"center\",\n            }}\n            as=\"h2\"\n          >\n            T-shirts\n          </Heading>\n          <Spacer height={44} />\n          <Text\n            style={{\n              ...textStyle,\n              color: \"#4b5563\",\n              fontSize: \"18px\",\n              lineHeight: \"28px\",\n              textAlign: \"center\",\n            }}\n          >\n            Style meets purpose in every piece. Designed with attention to\n            detail and built for everyday comfort, our collection brings\n            together modern design, timeless quality, and effortlessness.\n          </Text>\n          <Spacer height={44} />\n        </>\n      ) : null}\n      <Section style={{ width: \"100%\" }}>\n        <Fragment>\n          {visible\n            .map((item, index) => (\n              <Row key={`${item.name}-${index}`}>\n                <Column>\n                  <ProductRow item={item} variant={rowVariant} />\n                </Column>\n              </Row>\n            ))\n            .flatMap((row, index) =>\n              index === 0 && visible.length > 1\n                ? [\n                    row,\n                    <Row key=\"product-gap\">\n                      <Column style={{ lineHeight: \"44px\" }}>&zwj;</Column>\n                    </Row>,\n                  ]\n                : [row]\n            )}\n        </Fragment>\n      </Section>\n    </EmailShell>\n  );\n};\n\nconst ProductList_SharedProductListWithRowsSection = ProductListWithRowsSection;\n\ninterface ProductList_ProductListWithRowsProps {\n  theme?: EmailTheme;\n  products?: ProductListItem[];\n  variant?: ProductListVariant;\n}\n\nconst ProductList_ProductListWithRowsSectionWrapper = ({\n  products,\n  variant = \"basic\",\n}: Omit<ProductList_ProductListWithRowsProps, \"theme\">) => (\n  <ProductList_SharedProductListWithRowsSection\n    products={products}\n    variant={variant}\n  />\n);\n\nconst ProductList_ProductListWithRows = ({\n  theme = defaultTheme,\n  ...props\n}: ProductList_ProductListWithRowsProps) => (\n  <Html>\n    <EmailHead>\n      <DefaultFonts />\n    </EmailHead>\n    <Preview>Product list</Preview>\n    <Tailwind config={createEmailTailwindConfig(theme)}>\n      <Body className=\"m-0 bg-background font-sans\">\n        <ProductList_ProductListWithRowsSectionWrapper {...props} />\n      </Body>\n    </Tailwind>\n  </Html>\n);\n\nProductList_ProductListWithRows.PreviewProps = {\n  theme: defaultTheme,\n  variant: \"basic\",\n} satisfies ProductList_ProductListWithRowsProps;\n\nconst __ProductList = ProductList_ProductListWithRows;\n\nexport interface ProductListProps extends Omit<\n  Parameters<typeof __ProductList>[0],\n  \"theme\"\n> {\n  theme?: Parameters<typeof __ProductList>[0][\"theme\"];\n}\n\nexport const ProductList = (props: ProductListProps) => (\n  <__ProductList {...props} />\n);\n\nexport const ProductListSection = ProductListWithRowsSection;\n\nProductList.PreviewProps = {} satisfies ProductListProps;\n",
      "type": "registry:component",
      "target": "components/email/product-list.tsx"
    },
    {
      "path": "registry/email-assets.ts",
      "content": "const ASSET_ORIGIN = \"https://www.emailcn.run\";\nconst ICONS8_ORIGIN = \"https://img.icons8.com\";\nconst UNSPLASH_ORIGIN = \"https://images.unsplash.com\";\n\nconst portraitIds = [\n  \"photo-1500648767791-00dcc994a43e\",\n  \"photo-1507003211169-0a1dd7228f2d\",\n  \"photo-1494790108377-be9c29b29330\",\n  \"photo-1438761681033-6461ffad8d80\",\n  \"photo-1534528741775-53994a69daeb\",\n  \"photo-1506794778202-cad84cf45f1c\",\n] as const;\n\nconst productIds = [\n  \"photo-1521572163474-6864f9cf17ab\",\n  \"photo-1503341504253-dff4815485f1\",\n  \"photo-1576566588028-4147f3842f27\",\n  \"photo-1562157873-818bc0726f68\",\n] as const;\n\ntype Dimensions = readonly [width: number, height: number];\n\nconst dimensionRules = [\n  [/^bento-grids\\/1-bento-(?:1|4)\\./i, [336, 272]],\n  [/^bento-grids\\/1-bento-(?:2|3)\\./i, [720, 272]],\n  [/^bento-grids\\/2-bento-1\\./i, [336, 232]],\n  [/^bento-grids\\/2-bento-(?:2|7)\\./i, [720, 352]],\n  [/^bento-grids\\/2-bento-3\\./i, [720, 344]],\n  [/^bento-grids\\/2-bento-4\\./i, [336, 224]],\n  [/^bento-grids\\/2-bento-(?:5|6)\\./i, [336, 408]],\n  [/^bento-grids\\/3-bento-lg-\\d-pad\\./i, [304, 576]],\n  [/^bento-grids\\/3-bento-lg-\\d\\./i, [336, 576]],\n  [/^bento-grids\\/3-bento-sm-\\d\\./i, [336, 336]],\n  [/^bento-grids\\/4-bento-lg-(?:1|2)\\./i, [496, 440]],\n  [/^bento-grids\\/4-bento-lg-3\\./i, [688, 242]],\n  [/^bento-grids\\/4-bento-lg-4\\./i, [656, 242]],\n  [/^bento-grids\\/4-bento-sm-[1-4]\\./i, [456, 363]],\n  [/^bento-grids\\/4-bento-sm-5\\./i, [408, 363]],\n  [/^bento-grids\\/bento-1\\./i, [720, 376]],\n  [/^bento-grids\\/bento-[2-5]\\./i, [528, 376]],\n  [/^category-previews\\/landscape-/i, [508, 376]],\n  [/^category-previews\\/portrait-/i, [376, 377]],\n  [/^coupons\\/(?:bg-image-[1-3]|pattern)\\./i, [800, 500]],\n  [/^coupons\\/bg-image-[4-6]\\./i, [1200, 1200]],\n  [/^cta\\/cta-bg-1\\./i, [1200, 772]],\n  [/^cta\\/cta-bg-2\\./i, [1200, 760]],\n  [/^cta\\/cta-bg-3\\./i, [1104, 696]],\n  [/^cta\\/cta-bg-glow\\./i, [1200, 840]],\n  [/^cta\\/cta-collage-(?:1|4)\\./i, [248, 456]],\n  [/^cta\\/(?:cta-collage-(?:2|3)|cta-outwear-\\d)\\./i, [280, 456]],\n  [/^cta\\/cta-split-avatars-\\d\\./i, [280, 338]],\n  [/^cta\\/cta-with-image-\\d\\./i, [944, 606]],\n  [/^cta\\/strip-cut-\\d\\./i, [232, 163]],\n  [/^cta\\/strip-\\d\\./i, [232, 232]],\n  [/^feature\\/feature-1\\./i, [564, 564]],\n  [/^feature\\/feature-2-/i, [432, 432]],\n  [/^feature\\/feature-3-lg-/i, [646, 818]],\n  [/^feature\\/feature-3-sm-/i, [615, 615]],\n  [/^feature\\/stripes-bg-(?:1|2)\\./i, [1035, 1227]],\n  [/^feature\\/stripes-bg-3\\./i, [449, 1227]],\n  [/^feature\\/stripes-bg-(?:4|5)\\./i, [449, 840]],\n  [/^footers\\/bg-image-(?:1|2)\\./i, [1200, 881]],\n  [/^footers\\/bg-image-3\\./i, [1104, 881]],\n  [/^hero\\/aligned-overlay-bg-\\d\\./i, [702, 1200]],\n  [/^hero\\/block-overlay-bg\\./i, [2000, 3000]],\n  [/^hero\\/block-with-bleed-bg\\./i, [2000, 2922]],\n  [/^hero\\/mosaic-\\d\\./i, [240, 240]],\n  [/^hero\\/overlapped-content-bg-(?:1|3)\\./i, [1200, 1258]],\n  [/^hero\\/overlapped-content-bg-2\\./i, [1200, 1374]],\n  [/^hero\\/overlapped-content-bg-4\\./i, [1200, 1410]],\n  [/^hero\\/overlapped-image-\\d-bg\\./i, [2000, 3000]],\n  [/^hero\\/overlapped-image-bg\\./i, [2000, 2995]],\n  [/^hero\\/overlapped-image-2\\./i, [911, 610]],\n  [/^hero\\/overlapped-image-3\\./i, [911, 606]],\n  [/^hero\\/overlapped-image\\./i, [911, 657]],\n  [/^hero\\/(?:overlay-gradient-bg|overlayed-split-bg)\\./i, [2000, 3000]],\n  [/^hero\\/overlayed\\/hero-overlayed-bg\\./i, [1200, 1490]],\n  [/^hero\\/split-contained-bg\\./i, [488, 1104]],\n  [/^hero\\/split-contained-landscape-/i, [488, 376]],\n  [/^hero\\/split-contained-portrait-/i, [488, 680]],\n  [/^hero\\/split-contained-square-/i, [488, 550]],\n  [/^hero\\/split-full-bleed-bg\\./i, [2000, 2666]],\n  [/^hero\\/split-slanted-bg\\./i, [2002, 2534]],\n  [/^image-grids\\/2-col-landscape/i, [528, 372]],\n  [/^image-grids\\/2-col-portrait/i, [528, 792]],\n  [/^image-grids\\/2-col-square/i, [528, 528]],\n  [/^image-grids\\/3-col-masonry-stack/i, [503, 558]],\n  [/^image-grids\\/3-col-masonry/i, [720, 792]],\n  [/^image-grids\\/3-col-portrait/i, [504, 753]],\n  [/^image-grids\\/3-col-square/i, [504, 504]],\n  [/^image-grids\\/full-width\\./i, [1104, 720]],\n  [/^image-grids\\/full-width-\\d\\./i, [1104, 792]],\n  [/^product-detail\\/stacked-\\d\\./i, [508, 358]],\n  [/^product-detail\\/rating-below\\./i, [508, 508]],\n  [/^product-detail\\/single-landscape\\./i, [1104, 776]],\n  [/^product-detail\\/single-portrait\\./i, [508, 764]],\n  [/^product-detail\\/single-portrait-bleed\\./i, [532, 764]],\n  [/^product-detail\\/two-images-\\d\\./i, [528, 776]],\n  [/^product-detail\\/three-images-1\\./i, [528, 776]],\n  [/^product-detail\\/three-images-(?:2|3)\\./i, [528, 364]],\n  [/^product-detail\\/four-images-(?:1|4)\\./i, [528, 488]],\n  [/^product-detail\\/four-images-(?:2|3)\\./i, [528, 240]],\n  [/^stats\\/overlay-1\\./i, [1572, 2268]],\n  [/^stats\\/overlay-2\\./i, [1572, 2716]],\n  [/^stats\\/overlay-(?:3|4)\\./i, [1572, 3256]],\n  [/^stats\\/single-stat\\./i, [1035, 1032]],\n  [/^teams\\/hero\\./i, [1104, 776]],\n  [/^teams\\/member-\\d+-lg\\./i, [528, 376]],\n  [/^teams\\/member-\\d+-md\\./i, [328, 328]],\n  [/^teams\\/member-\\d+\\./i, [256, 256]],\n  [/^timelines\\/cards\\./i, [1072, 600]],\n  [/^testimonials\\/quote\\./i, [72, 62]],\n  [/^testimonials\\/user-\\d+\\./i, [256, 256]],\n  [/^feature\\/logo-north-face\\./i, [278, 128]],\n  [/^feature\\/logo-stripes-1\\./i, [390, 84]],\n  [/^feature\\/logo-stripes-2\\./i, [423, 72]],\n  [/^(?:reviews|testimonials)\\/logo-accentic(?:-light)?\\./i, [423, 72]],\n  [/^reviews\\/logo-amada\\./i, [411, 84]],\n  [/^(?:reviews|testimonials)\\/logo-monarch\\./i, [290, 64]],\n  [/^logos\\/logo-apple-pay\\./i, [180, 72]],\n  [/^logos\\/logo-google-pay\\./i, [177, 72]],\n  [/^logos\\/logo-klarna\\./i, [210, 48]],\n  [/^logos\\/logo-mastercard\\./i, [120, 72]],\n  [/^logos\\/logo-mock-1\\./i, [334, 72]],\n  [/^logos\\/logo-mock-2\\./i, [213, 36]],\n  [/^logos\\/logo-mock-3\\./i, [234, 48]],\n  [/^logos\\/logo-stripe\\./i, [171, 72]],\n  [/^logos\\/logo-visa\\./i, [150, 48]],\n  [/^order-summary\\/logo-fedex\\./i, [156, 48]],\n  [/^emailcn-logo(?:-light)?\\./i, [331, 48]],\n  [/^emailcn-insignia-mono(?:-light)?\\./i, [131, 96]],\n  [/^maizzle-insignia(?:-light)?-lg\\./i, [395, 289]],\n  [/^maizzle-insignia(?:-light)?\\./i, [176, 129]],\n] satisfies readonly (readonly [RegExp, Dimensions])[];\n\nconst socialIconSlugs = {\n  discord: \"discord-logo\",\n  facebook: \"facebook-new\",\n  github: \"github\",\n  instagram: \"instagram-new\",\n  linkedin: \"linkedin\",\n  slack: \"slack-new\",\n  x: \"twitterx--v1\",\n  youtube: \"youtube-play\",\n} as const;\n\nconst brandIconSlugs = [\n  [/(?:btc|bitcoin)/i, \"bitcoin--v1\"],\n  [/(?:eth|ethereum)/i, \"ethereum\"],\n] as const;\n\nconst hashString = (value: string) => {\n  let hash = 7;\n\n  for (const character of value) {\n    hash = (hash * 31 + (character.codePointAt(0) ?? 0)) % 2_147_483_647;\n  }\n\n  return hash;\n};\n\nconst getDimensions = (path: string) => {\n  const matchedRule = dimensionRules.find(([pattern]) => pattern.test(path));\n  if (matchedRule) {\n    return matchedRule[1];\n  }\n  if (/(?:avatar|user|headshot)/i.test(path)) {\n    return [160, 160] as const;\n  }\n  if (/(?:logo|insignia)/i.test(path)) {\n    return [320, 96] as const;\n  }\n  if (/(?:badge-app|badge-google)/i.test(path)) {\n    return [260, 80] as const;\n  }\n  if (/(?:icon|btc|eth)/i.test(path)) {\n    return [64, 64] as const;\n  }\n  if (/(?:portrait|member|team)/i.test(path)) {\n    return [600, 800] as const;\n  }\n  if (/(?:square|product|shopping-cart)/i.test(path)) {\n    return [700, 700] as const;\n  }\n  if (/(?:hero|footer|background|bg-|overlay|full-width)/i.test(path)) {\n    return [1200, 700] as const;\n  }\n  if (/(?:strip|landscape|bento|category-preview)/i.test(path)) {\n    return [800, 520] as const;\n  }\n\n  return [800, 600] as const;\n};\n\nconst getLabel = (path: string) => {\n  if (/insignia/i.test(path)) {\n    return \"EC\";\n  }\n  if (/(?:emailcn|maizzle)/i.test(path)) {\n    return \"emailcn\";\n  }\n\n  const fileName = path.split(\"/\").at(-1) ?? \"emailcn\";\n\n  return fileName\n    .replace(/\\.[^.]+$/, \"\")\n    .replace(/^(?:logo|badge|icon)-/, \"\")\n    .replaceAll(\"-\", \" \")\n    .replaceAll(/\\b\\w/g, (character) => character.toUpperCase());\n};\n\nconst getSeed = (path: string) =>\n  `emailcn-${path\n    .replace(/\\.[^.]+$/, \"\")\n    .replaceAll(/[^a-z0-9]+/gi, \"-\")\n    .replaceAll(/^-|-$/g, \"\")\n    .toLowerCase()}`;\n\nconst getIndexedAsset = (path: string, assets: readonly string[]) => {\n  const explicitIndex = path.match(\n    /(?:member|avatar|product-list|shopping-cart)-(\\d+)/i\n  )?.[1];\n  const index = explicitIndex\n    ? Number.parseInt(explicitIndex, 10) - 1\n    : hashString(path);\n\n  return assets[((index % assets.length) + assets.length) % assets.length];\n};\n\nconst getUnsplashAsset = (id: string, width: number, height: number) =>\n  `${UNSPLASH_ORIGIN}/${id}?auto=format&fit=crop&w=${width}&h=${height}&q=80`;\n\nconst getIcons8Asset = (\n  slug: string,\n  {\n    color,\n    style = \"ios-filled\",\n  }: {\n    color?: string;\n    style?: \"color\" | \"ios\" | \"ios-filled\";\n  } = {}\n) =>\n  color\n    ? `${ICONS8_ORIGIN}/${style}/50/${color}/${slug}.png`\n    : `${ICONS8_ORIGIN}/${style}/50/${slug}.png`;\n\nconst getStoreBadgeAsset = (path: string) => {\n  if (/badge-app-store/i.test(path)) {\n    return \"https://developer.apple.com/assets/elements/badges/download-on-the-app-store.svg\";\n  }\n  if (/badge-google-play/i.test(path)) {\n    return \"https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png\";\n  }\n};\n\nconst getSocialIconAsset = (path: string) => {\n  const isLight = /(?:-light|check-white)/i.test(path);\n  const foreground = isLight ? \"FFFFFF\" : \"111827\";\n\n  for (const [name, slug] of Object.entries(socialIconSlugs)) {\n    if (new RegExp(`(?:^|[-/])${name}(?:[-.]|$)`, \"i\").test(path)) {\n      return getIcons8Asset(slug, { color: foreground });\n    }\n  }\n};\n\nconst getUtilityIconAsset = (path: string) => {\n  const isLight = /(?:-light|check-white)/i.test(path);\n  const foreground = isLight ? \"FFFFFF\" : \"111827\";\n\n  if (/star-half/i.test(path)) {\n    return getIcons8Asset(\"star-half-empty\", { color: \"374151\" });\n  }\n  if (/star-solid/i.test(path)) {\n    return getIcons8Asset(\"star--v1\", { color: \"374151\" });\n  }\n  if (/edit/i.test(path)) {\n    return getIcons8Asset(\"edit--v1\", {\n      color: \"4F46E5\",\n      style: \"ios\",\n    });\n  }\n  if (/arrow-right/i.test(path)) {\n    const color = /(?:brand|indigo)/i.test(path) ? \"4F46E5\" : \"FFFFFF\";\n\n    return getIcons8Asset(\"long-arrow-right--v1\", {\n      color,\n      style: \"ios\",\n    });\n  }\n  if (/chevron-right/i.test(path)) {\n    return getIcons8Asset(\"chevron-right\", {\n      color: \"4F46E5\",\n      style: \"ios\",\n    });\n  }\n  if (/check/i.test(path)) {\n    return getIcons8Asset(\"checkmark--v1\", { color: foreground });\n  }\n};\n\nconst getBrandIconAsset = (path: string) => {\n  const match = brandIconSlugs.find(([pattern]) => pattern.test(path));\n\n  return match\n    ? getIcons8Asset(match[1], {\n        style: \"color\",\n      })\n    : undefined;\n};\n\nconst getWordmarkAsset = (path: string, [width, height]: Dimensions) => {\n  const isLight = /light/i.test(path);\n  const background = isLight ? \"030712\" : \"FFFFFF\";\n  const foreground = isLight ? \"FFFFFF\" : \"111827\";\n\n  return `https://placehold.co/${width}x${height}/${background}/${foreground}.png?text=${encodeURIComponent(\n    getLabel(path)\n  )}`;\n};\n\nconst getIconAsset = (path: string) =>\n  getStoreBadgeAsset(path) ??\n  getSocialIconAsset(path) ??\n  getUtilityIconAsset(path) ??\n  getBrandIconAsset(path);\n\nexport const emailAsset = (path: string) => {\n  const normalizedPath = path.replace(/^\\/+/, \"\");\n\n  if (\n    normalizedPath === \"bento-grids/trend.png\" ||\n    normalizedPath === \"bento-grids/trend-sm.png\"\n  ) {\n    return `${ASSET_ORIGIN}/email-assets/${normalizedPath}`;\n  }\n\n  const iconAsset = getIconAsset(normalizedPath);\n  if (iconAsset) {\n    return iconAsset;\n  }\n\n  const seed = getSeed(normalizedPath);\n  const [width, height] = getDimensions(normalizedPath);\n\n  if (/teams\\/hero/i.test(normalizedPath)) {\n    return getUnsplashAsset(\"photo-1521737711867-e3b97375f902\", width, height);\n  }\n\n  if (/(?:avatar|user|headshot|member|team)/i.test(normalizedPath)) {\n    return getUnsplashAsset(\n      getIndexedAsset(normalizedPath, portraitIds),\n      width,\n      height\n    );\n  }\n\n  if (/products\\/shoe/i.test(normalizedPath)) {\n    return getUnsplashAsset(\"photo-1542291026-7eec264c27ff\", width, height);\n  }\n\n  if (/products\\/phone/i.test(normalizedPath)) {\n    return getUnsplashAsset(\"photo-1511707171634-5f897ff02aa9\", width, height);\n  }\n\n  if (\n    /(?:category-previews|product-detail|product-lists|products\\/product|shopping-cart)/i.test(\n      normalizedPath\n    )\n  ) {\n    return getUnsplashAsset(\n      getIndexedAsset(normalizedPath, productIds),\n      width,\n      height\n    );\n  }\n\n  if (/(?:trend|chart)/i.test(normalizedPath)) {\n    return `${ASSET_ORIGIN}/email-assets/bento-grids/trend.png`;\n  }\n\n  if (/(?:emailcn|maizzle|logo|insignia)/i.test(normalizedPath)) {\n    return getWordmarkAsset(normalizedPath, [width, height]);\n  }\n\n  return `https://picsum.photos/seed/${encodeURIComponent(\n    seed\n  )}/${width}/${height}.jpg`;\n};\n",
      "type": "registry:file",
      "target": "components/email/email-assets.ts"
    }
  ],
  "categories": [
    "email-component"
  ],
  "type": "registry:component"
}