@mintlify/og 0.0.16 → 0.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/constants.d.ts +4 -2
  2. package/dist/constants.js +3 -1
  3. package/dist/hash.js +11 -4
  4. package/dist/index.d.ts +3 -1
  5. package/dist/index.js +2 -0
  6. package/dist/inputs/default-font-urls.d.ts +2 -0
  7. package/dist/inputs/default-font-urls.js +2 -0
  8. package/dist/inputs/font-file.d.ts +1 -0
  9. package/dist/inputs/font-file.js +36 -0
  10. package/dist/inputs/fonts.d.ts +1 -0
  11. package/dist/inputs/fonts.js +18 -5
  12. package/dist/inputs/image.d.ts +12 -0
  13. package/dist/inputs/image.js +76 -6
  14. package/dist/inputs/oklch.d.ts +7 -0
  15. package/dist/inputs/oklch.js +45 -0
  16. package/dist/inputs/palette.d.ts +19 -0
  17. package/dist/inputs/palette.js +108 -0
  18. package/dist/inputs/resvg.d.ts +1 -0
  19. package/dist/inputs/resvg.js +12 -0
  20. package/dist/inputs/text-fit.d.ts +10 -0
  21. package/dist/inputs/text-fit.js +84 -0
  22. package/dist/inputs/theme.d.ts +4 -0
  23. package/dist/inputs/theme.js +2 -2
  24. package/dist/layouts/font-stack.d.ts +1 -0
  25. package/dist/layouts/font-stack.js +7 -0
  26. package/dist/layouts/index.d.ts +8 -3
  27. package/dist/layouts/index.js +8 -3
  28. package/dist/layouts/legacy.d.ts +2 -0
  29. package/dist/layouts/{classic.js → legacy.js} +3 -8
  30. package/dist/layouts/preset.d.ts +2 -0
  31. package/dist/layouts/preset.js +240 -0
  32. package/dist/layouts/types.d.ts +15 -1
  33. package/dist/render.js +40 -24
  34. package/dist/tsconfig.build.tsbuildinfo +1 -1
  35. package/dist/types.d.ts +10 -4
  36. package/fonts/Inter-Medium.ttf +0 -0
  37. package/fonts/PaperMono-Regular.ttf +0 -0
  38. package/package.json +5 -3
  39. package/dist/layouts/classic.d.ts +0 -2
@@ -1,3 +1,8 @@
1
- import type { OgLayout } from '../types.js';
2
- import type { OgLayoutComponent } from './types.js';
3
- export declare const OG_LAYOUTS: Record<OgLayout, OgLayoutComponent>;
1
+ import type { OgEyebrowStyle } from '../types.js';
2
+ import type { OgPresetName } from './types.js';
3
+ export declare const OG_LAYOUTS: {
4
+ legacy: ({ content, theme, logo, fontFamily }: import("./types.js").OgLayoutProps) => import("react/jsx-runtime").JSX.Element;
5
+ };
6
+ export declare const PRESET_EYEBROW_STYLES: Record<OgPresetName, OgEyebrowStyle>;
7
+ export { LegacyLayout } from './legacy.js';
8
+ export { PresetLayout } from './preset.js';
@@ -1,4 +1,9 @@
1
- import { ClassicLayout } from './classic.js';
2
- export const OG_LAYOUTS = {
3
- classic: ClassicLayout,
1
+ import { LegacyLayout } from './legacy.js';
2
+ export const OG_LAYOUTS = { legacy: LegacyLayout };
3
+ export const PRESET_EYEBROW_STYLES = {
4
+ classic: 'bar',
5
+ headline: 'pill',
6
+ minimal: 'plain',
4
7
  };
8
+ export { LegacyLayout } from './legacy.js';
9
+ export { PresetLayout } from './preset.js';
@@ -0,0 +1,2 @@
1
+ import type { OgLayoutProps } from './types.js';
2
+ export declare const LegacyLayout: ({ content, theme, logo, fontFamily }: OgLayoutProps) => import("react/jsx-runtime").JSX.Element;
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { DEFAULT_FONT_FAMILY, LOGO_HEIGHT } from '../constants.js';
3
- const FALLBACK_FONT_STACK = 'Inter var, sans-serif';
2
+ import { LOGO_HEIGHT } from '../constants.js';
3
+ import { getFontStack } from './font-stack.js';
4
4
  const TEXT_MAX_WIDTH = '1000px';
5
5
  const DESCRIPTION_WIDTH = '750px';
6
6
  const MAX_TEXT_LINES = 2;
@@ -60,18 +60,13 @@ const styles = {
60
60
  lineHeight: 1.625,
61
61
  },
62
62
  };
