@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,59 @@
1
+ /**
2
+ * Global registry for fonts to avoid reloading and simplify configuration
3
+ */
4
+ export class FontRegistry {
5
+ static instance;
6
+ fonts = [];
7
+ constructor() { }
8
+ static getInstance() {
9
+ if (!FontRegistry.instance) {
10
+ FontRegistry.instance = new FontRegistry();
11
+ }
12
+ return FontRegistry.instance;
13
+ }
14
+ /**
15
+ * Register one or more fonts to be available globally
16
+ */
17
+ register(font) {
18
+ const fontList = Array.isArray(font) ? font : [font];
19
+ for (const f of fontList) {
20
+ const exists = this.fonts.some((ext) => ext.name === f.name &&
21
+ ext.weight === f.weight &&
22
+ ext.style === f.style);
23
+ if (!exists) {
24
+ this.fonts.push(f);
25
+ }
26
+ }
27
+ }
28
+ /**
29
+ * Helper to register Inter fonts with specified weights (local files)
30
+ * Only works in Bun/Node without bundlers
31
+ */
32
+ async registerInter(weights = [400, 700]) {
33
+ const { loadInterFont } = await import("./fonts");
34
+ const fonts = await Promise.all(weights.map((w) => loadInterFont(w)));
35
+ this.register(fonts);
36
+ }
37
+ /**
38
+ * Helper to register Inter fonts from Google Fonts CDN
39
+ * Works with all bundlers (Next.js, Vite, etc.)
40
+ */
41
+ async registerInterFromUrl(weights = [400, 700]) {
42
+ const { loadInterFromUrl } = await import("./fonts");
43
+ const fonts = await Promise.all(weights.map((w) => loadInterFromUrl(w)));
44
+ this.register(fonts);
45
+ }
46
+ /**
47
+ * Get all registered fonts
48
+ */
49
+ getFonts() {
50
+ return [...this.fonts];
51
+ }
52
+ /**
53
+ * Clear the registry
54
+ */
55
+ clear() {
56
+ this.fonts = [];
57
+ }
58
+ }
59
+ export const fontRegistry = FontRegistry.getInstance();
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,29 @@
1
+ import type { FontConfig } from "./types";
2
+ /**
3
+ * Load a font from a file path (Node.js/Bun)
4
+ */
5
+ export declare function loadFont(path: string): Promise<ArrayBuffer>;
6
+ /**
7
+ * Load a font from a URL
8
+ */
9
+ export declare function loadFontFromUrl(url: string): Promise<ArrayBuffer>;
10
+ /**
11
+ * Create a FontConfig from an ArrayBuffer
12
+ */
13
+ export declare function createFont(name: string, data: ArrayBuffer, options?: {
14
+ weight?: FontConfig["weight"];
15
+ style?: FontConfig["style"];
16
+ }): FontConfig;
17
+ /**
18
+ * Load Inter font from Google Fonts CDN
19
+ * Works with all bundlers (Next.js, Vite, etc.)
20
+ * Supports weights: 300 (Light), 400 (Regular), 500 (Medium), 600 (SemiBold), 700 (Bold)
21
+ */
22
+ export declare function loadInterFromUrl(weight?: 300 | 400 | 500 | 600 | 700): Promise<FontConfig>;
23
+ /**
24
+ * Load Inter font from local file
25
+ * Only works in Bun/Node without bundlers
26
+ * Supports weights: 300 (Light), 400 (Regular), 500 (Medium), 600 (SemiBold), 700 (Bold)
27
+ */
28
+ export declare function loadInterFont(weight?: 300 | 400 | 500 | 600 | 700): Promise<FontConfig>;
29
+ //# sourceMappingURL=fonts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fonts.d.ts","sourceRoot":"","sources":["../src/fonts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAI1C;;GAEG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAajE;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAYvE;AAED;;GAEG;AACH,wBAAgB,UAAU,CACzB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,WAAW,EACjB,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;IAAC,KAAK,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CAAA;CAAO,GAC1E,UAAU,CAOZ;AAWD;;;;GAIG;AACH,wBAAsB,gBAAgB,CACrC,MAAM,GAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAS,GACvC,OAAO,CAAC,UAAU,CAAC,CAIrB;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAClC,MAAM,GAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAS,GACvC,OAAO,CAAC,UAAU,CAAC,CAkCrB"}
package/dist/fonts.js ADDED
@@ -0,0 +1,90 @@
1
+ const fontCache = new Map();
2
+ /**
3
+ * Load a font from a file path (Node.js/Bun)
4
+ */
5
+ export async function loadFont(path) {
6
+ if (fontCache.has(path)) {
7
+ return fontCache.get(path);
8
+ }
9
+ const fs = await import("node:fs/promises");
10
+ const buffer = await fs.readFile(path);
11
+ const data = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
12
+ fontCache.set(path, data);
13
+ return data;
14
+ }
15
+ /**
16
+ * Load a font from a URL
17
+ */
18
+ export async function loadFontFromUrl(url) {
19
+ if (fontCache.has(url)) {
20
+ return fontCache.get(url);
21
+ }
22
+ const response = await fetch(url);
23
+ if (!response.ok) {
24
+ throw new Error(`Failed to load font from ${url}: ${response.statusText}`);
25
+ }
26
+ const data = await response.arrayBuffer();
27
+ fontCache.set(url, data);
28
+ return data;
29
+ }
30
+ /**
31
+ * Create a FontConfig from an ArrayBuffer
32
+ */
33
+ export function createFont(name, data, options = {}) {
34
+ return {
35
+ name,
36
+ data,
37
+ weight: options.weight ?? 400,
38
+ style: options.style ?? "normal",
39
+ };
40
+ }
41
+ // Google Fonts CDN URLs for Inter
42
+ const INTER_URLS = {
43
+ 300: "https://fonts.bunny.net/inter/files/inter-latin-300-normal.woff",
44
+ 400: "https://fonts.bunny.net/inter/files/inter-latin-400-normal.woff",
45
+ 500: "https://fonts.bunny.net/inter/files/inter-latin-500-normal.woff",
46
+ 600: "https://fonts.bunny.net/inter/files/inter-latin-600-normal.woff",
47
+ 700: "https://fonts.bunny.net/inter/files/inter-latin-700-normal.woff",
48
+ };
49
+ /**
50
+ * Load Inter font from Google Fonts CDN
51
+ * Works with all bundlers (Next.js, Vite, etc.)
52
+ * Supports weights: 300 (Light), 400 (Regular), 500 (Medium), 600 (SemiBold), 700 (Bold)
53
+ */
54
+ export async function loadInterFromUrl(weight = 400) {
55
+ const url = INTER_URLS[weight] ?? INTER_URLS[400];
56
+ const data = await loadFontFromUrl(url);
57
+ return createFont("Inter", data, { weight });
58
+ }
59
+ /**
60
+ * Load Inter font from local file
61
+ * Only works in Bun/Node without bundlers
62
+ * Supports weights: 300 (Light), 400 (Regular), 500 (Medium), 600 (SemiBold), 700 (Bold)
63
+ */
64
+ export async function loadInterFont(weight = 400) {
65
+ if (typeof globalThis !== "undefined" &&
66
+ globalThis.window !== undefined) {
67
+ throw new Error("loadInterFont is only available in Node.js/Bun environments.");
68
+ }
69
+ const base = "./fonts/inter";
70
+ const file = `inter-${weight}.ttf`;
71
+ const fontFile = [base, file].join("/");
72
+ // Hide import.meta.url from bundlers like Webpack/Next.js using a dynamic check
73
+ // this prevents the "Can't resolve <dynamic>" error during build
74
+ let metaUrl;
75
+ try {
76
+ const meta = import.meta;
77
+ if (meta?.url) {
78
+ metaUrl = String(meta.url);
79
+ }
80
+ }
81
+ catch {
82
+ // Fallback
83
+ }
84
+ if (!metaUrl) {
85
+ throw new Error("Could not determine base URL for local font loading.");
86
+ }
87
+ const path = new URL(fontFile, metaUrl).pathname;
88
+ const data = await loadFont(path);
89
+ return createFont("Inter", data, { weight });
90
+ }
@@ -0,0 +1,18 @@
1
+ export { absolute, badge, card, div, fluent, footer, grid, h, h1, h2, header, img, imgFromUrl, main, p, row, spacer, span, stack, svgFromContent, } from "./builder";
2
+ export { fontRegistry } from "./font-registry";
3
+ export { createFont, loadFont, loadFontFromUrl, loadInterFont, loadInterFromUrl, } from "./fonts";
4
+ export { ogx, ogxToSVG } from "./ogx";
5
+ export { render } from "./render-png";
6
+ export { renderToSVG } from "./render-svg";
7
+ export { parseTailwind } from "./tailwind";
8
+ export { blogPreset, docsPreset, minimalPreset, presets, socialPreset, } from "./presets";
9
+ export type { BlogPresetProps, DocsPresetProps, MinimalPresetProps, SocialPresetProps, } from "./presets";
10
+ export { getPlatformDimensions } from "./targets";
11
+ export type { Platform } from "./targets";
12
+ export { snapshotCache } from "./cache";
13
+ export { loadAsset, toDataUri } from "./utils/assets";
14
+ export { calculateFittingFontSize } from "./utils/text";
15
+ export type { FitTextOptions } from "./utils/text";
16
+ export type { CSSProperties } from "./css";
17
+ export type { FontConfig, OGXBaseConfig, OGXChildren, OGXConfig, OGXElement, OGXElementProps, Preset, PresetName, PresetProps, RenderOptions, } from "./types";
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACN,QAAQ,EACR,KAAK,EACL,IAAI,EACJ,GAAG,EACH,MAAM,EACN,MAAM,EACN,IAAI,EACJ,CAAC,EACD,EAAE,EACF,EAAE,EACF,MAAM,EACN,GAAG,EACH,UAAU,EACV,IAAI,EACJ,CAAC,EACD,GAAG,EACH,MAAM,EACN,IAAI,EACJ,KAAK,EACL,cAAc,GACd,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EACN,UAAU,EACV,QAAQ,EACR,eAAe,EACf,aAAa,EACb,gBAAgB,GAChB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAG3C,OAAO,EACN,UAAU,EACV,UAAU,EACV,aAAa,EACb,OAAO,EACP,YAAY,GACZ,MAAM,WAAW,CAAC;AACnB,YAAY,EACX,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,iBAAiB,GACjB,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAClD,YAAY,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAG1C,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AACxD,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAGnD,YAAY,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAC3C,YAAY,EACX,UAAU,EACV,aAAa,EACb,WAAW,EACX,SAAS,EACT,UAAU,EACV,eAAe,EACf,MAAM,EACN,UAAU,EACV,WAAW,EACX,aAAa,GACb,MAAM,SAAS,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,19 @@
1
+ // @ogxjs/core - Main exports
2
+ // Builder - Element construction
3
+ export { absolute, badge, card, div, fluent, footer, grid, h, h1, h2, header, img, imgFromUrl, main, p, row, spacer, span, stack, svgFromContent, } from "./builder";
4
+ // Fonts - Loading and registration
5
+ export { fontRegistry } from "./font-registry";
6
+ export { createFont, loadFont, loadFontFromUrl, loadInterFont, loadInterFromUrl, } from "./fonts";
7
+ // Rendering - SVG and PNG generation
8
+ export { ogx, ogxToSVG } from "./ogx";
9
+ export { render } from "./render-png";
10
+ export { renderToSVG } from "./render-svg";
11
+ export { parseTailwind } from "./tailwind";
12
+ // Presets - Ready-to-use templates
13
+ export { blogPreset, docsPreset, minimalPreset, presets, socialPreset, } from "./presets";
14
+ // Platform - Target dimensions
15
+ export { getPlatformDimensions } from "./targets";
16
+ // Utilities
17
+ export { snapshotCache } from "./cache";
18
+ export { loadAsset, toDataUri } from "./utils/assets";
19
+ export { calculateFittingFontSize } from "./utils/text";
package/dist/ogx.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ import type { OGXConfig, Platform, PresetName } from "./types";
2
+ /**
3
+ * Generate an OG image using a preset
4
+ */
5
+ export declare function ogx<T extends PresetName>(config: OGXConfig<T> & {
6
+ platform: Platform[];
7
+ }): Promise<Record<Platform, Uint8Array>>;
8
+ export declare function ogx<T extends PresetName>(config: OGXConfig<T>): Promise<Uint8Array>;
9
+ /**
10
+ * Generate an OG image as SVG using a preset
11
+ */
12
+ export declare function ogxToSVG<T extends PresetName>(config: OGXConfig<T> & {
13
+ platform: Platform[];
14
+ }): Promise<Record<Platform, string>>;
15
+ export declare function ogxToSVG<T extends PresetName>(config: OGXConfig<T>): Promise<string>;
16
+ //# sourceMappingURL=ogx.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ogx.d.ts","sourceRoot":"","sources":["../src/ogx.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAc,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAG3E;;GAEG;AACH,wBAAsB,GAAG,CAAC,CAAC,SAAS,UAAU,EAC7C,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG;IAAE,QAAQ,EAAE,QAAQ,EAAE,CAAA;CAAE,GAC7C,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC;AACzC,wBAAsB,GAAG,CAAC,CAAC,SAAS,UAAU,EAC7C,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,GAClB,OAAO,CAAC,UAAU,CAAC,CAAC;AA4EvB;;GAEG;AACH,wBAAsB,QAAQ,CAAC,CAAC,SAAS,UAAU,EAClD,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG;IAAE,QAAQ,EAAE,QAAQ,EAAE,CAAA;CAAE,GAC7C,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;AACrC,wBAAsB,QAAQ,CAAC,CAAC,SAAS,UAAU,EAClD,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,GAClB,OAAO,CAAC,MAAM,CAAC,CAAC"}
package/dist/ogx.js ADDED
@@ -0,0 +1,104 @@
1
+ import { snapshotCache } from "./cache";
2
+ import { fontRegistry } from "./font-registry";
3
+ import { loadInterFont } from "./fonts";
4
+ import { render } from "./render-png";
5
+ import { renderToSVG } from "./render-svg";
6
+ import { presets } from "./types";
7
+ export async function ogx(config) {
8
+ const { preset, slots, options = {}, platform, fonts, theme, colorScheme, cache = true, ...props } = config;
9
+ // Handle batch rendering
10
+ if (Array.isArray(platform)) {
11
+ const results = await Promise.all(platform.map((p) => ogx({ ...config, platform: p })));
12
+ return platform.reduce((acc, p, i) => {
13
+ acc[p] = results[i];
14
+ return acc;
15
+ }, {});
16
+ }
17
+ // Check cache
18
+ const hash = cache ? snapshotCache.getHash(config) : null;
19
+ if (hash) {
20
+ const cached = snapshotCache.get(hash);
21
+ if (cached)
22
+ return cached;
23
+ }
24
+ const presetFn = presets[preset];
25
+ if (!presetFn) {
26
+ throw new Error(`Unknown preset: ${preset}`);
27
+ }
28
+ const element = presetFn({ ...props, slots });
29
+ // Resolve fonts (Registry > Explicit > Default Inter)
30
+ let resolvedFonts = fonts ?? [];
31
+ if (resolvedFonts.length === 0) {
32
+ resolvedFonts = fontRegistry.getFonts();
33
+ }
34
+ if (resolvedFonts.length === 0) {
35
+ resolvedFonts = [
36
+ await loadInterFont(300),
37
+ await loadInterFont(400),
38
+ await loadInterFont(500),
39
+ await loadInterFont(600),
40
+ await loadInterFont(700),
41
+ ];
42
+ }
43
+ const result = await render(element, {
44
+ ...options,
45
+ platform: platform ?? options.platform,
46
+ fonts: resolvedFonts,
47
+ theme: theme ?? options.theme,
48
+ colorScheme: colorScheme ?? options.colorScheme,
49
+ });
50
+ if (hash) {
51
+ snapshotCache.set(hash, result);
52
+ }
53
+ return result;
54
+ }
55
+ export async function ogxToSVG(config) {
56
+ const { preset, slots, options = {}, platform, fonts, theme, colorScheme, cache = true, ...props } = config;
57
+ if (Array.isArray(platform)) {
58
+ const results = await Promise.all(platform.map((p) => ogxToSVG({ ...config, platform: p })));
59
+ return platform.reduce((acc, p, i) => {
60
+ acc[p] = results[i];
61
+ return acc;
62
+ }, {});
63
+ }
64
+ // Check cache
65
+ const hash = cache
66
+ ? snapshotCache.getHash({ ...config, format: "svg" })
67
+ : null;
68
+ if (hash) {
69
+ const cached = snapshotCache.get(hash);
70
+ if (cached)
71
+ return cached;
72
+ }
73
+ const presetFn = presets[preset];
74
+ if (!presetFn) {
75
+ throw new Error(`Unknown preset: ${preset}`);
76
+ }
77
+ const element = presetFn({ ...props, slots });
78
+ // Resolve fonts
79
+ let resolvedFonts = fonts ?? [];
80
+ if (resolvedFonts.length === 0) {
81
+ resolvedFonts = fontRegistry.getFonts();
82
+ }
83
+ if (resolvedFonts.length === 0) {
84
+ resolvedFonts = [
85
+ await loadInterFont(300),
86
+ await loadInterFont(400),
87
+ await loadInterFont(500),
88
+ await loadInterFont(600),
89
+ await loadInterFont(700),
90
+ ];
91
+ }
92
+ const result = await renderToSVG(element, {
93
+ ...options,
94
+ platform: platform ?? options.platform,
95
+ fonts: resolvedFonts,
96
+ theme: theme ?? options.theme,
97
+ colorScheme: colorScheme ?? options.colorScheme,
98
+ });
99
+ // Store in cache
100
+ if (hash) {
101
+ snapshotCache.set(hash, result);
102
+ }
103
+ return result;
104
+ }
@@ -0,0 +1,31 @@
1
+ import type { OGXElement, Preset } from "../types";
2
+ export interface BlogPresetProps {
3
+ /** Blog post title */
4
+ title: string;
5
+ /** Author name */
6
+ author?: string;
7
+ /** Author avatar URL */
8
+ authorAvatar?: string;
9
+ /** Publication date */
10
+ date?: string;
11
+ /** Category or tag */
12
+ category?: string;
13
+ /** Reading time */
14
+ readingTime?: string;
15
+ /** Cover image URL */
16
+ coverImage?: string;
17
+ /** Logo URL or base64 */
18
+ logo?: string;
19
+ /** Color scheme */
20
+ colorScheme?: "dark" | "light";
21
+ /** Custom slot overrides */
22
+ slots?: {
23
+ header?: OGXElement;
24
+ footer?: OGXElement;
25
+ };
26
+ }
27
+ /**
28
+ * Blog preset - ideal for blog posts
29
+ */
30
+ export declare const blogPreset: Preset<BlogPresetProps>;
31
+ //# sourceMappingURL=blog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blog.d.ts","sourceRoot":"","sources":["../../src/presets/blog.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEnD,MAAM,WAAW,eAAe;IAC9B,sBAAsB;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,kBAAkB;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wBAAwB;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uBAAuB;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sBAAsB;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yBAAyB;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mBAAmB;IACnB,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,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,UAAU,EAAE,MAAM,CAAC,eAAe,CAqI9C,CAAC"}
@@ -0,0 +1,98 @@
1
+ import { absolute, div, h1, img, row, span, stack } from "../builder";
2
+ /**
3
+ * Blog preset - ideal for blog posts
4
+ */
5
+ export const blogPreset = (props) => {
6
+ const { title, author, authorAvatar, date, category, readingTime, coverImage, colorScheme = "dark", slots = {}, } = props;
7
+ const isDark = colorScheme === "dark";
8
+ return row([
9
+ "w-full",
10
+ "h-full",
11
+ isDark ? "bg-zinc-950" : "bg-zinc-50",
12
+ "overflow-hidden",
13
+ "relative",
14
+ ], [
15
+ // Background Accent
16
+ absolute([
17
+ isDark ? "bg-grid-white/5-64" : "bg-grid-zinc-900/5-64",
18
+ "inset-0",
19
+ ]),
20
+ absolute(["bg-grain/5"]), // Quality Boost: Dithering
21
+ // Left content
22
+ stack("flex-[3.5] p-20 justify-between gap-12 relative", [
23
+ // Header
24
+ slots.header ??
25
+ row("items-center justify-between", [
26
+ category
27
+ ? div([
28
+ "px-4 py-1.5 rounded-full text-xs font-bold tracking-widest",
29
+ isDark
30
+ ? "bg-indigo-500/10 text-indigo-400 border border-indigo-500/20"
31
+ : "bg-indigo-600/5 text-indigo-600 border border-indigo-600/10",
32
+ ], category.toUpperCase())
33
+ : span(""),
34
+ ]),
35
+ // Main content
36
+ stack("gap-6", [
37
+ h1([
38
+ "text-7xl",
39
+ "font-black",
40
+ isDark ? "text-white" : "text-zinc-950",
41
+ "leading-[1.12]",
42
+ "tracking-tight", // Quality Boost
43
+ ], title),
44
+ ]),
45
+ // Footer with author info (Glass Card effect)
46
+ slots.footer ??
47
+ row(["items-center gap-5"], [
48
+ authorAvatar
49
+ ? img(authorAvatar, "w-16 h-16 rounded-full border-2 border-white/20 shadow-md")
50
+ : null,
51
+ stack("gap-1.5", [
52
+ author
53
+ ? span([
54
+ "text-2xl",
55
+ "font-bold",
56
+ isDark ? "text-white" : "text-zinc-950",
57
+ "tracking-tight",
58
+ ], author)
59
+ : null,
60
+ span([
61
+ "text-lg",
62
+ "font-medium",
63
+ isDark ? "text-zinc-400" : "text-zinc-500",
64
+ ], [date, readingTime].filter(Boolean).join(" · ")),
65
+ ]),
66
+ ]),
67
+ ]),
68
+ // Right side cover image
69
+ coverImage
70
+ ? row("flex-[2] h-full overflow-hidden relative", [
71
+ // Gradient transition from content to image
72
+ absolute([
73
+ "inset-0 z-10",
74
+ isDark
75
+ ? "bg-gradient-to-r from-zinc-950 via-zinc-950/20 to-transparent"
76
+ : "bg-gradient-to-r from-zinc-50 via-zinc-50/20 to-transparent",
77
+ ]),
78
+ img(coverImage, "w-full h-full object-cover"),
79
+ ])
80
+ : // Decorative background for no-image blogs
81
+ row([
82
+ "flex-[2]",
83
+ "h-full",
84
+ isDark ? "bg-zinc-900" : "bg-zinc-100",
85
+ "items-center",
86
+ "justify-center",
87
+ "bg-grid-zinc-500/5-24",
88
+ "relative",
89
+ ], [
90
+ absolute([
91
+ isDark
92
+ ? "bg-gradient-to-r from-zinc-950 to-transparent"
93
+ : "bg-gradient-to-r from-zinc-50 to-transparent",
94
+ "inset-0",
95
+ ]),
96
+ ]),
97
+ ]);
98
+ };
@@ -0,0 +1,23 @@
1
+ import type { OGXElement, Preset } from "../types";
2
+ export interface DocsPresetProps {
3
+ /** Page title */
4
+ title: string;
5
+ /** Page description */
6
+ description?: string;
7
+ /** Logo URL or base64 */
8
+ logo?: string;
9
+ /** Site name */
10
+ siteName?: string;
11
+ /** Color scheme */
12
+ colorScheme?: "dark" | "light";
13
+ /** Custom slot overrides */
14
+ slots?: {
15
+ header?: OGXElement;
16
+ footer?: OGXElement;
17
+ };
18
+ }
19
+ /**
20
+ * Docs preset - ideal for documentation sites
21
+ */
22
+ export declare const docsPreset: Preset<DocsPresetProps>;
23
+ //# sourceMappingURL=docs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs.d.ts","sourceRoot":"","sources":["../../src/presets/docs.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEnD,MAAM,WAAW,eAAe;IAC9B,iBAAiB;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yBAAyB;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gBAAgB;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB;IACnB,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,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,UAAU,EAAE,MAAM,CAAC,eAAe,CAuH9C,CAAC"}
@@ -0,0 +1,87 @@
1
+ import { absolute, div, h1, img, p, row, span, stack } from "../builder";
2
+ /**
3
+ * Docs preset - ideal for documentation sites
4
+ */
5
+ export const docsPreset = (props) => {
6
+ const { title, description, logo, siteName, colorScheme = "dark", slots = {}, } = props;
7
+ const isDark = colorScheme === "dark";
8
+ return stack([
9
+ "w-full",
10
+ "h-full",
11
+ isDark ? "bg-zinc-950" : "bg-white",
12
+ "p-20",
13
+ "relative",
14
+ ], [
15
+ // Background Accent (Subtle Grid + Glow)
16
+ absolute([
17
+ isDark ? "bg-grid-white/5-64" : "bg-grid-zinc-900/5-64",
18
+ "inset-0",
19
+ ]),
20
+ absolute([
21
+ "inset-0",
22
+ isDark
23
+ ? "bg-gradient-to-tr from-zinc-950 via-transparent to-indigo-500/10"
24
+ : "bg-gradient-to-tr from-white via-transparent to-indigo-500/5",
25
+ ]),
26
+ absolute(["bg-grain/10"]), // Quality Boost: Dithering
27
+ // Content Layout
28
+ stack("flex-1 gap-12 relative", [
29
+ // Header
30
+ slots.header ??
31
+ row("items-center justify-between", [
32
+ row("items-center gap-4", [
33
+ logo ? img(logo, "w-10 h-10 rounded-lg shadow-sm") : null,
34
+ siteName
35
+ ? span([
36
+ "text-2xl",
37
+ "font-bold",
38
+ isDark ? "text-white" : "text-zinc-900",
39
+ "tracking-tight",
40
+ ], siteName)
41
+ : null,
42
+ ]),
43
+ div([
44
+ "px-5 py-1 rounded-full border text-xs font-semibold",
45
+ isDark
46
+ ? "bg-white/5 border-white/10 text-zinc-400"
47
+ : "bg-zinc-100 border-zinc-200 text-zinc-600",
48
+ ], "DOCUMENTATION"),
49
+ ]),
50
+ // Main content
51
+ stack("flex-1 justify-center gap-8", [
52
+ h1([
53
+ "text-8xl",
54
+ "font-black",
55
+ isDark ? "text-white" : "text-zinc-950",
56
+ "leading-[1.1]",
57
+ "tracking-tight", // Quality Boost
58
+ ], title),
59
+ description
60
+ ? p([
61
+ "text-4xl",
62
+ isDark ? "text-zinc-400" : "text-zinc-500",
63
+ "max-w-[900px]",
64
+ "leading-relaxed",
65
+ "font-semibold",
66
+ ], description)
67
+ : null,
68
+ ]),
69
+ // Footer / Bottom Indicator
70
+ slots.footer ??
71
+ row("items-center gap-2", [
72
+ div([
73
+ "h-1 w-12 rounded-full",
74
+ isDark ? "bg-indigo-500" : "bg-indigo-600",
75
+ ], ""),
76
+ div([
77
+ "h-1 w-1.5 rounded-full",
78
+ isDark ? "bg-zinc-800" : "bg-zinc-200",
79
+ ], ""),
80
+ div([
81
+ "h-1 w-1.5 rounded-full",
82
+ isDark ? "bg-zinc-800" : "bg-zinc-200",
83
+ ], ""),
84
+ ]),
85
+ ]),
86
+ ]);
87
+ };
@@ -0,0 +1,20 @@
1
+ import { type BlogPresetProps, blogPreset } from "./blog";
2
+ import { type DocsPresetProps, docsPreset } from "./docs";
3
+ import { type MinimalPresetProps, minimalPreset } from "./minimal";
4
+ import { type SocialPresetProps, socialPreset } from "./social";
5
+ export declare const presets: {
6
+ readonly docs: import("..").Preset<DocsPresetProps>;
7
+ readonly blog: import("..").Preset<BlogPresetProps>;
8
+ readonly minimal: import("..").Preset<MinimalPresetProps>;
9
+ readonly social: import("..").Preset<SocialPresetProps>;
10
+ };
11
+ export type PresetName = keyof typeof presets;
12
+ export interface PresetProps {
13
+ docs: DocsPresetProps;
14
+ blog: BlogPresetProps;
15
+ minimal: MinimalPresetProps;
16
+ social: SocialPresetProps;
17
+ }
18
+ export { docsPreset, blogPreset, minimalPreset, socialPreset };
19
+ export type { DocsPresetProps, BlogPresetProps, MinimalPresetProps, SocialPresetProps, };
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/presets/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,EAAE,KAAK,eAAe,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,EAAE,KAAK,kBAAkB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACnE,OAAO,EAAE,KAAK,iBAAiB,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAEhE,eAAO,MAAM,OAAO;;;;;CAKV,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,OAAO,CAAC;AAE9C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,eAAe,CAAC;IACtB,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,EAAE,kBAAkB,CAAC;IAC5B,MAAM,EAAE,iBAAiB,CAAC;CAC3B;AAED,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,CAAC;AAC/D,YAAY,EACV,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,iBAAiB,GAClB,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { blogPreset } from "./blog";
2
+ import { docsPreset } from "./docs";
3
+ import { minimalPreset } from "./minimal";
4
+ import { socialPreset } from "./social";
5
+ export const presets = {
6
+ docs: docsPreset,
7
+ blog: blogPreset,
8
+ minimal: minimalPreset,
9
+ social: socialPreset,
10
+ };
11
+ export { docsPreset, blogPreset, minimalPreset, socialPreset };