@ogxjs/core 0.1.0

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 (82) hide show
  1. package/README.md +102 -0
  2. package/dist/builder.d.ts +75 -0
  3. package/dist/builder.d.ts.map +1 -0
  4. package/dist/builder.js +243 -0
  5. package/dist/cache.d.ts +32 -0
  6. package/dist/cache.d.ts.map +1 -0
  7. package/dist/cache.js +71 -0
  8. package/dist/css.d.ts +83 -0
  9. package/dist/css.d.ts.map +1 -0
  10. package/dist/css.js +1 -0
  11. package/dist/font-registry.d.ts +34 -0
  12. package/dist/font-registry.d.ts.map +1 -0
  13. package/dist/font-registry.js +59 -0
  14. package/dist/fonts/inter/inter-300.ttf +0 -0
  15. package/dist/fonts/inter/inter-400.ttf +0 -0
  16. package/dist/fonts/inter/inter-500.ttf +0 -0
  17. package/dist/fonts/inter/inter-600.ttf +0 -0
  18. package/dist/fonts/inter/inter-700.ttf +0 -0
  19. package/dist/fonts.d.ts +29 -0
  20. package/dist/fonts.d.ts.map +1 -0
  21. package/dist/fonts.js +90 -0
  22. package/dist/index.d.ts +18 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +19 -0
  25. package/dist/ogx.d.ts +16 -0
  26. package/dist/ogx.d.ts.map +1 -0
  27. package/dist/ogx.js +104 -0
  28. package/dist/presets/blog.d.ts +31 -0
  29. package/dist/presets/blog.d.ts.map +1 -0
  30. package/dist/presets/blog.js +98 -0
  31. package/dist/presets/docs.d.ts +23 -0
  32. package/dist/presets/docs.d.ts.map +1 -0
  33. package/dist/presets/docs.js +87 -0
  34. package/dist/presets/index.d.ts +20 -0
  35. package/dist/presets/index.d.ts.map +1 -0
  36. package/dist/presets/index.js +11 -0
  37. package/dist/presets/minimal.d.ts +20 -0
  38. package/dist/presets/minimal.d.ts.map +1 -0
  39. package/dist/presets/minimal.js +53 -0
  40. package/dist/presets/social.d.ts +29 -0
  41. package/dist/presets/social.d.ts.map +1 -0
  42. package/dist/presets/social.js +66 -0
  43. package/dist/render-png.d.ts +7 -0
  44. package/dist/render-png.d.ts.map +1 -0
  45. package/dist/render-png.js +19 -0
  46. package/dist/render-svg.d.ts +7 -0
  47. package/dist/render-svg.d.ts.map +1 -0
  48. package/dist/render-svg.js +123 -0
  49. package/dist/render.d.ts +10 -0
  50. package/dist/render.d.ts.map +1 -0
  51. package/dist/render.js +180 -0
  52. package/dist/tailwind/colors.d.ts +6 -0
  53. package/dist/tailwind/colors.d.ts.map +1 -0
  54. package/dist/tailwind/colors.js +281 -0
  55. package/dist/tailwind/index.d.ts +2 -0
  56. package/dist/tailwind/index.d.ts.map +1 -0
  57. package/dist/tailwind/index.js +1 -0
  58. package/dist/tailwind/parser.d.ts +15 -0
  59. package/dist/tailwind/parser.d.ts.map +1 -0
  60. package/dist/tailwind/parser.js +1037 -0
  61. package/dist/tailwind/scales.d.ts +26 -0
  62. package/dist/tailwind/scales.d.ts.map +1 -0
  63. package/dist/tailwind/scales.js +126 -0
  64. package/dist/tailwind/test-parser.d.ts +2 -0
  65. package/dist/tailwind/test-parser.d.ts.map +1 -0
  66. package/dist/tailwind/test-parser.js +10 -0
  67. package/dist/targets.d.ts +67 -0
  68. package/dist/targets.d.ts.map +1 -0
  69. package/dist/targets.js +25 -0
  70. package/dist/types.d.ts +173 -0
  71. package/dist/types.d.ts.map +1 -0
  72. package/dist/types.js +10 -0
  73. package/dist/utils/assets.d.ts +11 -0
  74. package/dist/utils/assets.d.ts.map +1 -0
  75. package/dist/utils/assets.js +35 -0
  76. package/dist/utils/color.d.ts +14 -0
  77. package/dist/utils/color.d.ts.map +1 -0
  78. package/dist/utils/color.js +68 -0
  79. package/dist/utils/text.d.ts +14 -0
  80. package/dist/utils/text.d.ts.map +1 -0
  81. package/dist/utils/text.js +50 -0
  82. package/package.json +71 -0