63
- const getFontStack = (fontFamily) => {
64
- if (fontFamily === DEFAULT_FONT_FAMILY)
65
- return FALLBACK_FONT_STACK;
66
- return `${fontFamily}, ${FALLBACK_FONT_STACK}`;
67
- };
68
63
  const getBackgroundStyle = (background) => {
69
64
  if (background.type === 'image') {
70
65
  return { backgroundImage: `url(${background.url})`, backgroundSize: '100% 100%' };
71
66
  }
72
67
  return { backgroundImage: background.css };
73
68
  };
74
- export const ClassicLayout = ({ content, theme, logo, fontFamily }) => {
69
+ export const LegacyLayout = ({ content, theme, logo, fontFamily }) => {
75
70
  const { eyebrow, title, description } = content;
76
71
  const textColors = TEXT_COLORS[theme.appearance];
77
72
  const eyebrowColor = theme.appearance === 'light' ? theme.primaryColor : theme.lightColor;
@@ -0,0 +1,2 @@
1
+ import type { OgPresetLayoutProps } from './types.js';
2
+ export declare const PresetLayout: ({ preset, content, palette, backgroundImageUrl, eyebrowStyle, logo, fontFamily, fitText, }: OgPresetLayoutProps) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,240 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { MONO_FONT_FAMILY, OG_HEIGHT, OG_WIDTH } from '../constants.js';
3
+ import { toRgba } from '../inputs/theme.js';
4
+ import { getFontStack } from './font-stack.js';
5
+ const PRESET_SETTINGS = {
6
+ classic: {
7
+ align: 'start',
8
+ contentLeft: 89.649,
9
+ contentTop: 264,
10
+ eyebrowGap: 22.741,
11
+ textGap: 32,
12
+ titleSize: 77.868,
13
+ titleWidth: 951.652,
14
+ descriptionWidth: 716,
15
+ eyebrowSize: 25.17,
16
+ eyebrowLineHeight: 37.755,
17
+ logo: 'top-left',
18
+ frame: true,
19
+ panel: false,
20
+ textLines: 3,
21
+ },
22
+ headline: {
23
+ align: 'start',
24
+ contentLeft: 106,
25
+ contentTop: 88.117,
26
+ eyebrowGap: 52,
27
+ textGap: 32,
28
+ titleSize: 77.868,
29
+ titleWidth: 716,
30
+ descriptionWidth: 716,
31
+ eyebrowSize: 25.17,
32
+ eyebrowLineHeight: 37.755,
33
+ logo: 'bottom-right',
34
+ frame: false,
35
+ panel: false,
36
+ textLines: 4,
37
+ },
38
+ minimal: {
39
+ align: 'center',
40
+ contentLeft: 0,
41
+ contentTop: 81.032,
42
+ eyebrowGap: 52,
43
+ textGap: 28,
44
+ titleSize: 70,
45
+ titleWidth: 836,
46
+ descriptionWidth: 637.886,
47
+ eyebrowSize: 29.155,
48
+ eyebrowLineHeight: 43.732,
49
+ logo: 'bottom-center',
50
+ frame: false,
51
+ panel: true,
52
+ textLines: 3,
53
+ },
54
+ };
55
+ const BRAND_GLOW = {
56
+ classic: {
57
+ light: {
58
+ angle: 200,
59
+ stops: [
60
+ [0.3, 0],
61
+ [0.14, 22],
62
+ [0, 58],
63
+ ],
64
+ },
65
+ dark: {
66
+ angle: 192,
67
+ stops: [
68
+ [0.48, 0],
69
+ [0.16, 22],
70
+ [0, 48],
71
+ ],
72
+ },
73
+ },
74
+ headline: {
75
+ light: {
76
+ angle: 351,
77
+ stops: [
78
+ [0.3, 0],
79
+ [0.15, 22],
80
+ [0, 65],
81
+ ],
82
+ },
83
+ dark: {
84
+ angle: 350,
85
+ stops: [
86
+ [0.5, 0],
87
+ [0.17, 22],
88
+ [0, 50],
89
+ ],
90
+ },
91
+ },
92
+ minimal: {
93
+ light: {
94
+ angle: 357,
95
+ stops: [
96
+ [0.32, 0],
97
+ [0.16, 22],
98
+ [0, 70],
99
+ ],
100
+ },
101
+ dark: {
102
+ angle: 352,
103
+ stops: [
104
+ [0.52, 0],
105
+ [0.17, 22],
106
+ [0, 50],
107
+ ],
108
+ },
109
+ },
110
+ };
111
+ const getGlowGradient = (preset, palette) => {
112
+ if (!palette.glow)
113
+ return undefined;
114
+ const { color, appearance } = palette.glow;
115
+ const { angle, stops } = BRAND_GLOW[preset][appearance];
116
+ const colorStops = stops.map(([alpha, position]) => `${toRgba(color, alpha)} ${position}%`);
117
+ return `linear-gradient(${angle}deg, ${colorStops.join(', ')})`;
118
+ };
119
+ const FRAME = { left: 62.938, width: 1074.12, dividers: [231.482, 565.189] };
120
+ const PANEL = { left: 89.386, top: 435.728, width: 1021.23, radius: 48 };
121
+ const LOGO_BOX = {
122
+ height: 56,
123
+ topLeft: { left: 89.649, top: 82.788 },
124
+ bottomRight: { bottom: 80.924, right: 86 },
125
+ bottomCenter: { bottom: 56.924 },
126
+ };
127
+ const BAR = { border: 2, padding: 25.229 };
128
+ const PILL = { paddingBlock: 5.5, paddingInline: 15 };
129
+ const TRACKING = -0.02;
130
+ const DESCRIPTION = { size: 33.288, lineHeight: 43.274 };
131
+ const MAX_TEXT_LINES = 2;
132
+ const tracking = (size) => `${size * TRACKING}px`;
133
+ const getLogoBoxStyle = (placement) => {
134
+ const box = {
135
+ position: 'absolute',
136
+ display: 'flex',
137
+ alignItems: 'center',
138
+ height: `${LOGO_BOX.height}px`,
139
+ };
140
+ if (placement === 'top-left') {
141
+ return Object.assign(Object.assign({}, box), { left: `${LOGO_BOX.topLeft.left}px`, top: `${LOGO_BOX.topLeft.top}px` });
142
+ }
143
+ if (placement === 'bottom-right') {
144
+ return Object.assign(Object.assign({}, box), { right: `${LOGO_BOX.bottomRight.right}px`, bottom: `${LOGO_BOX.bottomRight.bottom}px` });
145
+ }
146
+ return Object.assign(Object.assign({}, box), { left: 0, width: '100%', justifyContent: 'center', bottom: `${LOGO_BOX.bottomCenter.bottom}px` });
147
+ };
148
+ const TextLines = ({ lines, box, lineHeight, align, style, }) => (_jsx("div", { style: Object.assign(Object.assign({}, style), { display: 'flex', flexDirection: 'column', alignItems: align === 'center' ? 'center' : 'flex-start', width: `${box.width}px`, fontSize: `${box.fontSize}px`, fontWeight: box.fontWeight, lineHeight: `${lineHeight}px`, letterSpacing: `${box.letterSpacing}px` }), children: lines.map((line, index) => (_jsx("span", { style: { whiteSpace: 'nowrap' }, children: line }, index))) }));
149
+ const Logo = ({ logo }) => (_jsx("div", { style: {
150
+ position: 'relative',
151
+ display: 'flex',
152
+ width: `${logo.width}px`,
153
+ height: `${logo.height}px`,
154
+ overflow: 'hidden',
155
+ }, children: _jsx("img", { src: logo.src, alt: "", width: logo.image.width, height: logo.image.height, style: { position: 'absolute', left: `${logo.image.left}px`, top: `${logo.image.top}px` } }) }));
156
+ const Eyebrow = ({ text, style, palette, settings, }) => {
157
+ const textStyle = {
158
+ fontFamily: MONO_FONT_FAMILY,
159
+ fontSize: `${settings.eyebrowSize}px`,
160
+ lineHeight: `${settings.eyebrowLineHeight}px`,
161
+ letterSpacing: tracking(settings.eyebrowSize),
162
+ textTransform: 'uppercase',
163
+ whiteSpace: 'nowrap',
164
+ display: 'block',
165
+ overflow: 'hidden',
166
+ textOverflow: 'ellipsis',
167
+ maxWidth: `${settings.titleWidth}px`,
168
+ };
169
+ if (style === 'pill') {
170
+ return (_jsx("div", { style: {
171
+ display: 'flex',
172
+ alignItems: 'center',
173
+ padding: `${PILL.paddingBlock}px ${PILL.paddingInline}px`,
174
+ backgroundColor: palette.badge,
175
+ }, children: _jsx("span", { style: Object.assign(Object.assign({}, textStyle), { color: palette.badgeText }), children: text }) }));
176
+ }
177
+ if (style === 'bar') {
178
+ return (_jsx("div", { style: {
179
+ display: 'flex',
180
+ alignItems: 'center',
181
+ marginLeft: `${-(BAR.border + BAR.padding)}px`,
182
+ paddingLeft: `${BAR.padding}px`,
183
+ borderLeft: `${BAR.border}px solid ${palette.eyebrow}`,
184
+ }, children: _jsx("span", { style: Object.assign(Object.assign({}, textStyle), { color: palette.eyebrow }), children: text }) }));
185
+ }
186
+ return _jsx("span", { style: Object.assign(Object.assign({}, textStyle), { color: palette.eyebrow }), children: text });
187
+ };
188
+ export const PresetLayout = ({ preset, content, palette, backgroundImageUrl, eyebrowStyle, logo, fontFamily, fitText, }) => {
189
+ const settings = PRESET_SETTINGS[preset];
190
+ const { eyebrow, title, description } = content;
191
+ const isCentered = settings.align === 'center';
192
+ const titleBox = {
193
+ fontSize: settings.titleSize,
194
+ fontWeight: 500,
195
+ letterSpacing: settings.titleSize * TRACKING,
196
+ width: settings.titleWidth,
197
+ maxLines: MAX_TEXT_LINES,
198
+ };
199
+ const titleLines = title ? fitText(title, titleBox) : [];
200
+ const descriptionBox = {
201
+ fontSize: DESCRIPTION.size,
202
+ fontWeight: 400,
203
+ letterSpacing: DESCRIPTION.size * TRACKING,
204
+ width: settings.descriptionWidth,
205
+ maxLines: Math.min(MAX_TEXT_LINES, settings.textLines - titleLines.length),
206
+ };
207
+ const descriptionLines = description ? fitText(description, descriptionBox) : [];
208
+ const glowGradient = getGlowGradient(preset, palette);
209
+ return (_jsxs("div", { style: Object.assign(Object.assign({ position: 'relative', display: 'flex', width: `${OG_WIDTH}px`, height: `${OG_HEIGHT}px`, backgroundColor: palette.background }, (backgroundImageUrl
210
+ ? { backgroundImage: `url(${backgroundImageUrl})`, backgroundSize: '100% 100%' }
211
+ : glowGradient && { backgroundImage: glowGradient })), { fontFamily: getFontStack(fontFamily) }), children: [settings.frame && (_jsx("div", { style: {
212
+ position: 'absolute',
213
+ top: 0,
214
+ left: `${FRAME.left}px`,
215
+ width: `${FRAME.width}px`,
216
+ height: `${OG_HEIGHT}px`,
217
+ display: 'flex',
218
+ borderLeft: `1px solid ${palette.line}`,
219
+ borderRight: `1px solid ${palette.line}`,
220
+ }, children: FRAME.dividers.map((top) => (_jsx("div", { style: {
221
+ position: 'absolute',
222
+ left: 0,
223
+ top: `${top}px`,
224
+ width: '100%',
225
+ height: '1px',
226
+ backgroundColor: palette.line,
227
+ } }, top))) })), settings.panel && (_jsx("div", { style: Object.assign({ position: 'absolute', left: `${PANEL.left}px`, top: `${PANEL.top}px`, width: `${PANEL.width}px`, height: `${OG_HEIGHT - PANEL.top}px`, backgroundColor: palette.panel, borderTopLeftRadius: `${PANEL.radius}px`, borderTopRightRadius: `${PANEL.radius}px` }, (palette.panelBorder && {
228
+ borderTop: `1px solid ${palette.panelBorder}`,
229
+ borderLeft: `1px solid ${palette.panelBorder}`,
230
+ borderRight: `1px solid ${palette.panelBorder}`,
231
+ })) })), logo && (_jsx("div", { style: getLogoBoxStyle(settings.logo), children: _jsx(Logo, { logo: logo }) })), _jsxs("div", { style: Object.assign(Object.assign({ position: 'absolute', top: `${settings.contentTop}px`, left: `${settings.contentLeft}px` }, (isCentered && { width: `${OG_WIDTH}px` })), { display: 'flex', flexDirection: 'column', alignItems: isCentered ? 'center' : 'flex-start' }), children: [eyebrow && (_jsx(Eyebrow, { text: eyebrow, style: eyebrowStyle, palette: palette, settings: settings })), _jsxs("div", { style: {
232
+ display: 'flex',
233
+ flexDirection: 'column',
234
+ alignItems: isCentered ? 'center' : 'flex-start',
235
+ marginTop: eyebrow ? `${settings.eyebrowGap}px` : 0,
236
+ }, children: [titleLines.length > 0 && (_jsx(TextLines, { lines: titleLines, box: titleBox, lineHeight: settings.titleSize, align: settings.align, style: { color: palette.title } })), descriptionLines.length > 0 && (_jsx(TextLines, { lines: descriptionLines, box: descriptionBox, lineHeight: DESCRIPTION.lineHeight, align: settings.align, style: {
237
+ color: palette.description,
238
+ marginTop: titleLines.length > 0 ? `${settings.textGap}px` : 0,
239
+ } }))] })] })] }));
240
+ };
@@ -1,7 +1,10 @@
1
1
  import type { ReactElement } from 'react';
2
2
  import type { OgCardContent } from '../inputs/content.js';
3
- import type { OgLogo } from '../inputs/image.js';
3
+ import type { OgLogo, OgPresetLogo } from '../inputs/image.js';
4
+ import type { OgPalette } from '../inputs/palette.js';
5
+ import type { OgTextFitter } from '../inputs/text-fit.js';
4
6
  import type { OgTheme } from '../inputs/theme.js';
7
+ import type { OgEyebrowStyle, OgPreset } from '../types.js';
5
8
  export type OgLayoutProps = {
6
9
  content: OgCardContent;
7
10
  theme: OgTheme;
@@ -9,3 +12,14 @@ export type OgLayoutProps = {
9
12
  fontFamily: string;
10
13
  };
11
14
  export type OgLayoutComponent = (props: OgLayoutProps) => ReactElement;
15
+ export type OgPresetName = Exclude<OgPreset, 'legacy'>;
16
+ export type OgPresetLayoutProps = {
17
+ preset: OgPresetName;
18
+ content: OgCardContent;
19
+ palette: OgPalette;
20
+ backgroundImageUrl: string | undefined;
21
+ eyebrowStyle: OgEyebrowStyle;
22
+ logo: OgPresetLogo | null;
23
+ fontFamily: string;
24
+ fitText: OgTextFitter;
25
+ };
package/dist/render.js CHANGED
@@ -8,36 +8,52 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { jsx as _jsx } from "react/jsx-runtime";
11
- import { initWasm, Resvg } from '@resvg/resvg-wasm';
12
- import { readFile } from 'node:fs/promises';
13
- import { createRequire } from 'node:module';
11
+ import { Resvg } from '@resvg/resvg-wasm';
14
12
  import satori from 'satori';
15
- import { DEFAULT_LAYOUT, OG_HEIGHT, OG_WIDTH } from './constants.js';
13
+ import { OG_HEIGHT, OG_WIDTH } from './constants.js';
16
14
  import { buildOgCardContent } from './inputs/content.js';
17
- import { loadDefaultFont } from './inputs/fonts.js';
18
- import { buildOgLogo, toDataUri } from './inputs/image.js';
15
+ import { loadDefaultFont, loadMonoFont } from './inputs/fonts.js';
16
+ import { buildOgLogo, buildPresetLogo, toDataUri } from './inputs/image.js';
17
+ import { resolveOgPalette } from './inputs/palette.js';
18
+ import { initResvg } from './inputs/resvg.js';
19
+ import { createTextFitter } from './inputs/text-fit.js';
19
20
  import { resolveOgTheme } from './inputs/theme.js';
20
- import { OG_LAYOUTS } from './layouts/index.js';
21
- const require = createRequire(import.meta.url);
22
- let resvgReady;
23
- const initResvg = () => {
24
- resvgReady !== null && resvgReady !== void 0 ? resvgReady : (resvgReady = initWasm(readFile(require.resolve('@resvg/resvg-wasm/index_bg.wasm'))).catch((error) => {
25
- resvgReady = undefined;
26
- throw error;
27
- }));
28
- return resvgReady;
29
- };
21
+ import { LegacyLayout, PRESET_EYEBROW_STYLES, PresetLayout } from './layouts/index.js';
30
22
  const toSatoriFonts = ({ family, faces }) => faces.map(({ data, weight }) => ({ name: family, data, weight, style: 'normal' }));
23
+ const renderCardElement = (input, font, defaultFont, presetLogo) => {
24
+ var _a, _b;
25
+ const fontFamily = font.family;
26
+ const content = buildOgCardContent(input);
27
+ const preset = (_a = input.preset) !== null && _a !== void 0 ? _a : 'legacy';
28
+ const backgroundImageUrl = input.background ? toDataUri(input.background) : undefined;
29
+ if (preset === 'legacy') {
30
+ return (_jsx(LegacyLayout, { content: content, theme: resolveOgTheme(input.theme, backgroundImageUrl), logo: input.logo ? buildOgLogo(input.logo) : null, fontFamily: fontFamily }));
31
+ }
32
+ return (_jsx(PresetLayout, { preset: preset, content: content, palette: resolveOgPalette(input.theme), backgroundImageUrl: backgroundImageUrl, eyebrowStyle: (_b = input.eyebrowStyle) !== null && _b !== void 0 ? _b : PRESET_EYEBROW_STYLES[preset], logo: presetLogo, fontFamily: fontFamily, fitText: createTextFitter([font, defaultFont]) }));
33
+ };
31
34
  export function renderOgCardSvg(input) {
32
35
  return __awaiter(this, void 0, void 0, function* () {
33
- var _a, _b;
34
- const defaultFont = yield loadDefaultFont();
35
- const primaryFont = (_a = input.font) !== null && _a !== void 0 ? _a : defaultFont;
36
- const fonts = input.font ? [input.font, defaultFont] : [defaultFont];
37
- const Layout = OG_LAYOUTS[(_b = input.layout) !== null && _b !== void 0 ? _b : DEFAULT_LAYOUT];
38
- const backgroundImageUrl = input.background ? toDataUri(input.background) : undefined;
39
- const logo = input.logo ? buildOgLogo(input.logo) : null;
40
- return satori(_jsx(Layout, { content: buildOgCardContent(input), theme: resolveOgTheme(input.theme, backgroundImageUrl), logo: logo, fontFamily: primaryFont.family }), { width: OG_WIDTH, height: OG_HEIGHT, fonts: fonts.flatMap(toSatoriFonts) });
36
+ var _a;
37
+ const isPreset = ((_a = input.preset) !== null && _a !== void 0 ? _a : 'legacy') !== 'legacy';
38
+ const [defaultFont, monoFont] = yield Promise.all([
39
+ loadDefaultFont(),
40
+ isPreset ? loadMonoFont() : null,
41
+ isPreset && input.logo ? initResvg() : null,
42
+ ]);
43
+ const presetLogo = isPreset && input.logo ? buildPresetLogo(input.logo) : null;
44
+ const render = (font) => satori(renderCardElement(input, font !== null && font !== void 0 ? font : defaultFont, defaultFont, presetLogo), {
45
+ width: OG_WIDTH,
46
+ height: OG_HEIGHT,
47
+ fonts: [font, defaultFont, monoFont].filter((face) => face != null).flatMap(toSatoriFonts),
48
+ });
49
+ try {
50
+ return yield render(input.font);
51
+ }
52
+ catch (error) {
53
+ if (!input.font)
54
+ throw error;
55
+ return render(null);
56
+ }
41
57
  });
42
58
  }
43
59
  export function renderOgCardPng(input) {