├── .eslintrc.json ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ └── feature_request.md ├── card.png └── workflows │ ├── ci.yml │ └── pr.yml ├── .gitignore ├── .husky └── pre-commit ├── .npmrc ├── .prettierignore ├── .prettierrc ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── package.json ├── playground ├── LICENSE ├── cards │ ├── playground-data.ts │ └── preview-tabs.ts ├── components │ ├── introduction.module.css │ ├── introduction.tsx │ ├── panel-resize-handle.module.css │ ├── panel-resize-handle.tsx │ └── resvg_worker.ts ├── decs.d.ts ├── index.d.ts ├── next-env.d.ts ├── package.json ├── pages │ ├── _app.tsx │ ├── _document.tsx │ ├── api │ │ └── font.ts │ └── index.tsx ├── public │ ├── favicon.ico │ ├── iaw-mono-var.woff2 │ ├── inter-latin-ext-400-normal.woff │ ├── inter-latin-ext-700-normal.woff │ ├── material-icons-base-400-normal.woff │ └── og.png ├── styles.css ├── tsconfig.json └── utils │ ├── font.ts │ └── twemoji.ts ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── release.config.cjs ├── src ├── builder │ ├── background-image.ts │ ├── border-radius.ts │ ├── border.ts │ ├── clip-path.ts │ ├── content-mask.ts │ ├── gradient │ │ ├── linear.ts │ │ ├── radial.ts │ │ └── utils.ts │ ├── mask-image.ts │ ├── overflow.ts │ ├── rect.ts │ ├── shadow.ts │ ├── svg.ts │ ├── text-decoration.ts │ ├── text.ts │ └── transform.ts ├── font.ts ├── handler │ ├── compute.ts │ ├── expand.ts │ ├── image.ts │ ├── inheritable.ts │ ├── preprocess.ts │ ├── presets.ts │ └── tailwind.ts ├── index.ts ├── language.ts ├── layout.ts ├── parser │ ├── mask.ts │ └── shape.ts ├── satori.ts ├── text │ ├── characters.ts │ ├── index.ts │ ├── measurer.ts │ └── processor.ts ├── transform-origin.ts ├── types.d.ts ├── utils.ts ├── vendor │ ├── parse-css-dimension │ │ ├── LICENSE │ │ ├── index.js │ │ ├── package.json │ │ └── src.js │ └── twrnc │ │ ├── deprecate.js │ │ ├── log.js │ │ └── picocolors.js └── yoga │ ├── index.ts │ ├── yoga-prebuilt.ts │ └── yoga-prebuilt.wasm.ts ├── test ├── __image_snapshots__ │ ├── background-clip-test-tsx-test-background-clip-test-tsx-background-clip-should-render-background-clip-text-1-snap.png │ ├── background-clip-test-tsx-test-background-clip-test-tsx-background-clip-should-render-background-clip-text-compatible-with-mask-1-snap.png │ ├── background-clip-test-tsx-test-background-clip-test-tsx-background-clip-should-render-background-clip-text-compatible-with-transform-1-snap.png │ ├── basic-test-tsx-test-basic-test-tsx-basic-should-combine-text-nodes-correctly-1-snap.png │ ├── basic-test-tsx-test-basic-test-tsx-basic-should-render-basic-div-with-background-color-1-snap.png │ ├── basic-test-tsx-test-basic-test-tsx-basic-should-render-basic-div-with-text-1-snap.png │ ├── basic-test-tsx-test-basic-test-tsx-basic-should-render-basic-div-with-text-and-background-color-1-snap.png │ ├── basic-test-tsx-test-basic-test-tsx-basic-should-render-empty-div-1-snap.png │ ├── basic-test-tsx-test-basic-test-tsx-basic-should-support-array-in-jsx-children-1-snap.png │ ├── basic-test-tsx-test-basic-test-tsx-basic-should-support-hex-colors-1-snap.png │ ├── basic-test-tsx-test-basic-test-tsx-basic-should-support-skipping-embedded-fonts-1-snap.png │ ├── border-test-tsx-test-border-test-tsx-border-border-color-should-fallback-border-color-to-the-current-color-1-snap.png │ ├── border-test-tsx-test-border-test-tsx-border-border-color-should-render-black-border-by-default-1-snap.png │ ├── border-test-tsx-test-border-test-tsx-border-border-color-should-support-overriding-border-color-1-snap.png │ ├── border-test-tsx-test-border-test-tsx-border-border-color-should-support-specifying-border-color-1-snap.png │ ├── border-test-tsx-test-border-test-tsx-border-border-radius-should-not-exceed-the-length-of-the-short-side-1-snap.png │ ├── border-test-tsx-test-border-test-tsx-border-border-radius-should-support-percentage-border-radius-1-snap.png │ ├── border-test-tsx-test-border-test-tsx-border-border-radius-should-support-radius-for-a-certain-corner-1-snap.png │ ├── border-test-tsx-test-border-test-tsx-border-border-radius-should-support-slash-and-2-value-corner-1-snap.png │ ├── border-test-tsx-test-border-test-tsx-border-border-radius-should-support-the-shorthand-1-snap.png │ ├── border-test-tsx-test-border-test-tsx-border-border-radius-should-support-vw-vh-em-and-rem-units-1-snap.png │ ├── border-test-tsx-test-border-test-tsx-border-border-should-support-the-shorthand-1-snap.png │ ├── border-test-tsx-test-border-test-tsx-border-border-style-should-support-dashed-border-1-snap.png │ ├── border-test-tsx-test-border-test-tsx-border-border-width-should-render-border-inside-the-shape-1-snap.png │ ├── border-test-tsx-test-border-test-tsx-border-directional-should-support-advanced-border-with-radius-1-snap.png │ ├── border-test-tsx-test-border-test-tsx-border-directional-should-support-directional-border-1-snap.png │ ├── border-test-tsx-test-border-test-tsx-border-directional-should-support-non-complete-border-1-snap.png │ ├── clip-path-test-tsx-test-clip-path-test-tsx-border-should-make-clip-path-compatible-with-overflow-1-snap.png │ ├── clip-path-test-tsx-test-clip-path-test-tsx-border-should-render-clip-path-1-snap.png │ ├── clip-path-test-tsx-test-clip-path-test-tsx-border-should-render-clip-path-2-snap.png │ ├── clip-path-test-tsx-test-clip-path-test-tsx-border-should-render-clip-path-3-snap.png │ ├── clip-path-test-tsx-test-clip-path-test-tsx-border-should-render-clip-path-4-snap.png │ ├── clip-path-test-tsx-test-clip-path-test-tsx-border-should-render-clip-path-5-snap.png │ ├── clip-path-test-tsx-test-clip-path-test-tsx-border-should-render-clip-path-6-snap.png │ ├── clip-path-test-tsx-test-clip-path-test-tsx-border-should-render-clip-path-7-snap.png │ ├── clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-make-clip-path-compatible-with-overflow-1-snap.png │ ├── clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-render-clip-path-1-snap.png │ ├── clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-render-clip-path-2-snap.png │ ├── clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-render-clip-path-3-snap.png │ ├── clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-render-clip-path-4-snap.png │ ├── clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-render-clip-path-5-snap.png │ ├── clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-render-clip-path-6-snap.png │ ├── clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-render-clip-path-7-snap.png │ ├── clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-repect-left-and-top-1-snap.png │ ├── clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-respect-left-and-top-1-snap.png │ ├── clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-respect-the-position-value-1-snap.png │ ├── color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-currentcolor-when-background-1-snap.png │ ├── color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-currentcolor-when-border-1-snap.png │ ├── color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-currentcolor-when-inherit-1-snap.png │ ├── color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-hexadecimal-1-snap.png │ ├── color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-hexadecimal-with-transparency-1-snap.png │ ├── color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-hsl-1-snap.png │ ├── color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-hsla-1-snap.png │ ├── color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-inherit-color-1-snap.png │ ├── color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-predefined-color-names-1-snap.png │ ├── color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-rgb-1-snap.png │ ├── color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-rgba-1-snap.png │ ├── color-models-test-tsx-test-color-models-test-tsx-color-models-should-support-css-4-syntax-color-in-hsl-1-snap.png │ ├── color-models-test-tsx-test-color-models-test-tsx-color-models-should-support-css-4-syntax-color-in-hsl-if-inherited-1-snap.png │ ├── dynamic-size-test-tsx-test-dynamic-size-test-tsx-dynamic-size-should-render-image-with-dynamic-height-1-snap.png │ ├── dynamic-size-test-tsx-test-dynamic-size-test-tsx-dynamic-size-should-render-image-with-dynamic-width-1-snap.png │ ├── emoji-test-tsx-test-emoji-test-tsx-emojis-should-render-emojis-correctly-1-snap.png │ ├── emoji-test-tsx-test-emoji-test-tsx-emojis-should-render-emojis-correctly-with-alphabetic-emoji-1-snap.png │ ├── emoji-test-tsx-test-emoji-test-tsx-emojis-should-render-emojis-correctly-with-word-break-break-all-1-snap.png │ ├── font-test-tsx-test-font-test-tsx-font-font-size-should-allow-font-size-to-be-0-1-snap.png │ ├── font-test-tsx-test-font-test-tsx-font-should-handle-escape-html-when-embed-font-is-false-1-snap.png │ ├── font-test-tsx-test-font-test-tsx-font-should-handle-font-family-fallback-1-snap.png │ ├── font-test-tsx-test-font-test-tsx-font-should-handle-font-size-correctly-for-element-like-heading-1-snap.png │ ├── font-test-tsx-test-font-test-tsx-font-should-handle-font-size-correctly-for-element-like-heading-2-snap.png │ ├── font-test-tsx-test-font-test-tsx-font-should-handle-font-size-correctly-for-element-like-heading-3-snap.png │ ├── font-test-tsx-test-font-test-tsx-font-should-not-error-when-no-font-is-specified-and-no-text-rendered-1-snap.png │ ├── font-test-tsx-test-font-test-tsx-font-should-use-correct-fonts-1-snap.png │ ├── gap-test-tsx-test-gap-test-tsx-flex-gap-should-support-gap-1-snap.png │ ├── gap-test-tsx-test-gap-test-tsx-flex-gap-should-support-row-gap-and-column-gap-1-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-linear-gradient-should-support-linear-gradient-1-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-linear-gradient-should-support-linear-gradient-with-omitted-orientation-1-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-linear-gradient-should-support-linear-gradient-with-transparency-1-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-linear-gradient-should-support-multiple-direction-keywords-1-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-linear-gradient-should-support-other-degree-unit-1-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-linear-gradient-should-support-other-degree-unit-2-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-linear-gradient-should-support-other-degree-unit-3-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-linear-gradient-should-support-repeating-linear-gradient-1-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-linear-gradient-should-support-using-background-instead-of-background-image-1-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-default-value-1-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-explicitly-setting-rg-size-1-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-explicitly-setting-rg-size-2-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-radial-gradient-1-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-radial-gradient-with-unspecified-ending-shape-1-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-releative-unit-1-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-releative-unit-2-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-releative-unit-3-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-releative-unit-4-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-rg-size-with-rg-extent-keyword-1-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-rg-size-with-rg-extent-keyword-2-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-rg-size-with-rg-extent-keyword-3-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-compute-correct-cycle-1-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-compute-correct-cycle-2-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-support-background-size-and-background-repeat-1-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-support-degree-1-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-support-degree-2-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-support-degree-3-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-support-degree-4-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-support-multiple-repeating-linear-gradient-1-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-support-repeating-linear-gradient-1-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-support-repeating-linear-gradient-2-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-should-be-able-to-render-grid-backgrounds-1-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-should-calculate-the-gradient-angle-and-length-correctly-1-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-should-calculate-the-gradient-angle-and-length-correctly-with-offset-1-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-should-render-gradient-patterns-in-the-correct-object-space-1-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-should-resolve-gradient-layers-in-the-correct-order-1-snap.png │ ├── gradient-test-tsx-test-gradient-test-tsx-gradient-should-support-advanced-usage-1-snap.png │ ├── image-test-tsx-test-image-test-tsx-background-image-url-should-correctly-position-the-background-pattern-1-snap.png │ ├── image-test-tsx-test-image-test-tsx-background-image-url-should-handle-charset-utf-8-1-snap.png │ ├── image-test-tsx-test-image-test-tsx-background-image-url-should-handle-charset-utf-8-with-comma-in-data-1-snap.png │ ├── image-test-tsx-test-image-test-tsx-background-image-url-should-handle-charset-utf-8-with-in-base-64-1-snap.png │ ├── image-test-tsx-test-image-test-tsx-background-image-url-should-resolve-data-uris-with-size-for-supported-image-formats-1-snap.png │ ├── image-test-tsx-test-image-test-tsx-background-image-url-should-resolve-data-uris-with-size-for-supported-image-formats-2-snap.png │ ├── image-test-tsx-test-image-test-tsx-background-image-url-should-resolve-data-uris-with-size-for-supported-image-formats-3-snap.png │ ├── image-test-tsx-test-image-test-tsx-background-image-url-should-resolve-data-uris-with-size-for-supported-image-formats-4-snap.png │ ├── image-test-tsx-test-image-test-tsx-background-image-url-should-resolve-data-uris-with-size-for-supported-image-formats-5-snap.png │ ├── image-test-tsx-test-image-test-tsx-background-image-url-should-resolve-image-data-1-snap.png │ ├── image-test-tsx-test-image-test-tsx-background-image-url-should-support-double-quotes-inside-url-1-snap.png │ ├── image-test-tsx-test-image-test-tsx-background-image-url-should-support-single-quotes-inside-url-1-snap.png │ ├── image-test-tsx-test-image-test-tsx-background-image-url-should-support-stretched-background-size-1-snap.png │ ├── image-test-tsx-test-image-test-tsx-image-should-clip-content-in-the-border-and-padding-areas-1-snap.png │ ├── image-test-tsx-test-image-test-tsx-image-should-clip-content-in-the-border-area-1-snap.png │ ├── image-test-tsx-test-image-test-tsx-image-should-deduplicate-image-data-requests-1-snap.png │ ├── image-test-tsx-test-image-test-tsx-image-should-have-a-separate-border-radius-clip-path-when-transform-is-used-1-snap.png │ ├── image-test-tsx-test-image-test-tsx-image-should-not-throw-when-image-is-not-valid-1-snap.png │ ├── image-test-tsx-test-image-test-tsx-image-should-render-svg-with-image-1-snap.png │ ├── image-test-tsx-test-image-test-tsx-image-should-resolve-image-data-1-snap.png │ ├── image-test-tsx-test-image-test-tsx-image-should-resolve-non-square-image-size-correctly-1-snap.png │ ├── image-test-tsx-test-image-test-tsx-image-should-resolve-the-image-size-and-scale-automatically-1-snap.png │ ├── image-test-tsx-test-image-test-tsx-image-should-scale-image-to-fit-max-width-and-max-height-but-maintain-the-aspect-ratio-1-snap.png │ ├── image-test-tsx-test-image-test-tsx-image-should-scale-image-to-fit-max-width-and-max-height-but-maintain-the-aspect-ratio-2-snap.png │ ├── image-test-tsx-test-image-test-tsx-image-should-support-array-buffer-as-src-1-snap.png │ ├── image-test-tsx-test-image-test-tsx-image-should-support-opacity-1-snap.png │ ├── image-test-tsx-test-image-test-tsx-image-should-support-styles-1-snap.png │ ├── image-test-tsx-test-image-test-tsx-image-should-support-svg-images-and-percentage-with-correct-aspect-ratio-1-snap.png │ ├── image-test-tsx-test-image-test-tsx-image-should-support-transparent-image-with-background-1-snap.png │ ├── language-test-tsx-test-language-test-tsx-detect-language-code-should-not-crash-when-rendering-arabic-letters-1-snap.png │ ├── layout-test-tsx-test-layout-test-tsx-layout-should-stretch-items-by-default-1-snap.png │ ├── line-clamp-test-tsx-test-line-clamp-test-tsx-line-clamp-should-not-work-when-display-is-not-set-to-block-1-snap.png │ ├── line-clamp-test-tsx-test-line-clamp-test-tsx-line-clamp-should-replace-custom-block-ellipsis-with-default-ellipsis-when-too-long-1-snap.png │ ├── line-clamp-test-tsx-test-line-clamp-test-tsx-line-clamp-should-work-correctly-1-snap.png │ ├── line-clamp-test-tsx-test-line-clamp-test-tsx-line-clamp-should-work-correctly-when-text-align-center-1-snap.png │ ├── line-height-test-tsx-test-line-height-test-tsx-line-height-should-work-correctly-1-snap.png │ ├── line-height-test-tsx-test-line-height-test-tsx-line-height-should-work-correctly-2-snap.png │ ├── mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-mask-image-1-snap.png │ ├── mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-mask-image-2-snap.png │ ├── mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-mask-image-3-snap.png │ ├── mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-mask-image-on-img-1-snap.png │ ├── mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-mask-image-on-positioned-elements-1-snap.png │ ├── mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-mask-image-on-text-1-snap.png │ ├── mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-mask-position-1-snap.png │ ├── mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-mask-repeat-1-snap.png │ ├── mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-mask-size-1-snap.png │ ├── mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-multiple-mask-image-1-snap.png │ ├── overflow-test-tsx-test-overflow-test-tsx-overflow-should-not-show-overflowed-text-1-snap.png │ ├── overflow-test-tsx-test-overflow-test-tsx-overflow-should-not-work-when-overflow-is-not-hidden-and-overflow-property-should-not-be-inherited-1-snap.png │ ├── overflow-test-tsx-test-overflow-test-tsx-overflow-should-work-with-ellipsis-nowrap-1-snap.png │ ├── overflow-test-tsx-test-overflow-test-tsx-overflow-should-work-with-nested-border-border-radius-padding-1-snap.png │ ├── position-test-tsx-test-position-test-tsx-position-absolute-should-support-absolute-position-1-snap.png │ ├── shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-be-affected-by-container-opacity-1-snap.png │ ├── shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-render-box-shadow-with-offset-1-snap.png │ ├── shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-render-box-shadow-with-offset-and-spread-1-snap.png │ ├── shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-render-multiple-box-shadows-1-snap.png │ ├── shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-render-regular-box-shadow-1-snap.png │ ├── shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-show-box-shadow-without-specifying-height-1-snap.png │ ├── shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-support-box-shadow-for-transparent-elements-1-snap.png │ ├── shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-support-box-shadow-spread-with-transparency-1-snap.png │ ├── shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-support-inset-box-shadows-1-snap.png │ ├── shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-support-multiple-text-shadows-1-snap.png │ ├── shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-support-negative-spread-1-snap.png │ ├── shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-support-text-shadows-if-exist-unexpected-comma-1-snap.png │ ├── shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-work-correct-with-zero-border-radius-1-snap.png │ ├── svg-test-tsx-test-svg-test-tsx-svg-should-parse-view-box-correctly-1-snap.png │ ├── svg-test-tsx-test-svg-test-tsx-svg-should-render-svg-attributes-correctly-1-snap.png │ ├── svg-test-tsx-test-svg-test-tsx-svg-should-render-svg-nodes-1-snap.png │ ├── svg-test-tsx-test-svg-test-tsx-svg-should-render-svg-prefer-size-props-rather-than-view-box-1-snap.png │ ├── svg-test-tsx-test-svg-test-tsx-svg-should-render-svg-size-correctly-1-snap.png │ ├── svg-test-tsx-test-svg-test-tsx-svg-should-render-svg-without-view-box-1-snap.png │ ├── svg-test-tsx-test-svg-test-tsx-svg-should-respect-style-on-svg-node-1-snap.png │ ├── svg-test-tsx-test-svg-test-tsx-svg-should-support-current-color-for-svg-fill-1-snap.png │ ├── svg-test-tsx-test-svg-test-tsx-svg-should-support-current-color-for-svg-stroke-1-snap.png │ ├── svg-test-tsx-test-svg-test-tsx-svg-should-support-current-color-when-color-is-set-on-parent-element-1-snap.png │ ├── svg-test-tsx-test-svg-test-tsx-svg-should-support-current-color-when-used-on-svg-nodes-1-snap.png │ ├── svg-test-tsx-test-svg-test-tsx-svg-should-support-em-in-svg-size-1-snap.png │ ├── tab-size-test-tsx-test-tab-size-test-tsx-tab-size-tab-renders-as-space-when-white-space-is-not-pre-or-pre-wrap-1-snap.png │ ├── tab-size-test-tsx-test-tab-size-test-tsx-tab-size-tabs-render-correctly-when-tab-size-is-a-number-1-snap.png │ ├── tab-size-test-tsx-test-tab-size-test-tsx-tab-size-tabs-render-correctly-when-tab-size-is-a-string-1-snap.png │ ├── tab-size-test-tsx-test-tab-size-test-tsx-tab-size-tabs-render-correctly-with-default-tab-size-of-8-when-white-space-is-pre-1-snap.png │ ├── tab-size-test-tsx-test-tab-size-test-tsx-tab-size-tabs-render-correctly-with-default-tab-size-of-8-when-white-space-is-pre-wrap-1-snap.png │ ├── text-align-test-tsx-test-text-align-test-tsx-text-align-should-work-correctly-when-text-align-center-1-snap.png │ ├── text-align-test-tsx-test-text-align-test-tsx-text-align-should-work-correctly-when-text-align-end-1-snap.png │ ├── text-align-test-tsx-test-text-align-test-tsx-text-align-should-work-correctly-when-text-align-left-1-snap.png │ ├── text-align-test-tsx-test-text-align-test-tsx-text-align-should-work-correctly-when-text-align-right-1-snap.png │ ├── text-decoration-test-tsx-test-text-decoration-test-tsx-text-decoration-should-work-correctly-when-text-decoration-line-line-through-1-snap.png │ ├── text-decoration-test-tsx-test-text-decoration-test-tsx-text-decoration-should-work-correctly-when-text-decoration-line-line-through-and-text-align-right-1-snap.png │ ├── text-decoration-test-tsx-test-text-decoration-test-tsx-text-decoration-should-work-correctly-when-text-decoration-line-underline-and-text-align-right-1-snap.png │ ├── text-decoration-test-tsx-test-text-decoration-test-tsx-text-decoration-should-work-correctly-when-text-decoration-style-dashed-1-snap.png │ ├── text-decoration-test-tsx-test-text-decoration-test-tsx-text-decoration-should-work-correctly-when-text-decoration-style-dotted-1-snap.png │ ├── text-decoration-test-tsx-test-text-decoration-test-tsx-text-decoration-should-work-correctly-with-text-decoration-and-transform-1-snap.png │ ├── text-wrap-test-tsx-test-text-wrap-test-tsx-text-wrap-should-wrap-balancedly-with-text-wrap-balance-1-snap.png │ ├── text-wrap-test-tsx-test-text-wrap-test-tsx-text-wrap-should-wrap-normally-with-text-wrap-wrap-1-snap.png │ ├── transform-test-tsx-test-transform-test-tsx-transform-behavior-with-parent-overflow-should-not-inherit-parent-clip-path-1-snap.png │ ├── transform-test-tsx-test-transform-test-tsx-transform-multiple-transforms-should-support-translate-rotate-and-scale-1-snap.png │ ├── transform-test-tsx-test-transform-test-tsx-transform-rotate-should-rotate-shape-1-snap.png │ ├── transform-test-tsx-test-transform-test-tsx-transform-scale-should-scale-shape-1-snap.png │ ├── transform-test-tsx-test-transform-test-tsx-transform-scale-should-scale-shape-in-two-directions-1-snap.png │ ├── transform-test-tsx-test-transform-test-tsx-transform-translate-should-support-1-snap.png │ ├── transform-test-tsx-test-transform-test-tsx-transform-translate-should-translate-shape-1-snap.png │ ├── transform-test-tsx-test-transform-test-tsx-transform-translate-should-translate-shape-in-x-axis-1-snap.png │ ├── transform-test-tsx-test-transform-test-tsx-transform-translate-should-translate-shape-in-y-axis-1-snap.png │ ├── units-test-tsx-test-units-test-tsx-units-should-support-1-snap.png │ ├── units-test-tsx-test-units-test-tsx-units-should-support-em-1-snap.png │ ├── units-test-tsx-test-units-test-tsx-units-should-support-px-and-numbers-1-snap.png │ ├── units-test-tsx-test-units-test-tsx-units-should-support-rem-1-snap.png │ ├── units-test-tsx-test-units-test-tsx-units-should-support-rgb-syntaxs-1-snap.png │ ├── units-test-tsx-test-units-test-tsx-units-should-support-vh-and-vw-1-snap.png │ ├── webkit-text-stroke-test-tsx-test-webkit-text-stroke-test-tsx-webkit-text-stroke-should-work-basic-text-stroke-1-snap.png │ ├── webkit-text-stroke-test-tsx-test-webkit-text-stroke-test-tsx-webkit-text-stroke-should-work-nested-and-complex-text-stroke-1-snap.png │ ├── webkit-text-stroke-test-tsx-test-webkit-text-stroke-test-tsx-webkit-text-stroke-should-work-nested-text-stroke-1-snap.png │ ├── white-space-test-tsx-test-white-space-test-tsx-white-space-normal-should-have-line-break-before-fast-1-snap.png │ ├── white-space-test-tsx-test-white-space-test-tsx-white-space-normal-should-not-render-extra-line-breaks-with-white-space-normal-1-snap.png │ ├── white-space-test-tsx-test-white-space-test-tsx-white-space-normal-should-not-render-extra-spaces-with-white-space-normal-1-snap.png │ ├── white-space-test-tsx-test-white-space-test-tsx-white-space-normal-should-wrap-automatically-with-white-space-normal-1-snap.png │ ├── white-space-test-tsx-test-white-space-test-tsx-white-space-pre-should-always-preserve-extra-line-breaks-with-white-space-pre-1-snap.png │ ├── white-space-test-tsx-test-white-space-test-tsx-white-space-pre-should-always-preserve-extra-spaces-with-white-space-pre-1-snap.png │ ├── white-space-test-tsx-test-white-space-test-tsx-white-space-pre-should-not-wrap-with-white-space-pre-1-snap.png │ ├── white-space-test-tsx-test-white-space-test-tsx-white-space-pre-should-render-line-breaks-correctly-without-separators-1-snap.png │ ├── white-space-test-tsx-test-white-space-test-tsx-white-space-with-n-in-content-should-render-consecutive-line-breaks-with-pre-1-snap.png │ ├── white-space-test-tsx-test-white-space-test-tsx-white-space-with-n-in-content-should-render-n-as-a-line-break-with-pre-1-snap.png │ ├── white-space-test-tsx-test-white-space-test-tsx-white-space-with-n-in-content-should-render-n-as-a-whitespace-1-snap.png │ ├── white-space-test-tsx-test-white-space-test-tsx-white-space-with-white-space-nowrap-should-not-wrap-with-white-space-nowrap-and-swallow-extra-spaces-1-snap.png │ ├── white-space-test-tsx-test-white-space-test-tsx-white-space-with-white-space-pre-line-should-always-collapse-spaces-and-preserve-line-breaks-with-white-space-pre-line-1-snap.png │ ├── white-space-test-tsx-test-white-space-test-tsx-white-space-with-white-space-pre-wrap-should-always-preserve-extra-line-breaks-with-white-space-pre-wrap-1-snap.png │ ├── white-space-test-tsx-test-white-space-test-tsx-white-space-with-white-space-pre-wrap-should-always-preserve-extra-spaces-with-white-space-pre-wrap-1-snap.png │ ├── white-space-test-tsx-test-white-space-test-tsx-white-space-with-white-space-pre-wrap-should-automatically-wrap-with-white-space-pre-wrap-1-snap.png │ ├── word-break-test-tsx-test-word-break-test-tsx-word-break-break-all-should-always-break-words-eagerly-1-snap.png │ ├── word-break-test-tsx-test-word-break-test-tsx-word-break-break-word-should-break-words-if-cannot-fit-into-one-line-1-snap.png │ ├── word-break-test-tsx-test-word-break-test-tsx-word-break-break-word-should-not-break-cjk-with-word-break-keep-all-1-snap.png │ ├── word-break-test-tsx-test-word-break-test-tsx-word-break-break-word-should-try-to-wrap-words-if-possible-1-snap.png │ ├── word-break-test-tsx-test-word-break-test-tsx-word-break-break-word-should-wrap-first-and-then-break-long-words-1-snap.png │ ├── word-break-test-tsx-test-word-break-test-tsx-word-break-normal-should-not-break-long-word-1-snap.png │ ├── word-break-test-tsx-test-word-break-test-tsx-word-break-normal-should-not-break-word-if-possible-to-wrap-1-snap.png │ └── word-break-test-tsx-test-word-break-test-tsx-word-break-should-support-non-breaking-space-1-snap.png ├── assets │ ├── MontserratSubrayada-Regular.ttf │ ├── Roboto-Bold.ttf │ ├── Roboto-Regular.ttf │ ├── playfair-display.ttf │ ├── Χαίρετ │ ├── こんにちは │ ├── 你好 │ └── 안녕 ├── background-clip.test.tsx ├── basic.test.tsx ├── border.test.tsx ├── clip-path.test.tsx ├── color-models.test.tsx ├── dynamic-size.test.tsx ├── emoji.test.tsx ├── error.test.tsx ├── event.test.tsx ├── font.test.tsx ├── gap.test.tsx ├── gradient.test.tsx ├── image.test.tsx ├── language.test.tsx ├── layout.test.tsx ├── line-clamp.test.tsx ├── line-height.test.tsx ├── mask-image.test.tsx ├── overflow.test.tsx ├── position.test.tsx ├── shadow.test.tsx ├── svg.test.tsx ├── tab-size.test.tsx ├── text-align.test.tsx ├── text-decoration.test.tsx ├── text-wrap.test.tsx ├── transform.test.tsx ├── units.test.tsx ├── utils.tsx ├── webkit-text-stroke.test.tsx ├── white-space.test.tsx └── word-break.test.tsx ├── tsconfig.json ├── tsconfig.wasm.json ├── tsup.config.ts ├── turbo.json ├── vitest.config.ts └── wasm.js /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:react/recommended", 10 | "plugin:react/jsx-runtime", 11 | "plugin:@typescript-eslint/recommended" 12 | ], 13 | "overrides": [ 14 | { 15 | "files": ["src/**/*.js", "src/**/*.ts"], 16 | "rules": { 17 | "prefer-const": "off" 18 | } 19 | }, 20 | { 21 | "files": ["test/**/*.ts`", "test/**/*.tsx"], 22 | "rules": { 23 | "react/jsx-key": "off" 24 | } 25 | } 26 | ], 27 | "parser": "@typescript-eslint/parser", 28 | "parserOptions": { 29 | "project": [ 30 | "./tsconfig.json", 31 | "./playground/tsconfig.json", 32 | "./tsconfig.wasm.json" 33 | ] 34 | }, 35 | "plugins": ["react", "react-hooks", "@typescript-eslint"], 36 | "rules": { 37 | "no-inner-declarations": 0, 38 | "no-useless-escape": 1, 39 | "@typescript-eslint/ban-ts-comment": 1, 40 | "@typescript-eslint/no-extra-semi": 0, 41 | "@typescript-eslint/no-shadow": 2, 42 | "@typescript-eslint/ban-types": 0, 43 | "@typescript-eslint/no-namespace": 0, 44 | "react-hooks/rules-of-hooks": 2, 45 | "react-hooks/exhaustive-deps": 1, 46 | "react/prop-types": 0 47 | }, 48 | "ignorePatterns": ["dist/", "node_modules", "vendor"] 49 | } 50 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @shuding 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a bug report for Satori 4 | --- 5 | 6 | # Bug report 7 | 8 | ## Description / Observed Behavior 9 | 10 | What kind of issues did you encounter with Satori? 11 | 12 | ## Expected Behavior 13 | 14 | How did you expect Satori to behave here? 15 | 16 | ## Reproduction 17 | 18 | Create a shareable reproduction link for the issue using https://og-playground.vercel.app. 19 | 20 | ## Additional Context 21 | 22 | Satori version, and any other context about the problem here. 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Question & Ideas 4 | url: https://github.com/vercel/satori/discussions 5 | about: Ask questions and share your thoughts with other community members 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Request a new feature for Satori 4 | --- 5 | 6 | # Feature Request 7 | 8 | ## Description 9 | 10 | What do you want to add to Satori, and why? 11 | 12 | ## Additional Context 13 | 14 | You can add a shareable link using https://og-playground.vercel.app, or any other context that helps explaining this feature request here. 15 | -------------------------------------------------------------------------------- /.github/card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/.github/card.png -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: ['main'] 6 | pull_request: 7 | branches: ['main'] 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.ref }} 11 | cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} 12 | 13 | jobs: 14 | integrity: 15 | # prevents this action from running on forks 16 | if: github.repository_owner == 'vercel' 17 | runs-on: ubuntu-latest 18 | strategy: 19 | matrix: 20 | node: [ 20 ] 21 | steps: 22 | - name: Checkout 23 | uses: actions/checkout@v3 24 | - name: Use pnpm 25 | run: corepack enable pnpm && pnpm --version 26 | - name: Use Node.js ${{ matrix.node }} 27 | uses: actions/setup-node@v3 28 | with: 29 | node-version: ${{ matrix.node }} 30 | cache: 'pnpm' 31 | - run: pnpm install 32 | - run: pnpm ci-check 33 | 34 | test: 35 | # prevents this action from running on forks 36 | if: github.repository_owner == 'vercel' 37 | name: Node.js ${{ matrix.node }} on ${{ matrix.os }} 38 | timeout-minutes: 5 39 | strategy: 40 | fail-fast: false 41 | matrix: 42 | os: [ubuntu-latest] 43 | node: [18, 20] 44 | runs-on: ${{ matrix.os }} 45 | permissions: 46 | contents: write # to be able to publish a GitHub release 47 | issues: write # to be able to comment on released issues 48 | pull-requests: write # to be able to comment on released pull requests 49 | id-token: write # to enable use of OIDC for npm provenance 50 | steps: 51 | - name: Checkout 52 | uses: actions/checkout@v3 53 | - name: Use pnpm 54 | run: corepack enable pnpm && pnpm --version 55 | - name: Use Node.js ${{ matrix.node }} 56 | uses: actions/setup-node@v3 57 | with: 58 | node-version: ${{ matrix.node }} 59 | cache: 'pnpm' 60 | - run: pnpm install 61 | - run: pnpm build 62 | - run: pnpm test 63 | - name: Maybe Release 64 | if: matrix.os == 'ubuntu-latest' && matrix.node == 20 && github.event_name == 'push' && github.ref == 'refs/heads/main' 65 | env: 66 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 67 | NPM_TOKEN: ${{ secrets.NPM_TOKEN_ELEVATED }} 68 | NPM_CONFIG_PROVENANCE: 'true' 69 | run: pnpm dlx semantic-release@24.2.3 70 | -------------------------------------------------------------------------------- /.github/workflows/pr.yml: -------------------------------------------------------------------------------- 1 | name: PR 2 | on: 3 | pull_request: 4 | types: [opened, edited, synchronize] 5 | pull_request_target: 6 | types: [opened, edited, synchronize] 7 | 8 | jobs: 9 | lint: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: amannn/action-semantic-pull-request@0b14f54ac155d88e12522156e52cb6e397745cfd 13 | env: 14 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | .vercel 4 | .vscode 5 | .next 6 | .idea 7 | .turbo 8 | dist 9 | .pnpm-debug.log 10 | __diff_output__ 11 | .eslintcache 12 | coverage 13 | 14 | playground/public/yoga.wasm 15 | playground/tsconfig.tsbuildinfo 16 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | pnpm lint-staged 5 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | shell-emulator=true 2 | provenance=true 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .github/ 2 | node_modules 3 | **/.next/** 4 | **/_next/** 5 | **/dist/** 6 | src/vendor/ 7 | pnpm-lock.yaml 8 | *.md 9 | coverage/ -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "useTabs": false, 4 | "singleQuote": true, 5 | "jsxSingleQuote": true, 6 | "semi": false 7 | } 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Satori Contribution Guidelines 2 | 3 | Thank you for reading this guide and we appreciate any contribution. 4 | 5 | ## Ask a Question 6 | 7 | You can use the repository's [Discussions](https://github.com/vercel/satori/discussions) page to ask any questions, post feedback, or share your experience on how you use this library. 8 | 9 | ## Report a Bug 10 | 11 | Whenever you find something which is not working properly, please first search the repository's [Issues](https://github.com/vercel/satori/issues) page and make sure it's not reported by someone else already. 12 | 13 | If not, feel free to open an issue with a detailed description of the problem and the expected behavior. A bug reproduction using [Satori’s playground](https://og-playground.vercel.app) will be extremely helpful. 14 | 15 | ## Request for a New Feature 16 | 17 | For new features, it would be great to have some discussions from the community before starting working on it. You can either create an issue (if there isn't one) or post a thread on the [Discussions](https://github.com/vercel/satori/discussions) page to describe the feature that you want to have. 18 | 19 | If possible, you can add another additional context like how this feature can be implemented technically, what other alternative solutions we can have, etc. 20 | 21 | ## Local Development 22 | 23 | This project uses [pnpm](https://pnpm.io). To install dependencies, run: 24 | 25 | ```bash 26 | pnpm install 27 | ``` 28 | 29 | To start the playground together with Satori locally, run: 30 | 31 | ```bash 32 | pnpm dev:playground 33 | ``` 34 | 35 | And visit localhost:3000. 36 | 37 | To only start the development mode of Satori, run `pnpm dev` in the root directory (recommended to test together with the playground to see changes in live). 38 | 39 | ## Adding Tests 40 | 41 | Satori uses [Vitest](https://vitest.dev) to test and generate snapshots. To start and live-watch the tests, run: 42 | 43 | ```bash 44 | pnpm dev:test 45 | ``` 46 | 47 | It will update snapshot images as well. 48 | 49 | You can also use `pnpm test` to only run the test. 50 | -------------------------------------------------------------------------------- /playground/cards/preview-tabs.ts: -------------------------------------------------------------------------------- 1 | const previewTabs = [ 2 | 'SVG (Satori)', 3 | 'PNG (Satori + resvg-js)', // https://github.com/yisibl/resvg-js 4 | 'PDF (Satori + PDFKit)', 5 | 'HTML (Native)', 6 | ] 7 | 8 | export default previewTabs 9 | -------------------------------------------------------------------------------- /playground/components/introduction.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | position: fixed; 3 | left: 20px; 4 | bottom: 20px; 5 | width: 700px; 6 | min-height: 200px; 7 | max-width: calc(100vw - 40px); 8 | max-height: calc(100vh - 40px); 9 | margin: auto; 10 | border-radius: 8px; 11 | display: flex; 12 | flex-direction: column; 13 | justify-content: flex-start; 14 | padding: 20px; 15 | color: #ddd; 16 | font-size: 14px; 17 | backdrop-filter: blur(24px); 18 | background-color: rgb(0 0 0 / 88%); 19 | box-shadow: 0 20px 40px #0000005c, 0 0 0 1px #868686; 20 | z-index: 4; 21 | opacity: 0; 22 | line-height: 1.6; 23 | letter-spacing: -0.01rem; 24 | word-spacing: -0.12rem; 25 | animation: fadein 0.4s ease 0.4s forwards; 26 | } 27 | 28 | .container p { 29 | margin: 0; 30 | margin-bottom: 1em; 31 | } 32 | 33 | .container code { 34 | background-color: #4a4a4a; 35 | padding: 0 4px; 36 | border-radius: 4px; 37 | } 38 | 39 | .container button { 40 | appearance: none; 41 | border: none; 42 | background: white; 43 | border-radius: 5px; 44 | padding: 8px 12px; 45 | width: 120px; 46 | align-self: flex-start; 47 | font-family: inherit; 48 | font-weight: 600; 49 | cursor: pointer; 50 | transition: all 0.2s ease; 51 | } 52 | 53 | .container button:hover { 54 | background: #ccc; 55 | } 56 | 57 | @keyframes fadein { 58 | from { 59 | opacity: 0; 60 | } 61 | to { 62 | opacity: 1; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /playground/components/introduction.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styles from './introduction.module.css' 3 | 4 | interface IProps { 5 | onClose: React.MouseEventHandler 6 | } 7 | 8 | export default function Introduction({ onClose }: IProps) { 9 | return ( 10 |
11 |

👋 Welcome to the Vercel OG Image playground!

12 |

13 | You can use this tool to test and preview OG image cards generated with{' '} 14 | @vercel/og. To learn more about how to add it to your 15 | project, please read{' '} 16 | 21 | our documentation 22 | {' '} 23 | or the{' '} 24 | 29 | announcement post 30 | 31 | . 32 |

33 | 34 |
35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /playground/components/panel-resize-handle.module.css: -------------------------------------------------------------------------------- 1 | .handle { 2 | flex: 0 0 1.5em; 3 | position: relative; 4 | outline: none; 5 | --background-color: #efefef; 6 | } 7 | .handle:hover { 8 | --background-color: #dbdbdb; 9 | } 10 | .handle[data-resize-handle-active] { 11 | --background-color: #f5f5f5; 12 | } 13 | 14 | .handle > div { 15 | position: absolute; 16 | top: 0.25em; 17 | bottom: 0.25em; 18 | left: 0.25em; 19 | right: 0.25em; 20 | background-color: var(--background-color); 21 | border: 1px solid #0000000f; 22 | border-radius: 4px; 23 | transition: background-color 0.2s linear; 24 | } 25 | 26 | .handle[data-panel-group-direction='horizontal'] > div { 27 | top: 0; 28 | bottom: 0; 29 | } 30 | 31 | .handle[data-panel-group-direction='vertical'] > div { 32 | left: 0; 33 | right: 0; 34 | } 35 | 36 | .handle svg { 37 | width: 1em; 38 | height: 1em; 39 | position: absolute; 40 | left: calc(50% - 0.5rem); 41 | top: calc(50% - 0.5rem); 42 | } 43 | 44 | .handle[data-panel-group-direction='horizontal'] svg { 45 | transform: rotate(90deg); 46 | } 47 | -------------------------------------------------------------------------------- /playground/components/panel-resize-handle.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { PanelResizeHandle as PanelResizeHandleImpl } from 'react-resizable-panels' 3 | 4 | import styles from './panel-resize-handle.module.css' 5 | 6 | export default function PanelResizeHandle() { 7 | return ( 8 | 9 |
10 | 17 | 22 | 23 |
24 |
25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /playground/components/resvg_worker.ts: -------------------------------------------------------------------------------- 1 | import * as resvg from '@resvg/resvg-wasm' 2 | 3 | const wasmPath = new URL('@resvg/resvg-wasm/index_bg.wasm', import.meta.url) 4 | fetch(wasmPath).then((res) => resvg.initWasm(res)) 5 | 6 | self.onmessage = (e) => { 7 | const { svg, width, _id } = e.data 8 | 9 | const renderer = new resvg.Resvg(svg, { 10 | fitTo: { 11 | mode: 'width', 12 | value: width, 13 | }, 14 | }) 15 | const image = renderer.render() 16 | const pngBuffer = image.asPng() 17 | const url = URL.createObjectURL(new Blob([pngBuffer], { type: 'image/png' })) 18 | self.postMessage({ _id, url }) 19 | } 20 | -------------------------------------------------------------------------------- /playground/decs.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'pdfkit/js/pdfkit.standalone' 2 | declare module 'satori' 3 | -------------------------------------------------------------------------------- /playground/index.d.ts: -------------------------------------------------------------------------------- 1 | export {} 2 | 3 | declare global { 4 | interface Window { 5 | __resource: any 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /playground/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /playground/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "satori-playground", 4 | "license": "MPL-2.0", 5 | "scripts": { 6 | "dev": "next", 7 | "debug": "node --inspect node_modules/next/dist/bin/next", 8 | "build": "next build", 9 | "start": "next start" 10 | }, 11 | "dependencies": { 12 | "@babel/runtime": "^7.19.0", 13 | "@monaco-editor/react": "^4.4.5", 14 | "@resvg/resvg-wasm": "^2.3.1", 15 | "blob-stream": "^0.1.3", 16 | "copy-to-clipboard": "^3.3.2", 17 | "fflate": "^0.7.3", 18 | "intl-segmenter-polyfill": "^0.4.4", 19 | "js-base64": "^3.7.2", 20 | "next": "^12.2.5", 21 | "pdfkit": "^0.13.0", 22 | "react": "^17.0.2", 23 | "react-dom": "^17.0.2", 24 | "react-hot-toast": "^2.3.0", 25 | "react-live": "^2.4.1", 26 | "react-resizable-panels": "^0.0.30", 27 | "satori": "workspace:*", 28 | "svg-to-pdfkit": "^0.1.8" 29 | }, 30 | "devDependencies": { 31 | "@types/blob-stream": "^0.1.30", 32 | "@types/pdfkit": "^0.12.7", 33 | "@types/react-dom": "^18.0.6", 34 | "@types/svg-to-pdfkit": "^0.1.0", 35 | "regenerator": "link:@babel/runtime/regenerator" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /playground/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Head from 'next/head' 3 | import { AppProps } from 'next/app' 4 | 5 | import '../styles.css' 6 | 7 | export default function App({ Component, pageProps }: AppProps) { 8 | return ( 9 | <> 10 | 11 | Vercel OG Image Playground 12 | 16 | 17 | 18 | 22 | 23 | 24 | 25 | 29 | 33 | 34 | 38 | 39 | 43 | 47 | 53 | 59 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | ) 77 | } 78 | -------------------------------------------------------------------------------- /playground/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Html, Head, Main, NextScript } from 'next/document' 3 | 4 | export default function Document() { 5 | return ( 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /playground/pages/api/font.ts: -------------------------------------------------------------------------------- 1 | import type { NextRequest } from 'next/server' 2 | import { FontDetector, languageFontMap } from '../../utils/font' 3 | 4 | export const config = { 5 | runtime: 'experimental-edge', 6 | } 7 | 8 | const detector = new FontDetector() 9 | 10 | // Our own encoding of multiple fonts and their code, so we can fetch them in one request. The structure is: 11 | // [1 byte = X, length of language code][X bytes of language code string][4 bytes = Y, length of font][Y bytes of font data] 12 | // Note that: 13 | // - The language code can't be longer than 255 characters. 14 | // - The language code can't contain non-ASCII characters. 15 | // - The font data can't be longer than 4GB. 16 | // When there are multiple fonts, they are concatenated together. 17 | function encodeFontInfoAsArrayBuffer(code: string, fontData: ArrayBuffer) { 18 | // 1 byte per char 19 | const buffer = new ArrayBuffer(1 + code.length + 4 + fontData.byteLength) 20 | const bufferView = new Uint8Array(buffer) 21 | // 1 byte for the length of the language code 22 | bufferView[0] = code.length 23 | // X bytes for the language code 24 | for (let i = 0; i < code.length; i++) { 25 | bufferView[i + 1] = code.charCodeAt(i) 26 | } 27 | 28 | // 4 bytes for the length of the font data 29 | new DataView(buffer).setUint32(1 + code.length, fontData.byteLength, false) 30 | 31 | // Y bytes for the font data 32 | bufferView.set(new Uint8Array(fontData), 1 + code.length + 4) 33 | 34 | return buffer 35 | } 36 | 37 | export default async function loadGoogleFont(req: NextRequest) { 38 | if (req.nextUrl.pathname !== '/api/font') return 39 | 40 | const { searchParams } = new URL(req.url) 41 | 42 | const fonts = searchParams.getAll('fonts') 43 | const text = searchParams.get('text') 44 | 45 | if (!fonts || fonts.length === 0 || !text) return 46 | 47 | const textByFont = await detector.detect(text, fonts) 48 | 49 | const _fonts = Object.keys(textByFont) 50 | 51 | const encodedFontBuffers: ArrayBuffer[] = [] 52 | let fontBufferByteLength = 0 53 | ;( 54 | await Promise.all(_fonts.map((font) => fetchFont(textByFont[font], font))) 55 | ).forEach((fontData, i) => { 56 | if (fontData) { 57 | // TODO: We should be able to directly get the language code here :) 58 | const langCode = Object.entries(languageFontMap).find( 59 | ([, v]) => v === _fonts[i] 60 | )?.[0] 61 | 62 | if (langCode) { 63 | const buffer = encodeFontInfoAsArrayBuffer(langCode, fontData) 64 | encodedFontBuffers.push(buffer) 65 | fontBufferByteLength += buffer.byteLength 66 | } 67 | } 68 | }) 69 | 70 | const responseBuffer = new ArrayBuffer(fontBufferByteLength) 71 | const responseBufferView = new Uint8Array(responseBuffer) 72 | let offset = 0 73 | encodedFontBuffers.forEach((buffer) => { 74 | responseBufferView.set(new Uint8Array(buffer), offset) 75 | offset += buffer.byteLength 76 | }) 77 | 78 | return new Response(responseBuffer, { 79 | headers: { 80 | 'Content-Type': 'font/woff', 81 | 'Cache-Control': 'public, max-age=31536000, immutable', 82 | }, 83 | }) 84 | } 85 | 86 | async function fetchFont( 87 | text: string, 88 | font: string 89 | ): Promise { 90 | const API = `https://fonts.googleapis.com/css2?family=${font}&text=${encodeURIComponent( 91 | text 92 | )}` 93 | 94 | const css = await ( 95 | await fetch(API, { 96 | headers: { 97 | // Make sure it returns TTF. 98 | 'User-Agent': 99 | 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; de-at) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1', 100 | }, 101 | }) 102 | ).text() 103 | 104 | const resource = css.match(/src: url\((.+)\) format\('(opentype|truetype)'\)/) 105 | 106 | if (!resource) return null 107 | 108 | const res = await fetch(resource[1]) 109 | 110 | return res.arrayBuffer() 111 | } 112 | -------------------------------------------------------------------------------- /playground/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/playground/public/favicon.ico -------------------------------------------------------------------------------- /playground/public/iaw-mono-var.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/playground/public/iaw-mono-var.woff2 -------------------------------------------------------------------------------- /playground/public/inter-latin-ext-400-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/playground/public/inter-latin-ext-400-normal.woff -------------------------------------------------------------------------------- /playground/public/inter-latin-ext-700-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/playground/public/inter-latin-ext-700-normal.woff -------------------------------------------------------------------------------- /playground/public/material-icons-base-400-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/playground/public/material-icons-base-400-normal.woff -------------------------------------------------------------------------------- /playground/public/og.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/playground/public/og.png -------------------------------------------------------------------------------- /playground/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2015", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "incremental": true, 11 | "esModuleInterop": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "jsx": "preserve" 17 | }, 18 | "include": ["decs.d.ts", "**/*.ts", "**/*.tsx"], 19 | "exclude": ["node_modules"] 20 | } 21 | -------------------------------------------------------------------------------- /playground/utils/twemoji.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Modified version of https://unpkg.com/twemoji@13.1.0/dist/twemoji.esm.js. 3 | */ 4 | 5 | /*! Copyright Twitter Inc. and other contributors. Licensed under MIT */ 6 | 7 | const U200D = String.fromCharCode(8205) 8 | const UFE0Fg = /\uFE0F/g 9 | 10 | export function getIconCode(char: string) { 11 | return toCodePoint(char.indexOf(U200D) < 0 ? char.replace(UFE0Fg, '') : char) 12 | } 13 | 14 | function toCodePoint(unicodeSurrogates: string) { 15 | const r = [] 16 | let c = 0, 17 | p = 0, 18 | i = 0 19 | 20 | while (i < unicodeSurrogates.length) { 21 | c = unicodeSurrogates.charCodeAt(i++) 22 | if (p) { 23 | r.push((65536 + ((p - 55296) << 10) + (c - 56320)).toString(16)) 24 | p = 0 25 | } else if (55296 <= c && c <= 56319) { 26 | p = c 27 | } else { 28 | r.push(c.toString(16)) 29 | } 30 | } 31 | return r.join('-') 32 | } 33 | 34 | export const apis = { 35 | twemoji: (code: string) => 36 | 'https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/svg/' + 37 | code.toLowerCase() + 38 | '.svg', 39 | openmoji: 'https://cdn.jsdelivr.net/npm/@svgmoji/openmoji@2.0.0/svg/', 40 | blobmoji: 'https://cdn.jsdelivr.net/npm/@svgmoji/blob@2.0.0/svg/', 41 | noto: 'https://cdn.jsdelivr.net/gh/svgmoji/svgmoji/packages/svgmoji__noto/svg/', 42 | fluent: (code: string) => 43 | 'https://cdn.jsdelivr.net/gh/shuding/fluentui-emoji-unicode/assets/' + 44 | code.toLowerCase() + 45 | '_color.svg', 46 | fluentFlat: (code: string) => 47 | 'https://cdn.jsdelivr.net/gh/shuding/fluentui-emoji-unicode/assets/' + 48 | code.toLowerCase() + 49 | '_flat.svg', 50 | } 51 | 52 | const emojiCache: Record> = {} 53 | 54 | export function loadEmoji(type: keyof typeof apis, code: string) { 55 | const key = type + ':' + code 56 | if (key in emojiCache) return emojiCache[key] 57 | 58 | if (!type || !apis[type]) { 59 | type = 'twemoji' 60 | } 61 | 62 | const api = apis[type] 63 | if (typeof api === 'function') { 64 | return (emojiCache[key] = fetch(api(code)).then((r) => r.text())) 65 | } 66 | return (emojiCache[key] = fetch(`${api}${code.toUpperCase()}.svg`).then((r) => 67 | r.text() 68 | )) 69 | } 70 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'playground' 3 | - '.' 4 | -------------------------------------------------------------------------------- /release.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | branches: ['main'], 3 | tagFormat: '${version}', 4 | } 5 | -------------------------------------------------------------------------------- /src/builder/clip-path.ts: -------------------------------------------------------------------------------- 1 | import { buildXMLString } from '../utils.js' 2 | import { createShapeParser } from '../parser/shape.js' 3 | 4 | export function genClipPathId(id: string) { 5 | return `satori_cp-${id}` 6 | } 7 | export function genClipPath(id: string) { 8 | return `url(#${genClipPathId(id)})` 9 | } 10 | 11 | export function buildClipPath( 12 | v: { 13 | left: number 14 | top: number 15 | width: number 16 | height: number 17 | path: string 18 | matrix: string | undefined 19 | id: string 20 | currentClipPath: string | string 21 | src?: string 22 | }, 23 | style: Record, 24 | inheritedStyle: Record 25 | ) { 26 | if (style.clipPath === 'none') return '' 27 | 28 | const parser = createShapeParser(v, style, inheritedStyle) 29 | const clipPath = style.clipPath as string 30 | 31 | let tmp: { type: string; [p: string]: string | number } = { type: '' } 32 | 33 | for (const k of Object.keys(parser)) { 34 | tmp = parser[k](clipPath) 35 | if (tmp) break 36 | } 37 | 38 | if (tmp) { 39 | const { type, ...rest } = tmp 40 | return buildXMLString( 41 | 'clipPath', 42 | { 43 | id: genClipPathId(v.id), 44 | 'clip-path': v.currentClipPath, 45 | transform: `translate(${v.left}, ${v.top})`, 46 | }, 47 | buildXMLString(type, rest) 48 | ) 49 | } 50 | return '' 51 | } 52 | -------------------------------------------------------------------------------- /src/builder/content-mask.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * When there is border radius, the content area should be clipped by the 3 | * inner path of border + padding. This applies to element as well as any 4 | * child element inside a `overflow: hidden` container. 5 | */ 6 | 7 | import { buildXMLString } from '../utils.js' 8 | import border from './border.js' 9 | 10 | export default function contentMask( 11 | { 12 | id, 13 | left, 14 | top, 15 | width, 16 | height, 17 | matrix, 18 | borderOnly, 19 | }: { 20 | id: string 21 | left: number 22 | top: number 23 | width: number 24 | height: number 25 | matrix: string | undefined 26 | borderOnly?: boolean 27 | }, 28 | style: Record 29 | ) { 30 | const offsetLeft = 31 | ((style.borderLeftWidth as number) || 0) + 32 | (borderOnly ? 0 : (style.paddingLeft as number) || 0) 33 | const offsetTop = 34 | ((style.borderTopWidth as number) || 0) + 35 | (borderOnly ? 0 : (style.paddingTop as number) || 0) 36 | const offsetRight = 37 | ((style.borderRightWidth as number) || 0) + 38 | (borderOnly ? 0 : (style.paddingRight as number) || 0) 39 | const offsetBottom = 40 | ((style.borderBottomWidth as number) || 0) + 41 | (borderOnly ? 0 : (style.paddingBottom as number) || 0) 42 | 43 | const contentArea = { 44 | x: left + offsetLeft, 45 | y: top + offsetTop, 46 | width: width - offsetLeft - offsetRight, 47 | height: height - offsetTop - offsetBottom, 48 | } 49 | 50 | const _contentMask = buildXMLString( 51 | 'mask', 52 | { id }, 53 | buildXMLString('rect', { 54 | ...contentArea, 55 | fill: '#fff', 56 | // add transformation matrix to mask if overflow is hidden AND a 57 | // transformation style is defined, otherwise children will be clipped 58 | // incorrectly 59 | transform: 60 | style.overflow === 'hidden' && style.transform && matrix 61 | ? matrix 62 | : undefined, 63 | mask: style._inheritedMaskId 64 | ? `url(#${style._inheritedMaskId})` 65 | : undefined, 66 | }) + 67 | border( 68 | { 69 | left, 70 | top, 71 | width, 72 | height, 73 | props: { 74 | transform: matrix ? matrix : undefined, 75 | }, 76 | asContentMask: true, 77 | maskBorderOnly: borderOnly, 78 | }, 79 | style 80 | ) 81 | ) 82 | 83 | return _contentMask 84 | } 85 | -------------------------------------------------------------------------------- /src/builder/gradient/utils.ts: -------------------------------------------------------------------------------- 1 | import { lengthToNumber } from '../../utils.js' 2 | import cssColorParse from 'parse-css-color' 3 | import type { ColorStop } from 'css-gradient-parser' 4 | 5 | interface Stop { 6 | color: string 7 | offset?: number 8 | } 9 | 10 | export function normalizeStops( 11 | totalLength: number, 12 | colorStops: ColorStop[], 13 | inheritedStyle: Record, 14 | repeating: boolean, 15 | from?: 'background' | 'mask' 16 | ) { 17 | // Resolve the color stops based on the spec: 18 | // https://drafts.csswg.org/css-images/#color-stop-syntax 19 | const stops: Stop[] = [] 20 | for (const stop of colorStops) { 21 | const { color } = stop 22 | if (!stops.length) { 23 | // First stop, ensure it's at the start. 24 | stops.push({ 25 | offset: 0, 26 | color, 27 | }) 28 | 29 | if (!stop.offset) continue 30 | if (stop.offset.value === '0') continue 31 | } 32 | 33 | // All offsets are relative values (0-1) in SVG. 34 | const offset = 35 | typeof stop.offset === 'undefined' 36 | ? undefined 37 | : stop.offset.unit === '%' 38 | ? +stop.offset.value / 100 39 | : Number( 40 | lengthToNumber( 41 | `${stop.offset.value}${stop.offset.unit}`, 42 | inheritedStyle.fontSize as number, 43 | totalLength, 44 | inheritedStyle, 45 | true 46 | ) 47 | ) / totalLength 48 | 49 | stops.push({ 50 | offset, 51 | color, 52 | }) 53 | } 54 | if (!stops.length) { 55 | stops.push({ 56 | offset: 0, 57 | color: 'transparent', 58 | }) 59 | } 60 | // Last stop, ensure it's at the end. 61 | const lastStop = stops[stops.length - 1] 62 | if (lastStop.offset !== 1) { 63 | if (typeof lastStop.offset === 'undefined') { 64 | lastStop.offset = 1 65 | } else if (repeating) { 66 | stops[stops.length - 1] = { 67 | offset: 1, 68 | color: lastStop.color, 69 | } 70 | } else { 71 | stops.push({ 72 | offset: 1, 73 | color: lastStop.color, 74 | }) 75 | } 76 | } 77 | 78 | let previousStop = 0 79 | let nextStop = 1 80 | // Evenly distribute the missing stop offsets. 81 | for (let i = 0; i < stops.length; i++) { 82 | if (typeof stops[i].offset === 'undefined') { 83 | // Find the next stop that has an offset. 84 | if (nextStop < i) nextStop = i 85 | while (typeof stops[nextStop].offset === 'undefined') nextStop++ 86 | 87 | stops[i].offset = 88 | ((stops[nextStop].offset - stops[previousStop].offset) / 89 | (nextStop - previousStop)) * 90 | (i - previousStop) + 91 | stops[previousStop].offset 92 | } else { 93 | previousStop = i 94 | } 95 | } 96 | 97 | if (from === 'mask') { 98 | return stops.map((stop) => { 99 | const color = cssColorParse(stop.color) 100 | if (!color) return stop 101 | if (color.alpha === 0) { 102 | return { ...stop, color: `rgba(0, 0, 0, 1)` } 103 | } else { 104 | return { ...stop, color: `rgba(255, 255, 255, ${color.alpha})` } 105 | } 106 | }) 107 | } 108 | 109 | return stops 110 | } 111 | -------------------------------------------------------------------------------- /src/builder/mask-image.ts: -------------------------------------------------------------------------------- 1 | import { buildXMLString } from '../utils.js' 2 | import buildBackgroundImage from './background-image.js' 3 | import type { MaskProperty } from '../parser/mask.js' 4 | 5 | const genMaskImageId = (id: string) => `satori_mi-${id}` 6 | 7 | export default async function buildMaskImage( 8 | v: { 9 | id: string 10 | left: number 11 | top: number 12 | width: number 13 | height: number 14 | }, 15 | style: Record, 16 | inheritedStyle: Record 17 | ): Promise<[string, string]> { 18 | if (!style.maskImage) return ['', ''] 19 | const { left, top, width, height, id } = v 20 | const maskImage = style.maskImage as unknown as MaskProperty[] 21 | const length = maskImage.length 22 | if (!length) return ['', ''] 23 | const miId = genMaskImageId(id) 24 | 25 | let mask = '' 26 | 27 | for (let i = 0; i < length; i++) { 28 | const m = maskImage[i] 29 | 30 | const [_id, def] = await buildBackgroundImage( 31 | { id: `${miId}-${i}`, left, top, width, height }, 32 | m, 33 | inheritedStyle, 34 | 'mask' 35 | ) 36 | 37 | mask += 38 | def + 39 | buildXMLString('rect', { 40 | x: left, 41 | y: top, 42 | width, 43 | height, 44 | fill: `url(#${_id})`, 45 | }) 46 | } 47 | 48 | mask = buildXMLString('mask', { id: miId }, mask) 49 | 50 | return [miId, mask] 51 | } 52 | -------------------------------------------------------------------------------- /src/builder/overflow.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Generate clip path for the given element. 3 | */ 4 | 5 | import { buildXMLString } from '../utils.js' 6 | import mask from './content-mask.js' 7 | import { buildClipPath, genClipPathId } from './clip-path.js' 8 | 9 | export default function overflow( 10 | { 11 | left, 12 | top, 13 | width, 14 | height, 15 | path, 16 | matrix, 17 | id, 18 | currentClipPath, 19 | src, 20 | }: { 21 | left: number 22 | top: number 23 | width: number 24 | height: number 25 | path: string 26 | matrix: string | undefined 27 | id: string 28 | currentClipPath: string | string 29 | src?: string 30 | }, 31 | style: Record, 32 | inheritableStyle: Record 33 | ) { 34 | let overflowClipPath = '' 35 | const clipPath = 36 | style.clipPath && style.clipPath !== 'none' 37 | ? buildClipPath( 38 | { left, top, width, height, path, id, matrix, currentClipPath, src }, 39 | style as Record, 40 | inheritableStyle 41 | ) 42 | : '' 43 | 44 | if (style.overflow !== 'hidden' && !src) { 45 | overflowClipPath = '' 46 | } else { 47 | const _id = clipPath ? `satori_ocp-${id}` : genClipPathId(id) 48 | 49 | overflowClipPath = buildXMLString( 50 | 'clipPath', 51 | { 52 | id: _id, 53 | 'clip-path': currentClipPath, 54 | }, 55 | buildXMLString(path ? 'path' : 'rect', { 56 | x: left, 57 | y: top, 58 | width, 59 | height, 60 | d: path ? path : undefined, 61 | // add transformation matrix to clip path if overflow is hidden AND a 62 | // transformation style is defined, otherwise children will be clipped 63 | // relative to the parent's original plane instead of the transformed 64 | // plane 65 | transform: 66 | style.overflow === 'hidden' && style.transform && matrix 67 | ? matrix 68 | : undefined, 69 | }) 70 | ) 71 | } 72 | 73 | const contentMask = mask( 74 | { 75 | id: `satori_om-${id}`, 76 | left, 77 | top, 78 | width, 79 | height, 80 | matrix, 81 | borderOnly: src ? false : true, 82 | }, 83 | style 84 | ) 85 | 86 | return clipPath + overflowClipPath + contentMask 87 | } 88 | -------------------------------------------------------------------------------- /src/builder/svg.ts: -------------------------------------------------------------------------------- 1 | import { buildXMLString } from '../utils.js' 2 | 3 | export default function svg({ 4 | width, 5 | height, 6 | content, 7 | }: { 8 | width: number 9 | height: number 10 | content: string 11 | }) { 12 | return buildXMLString( 13 | 'svg', 14 | { 15 | width, 16 | height, 17 | viewBox: `0 0 ${width} ${height}`, 18 | xmlns: 'http://www.w3.org/2000/svg', 19 | }, 20 | content 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /src/builder/text-decoration.ts: -------------------------------------------------------------------------------- 1 | import { buildXMLString } from '../utils.js' 2 | 3 | export default function buildDecoration( 4 | { 5 | width, 6 | left, 7 | top, 8 | ascender, 9 | clipPathId, 10 | matrix, 11 | }: { 12 | width: number 13 | left: number 14 | top: number 15 | ascender: number 16 | clipPathId?: string 17 | matrix?: string 18 | }, 19 | style: Record 20 | ) { 21 | const { 22 | textDecorationColor, 23 | textDecorationStyle, 24 | textDecorationLine, 25 | fontSize, 26 | color, 27 | } = style 28 | if (!textDecorationLine || textDecorationLine === 'none') return '' 29 | 30 | // The UA should use such font-based information when choosing auto line thicknesses wherever appropriate. 31 | // https://drafts.csswg.org/css-text-decor-4/#text-decoration-thickness 32 | const height = Math.max(1, fontSize * 0.1) 33 | 34 | const y = 35 | textDecorationLine === 'line-through' 36 | ? top + ascender * 0.7 37 | : textDecorationLine === 'underline' 38 | ? top + ascender * 1.1 39 | : top 40 | 41 | const dasharray = 42 | textDecorationStyle === 'dashed' 43 | ? `${height * 1.2} ${height * 2}` 44 | : textDecorationStyle === 'dotted' 45 | ? `0 ${height * 2}` 46 | : undefined 47 | 48 | return ( 49 | (clipPathId ? `` : '') + 50 | buildXMLString('line', { 51 | x1: left, 52 | y1: y, 53 | x2: left + width, 54 | y2: y, 55 | stroke: textDecorationColor || color, 56 | 'stroke-width': height, 57 | 'stroke-dasharray': dasharray, 58 | 'stroke-linecap': textDecorationStyle === 'dotted' ? 'round' : 'square', 59 | transform: matrix, 60 | }) + 61 | (clipPathId ? '' : '') 62 | ) 63 | } 64 | -------------------------------------------------------------------------------- /src/handler/inheritable.ts: -------------------------------------------------------------------------------- 1 | import { SerializedStyle } from './expand.js' 2 | 3 | const list = new Set([ 4 | 'color', 5 | 'font', 6 | 'fontFamily', 7 | 'fontSize', 8 | 'fontStyle', 9 | 'fontWeight', 10 | 'letterSpacing', 11 | 'lineHeight', 12 | 'textAlign', 13 | 'textTransform', 14 | 'textShadowOffset', 15 | 'textShadowColor', 16 | 'textShadowRadius', 17 | 'WebkitTextStrokeWidth', 18 | 'WebkitTextStrokeColor', 19 | 'textDecorationLine', 20 | 'textDecorationStyle', 21 | 'textDecorationColor', 22 | 'whiteSpace', 23 | 'transform', 24 | 'wordBreak', 25 | 'tabSize', 26 | 27 | // Special case: SVG doesn't apply these to children elements so we need to 28 | // make it inheritable here. 29 | 'opacity', 30 | 'filter', 31 | 32 | // Special properties of Satori: 33 | '_viewportWidth', 34 | '_viewportHeight', 35 | '_inheritedClipPathId', 36 | '_inheritedMaskId', 37 | '_inheritedBackgroundClipTextPath', 38 | ]) 39 | 40 | export default function inheritable(style: SerializedStyle): SerializedStyle { 41 | const inheritedStyle: SerializedStyle = {} 42 | for (const prop in style) { 43 | if (list.has(prop)) { 44 | inheritedStyle[prop] = style[prop] 45 | } 46 | } 47 | return inheritedStyle 48 | } 49 | -------------------------------------------------------------------------------- /src/handler/presets.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Pre-defined styles for elements. Here we hand pick some from Chromium's 3 | * default styles: 4 | * https://chromium.googlesource.com/chromium/blink/+/master/Source/core/css/html.css 5 | * 6 | * We try to only include commonly used, styling elements rather than semantic elements. 7 | */ 8 | 9 | const DEFAULT_DISPLAY = 'flex' 10 | 11 | export default { 12 | // Generic block-level elements 13 | p: { 14 | display: DEFAULT_DISPLAY, 15 | marginTop: '1em', 16 | marginBottom: '1em', 17 | }, 18 | div: { 19 | display: DEFAULT_DISPLAY, 20 | }, 21 | blockquote: { 22 | display: DEFAULT_DISPLAY, 23 | marginTop: '1em', 24 | marginBottom: '1em', 25 | marginLeft: 40, 26 | marginRight: 40, 27 | }, 28 | center: { 29 | display: DEFAULT_DISPLAY, 30 | textAlign: 'center', 31 | }, 32 | hr: { 33 | display: DEFAULT_DISPLAY, 34 | marginTop: '0.5em', 35 | marginBottom: '0.5em', 36 | marginLeft: 'auto', 37 | marginRight: 'auto', 38 | borderWidth: 1, 39 | // We don't have `inset` 40 | borderStyle: 'solid', 41 | }, 42 | // Heading elements 43 | h1: { 44 | display: DEFAULT_DISPLAY, 45 | fontSize: '2em', 46 | marginTop: '0.67em', 47 | marginBottom: '0.67em', 48 | marginLeft: 0, 49 | marginRight: 0, 50 | fontWeight: 'bold', 51 | }, 52 | h2: { 53 | display: DEFAULT_DISPLAY, 54 | fontSize: '1.5em', 55 | marginTop: '0.83em', 56 | marginBottom: '0.83em', 57 | marginLeft: 0, 58 | marginRight: 0, 59 | fontWeight: 'bold', 60 | }, 61 | h3: { 62 | display: DEFAULT_DISPLAY, 63 | fontSize: '1.17em', 64 | marginTop: '1em', 65 | marginBottom: '1em', 66 | marginLeft: 0, 67 | marginRight: 0, 68 | fontWeight: 'bold', 69 | }, 70 | h4: { 71 | display: DEFAULT_DISPLAY, 72 | marginTop: '1.33em', 73 | marginBottom: '1.33em', 74 | marginLeft: 0, 75 | marginRight: 0, 76 | fontWeight: 'bold', 77 | }, 78 | h5: { 79 | display: DEFAULT_DISPLAY, 80 | fontSize: '0.83em', 81 | marginTop: '1.67em', 82 | marginBottom: '1.67em', 83 | marginLeft: 0, 84 | marginRight: 0, 85 | fontWeight: 'bold', 86 | }, 87 | h6: { 88 | display: DEFAULT_DISPLAY, 89 | fontSize: '0.67em', 90 | marginTop: '2.33em', 91 | marginBottom: '2.33em', 92 | marginLeft: 0, 93 | marginRight: 0, 94 | fontWeight: 'bold', 95 | }, 96 | // Tables 97 | // Lists 98 | // Form elements 99 | // Inline elements 100 | u: { 101 | textDecoration: 'underline', 102 | }, 103 | strong: { 104 | fontWeight: 'bold', 105 | }, 106 | b: { 107 | fontWeight: 'bold', 108 | }, 109 | i: { 110 | fontStyle: 'italic', 111 | }, 112 | em: { 113 | fontStyle: 'italic', 114 | }, 115 | code: { 116 | fontFamily: 'monospace', 117 | }, 118 | kbd: { 119 | fontFamily: 'monospace', 120 | }, 121 | pre: { 122 | display: DEFAULT_DISPLAY, 123 | fontFamily: 'monospace', 124 | whiteSpace: 'pre', 125 | marginTop: '1em', 126 | marginBottom: '1em', 127 | }, 128 | mark: { 129 | backgroundColor: 'yellow', 130 | color: 'black', 131 | }, 132 | big: { 133 | fontSize: 'larger', 134 | }, 135 | small: { 136 | fontSize: 'smaller', 137 | }, 138 | s: { 139 | textDecoration: 'line-through', 140 | }, 141 | } 142 | -------------------------------------------------------------------------------- /src/handler/tailwind.ts: -------------------------------------------------------------------------------- 1 | import type { TwConfig } from 'twrnc' 2 | 3 | import * as twrnc from 'twrnc/create' 4 | 5 | type TwPlugin = TwConfig['plugins'][number] 6 | 7 | const defaultShadows: TwPlugin = { 8 | handler: ({ addUtilities }) => { 9 | const presets = { 10 | 'shadow-sm': { boxShadow: '0 1px 2px 0 rgb(0 0 0 / 0.05)' }, 11 | shadow: { 12 | boxShadow: 13 | '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)', 14 | }, 15 | 'shadow-md': { 16 | boxShadow: 17 | '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', 18 | }, 19 | 'shadow-lg': { 20 | boxShadow: 21 | '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)', 22 | }, 23 | 'shadow-xl': { 24 | boxShadow: 25 | '0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)', 26 | }, 27 | 'shadow-2xl': { 28 | boxShadow: '0 25px 50px -12px rgb(0 0 0 / 0.25)', 29 | }, 30 | 'shadow-inner': { 31 | boxShadow: 'inset 0 2px 4px 0 rgb(0 0 0 / 0.05)', 32 | }, 33 | 'shadow-none': { boxShadow: '0 0 #0000' }, 34 | } 35 | 36 | addUtilities(presets) 37 | }, 38 | } 39 | 40 | function createTw(config?: TwConfig) { 41 | return twrnc.create( 42 | { 43 | ...config, 44 | plugins: [...(config?.plugins ?? []), defaultShadows], 45 | }, 46 | 'web' 47 | ) 48 | } 49 | 50 | let tw 51 | export default function getTw({ 52 | width, 53 | height, 54 | config, 55 | }: { 56 | width: number 57 | height: number 58 | config?: TwConfig 59 | }) { 60 | if (!tw) { 61 | tw = createTw(config) 62 | } 63 | tw.setWindowDimensions({ width: +width, height: +height }) 64 | return tw 65 | } 66 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export type { 2 | FontOptions as Font, 3 | Weight as FontWeight, 4 | FontStyle, 5 | } from './font.js' 6 | export type { Locale } from './language.js' 7 | 8 | export * from './satori.js' 9 | export { default } from './satori.js' 10 | -------------------------------------------------------------------------------- /src/language.ts: -------------------------------------------------------------------------------- 1 | // This function guesses the human language (writing system) of the given 2 | // JavaScript string, using the Unicode Alias in extended RegExp. 3 | // 4 | // You can learn more about this in: 5 | // - https://en.wikipedia.org/wiki/Script_(Unicode) 6 | // - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Unicode_Property_Escapes 7 | // - https://unicode.org/reports/tr18/#General_Category_Property 8 | // - https://tc39.es/ecma262/multipage/text-processing.html#table-unicode-script-values 9 | 10 | import createEmojiRegex from 'emoji-regex-xs' 11 | 12 | const emojiRegex = new RegExp(createEmojiRegex(), 'u') 13 | 14 | // Supported languages. The order matters. 15 | // Usually, this is only for "special cases" like CJKV languages as latin 16 | // characters are usually included in the base font, and can be safely fallback 17 | // to the Noto Sans font. A list of special cases we want to support can be 18 | // found here (sort by popularity): 19 | // - https://fonts.google.com/noto/fonts?sort=popularity¬o.query=sans 20 | // 21 | // We can't tell if a hanzi(kanji) is Chinese or Japanese by regular expressions. 22 | // - https://unicode.org/faq/han_cjk.html 23 | 24 | const specialCode = { 25 | emoji: emojiRegex, 26 | symbol: /\p{Symbol}/u, 27 | math: /\p{Math}/u, 28 | } as const 29 | 30 | const code = { 31 | 'ja-JP': /\p{scx=Hira}|\p{scx=Kana}|\p{scx=Han}|[\u3000]|[\uFF00-\uFFEF]/u, 32 | 'ko-KR': /\p{scx=Hangul}/u, 33 | 'zh-CN': /\p{scx=Han}/u, 34 | 'zh-TW': /\p{scx=Han}/u, 35 | 'zh-HK': /\p{scx=Han}/u, 36 | 'th-TH': /\p{scx=Thai}/u, 37 | 'bn-IN': /\p{scx=Bengali}/u, 38 | 'ar-AR': /\p{scx=Arabic}/u, 39 | 'ta-IN': /\p{scx=Tamil}/u, 40 | 'ml-IN': /\p{scx=Malayalam}/u, 41 | 'he-IL': /\p{scx=Hebrew}/u, 42 | 'te-IN': /\p{scx=Telugu}/u, 43 | devanagari: /\p{scx=Devanagari}/u, 44 | kannada: /\p{scx=Kannada}/u, 45 | } as const 46 | 47 | type SpecialCodeKey = keyof typeof specialCode 48 | type CodeKey = keyof typeof specialCode | keyof typeof code 49 | export type Locale = keyof typeof code 50 | export type LangCode = CodeKey | 'unknown' 51 | 52 | export const locales = Object.keys({ ...code, ...specialCode }) as Locale[] 53 | export function isValidLocale(x: any): x is Locale { 54 | return locales.includes(x) 55 | } 56 | 57 | export function detectLanguageCode( 58 | segment: string, 59 | locale?: Locale 60 | ): Array | ['unknown'] | [SpecialCodeKey] { 61 | for (const c of Object.keys(specialCode) as SpecialCodeKey[]) { 62 | if (specialCode[c].test(segment)) { 63 | return [c] 64 | } 65 | } 66 | 67 | const languages = Object.keys(code).filter((lang) => 68 | code[lang].test(segment) 69 | ) as Locale[] 70 | 71 | if (languages.length === 0) { 72 | return ['unknown'] 73 | } 74 | 75 | if (locale) { 76 | const index = languages.findIndex((lang) => lang === locale) 77 | if (index !== -1) { 78 | languages.splice(index, 1) 79 | languages.unshift(locale) 80 | } 81 | } 82 | 83 | return languages 84 | } 85 | 86 | export function normalizeLocale(locale?: string): Locale | undefined { 87 | if (locale) { 88 | return locales.find((l) => l.toLowerCase().startsWith(locale.toLowerCase())) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/parser/mask.ts: -------------------------------------------------------------------------------- 1 | import { getPropertyName } from 'css-to-react-native' 2 | import { splitEffects } from '../utils.js' 3 | 4 | function getMaskProperty(style: Record, name: string) { 5 | const key = getPropertyName(`mask-${name}`) 6 | return (style[key] || style[`WebkitM${key.substring(1)}`]) as string 7 | } 8 | 9 | export interface MaskProperty { 10 | image: string 11 | position: string 12 | size: string 13 | repeat: string 14 | origin: string 15 | clip: string 16 | } 17 | 18 | export function parseMask( 19 | style: Record 20 | ): MaskProperty[] { 21 | const maskImage = (style.maskImage || style.WebkitMaskImage) as string 22 | 23 | const common = { 24 | position: getMaskProperty(style, 'position') || '0% 0%', 25 | size: getMaskProperty(style, 'size') || '100% 100%', 26 | repeat: getMaskProperty(style, 'repeat') || 'repeat', 27 | origin: getMaskProperty(style, 'origin') || 'border-box', 28 | clip: getMaskProperty(style, 'origin') || 'border-box', 29 | } 30 | 31 | let maskImages = splitEffects(maskImage).filter((v) => v && v !== 'none') 32 | 33 | return maskImages.reverse().map((m) => ({ 34 | image: m, 35 | ...common, 36 | })) 37 | } 38 | -------------------------------------------------------------------------------- /src/text/characters.ts: -------------------------------------------------------------------------------- 1 | export function stringFromCode(code: string): string { 2 | code = code.replace('U+', '0x') 3 | 4 | return String.fromCodePoint(Number(code)) 5 | } 6 | 7 | export const Space = stringFromCode('U+0020') 8 | export const Tab = stringFromCode('U+0009') 9 | export const HorizontalEllipsis = stringFromCode('U+2026') 10 | -------------------------------------------------------------------------------- /src/text/measurer.ts: -------------------------------------------------------------------------------- 1 | import { FontEngine } from '../font.js' 2 | import { segment } from '../utils.js' 3 | 4 | export function genMeasurer( 5 | engine: FontEngine, 6 | isImage: (grapheme: string) => boolean, 7 | style: { 8 | fontSize: number 9 | letterSpacing: number 10 | } 11 | ): { 12 | measureGrapheme: (grapheme: string) => number 13 | measureGraphemeArray: (graphemes: string[]) => number 14 | measureText: (text: string) => number 15 | } { 16 | const { fontSize, letterSpacing } = style 17 | 18 | const cache = new Map() 19 | 20 | function measureGrapheme(grapheme: string): number { 21 | if (cache.has(grapheme)) { 22 | return cache.get(grapheme) 23 | } 24 | 25 | const width = engine.measure(grapheme, { fontSize, letterSpacing }) 26 | cache.set(grapheme, width) 27 | 28 | return width 29 | } 30 | 31 | function measureGraphemeArray(graphemes: string[]): number { 32 | let width = 0 33 | 34 | for (const grapheme of graphemes) { 35 | if (isImage(grapheme)) { 36 | width += fontSize 37 | } else { 38 | width += measureGrapheme(grapheme) 39 | } 40 | } 41 | 42 | return width 43 | } 44 | 45 | function measureText(text: string): number { 46 | return measureGraphemeArray(segment(text, 'grapheme')) 47 | } 48 | 49 | return { 50 | measureGrapheme, 51 | measureGraphemeArray, 52 | measureText, 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/transform-origin.ts: -------------------------------------------------------------------------------- 1 | import valueParser from 'postcss-value-parser' 2 | 3 | import CssDimension from './vendor/parse-css-dimension/index.js' 4 | 5 | /** 6 | * If key for each direction is missing, assume default (50%) 7 | */ 8 | export interface ParsedTransformOrigin { 9 | /** Relative horizontal transform origin in % */ 10 | xRelative?: number 11 | /** Relative vertical transform origin in % */ 12 | yRelative?: number 13 | /** Absolute horizontal transform origin in pixels */ 14 | xAbsolute?: number 15 | /** Absolute horizontal transform origin in pixels */ 16 | yAbsolute?: number 17 | } 18 | 19 | interface ParsedUnit { 20 | /** Relative unit in % */ 21 | relative?: number 22 | /** Absolute unit in pixels */ 23 | absolute?: number 24 | } 25 | 26 | function parseUnit(word: string, baseFontSize: number): ParsedUnit { 27 | try { 28 | const parsed = new CssDimension(word) 29 | switch (parsed.unit) { 30 | case 'px': 31 | return { absolute: parsed.value } 32 | case 'em': 33 | return { absolute: parsed.value * baseFontSize } 34 | case 'rem': 35 | return { absolute: parsed.value * 16 } 36 | case '%': 37 | return { relative: parsed.value } 38 | default: 39 | return {} 40 | } 41 | } catch (e) { 42 | return {} 43 | } 44 | } 45 | 46 | function handleWord( 47 | word: string, 48 | baseFontSize: number, 49 | unitIsHorizontal: boolean 50 | ) { 51 | switch (word) { 52 | case 'top': 53 | return { yRelative: 0 } 54 | case 'left': 55 | return { xRelative: 0 } 56 | case 'right': 57 | return { xRelative: 100 } 58 | case 'bottom': 59 | return { yRelative: 100 } 60 | case 'center': 61 | return {} 62 | default: { 63 | const parsedUnit = parseUnit(word, baseFontSize) 64 | return parsedUnit.absolute 65 | ? { 66 | [unitIsHorizontal ? 'xAbsolute' : 'yAbsolute']: parsedUnit.absolute, 67 | } 68 | : parsedUnit.relative 69 | ? { 70 | [unitIsHorizontal ? 'xRelative' : 'yRelative']: parsedUnit.relative, 71 | } 72 | : {} 73 | } 74 | } 75 | } 76 | 77 | export default function parseTransformOrigin( 78 | value: string | number, 79 | baseFontSize: number 80 | ): ParsedTransformOrigin { 81 | // If it's a single value and a number, then it's horizontal 82 | if (typeof value === 'number') { 83 | return { xAbsolute: value } 84 | } 85 | let words: string[] 86 | try { 87 | words = valueParser(value) 88 | .nodes.filter((node) => node.type === 'word') 89 | .map((node) => node.value) 90 | } catch (e) { 91 | return {} 92 | } 93 | 94 | if (words.length === 1) { 95 | // If it's a single value and a number, then it's horizontal, so 96 | // pass `true` to `unitIsHorizontal` 97 | return handleWord(words[0], baseFontSize, true) 98 | } else if (words.length === 2) { 99 | // Make words to be [horizontal, vertical] 100 | if ( 101 | words[0] === 'top' || 102 | words[0] === 'bottom' || 103 | words[1] === 'left' || 104 | words[1] === 'right' 105 | ) { 106 | words.reverse() 107 | } 108 | 109 | return { 110 | ...handleWord(words[0], baseFontSize, true), 111 | ...handleWord(words[1], baseFontSize, false), 112 | } 113 | } else { 114 | return {} 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/types.d.ts: -------------------------------------------------------------------------------- 1 | declare module '@shuding/opentype.js' { 2 | export = opentype 3 | } 4 | -------------------------------------------------------------------------------- /src/vendor/parse-css-dimension/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jed Mao 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/vendor/parse-css-dimension/index.js: -------------------------------------------------------------------------------- 1 | var e=(t,r)=>()=>(r||t((r={exports:{}}).exports,r),r.exports);var u=e((k,g)=>{g.exports=["em","ex","ch","rem","vh","vw","vmin","vmax","px","mm","cm","in","pt","pc","mozmm"]});var a=e((z,v)=>{v.exports=["deg","grad","rad","turn"]});var c=e((L,w)=>{w.exports=["dpi","dpcm","dppx"]});var h=e(($,y)=>{y.exports=["Hz","kHz"]});var m=e((j,b)=>{b.exports=["s","ms"]});var q=u(),f=a(),p=c(),l=h(),d=m();function s(t){if(/\.\D?$/.test(t))throw new Error("The dot should be followed by a number");if(/^[+-]{2}/.test(t))throw new Error("Only one leading +/- is allowed");if(x(t)>1)throw new Error("Only one dot is allowed");if(/%$/.test(t)){this.type="percentage",this.value=o(t),this.unit="%";return}var r=O(t);if(!r){this.type="number",this.value=o(t);return}this.type=F(r),this.value=o(t.substr(0,t.length-r.length)),this.unit=r}s.prototype.valueOf=function(){return this.value};s.prototype.toString=function(){return this.value+(this.unit||"")};function U(t){return new s(t)}function x(t){var r=t.match(/\./g);return r?r.length:0}function o(t){var r=parseFloat(t);if(isNaN(r))throw new Error("Invalid number: "+t);return r}var E=[].concat(f,l,q,p,d);function O(t){var r=t.match(/\D+$/),n=r&&r[0];if(n&&E.indexOf(n)===-1)throw new Error("Invalid unit: "+n);return n}var D=Object.assign(i(f,"angle"),i(l,"frequency"),i(p,"resolution"),i(d,"time"));function i(t,r){return Object.fromEntries(t.map(n=>[n,r]))}function F(t){return D[t]||"length"}export{U as default}; 2 | -------------------------------------------------------------------------------- /src/vendor/parse-css-dimension/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "parse-css-dimension", 3 | "version": "0.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "esbuild src.js --bundle --outfile=index.js --format=esm --minify" 8 | }, 9 | "dependencies": { 10 | "css-angle-units": "^1.0.1", 11 | "css-frequency-units": "^1.0.1", 12 | "css-length-units": "^1.0.0", 13 | "css-resolution-units": "^1.0.1", 14 | "css-time-units": "^1.0.1" 15 | }, 16 | "devDependencies": { 17 | "esbuild": "^0.14.28" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/vendor/parse-css-dimension/src.js: -------------------------------------------------------------------------------- 1 | var cssLengthUnits = require('css-length-units') 2 | var cssAngleUnits = require('css-angle-units') 3 | var cssResolutionUnits = require('css-resolution-units') 4 | var cssFrequencyUnits = require('css-frequency-units') 5 | var cssTimeUnits = require('css-time-units') 6 | 7 | function CssDimension(value) { 8 | if (/\.\D?$/.test(value)) { 9 | throw new Error('The dot should be followed by a number') 10 | } 11 | 12 | if (/^[+-]{2}/.test(value)) { 13 | throw new Error('Only one leading +/- is allowed') 14 | } 15 | 16 | if (countDots(value) > 1) { 17 | throw new Error('Only one dot is allowed') 18 | } 19 | 20 | if (/%$/.test(value)) { 21 | this.type = 'percentage' 22 | this.value = tryParseFloat(value) 23 | this.unit = '%' 24 | return 25 | } 26 | 27 | var unit = parseUnit(value) 28 | if (!unit) { 29 | this.type = 'number' 30 | this.value = tryParseFloat(value) 31 | return 32 | } 33 | 34 | this.type = getTypeFromUnit(unit) 35 | this.value = tryParseFloat(value.substr(0, value.length - unit.length)) 36 | this.unit = unit 37 | } 38 | 39 | CssDimension.prototype.valueOf = function () { 40 | return this.value 41 | } 42 | 43 | CssDimension.prototype.toString = function () { 44 | return this.value + (this.unit || '') 45 | } 46 | 47 | export default function factory(value) { 48 | return new CssDimension(value) 49 | } 50 | 51 | function countDots(value) { 52 | var m = value.match(/\./g) 53 | return m ? m.length : 0 54 | } 55 | 56 | function tryParseFloat(value) { 57 | var result = parseFloat(value) 58 | if (isNaN(result)) { 59 | throw new Error('Invalid number: ' + value) 60 | } 61 | return result 62 | } 63 | 64 | var units = [].concat( 65 | cssAngleUnits, 66 | cssFrequencyUnits, 67 | cssLengthUnits, 68 | cssResolutionUnits, 69 | cssTimeUnits 70 | ) 71 | 72 | function parseUnit(value) { 73 | var m = value.match(/\D+$/) 74 | var unit = m && m[0] 75 | if (unit && units.indexOf(unit) === -1) { 76 | throw new Error('Invalid unit: ' + unit) 77 | } 78 | return unit 79 | } 80 | 81 | var unitTypeLookup = Object.assign( 82 | createLookups(cssAngleUnits, 'angle'), 83 | createLookups(cssFrequencyUnits, 'frequency'), 84 | createLookups(cssResolutionUnits, 'resolution'), 85 | createLookups(cssTimeUnits, 'time') 86 | ) 87 | 88 | function createLookups(list, value) { 89 | return Object.fromEntries(list.map((unit) => [unit, value])) 90 | } 91 | 92 | function getTypeFromUnit(unit) { 93 | return unitTypeLookup[unit] || 'length' 94 | } 95 | -------------------------------------------------------------------------------- /src/vendor/twrnc/deprecate.js: -------------------------------------------------------------------------------- 1 | module.exports = function deprecate(fn, message) { 2 | return function (...args) { 3 | console.warn(message) 4 | return fn(...args) 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/vendor/twrnc/log.js: -------------------------------------------------------------------------------- 1 | export default { 2 | info(key, messages) { 3 | console.info(...(Array.isArray(key) ? [key] : [messages, key])) 4 | }, 5 | warn(key, messages) { 6 | console.warn(...(Array.isArray(key) ? [key] : [messages, key])) 7 | }, 8 | risk(key, messages) { 9 | console.error(...(Array.isArray(key) ? [key] : [messages, key])) 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /src/vendor/twrnc/picocolors.js: -------------------------------------------------------------------------------- 1 | export default { 2 | yellow: (s) => s, 3 | } 4 | -------------------------------------------------------------------------------- /src/yoga/index.ts: -------------------------------------------------------------------------------- 1 | import type { Yoga } from 'yoga-wasm-web' 2 | 3 | let yogaInstance: Yoga 4 | 5 | export function init(yoga: Yoga) { 6 | yogaInstance = yoga 7 | } 8 | 9 | let initializationPromise = null 10 | 11 | export default async function getYoga(): Promise { 12 | if (yogaInstance) return yogaInstance 13 | 14 | if (initializationPromise) { 15 | await initializationPromise 16 | return yogaInstance 17 | } 18 | 19 | initializationPromise = import('@yoga') 20 | .then((mod) => mod.getYogaModule()) 21 | .then((yoga) => (yogaInstance = yoga)) 22 | 23 | await initializationPromise 24 | initializationPromise = null 25 | 26 | return yogaInstance 27 | } 28 | -------------------------------------------------------------------------------- /src/yoga/yoga-prebuilt.ts: -------------------------------------------------------------------------------- 1 | export async function getYogaModule() { 2 | const { default: initYoga } = await import('yoga-wasm-web/asm') 3 | return initYoga() 4 | } 5 | -------------------------------------------------------------------------------- /src/yoga/yoga-prebuilt.wasm.ts: -------------------------------------------------------------------------------- 1 | // For WASM build, we don't include the prebuilt version of Yoga but let the 2 | // user specify the module manually with e.g.: 3 | // https://github.com/shuding/yoga-wasm-web. 4 | export async function getYogaModule() { 5 | return {} 6 | } 7 | -------------------------------------------------------------------------------- /test/__image_snapshots__/background-clip-test-tsx-test-background-clip-test-tsx-background-clip-should-render-background-clip-text-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/background-clip-test-tsx-test-background-clip-test-tsx-background-clip-should-render-background-clip-text-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/background-clip-test-tsx-test-background-clip-test-tsx-background-clip-should-render-background-clip-text-compatible-with-mask-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/background-clip-test-tsx-test-background-clip-test-tsx-background-clip-should-render-background-clip-text-compatible-with-mask-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/background-clip-test-tsx-test-background-clip-test-tsx-background-clip-should-render-background-clip-text-compatible-with-transform-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/background-clip-test-tsx-test-background-clip-test-tsx-background-clip-should-render-background-clip-text-compatible-with-transform-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/basic-test-tsx-test-basic-test-tsx-basic-should-combine-text-nodes-correctly-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/basic-test-tsx-test-basic-test-tsx-basic-should-combine-text-nodes-correctly-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/basic-test-tsx-test-basic-test-tsx-basic-should-render-basic-div-with-background-color-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/basic-test-tsx-test-basic-test-tsx-basic-should-render-basic-div-with-background-color-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/basic-test-tsx-test-basic-test-tsx-basic-should-render-basic-div-with-text-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/basic-test-tsx-test-basic-test-tsx-basic-should-render-basic-div-with-text-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/basic-test-tsx-test-basic-test-tsx-basic-should-render-basic-div-with-text-and-background-color-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/basic-test-tsx-test-basic-test-tsx-basic-should-render-basic-div-with-text-and-background-color-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/basic-test-tsx-test-basic-test-tsx-basic-should-render-empty-div-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/basic-test-tsx-test-basic-test-tsx-basic-should-render-empty-div-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/basic-test-tsx-test-basic-test-tsx-basic-should-support-array-in-jsx-children-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/basic-test-tsx-test-basic-test-tsx-basic-should-support-array-in-jsx-children-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/basic-test-tsx-test-basic-test-tsx-basic-should-support-hex-colors-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/basic-test-tsx-test-basic-test-tsx-basic-should-support-hex-colors-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/basic-test-tsx-test-basic-test-tsx-basic-should-support-skipping-embedded-fonts-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/basic-test-tsx-test-basic-test-tsx-basic-should-support-skipping-embedded-fonts-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-border-color-should-fallback-border-color-to-the-current-color-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-border-color-should-fallback-border-color-to-the-current-color-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-border-color-should-render-black-border-by-default-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-border-color-should-render-black-border-by-default-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-border-color-should-support-overriding-border-color-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-border-color-should-support-overriding-border-color-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-border-color-should-support-specifying-border-color-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-border-color-should-support-specifying-border-color-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-border-radius-should-not-exceed-the-length-of-the-short-side-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-border-radius-should-not-exceed-the-length-of-the-short-side-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-border-radius-should-support-percentage-border-radius-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-border-radius-should-support-percentage-border-radius-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-border-radius-should-support-radius-for-a-certain-corner-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-border-radius-should-support-radius-for-a-certain-corner-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-border-radius-should-support-slash-and-2-value-corner-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-border-radius-should-support-slash-and-2-value-corner-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-border-radius-should-support-the-shorthand-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-border-radius-should-support-the-shorthand-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-border-radius-should-support-vw-vh-em-and-rem-units-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-border-radius-should-support-vw-vh-em-and-rem-units-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-border-should-support-the-shorthand-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-border-should-support-the-shorthand-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-border-style-should-support-dashed-border-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-border-style-should-support-dashed-border-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-border-width-should-render-border-inside-the-shape-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-border-width-should-render-border-inside-the-shape-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-directional-should-support-advanced-border-with-radius-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-directional-should-support-advanced-border-with-radius-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-directional-should-support-directional-border-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-directional-should-support-directional-border-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-directional-should-support-non-complete-border-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/border-test-tsx-test-border-test-tsx-border-directional-should-support-non-complete-border-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-border-should-make-clip-path-compatible-with-overflow-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-border-should-make-clip-path-compatible-with-overflow-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-border-should-render-clip-path-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-border-should-render-clip-path-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-border-should-render-clip-path-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-border-should-render-clip-path-2-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-border-should-render-clip-path-3-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-border-should-render-clip-path-3-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-border-should-render-clip-path-4-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-border-should-render-clip-path-4-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-border-should-render-clip-path-5-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-border-should-render-clip-path-5-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-border-should-render-clip-path-6-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-border-should-render-clip-path-6-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-border-should-render-clip-path-7-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-border-should-render-clip-path-7-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-make-clip-path-compatible-with-overflow-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-make-clip-path-compatible-with-overflow-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-render-clip-path-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-render-clip-path-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-render-clip-path-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-render-clip-path-2-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-render-clip-path-3-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-render-clip-path-3-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-render-clip-path-4-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-render-clip-path-4-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-render-clip-path-5-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-render-clip-path-5-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-render-clip-path-6-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-render-clip-path-6-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-render-clip-path-7-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-render-clip-path-7-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-repect-left-and-top-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-repect-left-and-top-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-respect-left-and-top-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-respect-left-and-top-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-respect-the-position-value-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/clip-path-test-tsx-test-clip-path-test-tsx-clip-path-should-respect-the-position-value-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-currentcolor-when-background-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-currentcolor-when-background-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-currentcolor-when-border-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-currentcolor-when-border-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-currentcolor-when-inherit-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-currentcolor-when-inherit-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-hexadecimal-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-hexadecimal-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-hexadecimal-with-transparency-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-hexadecimal-with-transparency-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-hsl-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-hsl-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-hsla-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-hsla-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-inherit-color-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-inherit-color-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-predefined-color-names-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-predefined-color-names-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-rgb-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-rgb-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-rgba-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/color-models-test-tsx-test-color-models-test-tsx-color-models-background-color-and-color-should-support-rgba-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/color-models-test-tsx-test-color-models-test-tsx-color-models-should-support-css-4-syntax-color-in-hsl-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/color-models-test-tsx-test-color-models-test-tsx-color-models-should-support-css-4-syntax-color-in-hsl-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/color-models-test-tsx-test-color-models-test-tsx-color-models-should-support-css-4-syntax-color-in-hsl-if-inherited-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/color-models-test-tsx-test-color-models-test-tsx-color-models-should-support-css-4-syntax-color-in-hsl-if-inherited-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/dynamic-size-test-tsx-test-dynamic-size-test-tsx-dynamic-size-should-render-image-with-dynamic-height-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/dynamic-size-test-tsx-test-dynamic-size-test-tsx-dynamic-size-should-render-image-with-dynamic-height-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/dynamic-size-test-tsx-test-dynamic-size-test-tsx-dynamic-size-should-render-image-with-dynamic-width-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/dynamic-size-test-tsx-test-dynamic-size-test-tsx-dynamic-size-should-render-image-with-dynamic-width-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/emoji-test-tsx-test-emoji-test-tsx-emojis-should-render-emojis-correctly-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/emoji-test-tsx-test-emoji-test-tsx-emojis-should-render-emojis-correctly-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/emoji-test-tsx-test-emoji-test-tsx-emojis-should-render-emojis-correctly-with-alphabetic-emoji-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/emoji-test-tsx-test-emoji-test-tsx-emojis-should-render-emojis-correctly-with-alphabetic-emoji-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/emoji-test-tsx-test-emoji-test-tsx-emojis-should-render-emojis-correctly-with-word-break-break-all-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/emoji-test-tsx-test-emoji-test-tsx-emojis-should-render-emojis-correctly-with-word-break-break-all-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/font-test-tsx-test-font-test-tsx-font-font-size-should-allow-font-size-to-be-0-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/font-test-tsx-test-font-test-tsx-font-font-size-should-allow-font-size-to-be-0-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/font-test-tsx-test-font-test-tsx-font-should-handle-escape-html-when-embed-font-is-false-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/font-test-tsx-test-font-test-tsx-font-should-handle-escape-html-when-embed-font-is-false-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/font-test-tsx-test-font-test-tsx-font-should-handle-font-family-fallback-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/font-test-tsx-test-font-test-tsx-font-should-handle-font-family-fallback-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/font-test-tsx-test-font-test-tsx-font-should-handle-font-size-correctly-for-element-like-heading-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/font-test-tsx-test-font-test-tsx-font-should-handle-font-size-correctly-for-element-like-heading-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/font-test-tsx-test-font-test-tsx-font-should-handle-font-size-correctly-for-element-like-heading-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/font-test-tsx-test-font-test-tsx-font-should-handle-font-size-correctly-for-element-like-heading-2-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/font-test-tsx-test-font-test-tsx-font-should-handle-font-size-correctly-for-element-like-heading-3-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/font-test-tsx-test-font-test-tsx-font-should-handle-font-size-correctly-for-element-like-heading-3-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/font-test-tsx-test-font-test-tsx-font-should-not-error-when-no-font-is-specified-and-no-text-rendered-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/font-test-tsx-test-font-test-tsx-font-should-not-error-when-no-font-is-specified-and-no-text-rendered-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/font-test-tsx-test-font-test-tsx-font-should-use-correct-fonts-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/font-test-tsx-test-font-test-tsx-font-should-use-correct-fonts-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gap-test-tsx-test-gap-test-tsx-flex-gap-should-support-gap-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gap-test-tsx-test-gap-test-tsx-flex-gap-should-support-gap-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gap-test-tsx-test-gap-test-tsx-flex-gap-should-support-row-gap-and-column-gap-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gap-test-tsx-test-gap-test-tsx-flex-gap-should-support-row-gap-and-column-gap-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-linear-gradient-should-support-linear-gradient-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-linear-gradient-should-support-linear-gradient-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-linear-gradient-should-support-linear-gradient-with-omitted-orientation-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-linear-gradient-should-support-linear-gradient-with-omitted-orientation-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-linear-gradient-should-support-linear-gradient-with-transparency-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-linear-gradient-should-support-linear-gradient-with-transparency-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-linear-gradient-should-support-multiple-direction-keywords-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-linear-gradient-should-support-multiple-direction-keywords-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-linear-gradient-should-support-other-degree-unit-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-linear-gradient-should-support-other-degree-unit-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-linear-gradient-should-support-other-degree-unit-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-linear-gradient-should-support-other-degree-unit-2-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-linear-gradient-should-support-other-degree-unit-3-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-linear-gradient-should-support-other-degree-unit-3-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-linear-gradient-should-support-repeating-linear-gradient-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-linear-gradient-should-support-repeating-linear-gradient-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-linear-gradient-should-support-using-background-instead-of-background-image-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-linear-gradient-should-support-using-background-instead-of-background-image-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-default-value-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-default-value-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-explicitly-setting-rg-size-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-explicitly-setting-rg-size-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-explicitly-setting-rg-size-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-explicitly-setting-rg-size-2-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-radial-gradient-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-radial-gradient-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-radial-gradient-with-unspecified-ending-shape-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-radial-gradient-with-unspecified-ending-shape-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-releative-unit-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-releative-unit-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-releative-unit-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-releative-unit-2-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-releative-unit-3-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-releative-unit-3-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-releative-unit-4-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-releative-unit-4-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-rg-size-with-rg-extent-keyword-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-rg-size-with-rg-extent-keyword-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-rg-size-with-rg-extent-keyword-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-rg-size-with-rg-extent-keyword-2-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-rg-size-with-rg-extent-keyword-3-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-radial-gradient-should-support-rg-size-with-rg-extent-keyword-3-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-compute-correct-cycle-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-compute-correct-cycle-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-compute-correct-cycle-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-compute-correct-cycle-2-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-support-background-size-and-background-repeat-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-support-background-size-and-background-repeat-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-support-degree-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-support-degree-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-support-degree-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-support-degree-2-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-support-degree-3-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-support-degree-3-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-support-degree-4-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-support-degree-4-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-support-multiple-repeating-linear-gradient-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-support-multiple-repeating-linear-gradient-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-support-repeating-linear-gradient-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-support-repeating-linear-gradient-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-support-repeating-linear-gradient-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-repeating-linear-gradient-should-support-repeating-linear-gradient-2-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-should-be-able-to-render-grid-backgrounds-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-should-be-able-to-render-grid-backgrounds-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-should-calculate-the-gradient-angle-and-length-correctly-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-should-calculate-the-gradient-angle-and-length-correctly-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-should-calculate-the-gradient-angle-and-length-correctly-with-offset-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-should-calculate-the-gradient-angle-and-length-correctly-with-offset-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-should-render-gradient-patterns-in-the-correct-object-space-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-should-render-gradient-patterns-in-the-correct-object-space-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-should-resolve-gradient-layers-in-the-correct-order-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-should-resolve-gradient-layers-in-the-correct-order-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-should-support-advanced-usage-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/gradient-test-tsx-test-gradient-test-tsx-gradient-should-support-advanced-usage-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-background-image-url-should-correctly-position-the-background-pattern-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-background-image-url-should-correctly-position-the-background-pattern-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-background-image-url-should-handle-charset-utf-8-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-background-image-url-should-handle-charset-utf-8-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-background-image-url-should-handle-charset-utf-8-with-comma-in-data-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-background-image-url-should-handle-charset-utf-8-with-comma-in-data-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-background-image-url-should-handle-charset-utf-8-with-in-base-64-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-background-image-url-should-handle-charset-utf-8-with-in-base-64-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-background-image-url-should-resolve-data-uris-with-size-for-supported-image-formats-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-background-image-url-should-resolve-data-uris-with-size-for-supported-image-formats-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-background-image-url-should-resolve-data-uris-with-size-for-supported-image-formats-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-background-image-url-should-resolve-data-uris-with-size-for-supported-image-formats-2-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-background-image-url-should-resolve-data-uris-with-size-for-supported-image-formats-3-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-background-image-url-should-resolve-data-uris-with-size-for-supported-image-formats-3-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-background-image-url-should-resolve-data-uris-with-size-for-supported-image-formats-4-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-background-image-url-should-resolve-data-uris-with-size-for-supported-image-formats-4-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-background-image-url-should-resolve-data-uris-with-size-for-supported-image-formats-5-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-background-image-url-should-resolve-data-uris-with-size-for-supported-image-formats-5-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-background-image-url-should-resolve-image-data-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-background-image-url-should-resolve-image-data-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-background-image-url-should-support-double-quotes-inside-url-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-background-image-url-should-support-double-quotes-inside-url-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-background-image-url-should-support-single-quotes-inside-url-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-background-image-url-should-support-single-quotes-inside-url-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-background-image-url-should-support-stretched-background-size-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-background-image-url-should-support-stretched-background-size-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-clip-content-in-the-border-and-padding-areas-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-clip-content-in-the-border-and-padding-areas-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-clip-content-in-the-border-area-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-clip-content-in-the-border-area-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-deduplicate-image-data-requests-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-deduplicate-image-data-requests-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-have-a-separate-border-radius-clip-path-when-transform-is-used-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-have-a-separate-border-radius-clip-path-when-transform-is-used-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-not-throw-when-image-is-not-valid-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-not-throw-when-image-is-not-valid-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-render-svg-with-image-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-render-svg-with-image-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-resolve-image-data-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-resolve-image-data-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-resolve-non-square-image-size-correctly-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-resolve-non-square-image-size-correctly-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-resolve-the-image-size-and-scale-automatically-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-resolve-the-image-size-and-scale-automatically-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-scale-image-to-fit-max-width-and-max-height-but-maintain-the-aspect-ratio-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-scale-image-to-fit-max-width-and-max-height-but-maintain-the-aspect-ratio-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-scale-image-to-fit-max-width-and-max-height-but-maintain-the-aspect-ratio-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-scale-image-to-fit-max-width-and-max-height-but-maintain-the-aspect-ratio-2-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-support-array-buffer-as-src-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-support-array-buffer-as-src-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-support-opacity-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-support-opacity-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-support-styles-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-support-styles-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-support-svg-images-and-percentage-with-correct-aspect-ratio-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-support-svg-images-and-percentage-with-correct-aspect-ratio-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-support-transparent-image-with-background-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/image-test-tsx-test-image-test-tsx-image-should-support-transparent-image-with-background-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/language-test-tsx-test-language-test-tsx-detect-language-code-should-not-crash-when-rendering-arabic-letters-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/language-test-tsx-test-language-test-tsx-detect-language-code-should-not-crash-when-rendering-arabic-letters-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/layout-test-tsx-test-layout-test-tsx-layout-should-stretch-items-by-default-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/layout-test-tsx-test-layout-test-tsx-layout-should-stretch-items-by-default-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/line-clamp-test-tsx-test-line-clamp-test-tsx-line-clamp-should-not-work-when-display-is-not-set-to-block-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/line-clamp-test-tsx-test-line-clamp-test-tsx-line-clamp-should-not-work-when-display-is-not-set-to-block-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/line-clamp-test-tsx-test-line-clamp-test-tsx-line-clamp-should-replace-custom-block-ellipsis-with-default-ellipsis-when-too-long-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/line-clamp-test-tsx-test-line-clamp-test-tsx-line-clamp-should-replace-custom-block-ellipsis-with-default-ellipsis-when-too-long-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/line-clamp-test-tsx-test-line-clamp-test-tsx-line-clamp-should-work-correctly-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/line-clamp-test-tsx-test-line-clamp-test-tsx-line-clamp-should-work-correctly-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/line-clamp-test-tsx-test-line-clamp-test-tsx-line-clamp-should-work-correctly-when-text-align-center-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/line-clamp-test-tsx-test-line-clamp-test-tsx-line-clamp-should-work-correctly-when-text-align-center-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/line-height-test-tsx-test-line-height-test-tsx-line-height-should-work-correctly-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/line-height-test-tsx-test-line-height-test-tsx-line-height-should-work-correctly-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/line-height-test-tsx-test-line-height-test-tsx-line-height-should-work-correctly-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/line-height-test-tsx-test-line-height-test-tsx-line-height-should-work-correctly-2-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-mask-image-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-mask-image-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-mask-image-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-mask-image-2-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-mask-image-3-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-mask-image-3-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-mask-image-on-img-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-mask-image-on-img-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-mask-image-on-positioned-elements-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-mask-image-on-positioned-elements-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-mask-image-on-text-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-mask-image-on-text-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-mask-position-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-mask-position-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-mask-repeat-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-mask-repeat-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-mask-size-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-mask-size-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-multiple-mask-image-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/mask-image-test-tsx-test-mask-image-test-tsx-mask-should-support-multiple-mask-image-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/overflow-test-tsx-test-overflow-test-tsx-overflow-should-not-show-overflowed-text-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/overflow-test-tsx-test-overflow-test-tsx-overflow-should-not-show-overflowed-text-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/overflow-test-tsx-test-overflow-test-tsx-overflow-should-not-work-when-overflow-is-not-hidden-and-overflow-property-should-not-be-inherited-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/overflow-test-tsx-test-overflow-test-tsx-overflow-should-not-work-when-overflow-is-not-hidden-and-overflow-property-should-not-be-inherited-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/overflow-test-tsx-test-overflow-test-tsx-overflow-should-work-with-ellipsis-nowrap-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/overflow-test-tsx-test-overflow-test-tsx-overflow-should-work-with-ellipsis-nowrap-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/overflow-test-tsx-test-overflow-test-tsx-overflow-should-work-with-nested-border-border-radius-padding-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/overflow-test-tsx-test-overflow-test-tsx-overflow-should-work-with-nested-border-border-radius-padding-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/position-test-tsx-test-position-test-tsx-position-absolute-should-support-absolute-position-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/position-test-tsx-test-position-test-tsx-position-absolute-should-support-absolute-position-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-be-affected-by-container-opacity-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-be-affected-by-container-opacity-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-render-box-shadow-with-offset-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-render-box-shadow-with-offset-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-render-box-shadow-with-offset-and-spread-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-render-box-shadow-with-offset-and-spread-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-render-multiple-box-shadows-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-render-multiple-box-shadows-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-render-regular-box-shadow-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-render-regular-box-shadow-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-show-box-shadow-without-specifying-height-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-show-box-shadow-without-specifying-height-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-support-box-shadow-for-transparent-elements-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-support-box-shadow-for-transparent-elements-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-support-box-shadow-spread-with-transparency-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-support-box-shadow-spread-with-transparency-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-support-inset-box-shadows-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-support-inset-box-shadows-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-support-multiple-text-shadows-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-support-multiple-text-shadows-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-support-negative-spread-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-support-negative-spread-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-support-text-shadows-if-exist-unexpected-comma-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-support-text-shadows-if-exist-unexpected-comma-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-work-correct-with-zero-border-radius-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/shadow-test-tsx-test-shadow-test-tsx-shadow-box-shadow-should-work-correct-with-zero-border-radius-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/svg-test-tsx-test-svg-test-tsx-svg-should-parse-view-box-correctly-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/svg-test-tsx-test-svg-test-tsx-svg-should-parse-view-box-correctly-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/svg-test-tsx-test-svg-test-tsx-svg-should-render-svg-attributes-correctly-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/svg-test-tsx-test-svg-test-tsx-svg-should-render-svg-attributes-correctly-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/svg-test-tsx-test-svg-test-tsx-svg-should-render-svg-nodes-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/svg-test-tsx-test-svg-test-tsx-svg-should-render-svg-nodes-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/svg-test-tsx-test-svg-test-tsx-svg-should-render-svg-prefer-size-props-rather-than-view-box-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/svg-test-tsx-test-svg-test-tsx-svg-should-render-svg-prefer-size-props-rather-than-view-box-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/svg-test-tsx-test-svg-test-tsx-svg-should-render-svg-size-correctly-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/svg-test-tsx-test-svg-test-tsx-svg-should-render-svg-size-correctly-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/svg-test-tsx-test-svg-test-tsx-svg-should-render-svg-without-view-box-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/svg-test-tsx-test-svg-test-tsx-svg-should-render-svg-without-view-box-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/svg-test-tsx-test-svg-test-tsx-svg-should-respect-style-on-svg-node-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/svg-test-tsx-test-svg-test-tsx-svg-should-respect-style-on-svg-node-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/svg-test-tsx-test-svg-test-tsx-svg-should-support-current-color-for-svg-fill-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/svg-test-tsx-test-svg-test-tsx-svg-should-support-current-color-for-svg-fill-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/svg-test-tsx-test-svg-test-tsx-svg-should-support-current-color-for-svg-stroke-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/svg-test-tsx-test-svg-test-tsx-svg-should-support-current-color-for-svg-stroke-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/svg-test-tsx-test-svg-test-tsx-svg-should-support-current-color-when-color-is-set-on-parent-element-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/svg-test-tsx-test-svg-test-tsx-svg-should-support-current-color-when-color-is-set-on-parent-element-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/svg-test-tsx-test-svg-test-tsx-svg-should-support-current-color-when-used-on-svg-nodes-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/svg-test-tsx-test-svg-test-tsx-svg-should-support-current-color-when-used-on-svg-nodes-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/svg-test-tsx-test-svg-test-tsx-svg-should-support-em-in-svg-size-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/svg-test-tsx-test-svg-test-tsx-svg-should-support-em-in-svg-size-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/tab-size-test-tsx-test-tab-size-test-tsx-tab-size-tab-renders-as-space-when-white-space-is-not-pre-or-pre-wrap-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/tab-size-test-tsx-test-tab-size-test-tsx-tab-size-tab-renders-as-space-when-white-space-is-not-pre-or-pre-wrap-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/tab-size-test-tsx-test-tab-size-test-tsx-tab-size-tabs-render-correctly-when-tab-size-is-a-number-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/tab-size-test-tsx-test-tab-size-test-tsx-tab-size-tabs-render-correctly-when-tab-size-is-a-number-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/tab-size-test-tsx-test-tab-size-test-tsx-tab-size-tabs-render-correctly-when-tab-size-is-a-string-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/tab-size-test-tsx-test-tab-size-test-tsx-tab-size-tabs-render-correctly-when-tab-size-is-a-string-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/tab-size-test-tsx-test-tab-size-test-tsx-tab-size-tabs-render-correctly-with-default-tab-size-of-8-when-white-space-is-pre-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/tab-size-test-tsx-test-tab-size-test-tsx-tab-size-tabs-render-correctly-with-default-tab-size-of-8-when-white-space-is-pre-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/tab-size-test-tsx-test-tab-size-test-tsx-tab-size-tabs-render-correctly-with-default-tab-size-of-8-when-white-space-is-pre-wrap-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/tab-size-test-tsx-test-tab-size-test-tsx-tab-size-tabs-render-correctly-with-default-tab-size-of-8-when-white-space-is-pre-wrap-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/text-align-test-tsx-test-text-align-test-tsx-text-align-should-work-correctly-when-text-align-center-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/text-align-test-tsx-test-text-align-test-tsx-text-align-should-work-correctly-when-text-align-center-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/text-align-test-tsx-test-text-align-test-tsx-text-align-should-work-correctly-when-text-align-end-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/text-align-test-tsx-test-text-align-test-tsx-text-align-should-work-correctly-when-text-align-end-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/text-align-test-tsx-test-text-align-test-tsx-text-align-should-work-correctly-when-text-align-left-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/text-align-test-tsx-test-text-align-test-tsx-text-align-should-work-correctly-when-text-align-left-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/text-align-test-tsx-test-text-align-test-tsx-text-align-should-work-correctly-when-text-align-right-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/text-align-test-tsx-test-text-align-test-tsx-text-align-should-work-correctly-when-text-align-right-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/text-decoration-test-tsx-test-text-decoration-test-tsx-text-decoration-should-work-correctly-when-text-decoration-line-line-through-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/text-decoration-test-tsx-test-text-decoration-test-tsx-text-decoration-should-work-correctly-when-text-decoration-line-line-through-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/text-decoration-test-tsx-test-text-decoration-test-tsx-text-decoration-should-work-correctly-when-text-decoration-line-line-through-and-text-align-right-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/text-decoration-test-tsx-test-text-decoration-test-tsx-text-decoration-should-work-correctly-when-text-decoration-line-line-through-and-text-align-right-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/text-decoration-test-tsx-test-text-decoration-test-tsx-text-decoration-should-work-correctly-when-text-decoration-line-underline-and-text-align-right-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/text-decoration-test-tsx-test-text-decoration-test-tsx-text-decoration-should-work-correctly-when-text-decoration-line-underline-and-text-align-right-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/text-decoration-test-tsx-test-text-decoration-test-tsx-text-decoration-should-work-correctly-when-text-decoration-style-dashed-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/text-decoration-test-tsx-test-text-decoration-test-tsx-text-decoration-should-work-correctly-when-text-decoration-style-dashed-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/text-decoration-test-tsx-test-text-decoration-test-tsx-text-decoration-should-work-correctly-when-text-decoration-style-dotted-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/text-decoration-test-tsx-test-text-decoration-test-tsx-text-decoration-should-work-correctly-when-text-decoration-style-dotted-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/text-decoration-test-tsx-test-text-decoration-test-tsx-text-decoration-should-work-correctly-with-text-decoration-and-transform-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/text-decoration-test-tsx-test-text-decoration-test-tsx-text-decoration-should-work-correctly-with-text-decoration-and-transform-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/text-wrap-test-tsx-test-text-wrap-test-tsx-text-wrap-should-wrap-balancedly-with-text-wrap-balance-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/text-wrap-test-tsx-test-text-wrap-test-tsx-text-wrap-should-wrap-balancedly-with-text-wrap-balance-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/text-wrap-test-tsx-test-text-wrap-test-tsx-text-wrap-should-wrap-normally-with-text-wrap-wrap-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/text-wrap-test-tsx-test-text-wrap-test-tsx-text-wrap-should-wrap-normally-with-text-wrap-wrap-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/transform-test-tsx-test-transform-test-tsx-transform-behavior-with-parent-overflow-should-not-inherit-parent-clip-path-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/transform-test-tsx-test-transform-test-tsx-transform-behavior-with-parent-overflow-should-not-inherit-parent-clip-path-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/transform-test-tsx-test-transform-test-tsx-transform-multiple-transforms-should-support-translate-rotate-and-scale-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/transform-test-tsx-test-transform-test-tsx-transform-multiple-transforms-should-support-translate-rotate-and-scale-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/transform-test-tsx-test-transform-test-tsx-transform-rotate-should-rotate-shape-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/transform-test-tsx-test-transform-test-tsx-transform-rotate-should-rotate-shape-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/transform-test-tsx-test-transform-test-tsx-transform-scale-should-scale-shape-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/transform-test-tsx-test-transform-test-tsx-transform-scale-should-scale-shape-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/transform-test-tsx-test-transform-test-tsx-transform-scale-should-scale-shape-in-two-directions-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/transform-test-tsx-test-transform-test-tsx-transform-scale-should-scale-shape-in-two-directions-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/transform-test-tsx-test-transform-test-tsx-transform-translate-should-support-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/transform-test-tsx-test-transform-test-tsx-transform-translate-should-support-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/transform-test-tsx-test-transform-test-tsx-transform-translate-should-translate-shape-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/transform-test-tsx-test-transform-test-tsx-transform-translate-should-translate-shape-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/transform-test-tsx-test-transform-test-tsx-transform-translate-should-translate-shape-in-x-axis-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/transform-test-tsx-test-transform-test-tsx-transform-translate-should-translate-shape-in-x-axis-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/transform-test-tsx-test-transform-test-tsx-transform-translate-should-translate-shape-in-y-axis-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/transform-test-tsx-test-transform-test-tsx-transform-translate-should-translate-shape-in-y-axis-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/units-test-tsx-test-units-test-tsx-units-should-support-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/units-test-tsx-test-units-test-tsx-units-should-support-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/units-test-tsx-test-units-test-tsx-units-should-support-em-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/units-test-tsx-test-units-test-tsx-units-should-support-em-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/units-test-tsx-test-units-test-tsx-units-should-support-px-and-numbers-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/units-test-tsx-test-units-test-tsx-units-should-support-px-and-numbers-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/units-test-tsx-test-units-test-tsx-units-should-support-rem-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/units-test-tsx-test-units-test-tsx-units-should-support-rem-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/units-test-tsx-test-units-test-tsx-units-should-support-rgb-syntaxs-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/units-test-tsx-test-units-test-tsx-units-should-support-rgb-syntaxs-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/units-test-tsx-test-units-test-tsx-units-should-support-vh-and-vw-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/units-test-tsx-test-units-test-tsx-units-should-support-vh-and-vw-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/webkit-text-stroke-test-tsx-test-webkit-text-stroke-test-tsx-webkit-text-stroke-should-work-basic-text-stroke-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/webkit-text-stroke-test-tsx-test-webkit-text-stroke-test-tsx-webkit-text-stroke-should-work-basic-text-stroke-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/webkit-text-stroke-test-tsx-test-webkit-text-stroke-test-tsx-webkit-text-stroke-should-work-nested-and-complex-text-stroke-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/webkit-text-stroke-test-tsx-test-webkit-text-stroke-test-tsx-webkit-text-stroke-should-work-nested-and-complex-text-stroke-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/webkit-text-stroke-test-tsx-test-webkit-text-stroke-test-tsx-webkit-text-stroke-should-work-nested-text-stroke-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/webkit-text-stroke-test-tsx-test-webkit-text-stroke-test-tsx-webkit-text-stroke-should-work-nested-text-stroke-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-normal-should-have-line-break-before-fast-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-normal-should-have-line-break-before-fast-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-normal-should-not-render-extra-line-breaks-with-white-space-normal-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-normal-should-not-render-extra-line-breaks-with-white-space-normal-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-normal-should-not-render-extra-spaces-with-white-space-normal-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-normal-should-not-render-extra-spaces-with-white-space-normal-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-normal-should-wrap-automatically-with-white-space-normal-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-normal-should-wrap-automatically-with-white-space-normal-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-pre-should-always-preserve-extra-line-breaks-with-white-space-pre-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-pre-should-always-preserve-extra-line-breaks-with-white-space-pre-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-pre-should-always-preserve-extra-spaces-with-white-space-pre-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-pre-should-always-preserve-extra-spaces-with-white-space-pre-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-pre-should-not-wrap-with-white-space-pre-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-pre-should-not-wrap-with-white-space-pre-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-pre-should-render-line-breaks-correctly-without-separators-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-pre-should-render-line-breaks-correctly-without-separators-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-with-n-in-content-should-render-consecutive-line-breaks-with-pre-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-with-n-in-content-should-render-consecutive-line-breaks-with-pre-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-with-n-in-content-should-render-n-as-a-line-break-with-pre-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-with-n-in-content-should-render-n-as-a-line-break-with-pre-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-with-n-in-content-should-render-n-as-a-whitespace-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-with-n-in-content-should-render-n-as-a-whitespace-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-with-white-space-nowrap-should-not-wrap-with-white-space-nowrap-and-swallow-extra-spaces-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-with-white-space-nowrap-should-not-wrap-with-white-space-nowrap-and-swallow-extra-spaces-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-with-white-space-pre-line-should-always-collapse-spaces-and-preserve-line-breaks-with-white-space-pre-line-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-with-white-space-pre-line-should-always-collapse-spaces-and-preserve-line-breaks-with-white-space-pre-line-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-with-white-space-pre-wrap-should-always-preserve-extra-line-breaks-with-white-space-pre-wrap-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-with-white-space-pre-wrap-should-always-preserve-extra-line-breaks-with-white-space-pre-wrap-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-with-white-space-pre-wrap-should-always-preserve-extra-spaces-with-white-space-pre-wrap-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-with-white-space-pre-wrap-should-always-preserve-extra-spaces-with-white-space-pre-wrap-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-with-white-space-pre-wrap-should-automatically-wrap-with-white-space-pre-wrap-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/white-space-test-tsx-test-white-space-test-tsx-white-space-with-white-space-pre-wrap-should-automatically-wrap-with-white-space-pre-wrap-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/word-break-test-tsx-test-word-break-test-tsx-word-break-break-all-should-always-break-words-eagerly-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/word-break-test-tsx-test-word-break-test-tsx-word-break-break-all-should-always-break-words-eagerly-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/word-break-test-tsx-test-word-break-test-tsx-word-break-break-word-should-break-words-if-cannot-fit-into-one-line-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/word-break-test-tsx-test-word-break-test-tsx-word-break-break-word-should-break-words-if-cannot-fit-into-one-line-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/word-break-test-tsx-test-word-break-test-tsx-word-break-break-word-should-not-break-cjk-with-word-break-keep-all-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/word-break-test-tsx-test-word-break-test-tsx-word-break-break-word-should-not-break-cjk-with-word-break-keep-all-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/word-break-test-tsx-test-word-break-test-tsx-word-break-break-word-should-try-to-wrap-words-if-possible-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/word-break-test-tsx-test-word-break-test-tsx-word-break-break-word-should-try-to-wrap-words-if-possible-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/word-break-test-tsx-test-word-break-test-tsx-word-break-break-word-should-wrap-first-and-then-break-long-words-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/word-break-test-tsx-test-word-break-test-tsx-word-break-break-word-should-wrap-first-and-then-break-long-words-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/word-break-test-tsx-test-word-break-test-tsx-word-break-normal-should-not-break-long-word-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/word-break-test-tsx-test-word-break-test-tsx-word-break-normal-should-not-break-long-word-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/word-break-test-tsx-test-word-break-test-tsx-word-break-normal-should-not-break-word-if-possible-to-wrap-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/word-break-test-tsx-test-word-break-test-tsx-word-break-normal-should-not-break-word-if-possible-to-wrap-1-snap.png -------------------------------------------------------------------------------- /test/__image_snapshots__/word-break-test-tsx-test-word-break-test-tsx-word-break-should-support-non-breaking-space-1-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/__image_snapshots__/word-break-test-tsx-test-word-break-test-tsx-word-break-should-support-non-breaking-space-1-snap.png -------------------------------------------------------------------------------- /test/assets/MontserratSubrayada-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/assets/MontserratSubrayada-Regular.ttf -------------------------------------------------------------------------------- /test/assets/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/assets/Roboto-Bold.ttf -------------------------------------------------------------------------------- /test/assets/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/assets/Roboto-Regular.ttf -------------------------------------------------------------------------------- /test/assets/playfair-display.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/assets/playfair-display.ttf -------------------------------------------------------------------------------- /test/assets/Χαίρετ: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/assets/Χαίρετ -------------------------------------------------------------------------------- /test/assets/こんにちは: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/assets/こんにちは -------------------------------------------------------------------------------- /test/assets/你好: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/assets/你好 -------------------------------------------------------------------------------- /test/assets/안녕: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/satori/aa7cdb6b0d47e93781822c3ec4a7614fb2e68949/test/assets/안녕 -------------------------------------------------------------------------------- /test/background-clip.test.tsx: -------------------------------------------------------------------------------- 1 | import { it, describe, expect } from 'vitest' 2 | 3 | import { initFonts, toImage } from './utils.js' 4 | import satori from '../src/index.js' 5 | 6 | describe('backgroundClip', () => { 7 | let fonts 8 | initFonts((f) => (fonts = f)) 9 | 10 | it('should render background-clip:text', async () => { 11 | const svg = await satori( 12 |
20 |
28 | lynn 29 |
30 |
, 31 | { 32 | width: 100, 33 | height: 100, 34 | fonts, 35 | } 36 | ) 37 | 38 | expect(toImage(svg)).toMatchImageSnapshot() 39 | }) 40 | 41 | it('should render background-clip:text compatible with transform', async () => { 42 | const svg = await satori( 43 |
51 |
60 | lynn 61 |
62 |
, 63 | { 64 | width: 100, 65 | height: 100, 66 | fonts, 67 | } 68 | ) 69 | 70 | expect(toImage(svg)).toMatchImageSnapshot() 71 | }) 72 | 73 | it('should render background-clip:text compatible with mask', async () => { 74 | const svg = await satori( 75 |
83 |
93 | lynn 94 |
95 |
, 96 | { 97 | width: 100, 98 | height: 100, 99 | fonts, 100 | } 101 | ) 102 | 103 | expect(toImage(svg)).toMatchImageSnapshot() 104 | }) 105 | }) 106 | -------------------------------------------------------------------------------- /test/basic.test.tsx: -------------------------------------------------------------------------------- 1 | import { it, describe, expect } from 'vitest' 2 | 3 | import { initFonts, toImage } from './utils.js' 4 | import satori from '../src/index.js' 5 | 6 | describe('Basic', () => { 7 | let fonts 8 | initFonts((f) => (fonts = f)) 9 | 10 | it('should render empty div', async () => { 11 | const svg = await satori(
, { width: 100, height: 100, fonts }) 12 | expect(toImage(svg, 100)).toMatchImageSnapshot() 13 | }) 14 | 15 | it('should render basic div with text', async () => { 16 | const svg = await satori(
Hello
, { 17 | width: 100, 18 | height: 100, 19 | fonts, 20 | }) 21 | expect(toImage(svg, 100)).toMatchImageSnapshot() 22 | }) 23 | 24 | it('should render basic div with background color', async () => { 25 | const svg = await satori( 26 |
, 29 | { 30 | width: 100, 31 | height: 100, 32 | fonts, 33 | } 34 | ) 35 | expect(toImage(svg, 100)).toMatchImageSnapshot() 36 | }) 37 | 38 | it('should render basic div with text and background color', async () => { 39 | const svg = await satori( 40 |
41 | Hello 42 |
, 43 | { 44 | width: 100, 45 | height: 100, 46 | fonts, 47 | } 48 | ) 49 | expect(toImage(svg, 100)).toMatchImageSnapshot() 50 | }) 51 | 52 | it('should support skipping embedded fonts', async () => { 53 | const svg = await satori(
Hello
, { 54 | width: 100, 55 | height: 100, 56 | fonts, 57 | embedFont: false, 58 | }) 59 | expect(toImage(svg, 100)).toMatchImageSnapshot() 60 | }) 61 | 62 | it('should support hex colors', async () => { 63 | const svg = await satori( 64 |
, 67 | { 68 | width: 100, 69 | height: 100, 70 | fonts, 71 | } 72 | ) 73 | expect(toImage(svg, 100)).toMatchImageSnapshot() 74 | }) 75 | 76 | it('should support array in JSX children', async () => { 77 | const svg = await satori( 78 |
86 |
1
87 | {[ 88 |
2{[
3
]}
, 89 |
{[4]}
, 90 | ]} 91 |
, 92 | { 93 | width: 100, 94 | height: 100, 95 | fonts, 96 | } 97 | ) 98 | expect(toImage(svg, 100)).toMatchImageSnapshot() 99 | }) 100 | 101 | it('should combine textNodes correctly', async () => { 102 | const svg = await satori( 103 |
111 | Hi {0}
hi
{0} {false} {undefined} {0} {null} {0} {true} {'x'}{' '} 112 | {0} 113 |
, 114 | { 115 | width: 100, 116 | height: 100, 117 | fonts, 118 | } 119 | ) 120 | expect(toImage(svg, 100)).toMatchImageSnapshot() 121 | }) 122 | }) 123 | -------------------------------------------------------------------------------- /test/clip-path.test.tsx: -------------------------------------------------------------------------------- 1 | import { it, describe, expect } from 'vitest' 2 | 3 | import { initFonts, toImage } from './utils.js' 4 | import satori from '../src/index.js' 5 | 6 | describe('clipPath', () => { 7 | let fonts 8 | initFonts((f) => (fonts = f)) 9 | 10 | it('should render clip-path', async () => { 11 | const svgs = await Promise.all( 12 | [ 13 | 'circle(20px)', 14 | 'circle(20% at bottom left)', 15 | 'ellipse(10px 0.625em at 10% 20%)', 16 | 'polygon(50% 0, 100% 50%, 50% 100%, 0 50%)', 17 | `path('M 0 200 L 0,75 A 5,5 0,0,1 150,75 L 200 200 z')`, 18 | 'inset(10px 20px)', 19 | 'inset(0.5rem round 20% 1em 1rem 2px)', 20 | ].map((clipPath) => 21 | satori( 22 |
36 |
Hello, World
37 |
, 38 | { 39 | width: 100, 40 | height: 100, 41 | fonts, 42 | } 43 | ) 44 | ) 45 | ) 46 | 47 | svgs.forEach((svg) => expect(toImage(svg)).toMatchImageSnapshot()) 48 | }) 49 | 50 | it('should make clip-path compatible with overflow', async () => { 51 | const svg = await satori( 52 |
67 |
Lynnnnnnnnnnnnnnnnnnnnn
68 |
, 69 | { 70 | width: 100, 71 | height: 100, 72 | fonts, 73 | } 74 | ) 75 | 76 | expect(toImage(svg)).toMatchImageSnapshot() 77 | }) 78 | 79 | it('should respect the position value', async () => { 80 | const svg = await satori( 81 |
95 |
Lynnnnnnnnnnnnnnnnnnnnn
96 |
, 97 | { 98 | width: 100, 99 | height: 100, 100 | fonts, 101 | } 102 | ) 103 | 104 | expect(toImage(svg)).toMatchImageSnapshot() 105 | }) 106 | 107 | it('should respect left and top', async () => { 108 | const svg = await satori( 109 |
122 |
130 |
, 131 | { 132 | width: 100, 133 | height: 100, 134 | fonts, 135 | } 136 | ) 137 | 138 | expect(toImage(svg)).toMatchImageSnapshot() 139 | }) 140 | }) 141 | -------------------------------------------------------------------------------- /test/dynamic-size.test.tsx: -------------------------------------------------------------------------------- 1 | import { it, describe, expect } from 'vitest' 2 | 3 | import { initFonts, toImage } from './utils.js' 4 | import satori from '../src/index.js' 5 | 6 | describe('Dynamic size', () => { 7 | let fonts 8 | initFonts((f) => (fonts = f)) 9 | 10 | it('should render image with dynamic height', async () => { 11 | const svg = await satori( 12 |
13 | Lorem Ipsum is simply dummy text of the printing and typesetting 14 | industry. 15 |
, 16 | { width: 100, fonts } 17 | ) 18 | expect(toImage(svg, 100)).toMatchImageSnapshot() 19 | }) 20 | 21 | it('should render image with dynamic width', async () => { 22 | const svg = await satori( 23 |
24 | Lorem Ipsum is simply dummy text of the printing and typesetting 25 | industry. 26 |
, 27 | { 28 | height: 25, 29 | fonts, 30 | } 31 | ) 32 | expect(toImage(svg, 300)).toMatchImageSnapshot() 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /test/error.test.tsx: -------------------------------------------------------------------------------- 1 | import { it, describe, expect } from 'vitest' 2 | 3 | import { initFonts } from './utils.js' 4 | import satori from '../src/index.js' 5 | 6 | describe('Error', () => { 7 | let fonts 8 | initFonts((f) => (fonts = f)) 9 | 10 | it('should throw if flex missing on div that has children', async () => { 11 | let error = new Error() 12 | try { 13 | await satori( 14 |
15 | Test satori with space 16 |
, 17 | { 18 | width: 10, 19 | height: 10, 20 | fonts, 21 | } 22 | ) 23 | } catch (err) { 24 | error = err 25 | } 26 | expect(error?.message).toBe( 27 | 'Expected
to have explicit "display: flex" or "display: none" if it has more than one child node.' 28 | ) 29 | }) 30 | 31 | it('should throw if display inline-block on div that has children', async () => { 32 | const result = satori( 33 |
34 | Test satori with space 35 |
, 36 | { 37 | width: 10, 38 | height: 10, 39 | fonts, 40 | } 41 | ) 42 | expect(result).rejects.toThrowError( 43 | `Invalid value for CSS property "display". Allowed values: "flex" | "block" | "none" | "-webkit-box". Received: "inline-block".` 44 | ) 45 | }) 46 | 47 | it('should throw if using invalid values', async () => { 48 | const result = satori( 49 | // @ts-expect-error 50 |
Test
, 51 | { 52 | width: 10, 53 | height: 10, 54 | fonts, 55 | } 56 | ) 57 | expect(result).rejects.toThrowError( 58 | `Invalid value for CSS property "position". Allowed values: "absolute" | "relative". Received: "fixed".` 59 | ) 60 | }) 61 | 62 | it('should not throw if display none on div that has children', async () => { 63 | const svg = await satori( 64 |
65 | Test satori with space 66 |
, 67 | { 68 | width: 10, 69 | height: 10, 70 | fonts, 71 | } 72 | ) 73 | expect(typeof svg).toBe('string') 74 | }) 75 | 76 | it('should not throw if flex missing on span that has children', async () => { 77 | const svg = await satori( 78 | 79 | Test satori with space 80 | , 81 | { 82 | width: 10, 83 | height: 10, 84 | fonts, 85 | } 86 | ) 87 | expect(typeof svg).toBe('string') 88 | }) 89 | 90 | it('should not throw if flex missing on div without children', async () => { 91 | const svg = await satori(
, { 92 | width: 10, 93 | height: 10, 94 | fonts, 95 | }) 96 | expect(typeof svg).toBe('string') 97 | }) 98 | 99 | it('should not allowed to set negative value to rg-size', async () => { 100 | const result = satori( 101 |
, 115 | { 116 | width: 100, 117 | height: 100, 118 | fonts, 119 | } 120 | ) 121 | 122 | expect(result).rejects.toThrowError( 123 | 'disallow setting negative values to the size of the shape. Check https://w3c.github.io/csswg-drafts/css-images/#valdef-rg-size-length-0' 124 | ) 125 | }) 126 | }) 127 | -------------------------------------------------------------------------------- /test/event.test.tsx: -------------------------------------------------------------------------------- 1 | import { it, describe, expect } from 'vitest' 2 | 3 | import { initFonts } from './utils.js' 4 | import satori from '../src/index.js' 5 | 6 | describe('Event', () => { 7 | let fonts 8 | initFonts((f) => (fonts = f)) 9 | 10 | it('should trigger the onNodeDetected callback', async () => { 11 | const nodes = [] 12 | await satori( 13 |
14 |
Hello
15 |
World
16 |
, 17 | { 18 | width: 100, 19 | height: 100, 20 | fonts, 21 | onNodeDetected: (node) => { 22 | nodes.push(node) 23 | }, 24 | } 25 | ) 26 | expect(nodes).toMatchInlineSnapshot(` 27 | [ 28 | { 29 | "height": 50, 30 | "key": null, 31 | "left": 0, 32 | "props": { 33 | "style": { 34 | "display": "flex", 35 | "height": 50, 36 | "width": "100%", 37 | }, 38 | }, 39 | "textContent": undefined, 40 | "top": 0, 41 | "type": "div", 42 | "width": 100, 43 | }, 44 | { 45 | "height": 50, 46 | "key": null, 47 | "left": 0, 48 | "props": {}, 49 | "textContent": "Hello", 50 | "top": 0, 51 | "type": "div", 52 | "width": 37, 53 | }, 54 | { 55 | "height": 50, 56 | "key": null, 57 | "left": 37, 58 | "props": {}, 59 | "textContent": "World", 60 | "top": 0, 61 | "type": "div", 62 | "width": 42, 63 | }, 64 | ] 65 | `) 66 | }) 67 | }) 68 | -------------------------------------------------------------------------------- /test/gap.test.tsx: -------------------------------------------------------------------------------- 1 | import { it, describe, expect } from 'vitest' 2 | 3 | import { initYogaWasm, toImage } from './utils.js' 4 | import satori from '../src/index.js' 5 | 6 | initYogaWasm() 7 | 8 | const items = [ 9 | 'red', 10 | 'red', 11 | 'red', 12 | 'green', 13 | 'green', 14 | 'green', 15 | 'blue', 16 | 'blue', 17 | 'blue', 18 | ] 19 | 20 | describe('flex gap', () => { 21 | it('should support gap', async () => { 22 | const svg = await satori( 23 |
33 | {items.map((color, index) => ( 34 |
42 | ))} 43 |
, 44 | { width: 100, height: 100, fonts: [] } 45 | ) 46 | expect(toImage(svg, 100)).toMatchImageSnapshot() 47 | }) 48 | 49 | it('should support rowGap and columnGap', async () => { 50 | const svg = await satori( 51 |
62 | {items.slice(0, 4).map((color, index) => ( 63 |
71 | ))} 72 |
, 73 | { width: 100, height: 100, fonts: [] } 74 | ) 75 | expect(toImage(svg, 100)).toMatchImageSnapshot() 76 | }) 77 | }) 78 | -------------------------------------------------------------------------------- /test/layout.test.tsx: -------------------------------------------------------------------------------- 1 | import { it, describe, expect } from 'vitest' 2 | 3 | import { initFonts, toImage } from './utils.js' 4 | import satori from '../src/index.js' 5 | 6 | describe('Layout', () => { 7 | let fonts 8 | initFonts((f) => (fonts = f)) 9 | 10 | it('should stretch items by default', async () => { 11 | const svg = await satori( 12 |
13 |
x
14 |
, 15 | { width: 100, height: 100, fonts } 16 | ) 17 | expect(toImage(svg, 100)).toMatchImageSnapshot() 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /test/line-height.test.tsx: -------------------------------------------------------------------------------- 1 | import { it, describe, expect } from 'vitest' 2 | 3 | import { initFonts, toImage } from './utils.js' 4 | import satori from '../src/index.js' 5 | 6 | describe('line-height', () => { 7 | let fonts 8 | initFonts((f) => (fonts = f)) 9 | it('should work correctly', async () => { 10 | const svgs = await Promise.all( 11 | [1, '1'].map((lineHeight) => 12 | satori( 13 |
24 | Hello I am some text that is here. 25 |
, 26 | { width: 100, height: 100, fonts, embedFont: true } 27 | ) 28 | ) 29 | ) 30 | 31 | svgs.forEach((svg) => { 32 | expect(toImage(svg, 100)).toMatchImageSnapshot() 33 | }) 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /test/position.test.tsx: -------------------------------------------------------------------------------- 1 | import { it, describe, expect } from 'vitest' 2 | 3 | import { initFonts, toImage } from './utils.js' 4 | import satori from '../src/index.js' 5 | 6 | describe('Position', () => { 7 | let fonts 8 | initFonts((f) => (fonts = f)) 9 | 10 | describe('absolute', () => { 11 | it('should support absolute position', async () => { 12 | const svg = await satori( 13 |
20 |
30 |
, 31 | { width: 100, height: 100, fonts } 32 | ) 33 | expect(toImage(svg, 100)).toMatchImageSnapshot() 34 | }) 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /test/text-wrap.test.tsx: -------------------------------------------------------------------------------- 1 | import { it, describe, expect } from 'vitest' 2 | 3 | import { initFonts, toImage } from './utils.js' 4 | import satori from '../src/index.js' 5 | 6 | describe('text-wrap', () => { 7 | let fonts 8 | initFonts((f) => (fonts = f)) 9 | 10 | it('should wrap normally with text-wrap: wrap', async () => { 11 | const svg = await satori( 12 |
22 | {'a a a a a a a a'} 23 |
, 24 | { 25 | width: 100, 26 | height: 100, 27 | fonts, 28 | } 29 | ) 30 | 31 | expect(toImage(svg, 100)).toMatchImageSnapshot() 32 | }) 33 | 34 | it('should wrap balancedly with text-wrap: balance', async () => { 35 | const svg = await satori( 36 |
46 | {'a a a a a a a a'} 47 |
, 48 | { 49 | width: 100, 50 | height: 100, 51 | fonts, 52 | } 53 | ) 54 | 55 | expect(toImage(svg, 100)).toMatchImageSnapshot() 56 | }) 57 | }) 58 | -------------------------------------------------------------------------------- /test/utils.tsx: -------------------------------------------------------------------------------- 1 | import { beforeAll, expect } from 'vitest' 2 | import { join } from 'path' 3 | import { Resvg } from '@resvg/resvg-js' 4 | import { toMatchImageSnapshot } from 'jest-image-snapshot' 5 | import { readFile } from 'node:fs/promises' 6 | import yoga from 'yoga-wasm-web/auto' 7 | 8 | import { init, type SatoriOptions } from '../src/index.js' 9 | 10 | export function initYogaWasm() { 11 | beforeAll(async () => { 12 | init(yoga) 13 | }) 14 | } 15 | 16 | export async function getDynamicAsset(text: string): Promise { 17 | const fontPath = join(process.cwd(), 'test', 'assets', text) 18 | return await readFile(fontPath) 19 | } 20 | 21 | export async function loadDynamicAsset(code: string, text: string) { 22 | return [ 23 | { 24 | name: `satori_${code}_fallback_${text}`, 25 | data: await getDynamicAsset(text), 26 | weight: 400, 27 | style: 'normal', 28 | lang: code === 'unknown' ? undefined : code.split('|')[0], 29 | }, 30 | ] 31 | } 32 | 33 | export function initFonts(callback: (fonts: SatoriOptions['fonts']) => void) { 34 | beforeAll(async () => { 35 | const fontPath = join(process.cwd(), 'test', 'assets', 'Roboto-Regular.ttf') 36 | const fontData = await readFile(fontPath) 37 | callback([ 38 | { 39 | name: 'Roboto', 40 | data: fontData, 41 | weight: 400, 42 | style: 'normal', 43 | }, 44 | ]) 45 | }) 46 | } 47 | 48 | export function toImage(svg: string, width = 100) { 49 | const resvg = new Resvg(svg, { 50 | fitTo: { 51 | mode: 'width', 52 | value: width, 53 | }, 54 | font: { 55 | // As system fallback font 56 | fontFiles: [ 57 | join(process.cwd(), 'test', 'assets', 'playfair-display.ttf'), 58 | ], 59 | loadSystemFonts: false, 60 | defaultFontFamily: 'Playfair Display', 61 | }, 62 | }) 63 | const pngData = resvg.render() 64 | return pngData.asPng() 65 | } 66 | 67 | declare global { 68 | namespace jest { 69 | interface Matchers { 70 | toMatchImageSnapshot(): R 71 | } 72 | } 73 | } 74 | 75 | expect.extend({ toMatchImageSnapshot }) 76 | -------------------------------------------------------------------------------- /test/webkit-text-stroke.test.tsx: -------------------------------------------------------------------------------- 1 | import { it, describe, expect } from 'vitest' 2 | 3 | import { initFonts, toImage } from './utils.js' 4 | import satori from '../src/index.js' 5 | 6 | describe('webkit-text-stroke', () => { 7 | let fonts 8 | initFonts((f) => (fonts = f)) 9 | 10 | it('should work basic text stroke', async () => { 11 | const svg = await satori( 12 |
22 | Hello, world 23 |
, 24 | { width: 100, height: 100, fonts } 25 | ) 26 | expect(toImage(svg, 100)).toMatchImageSnapshot() 27 | }) 28 | 29 | it('should work nested text stroke', async () => { 30 | const svg = await satori( 31 |
43 | Hello, world 44 |
, 45 | { width: 100, height: 100, fonts } 46 | ) 47 | expect(toImage(svg, 100)).toMatchImageSnapshot() 48 | }) 49 | 50 | it('should work nested and complex text stroke', async () => { 51 | const svg = await satori( 52 |
64 | Hello, 65 | w 66 | o 67 | r 68 | l 69 | d 70 | 76 | ! 77 | 78 |
, 79 | { width: 100, height: 100, fonts } 80 | ) 81 | expect(toImage(svg, 100)).toMatchImageSnapshot() 82 | }) 83 | }) 84 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "jsx": "react-jsx", 4 | "module": "node16", 5 | "moduleResolution": "node16", 6 | "esModuleInterop": true, 7 | "target": "ES2021", 8 | "lib": ["esnext", "dom"], 9 | "baseUrl": ".", 10 | "skipLibCheck": true, 11 | "paths": { 12 | "@yoga": ["src/yoga/yoga-prebuilt.ts"] 13 | } 14 | }, 15 | "include": ["src/*", "test/*"] 16 | } 17 | -------------------------------------------------------------------------------- /tsconfig.wasm.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "jsx": "react-jsx", 4 | "module": "node16", 5 | "moduleResolution": "node16", 6 | "esModuleInterop": true, 7 | "lib": ["esnext", "dom"], 8 | "baseUrl": ".", 9 | "paths": { 10 | "@yoga": ["src/yoga/yoga-prebuilt.wasm.ts"] 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tsup.config.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This configuration ensures that the prebuilt Yoga (asm.js) is not included in 3 | * the WASM bundle. 4 | */ 5 | 6 | import { defineConfig } from 'tsup' 7 | import { join } from 'path' 8 | import { replace } from 'esbuild-plugin-replace' 9 | 10 | export default defineConfig({ 11 | entry: ['src/index.ts'], 12 | splitting: false, 13 | sourcemap: true, 14 | target: 'node16', 15 | dts: process.env.NODE_ENV !== 'development' && { 16 | resolve: ['twrnc', './tw-config', './types'], 17 | }, 18 | minify: process.env.NODE_ENV !== 'development', 19 | format: ['esm', 'cjs'], 20 | noExternal: ['twrnc', 'emoji-regex-xs'], 21 | esbuildOptions(options) { 22 | if (process.env.WASM) { 23 | options.outExtension = { 24 | '.js': `.wasm.${options.format === 'cjs' ? 'cjs' : 'js'}`, 25 | } 26 | } 27 | options.tsconfig = process.env.WASM ? 'tsconfig.wasm.json' : 'tsconfig.json' 28 | options.legalComments = 'external' 29 | }, 30 | esbuildPlugins: [ 31 | { 32 | name: 'optimize tailwind', 33 | setup(build) { 34 | // Get rid of chalk 35 | // https://github.com/tailwindlabs/tailwindcss/blob/b8cda161dd0993083dcef1e2a03988c70be0ce93/src/util/log.js 36 | build.onResolve({ filter: /\/log$/ }, (args) => { 37 | if (args.importer.includes('/tailwindcss/')) { 38 | return { 39 | path: join(__dirname, 'src', 'vendor', 'twrnc', 'log.js'), 40 | } 41 | } 42 | }) 43 | 44 | // Get rid of picocolors 45 | // https://github.com/tailwindlabs/tailwindcss/blob/bf4494104953b13a5f326b250d7028074815e77e/src/featureFlags.js 46 | build.onResolve({ filter: /^picocolors$/ }, () => { 47 | return { 48 | path: join(__dirname, 'src', 'vendor', 'twrnc', 'picocolors.js'), 49 | } 50 | }) 51 | 52 | // Get rid of util-deprecate/node.js 53 | build.onResolve({ filter: /util-deprecate/ }, () => { 54 | return { 55 | path: join(__dirname, 'src', 'vendor', 'twrnc', 'deprecate.js'), 56 | } 57 | }) 58 | }, 59 | }, 60 | // We don't like `Function`. 61 | // https://github.com/tailwindlabs/tailwindcss/blob/bf4494104953b13a5f326b250d7028074815e77e/src/util/getAllConfigs.js#L8 62 | replace({ 63 | 'preset instanceof Function': 'typeof preset === "function"', 64 | }), 65 | ], 66 | }) 67 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turbo.build/schema.json", 3 | "pipeline": { 4 | "dev": { 5 | "cache": false 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import { defineConfig } from 'vitest/config' 3 | 4 | export default defineConfig({ 5 | test: { 6 | coverage: { 7 | reporter: ['text', 'json', 'html'], 8 | }, 9 | }, 10 | resolve: { 11 | alias: [ 12 | { 13 | find: '@yoga', 14 | replacement: path.resolve(__dirname, 'src', 'yoga', 'yoga-prebuilt.ts'), 15 | }, 16 | ], 17 | }, 18 | }) 19 | -------------------------------------------------------------------------------- /wasm.js: -------------------------------------------------------------------------------- 1 | import Satori, { init } from './dist/index.wasm.js' 2 | 3 | export default Satori 4 | export { init } 5 | --------------------------------------------------------------------------------