@@ -0,0 +1,20 @@
1
+ import type { OGXElement, Preset } from "../types";
2
+ export interface MinimalPresetProps {
3
+ /** Main text */
4
+ title: string;
5
+ /** Secondary text */
6
+ subtitle?: string;
7
+ /** Background color (Tailwind class or hex) */
8
+ background?: string;
9
+ /** Text color (Tailwind class or hex) */
10
+ textColor?: string;
11
+ /** Custom slot overrides */
12
+ slots?: {
13
+ content?: OGXElement;
14
+ };
15
+ }
16
+ /**
17
+ * Minimal preset - ultra clean, just text
18
+ */
19
+ export declare const minimalPreset: Preset<MinimalPresetProps>;
20
+ //# sourceMappingURL=minimal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"minimal.d.ts","sourceRoot":"","sources":["../../src/presets/minimal.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEnD,MAAM,WAAW,kBAAkB;IACjC,gBAAgB;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+CAA+C;IAC/C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yCAAyC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4BAA4B;IAC5B,KAAK,CAAC,EAAE;QACN,OAAO,CAAC,EAAE,UAAU,CAAC;KACtB,CAAC;CACH;AAED;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,kBAAkB,CAqEpD,CAAC"}
@@ -0,0 +1,53 @@
1
+ import { absolute, h1, p, stack } from "../builder";
2
+ /**
3
+ * Minimal preset - ultra clean, just text
4
+ */
5
+ export const minimalPreset = (props) => {
6
+ const { title, subtitle, background = "bg-zinc-950", textColor = "text-white", slots = {}, } = props;
7
+ const bgClass = background.startsWith("bg-")
8
+ ? background
9
+ : `bg-[${background}]`;
10
+ const textClass = textColor.startsWith("text-")
11
+ ? textColor
12
+ : `text-[${textColor}]`;
13
+ return stack([
14
+ "w-full",
15
+ "h-full",
16
+ "items-center",
17
+ "justify-center",
18
+ "p-24",
19
+ "relative",
20
+ bgClass,
21
+ ], [
22
+ // Subtle Background Texture
23
+ absolute([
24
+ "inset-0 opacity-[0.03]",
25
+ background.includes("light") || background === "bg-white"
26
+ ? "bg-grid-black-32"
27
+ : "bg-grid-white-32",
28
+ ]),
29
+ absolute(["bg-grain/5"]), // Quality Boost: Dithering
30
+ // Content
31
+ stack("items-center justify-center relative", slots.content ?? [
32
+ h1([
33
+ "text-9xl",
34
+ "font-black",
35
+ "text-center",
36
+ "tracking-tightest", // Quality Boost
37
+ "leading-none",
38
+ textClass,
39
+ ], title),
40
+ subtitle
41
+ ? p([
42
+ "text-3xl",
43
+ "text-center",
44
+ "mt-10",
45
+ "opacity-40",
46
+ "font-medium",
47
+ "tracking-wide",
48
+ textClass,
49
+ ], subtitle.toUpperCase())
50
+ : null,
51
+ ]),
52
+ ]);
53
+ };
@@ -0,0 +1,29 @@
1
+ import type { OGXElement, Preset } from "../types";
2
+ export interface SocialPresetProps {
3
+ /** Main title or headline */
4
+ title: string;
5
+ /** Handle or username */
6
+ handle?: string;
7
+ /** Avatar URL */
8
+ avatar?: string;
9
+ /** Brand/app name */
10
+ brand?: string;
11
+ /** Brand logo URL */
12
+ logo?: string;
13
+ /** Gradient direction for background */
14
+ gradient?: "to-r" | "to-br" | "to-b" | "to-bl";
15
+ /** From color (hex) */
16
+ fromColor?: string;
17
+ /** To color (hex) */
18
+ toColor?: string;
19
+ /** Custom slot overrides */
20
+ slots?: {
21
+ header?: OGXElement;
22
+ footer?: OGXElement;
23
+ };
24
+ }
25
+ /**
26
+ * Social preset - card-style for social media
27
+ */
28
+ export declare const socialPreset: Preset<SocialPresetProps>;
29
+ //# sourceMappingURL=social.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"social.d.ts","sourceRoot":"","sources":["../../src/presets/social.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEnD,MAAM,WAAW,iBAAiB;IAChC,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,yBAAyB;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iBAAiB;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qBAAqB;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;IAC/C,uBAAuB;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qBAAqB;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4BAA4B;IAC5B,KAAK,CAAC,EAAE;QACN,MAAM,CAAC,EAAE,UAAU,CAAC;QACpB,MAAM,CAAC,EAAE,UAAU,CAAC;KACrB,CAAC;CACH;AAED;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,iBAAiB,CAkGlD,CAAC"}
@@ -0,0 +1,66 @@
1
+ import { absolute, h1, img, row, span, stack } from "../builder";
2
+ /**
3
+ * Social preset - card-style for social media
4
+ */
5
+ export const socialPreset = (props) => {
6
+ const { title, handle, avatar, brand, logo, gradient = "to-br", fromColor = "oklch(65% 0.25 260)", // Vibrant Indigo-ish
7
+ toColor = "oklch(60% 0.3 300)", // Vibrant Purple-ish
8
+ slots = {}, } = props;
9
+ const gradientStyle = `linear-gradient(${gradient === "to-r"
10
+ ? "90deg"
11
+ : gradient === "to-br"
12
+ ? "135deg"
13
+ : gradient === "to-b"
14
+ ? "180deg"
15
+ : "225deg"}, ${fromColor}, ${toColor})`;
16
+ return stack(["w-full", "h-full", "p-20", "relative", "overflow-hidden"], [
17
+ // Background Gradient
18
+ absolute("", null, { style: { backgroundImage: gradientStyle } }),
19
+ // Subtle Background Texture (Grain/Noise-like mesh)
20
+ absolute(["inset-0 opacity-20", "bg-grid-white/5-32"]),
21
+ absolute(["bg-grain/15"]), // Quality Boost: Dithering
22
+ // Content Layout
23
+ stack("flex-1 justify-between relative", [
24
+ // Header
25
+ slots.header ??
26
+ row("items-center justify-between", [
27
+ row("items-center gap-4", [
28
+ logo ? img(logo, "w-12 h-12 rounded-xl bg-zinc-950") : null,
29
+ brand
30
+ ? span(["text-2xl font-black text-white tracking-tighter"], brand.toUpperCase())
31
+ : null,
32
+ ]),
33
+ // Subtle platform indicator
34
+ span(["text-white/30 text-xs font-bold tracking-widest"], "SOCIAL CARD"),
35
+ ]),
36
+ // Main content
37
+ stack("gap-6 overflow-hidden", [
38
+ h1([
39
+ "text-7xl",
40
+ "font-black",
41
+ "text-white",
42
+ "leading-[1.05]",
43
+ "tracking-tight", // Quality Boost
44
+ ], title),
45
+ ]),
46
+ // Footer with user info (Clean Glass Badge)
47
+ slots.footer ??
48
+ (handle || avatar
49
+ ? row([
50
+ "items-center gap-4 p-3 pr-6 rounded-full self-start shadow-premium",
51
+ "bg-black/10 border border-white/10 backdrop-blur-md",
52
+ ], [
53
+ avatar
54
+ ? img(avatar, "w-10 h-10 rounded-full border border-white/20 shadow-sm")
55
+ : null,
56
+ handle
57
+ ? span([
58
+ "text-xl font-bold text-white/90 tracking-tight",
59
+ "w-fit truncate shrink-0",
60
+ ], handle)
61
+ : null,
62
+ ])
63
+ : null),
64
+ ]),
65
+ ]);
66
+ };
@@ -0,0 +1,7 @@
1
+ import type { OGXElement, RenderOptions } from "./types";
2
+ /**
3
+ * Render an OGX element to PNG image
4
+ * Node.js only: uses @resvg/resvg-js for SVG → PNG conversion
5
+ */
6
+ export declare function render(element: OGXElement, options?: RenderOptions): Promise<Uint8Array>;
7
+ //# sourceMappingURL=render-png.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-png.d.ts","sourceRoot":"","sources":["../src/render-png.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAGzD;;;GAGG;AACH,wBAAsB,MAAM,CAC3B,OAAO,EAAE,UAAU,EACnB,OAAO,GAAE,aAAkB,GACzB,OAAO,CAAC,UAAU,CAAC,CAiBrB"}
@@ -0,0 +1,19 @@
1
+ import { renderToSVG } from "./render-svg";
2
+ /**
3
+ * Render an OGX element to PNG image
4
+ * Node.js only: uses @resvg/resvg-js for SVG → PNG conversion
5
+ */
6
+ export async function render(element, options = {}) {
7
+ const svg = await renderToSVG(element, options);
8
+ const width = options.width ?? 1200;
9
+ const resvgPkg = "@resvg/resvg-js";
10
+ const { Resvg } = await import(resvgPkg);
11
+ const resvg = new Resvg(svg, {
12
+ fitTo: {
13
+ mode: "width",
14
+ value: width,
15
+ },
16
+ });
17
+ const pngData = resvg.render();
18
+ return pngData.asPng();
19
+ }
@@ -0,0 +1,7 @@
1
+ import type { OGXElement, RenderOptions } from "./types";
2
+ /**
3
+ * Render an OGX element to SVG string
4
+ * Browser-safe: uses only Satori (no Node.js dependencies)
5
+ */
6
+ export declare function renderToSVG(element: OGXElement, options?: RenderOptions): Promise<string>;
7
+ //# sourceMappingURL=render-svg.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-svg.d.ts","sourceRoot":"","sources":["../src/render-svg.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAEX,UAAU,EACV,aAAa,EAEb,MAAM,SAAS,CAAC;AAKjB;;;GAGG;AACH,wBAAsB,WAAW,CAChC,OAAO,EAAE,UAAU,EACnB,OAAO,GAAE,aAAkB,GACzB,OAAO,CAAC,MAAM,CAAC,CA2CjB"}
@@ -0,0 +1,123 @@
1
+ import satori from "satori";
2
+ import { fontRegistry } from "./font-registry";
3
+ import { parseTailwind } from "./tailwind";
4
+ import { getPlatformDimensions } from "./targets";
5
+ const DEFAULT_WIDTH = 1200;
6
+ const DEFAULT_HEIGHT = 630;
7
+ /**
8
+ * Render an OGX element to SVG string
9
+ * Browser-safe: uses only Satori (no Node.js dependencies)
10
+ */
11
+ export async function renderToSVG(element, options = {}) {
12
+ const platformDims = options.platform
13
+ ? getPlatformDimensions(options.platform)
14
+ : null;
15
+ const { width = platformDims?.width ?? DEFAULT_WIDTH, height = platformDims?.height ?? DEFAULT_HEIGHT, debug = false, } = options;
16
+ let resolvedFonts = options.fonts ?? [];
17
+ if (resolvedFonts.length === 0) {
18
+ resolvedFonts = fontRegistry.getFonts();
19
+ }
20
+ if (resolvedFonts.length === 0) {
21
+ const { loadInterFromUrl } = await import("./fonts");
22
+ resolvedFonts = [
23
+ await loadInterFromUrl(300),
24
+ await loadInterFromUrl(400),
25
+ await loadInterFromUrl(500),
26
+ await loadInterFromUrl(600),
27
+ await loadInterFromUrl(700),
28
+ ];
29
+ }
30
+ const fullOptions = {
31
+ width,
32
+ height,
33
+ fonts: resolvedFonts,
34
+ debug,
35
+ theme: options.theme,
36
+ colorScheme: options.colorScheme,
37
+ };
38
+ const transformedElement = transformElement(element, debug, fullOptions);
39
+ return await satori(transformedElement, {
40
+ width,
41
+ height,
42
+ fonts: resolvedFonts.map(fontToSatoriFont),
43
+ debug,
44
+ });
45
+ }
46
+ /**
47
+ * Transform OGX element tree to Satori-compatible format
48
+ * Processes `tw` props into inline styles
49
+ */
50
+ function transformElement(element, debug, options) {
51
+ if (element === null || element === undefined) {
52
+ return null;
53
+ }
54
+ if (typeof element === "string" || typeof element === "number") {
55
+ return element;
56
+ }
57
+ const { tw, style, children, ...restProps } = element.props;
58
+ let mergedStyle;
59
+ if (tw) {
60
+ const twStyles = parseTailwind(tw, options.theme, options.colorScheme);
61
+ mergedStyle = style ? { ...twStyles, ...style } : twStyles;
62
+ }
63
+ else {
64
+ mergedStyle = style ? { ...style } : {};
65
+ }
66
+ // Default resets for Satori (only if not already set by Tailwind)
67
+ if (mergedStyle.margin === undefined)
68
+ mergedStyle.margin = 0;
69
+ if (mergedStyle.padding === undefined &&
70
+ mergedStyle.paddingTop === undefined &&
71
+ mergedStyle.paddingBottom === undefined &&
72
+ mergedStyle.paddingLeft === undefined &&
73
+ mergedStyle.paddingRight === undefined) {
74
+ mergedStyle.padding = 0;
75
+ }
76
+ // Apply default fontFamily if not specified
77
+ if (!mergedStyle.fontFamily) {
78
+ mergedStyle.fontFamily = "Inter";
79
+ }
80
+ // h1/p tags have default margins in many browsers, reset them
81
+ if (element.type === "h1" || element.type === "p") {
82
+ mergedStyle.margin = 0;
83
+ }
84
+ // Add debug border if enabled
85
+ if (debug) {
86
+ mergedStyle.border = "1px solid rgba(255, 0, 0, 0.3)";
87
+ }
88
+ // Transform children recursively
89
+ let transformedChildren = null;
90
+ if (children !== null && children !== undefined) {
91
+ if (Array.isArray(children)) {
92
+ const filtered = children.filter((c) => c !== null && c !== undefined);
93
+ if (filtered.length === 1) {
94
+ transformedChildren = transformElement(filtered[0], debug, options);
95
+ }
96
+ else {
97
+ transformedChildren = filtered.map((c) => transformElement(c, debug, options));
98
+ }
99
+ }
100
+ else {
101
+ transformedChildren = transformElement(children, debug, options);
102
+ }
103
+ }
104
+ return {
105
+ type: element.type,
106
+ props: {
107
+ ...restProps,
108
+ style: mergedStyle,
109
+ children: transformedChildren,
110
+ },
111
+ };
112
+ }
113
+ /**
114
+ * Convert OGX FontConfig to Satori font format
115
+ */
116
+ function fontToSatoriFont(font) {
117
+ return {
118
+ name: font.name,
119
+ data: font.data,
120
+ weight: font.weight ?? 400,
121
+ style: font.style ?? "normal",
122
+ };
123
+ }
@@ -0,0 +1,10 @@
1
+ import type { OGXElement, RenderOptions } from "./types";
2
+ /**
3
+ * Render an OGX element to PNG image
4
+ */
5
+ export declare function render(element: OGXElement, options?: RenderOptions): Promise<Uint8Array>;
6
+ /**
7
+ * Render an OGX element to SVG string
8
+ */
9
+ export declare function renderToSVG(element: OGXElement, options?: RenderOptions): Promise<string>;
10
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAEX,UAAU,EACV,aAAa,EAEb,MAAM,SAAS,CAAC;AAKjB;;GAEG;AACH,wBAAsB,MAAM,CAC3B,OAAO,EAAE,UAAU,EACnB,OAAO,GAAE,aAAkB,GACzB,OAAO,CAAC,UAAU,CAAC,CA6DrB;AAED;;GAEG;AACH,wBAAsB,WAAW,CAChC,OAAO,EAAE,UAAU,EACnB,OAAO,GAAE,aAAkB,GACzB,OAAO,CAAC,MAAM,CAAC,CA6CjB"}
package/dist/render.js ADDED
@@ -0,0 +1,180 @@
1
+ import satori from "satori";
2
+ import { fontRegistry } from "./font-registry";
3
+ import { parseTailwind } from "./tailwind";
4
+ import { getPlatformDimensions } from "./targets";
5
+ const DEFAULT_WIDTH = 1200;
6
+ const DEFAULT_HEIGHT = 630;
7
+ /**
8
+ * Render an OGX element to PNG image
9
+ */
10
+ export async function render(element, options = {}) {
11
+ // Resolve platform dimensions
12
+ const platformDims = options.platform
13
+ ? getPlatformDimensions(options.platform)
14
+ : null;
15
+ const { width = platformDims?.width ?? DEFAULT_WIDTH, height = platformDims?.height ?? DEFAULT_HEIGHT, debug = false, } = options;
16
+ // Resolve fonts
17
+ let resolvedFonts = options.fonts ?? [];
18
+ if (resolvedFonts.length === 0) {
19
+ resolvedFonts = fontRegistry.getFonts();
20
+ }
21
+ if (resolvedFonts.length === 0) {
22
+ const { loadInterFromUrl } = await import("./fonts");
23
+ resolvedFonts = [
24
+ await loadInterFromUrl(300),
25
+ await loadInterFromUrl(400),
26
+ await loadInterFromUrl(500),
27
+ await loadInterFromUrl(600),
28
+ await loadInterFromUrl(700),
29
+ ];
30
+ }
31
+ const fullOptions = {
32
+ width,
33
+ height,
34
+ fonts: resolvedFonts,
35
+ debug,
36
+ theme: options.theme,
37
+ colorScheme: options.colorScheme,
38
+ };
39
+ // Transform the element tree to process tw props
40
+ const transformedElement = transformElement(element, debug, fullOptions);
41
+ // Generate SVG with Satori
42
+ const svg = await satori(transformedElement, {
43
+ width,
44
+ height,
45
+ fonts: resolvedFonts.map(fontToSatoriFont),
46
+ debug,
47
+ });
48
+ // Dynamic import resvg to avoid bundler resolution issues with native binaries
49
+ const resvgPkg = "@resvg/resvg-js";
50
+ const { Resvg } = await import(resvgPkg);
51
+ // Convert SVG to PNG with resvg
52
+ const resvg = new Resvg(svg, {
53
+ fitTo: {
54
+ mode: "width",
55
+ value: width,
56
+ },
57
+ });
58
+ const pngData = resvg.render();
59
+ return pngData.asPng();
60
+ }
61
+ /**
62
+ * Render an OGX element to SVG string
63
+ */
64
+ export async function renderToSVG(element, options = {}) {
65
+ // Resolve platform dimensions
66
+ const platformDims = options.platform
67
+ ? getPlatformDimensions(options.platform)
68
+ : null;
69
+ const { width = platformDims?.width ?? DEFAULT_WIDTH, height = platformDims?.height ?? DEFAULT_HEIGHT, debug = false, } = options;
70
+ // Resolve fonts
71
+ let resolvedFonts = options.fonts ?? [];
72
+ if (resolvedFonts.length === 0) {
73
+ resolvedFonts = fontRegistry.getFonts();
74
+ }
75
+ if (resolvedFonts.length === 0) {
76
+ const { loadInterFromUrl } = await import("./fonts");
77
+ resolvedFonts = [
78
+ await loadInterFromUrl(300),
79
+ await loadInterFromUrl(400),
80
+ await loadInterFromUrl(500),
81
+ await loadInterFromUrl(600),
82
+ await loadInterFromUrl(700),
83
+ ];
84
+ }
85
+ const fullOptions = {
86
+ width,
87
+ height,
88
+ fonts: resolvedFonts,
89
+ debug,
90
+ theme: options.theme,
91
+ colorScheme: options.colorScheme,
92
+ };
93
+ const transformedElement = transformElement(element, debug, fullOptions);
94
+ return await satori(transformedElement, {
95
+ width,
96
+ height,
97
+ fonts: resolvedFonts.map(fontToSatoriFont),
98
+ debug,
99
+ });
100
+ }
101
+ /**
102
+ * Transform OGX element tree to Satori-compatible format
103
+ * Processes `tw` props into inline styles
104
+ */
105
+ function transformElement(element, debug, options) {
106
+ // Handle primitives
107
+ if (element === null || element === undefined) {
108
+ return null;
109
+ }
110
+ if (typeof element === "string" || typeof element === "number") {
111
+ return element;
112
+ }
113
+ const { tw, style, children, ...restProps } = element.props;
114
+ // Parse Tailwind classes and merge with inline styles
115
+ let mergedStyle;
116
+ if (tw) {
117
+ const twStyles = parseTailwind(tw, options.theme, options.colorScheme);
118
+ mergedStyle = style ? { ...twStyles, ...style } : twStyles;
119
+ }
120
+ else {
121
+ mergedStyle = style ? { ...style } : {};
122
+ }
123
+ // Default resets for Satori (only if not already set by Tailwind)
124
+ if (mergedStyle.margin === undefined)
125
+ mergedStyle.margin = 0;
126
+ if (mergedStyle.padding === undefined &&
127
+ mergedStyle.paddingTop === undefined &&
128
+ mergedStyle.paddingBottom === undefined &&
129
+ mergedStyle.paddingLeft === undefined &&
130
+ mergedStyle.paddingRight === undefined) {
131
+ mergedStyle.padding = 0;
132
+ }
133
+ // Apply default fontFamily if not specified
134
+ if (!mergedStyle.fontFamily) {
135
+ mergedStyle.fontFamily = "Inter";
136
+ }
137
+ // h1/p tags have default margins in many browsers, reset them
138
+ if (element.type === "h1" || element.type === "p") {
139
+ mergedStyle.margin = 0;
140
+ }
141
+ // Add debug border if enabled
142
+ if (debug) {
143
+ mergedStyle.border = "1px solid rgba(255, 0, 0, 0.3)";
144
+ }
145
+ // Transform children recursively
146
+ let transformedChildren = null;
147
+ if (children !== null && children !== undefined) {
148
+ if (Array.isArray(children)) {
149
+ const filtered = children.filter((c) => c !== null && c !== undefined);
150
+ if (filtered.length === 1) {
151
+ transformedChildren = transformElement(filtered[0], debug, options);
152
+ }
153
+ else {
154
+ transformedChildren = filtered.map((c) => transformElement(c, debug, options));
155
+ }
156
+ }
157
+ else {
158
+ transformedChildren = transformElement(children, debug, options);
159
+ }
160
+ }
161
+ return {
162
+ type: element.type,
163
+ props: {
164
+ ...restProps,
165
+ style: mergedStyle,
166
+ children: transformedChildren,
167
+ },
168
+ };
169
+ }
170
+ /**
171
+ * Convert OGX FontConfig to Satori font format
172
+ */
173
+ function fontToSatoriFont(font) {
174
+ return {
175
+ name: font.name,
176
+ data: font.data,
177
+ weight: font.weight ?? 400,
178
+ style: font.style ?? "normal",
179
+ };
180
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Tailwind color palette
3
+ * https://tailwindcss.com/docs/customizing-colors
4
+ */
5
+ export declare const colors: Record<string, string>;
6
+ //# sourceMappingURL=colors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colors.d.ts","sourceRoot":"","sources":["../../src/tailwind/colors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CA2SzC,CAAC"}