@mintlify/og 0.0.19 → 0.0.21
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.
- package/dist/config/card-input.d.ts +9 -0
- package/dist/config/card-input.js +59 -0
- package/dist/config/checks.d.ts +3 -0
- package/dist/config/checks.js +36 -0
- package/dist/config/index.d.ts +7 -0
- package/dist/config/index.js +7 -0
- package/dist/config/local-assets.d.ts +6 -0
- package/dist/config/local-assets.js +71 -0
- package/dist/config/navigation.d.ts +6 -0
- package/dist/config/navigation.js +45 -0
- package/dist/config/page-params.d.ts +14 -0
- package/dist/config/page-params.js +27 -0
- package/dist/config/theme-params.d.ts +16 -0
- package/dist/config/theme-params.js +46 -0
- package/dist/config/thumbnail-override.d.ts +7 -0
- package/dist/config/thumbnail-override.js +54 -0
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/inputs/palette.d.ts +1 -0
- package/dist/inputs/palette.js +1 -1
- package/dist/layouts/classic-frame.d.ts +4 -0
- package/dist/layouts/classic-frame.js +2 -1
- package/dist/layouts/index.d.ts +5 -1
- package/dist/layouts/index.js +3 -1
- package/dist/layouts/legacy.d.ts +4 -0
- package/dist/layouts/legacy.js +3 -2
- package/dist/layouts/preset.d.ts +5 -1
- package/dist/layouts/preset.js +23 -9
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +6 -2
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { OgCardInput, OgImageAsset } from '../types.js';
|
|
2
|
+
import type { OgPageParams } from './page-params.js';
|
|
3
|
+
import type { OgThemeParams } from './theme-params.js';
|
|
4
|
+
export type OgAssetLoader = (src: string) => Promise<OgImageAsset | null>;
|
|
5
|
+
export type OgAssetLoaders = {
|
|
6
|
+
image: OgAssetLoader;
|
|
7
|
+
font: OgAssetLoader;
|
|
8
|
+
};
|
|
9
|
+
export declare function buildOgCardInput(pageParams: OgPageParams, themeParams: OgThemeParams, loaders: OgAssetLoaders): Promise<OgCardInput>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { DEFAULT_PRESET } from '../constants.js';
|
|
11
|
+
import { loadGoogleFont } from '../inputs/fonts.js';
|
|
12
|
+
import { resolveOgLogoAppearance } from '../inputs/palette.js';
|
|
13
|
+
const SELF_HOSTED_FONT_WEIGHT = 400;
|
|
14
|
+
const loadCardFont = (themeParams, text, loadFont) => __awaiter(void 0, void 0, void 0, function* () {
|
|
15
|
+
const { fontFamily, fontSource } = themeParams;
|
|
16
|
+
if (!fontFamily)
|
|
17
|
+
return null;
|
|
18
|
+
if (!fontSource)
|
|
19
|
+
return loadGoogleFont(fontFamily, text);
|
|
20
|
+
const asset = yield loadFont(fontSource);
|
|
21
|
+
if (!asset)
|
|
22
|
+
return null;
|
|
23
|
+
const data = Buffer.from(asset.bytes.buffer, asset.bytes.byteOffset, asset.bytes.byteLength);
|
|
24
|
+
return { family: fontFamily, faces: [{ weight: SELF_HOSTED_FONT_WEIGHT, data }] };
|
|
25
|
+
});
|
|
26
|
+
export function buildOgCardInput(pageParams, themeParams, loaders) {
|
|
27
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
28
|
+
var _a;
|
|
29
|
+
const { division, title, description } = pageParams;
|
|
30
|
+
const theme = {
|
|
31
|
+
appearance: themeParams.appearance,
|
|
32
|
+
primaryColor: themeParams.primaryColor,
|
|
33
|
+
lightColor: themeParams.lightColor,
|
|
34
|
+
backgroundLight: themeParams.backgroundLight,
|
|
35
|
+
backgroundDark: themeParams.backgroundDark,
|
|
36
|
+
surface: themeParams.surface,
|
|
37
|
+
eyebrowColor: themeParams.eyebrowColor,
|
|
38
|
+
};
|
|
39
|
+
const isLight = resolveOgLogoAppearance(theme, (_a = themeParams.preset) !== null && _a !== void 0 ? _a : DEFAULT_PRESET) === 'light';
|
|
40
|
+
const logoSrc = isLight ? themeParams.logoLight : themeParams.logoDark;
|
|
41
|
+
const fontText = [division, title, description].filter(Boolean).join(' ');
|
|
42
|
+
const [logo, background, font] = yield Promise.all([
|
|
43
|
+
logoSrc ? loaders.image(logoSrc) : null,
|
|
44
|
+
themeParams.background ? loaders.image(themeParams.background) : null,
|
|
45
|
+
loadCardFont(themeParams, fontText, loaders.font),
|
|
46
|
+
]);
|
|
47
|
+
return {
|
|
48
|
+
preset: themeParams.preset,
|
|
49
|
+
eyebrowStyle: themeParams.eyebrowStyle,
|
|
50
|
+
eyebrow: division,
|
|
51
|
+
title,
|
|
52
|
+
description,
|
|
53
|
+
theme,
|
|
54
|
+
logo,
|
|
55
|
+
background,
|
|
56
|
+
font,
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { DEFAULT_PRESET } from '../constants.js';
|
|
2
|
+
import { contrast, isBrandSurface, resolveOgPalette } from '../inputs/palette.js';
|
|
3
|
+
import { getTitleBox } from '../layouts/index.js';
|
|
4
|
+
const AVERAGE_CHAR_WIDTH_EM = 0.55;
|
|
5
|
+
const MAX_TITLE_LINES = 2;
|
|
6
|
+
const MIN_BRAND_CONTRAST = 3;
|
|
7
|
+
const estimateTitleLines = (input) => {
|
|
8
|
+
var _a, _b, _c;
|
|
9
|
+
const { width, size } = getTitleBox((_a = input.preset) !== null && _a !== void 0 ? _a : DEFAULT_PRESET);
|
|
10
|
+
const length = (_c = (_b = input.title) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0;
|
|
11
|
+
return Math.ceil((length * AVERAGE_CHAR_WIDTH_EM * size) / width);
|
|
12
|
+
};
|
|
13
|
+
export function checkOgCard(input, themeParams) {
|
|
14
|
+
const warnings = [];
|
|
15
|
+
const titleLines = estimateTitleLines(input);
|
|
16
|
+
if (titleLines > MAX_TITLE_LINES) {
|
|
17
|
+
warnings.push(`The title needs about ${titleLines} lines but the card shows ${MAX_TITLE_LINES}, so it will be cut off.`);
|
|
18
|
+
}
|
|
19
|
+
if (!themeParams.logoLight && !themeParams.logoDark) {
|
|
20
|
+
warnings.push('No logo is configured in docs.json, so the card has no logo.');
|
|
21
|
+
}
|
|
22
|
+
else if (!input.logo) {
|
|
23
|
+
warnings.push("The logo couldn't be loaded, so the card has no logo.");
|
|
24
|
+
}
|
|
25
|
+
if (themeParams.fontFamily && !input.font) {
|
|
26
|
+
warnings.push(`The font "${themeParams.fontFamily}" couldn't be loaded, so the card uses Inter.`);
|
|
27
|
+
}
|
|
28
|
+
if (input.preset && input.preset !== DEFAULT_PRESET && isBrandSurface(input.theme)) {
|
|
29
|
+
const palette = resolveOgPalette(input.theme);
|
|
30
|
+
const lowContrast = [palette.eyebrow, palette.description].some((color) => contrast(color, palette.background) < MIN_BRAND_CONTRAST);
|
|
31
|
+
if (lowContrast) {
|
|
32
|
+
warnings.push('Some text has low contrast on the brand surface. Try another eyebrow color.');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return warnings;
|
|
36
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { buildOgCardInput, type OgAssetLoader, type OgAssetLoaders } from './card-input.js';
|
|
2
|
+
export { checkOgCard } from './checks.js';
|
|
3
|
+
export { createLocalAssetLoaders, MAX_OG_ASSET_BYTES, toOgFontAsset, toOgImageAsset, } from './local-assets.js';
|
|
4
|
+
export { findPageAncestors, findPageDivisionName, findPageGroupName } from './navigation.js';
|
|
5
|
+
export { getOgPageParams, type OgPageInput, type OgPageParams } from './page-params.js';
|
|
6
|
+
export { deriveOgThemeParams, getOgThemeRevision, type OgThemeParams } from './theme-params.js';
|
|
7
|
+
export { applyCardThemeOverride, applyThumbnailOverride, resolvePageThumbnailOverride, serializeCardThemeOverride, type CardThemeOverride, } from './thumbnail-override.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { buildOgCardInput } from './card-input.js';
|
|
2
|
+
export { checkOgCard } from './checks.js';
|
|
3
|
+
export { createLocalAssetLoaders, MAX_OG_ASSET_BYTES, toOgFontAsset, toOgImageAsset, } from './local-assets.js';
|
|
4
|
+
export { findPageAncestors, findPageDivisionName, findPageGroupName } from './navigation.js';
|
|
5
|
+
export { getOgPageParams } from './page-params.js';
|
|
6
|
+
export { deriveOgThemeParams, getOgThemeRevision } from './theme-params.js';
|
|
7
|
+
export { applyCardThemeOverride, applyThumbnailOverride, resolvePageThumbnailOverride, serializeCardThemeOverride, } from './thumbnail-override.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { OgImageAsset } from '../types.js';
|
|
2
|
+
import type { OgAssetLoaders } from './card-input.js';
|
|
3
|
+
export declare const MAX_OG_ASSET_BYTES: number;
|
|
4
|
+
export declare function toOgImageAsset(bytes: Uint8Array, contentTypeHeader: string | undefined): OgImageAsset | null;
|
|
5
|
+
export declare function toOgFontAsset(bytes: Uint8Array): OgImageAsset | null;
|
|
6
|
+
export declare function createLocalAssetLoaders(root: string, remote: OgAssetLoaders): OgAssetLoaders;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { isAbsoluteUrl } from '@mintlify/common';
|
|
11
|
+
import { readFile } from 'node:fs/promises';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
import { getFontContentType } from '../inputs/font-file.js';
|
|
14
|
+
export const MAX_OG_ASSET_BYTES = 5 * 1024 * 1024;
|
|
15
|
+
const IMAGE_CONTENT_TYPES = {
|
|
16
|
+
'.avif': 'image/avif',
|
|
17
|
+
'.gif': 'image/gif',
|
|
18
|
+
'.jpeg': 'image/jpeg',
|
|
19
|
+
'.jpg': 'image/jpeg',
|
|
20
|
+
'.png': 'image/png',
|
|
21
|
+
'.svg': 'image/svg+xml',
|
|
22
|
+
'.webp': 'image/webp',
|
|
23
|
+
};
|
|
24
|
+
const SUPPORTED_IMAGE_CONTENT_TYPES = new Set(Object.values(IMAGE_CONTENT_TYPES));
|
|
25
|
+
export function toOgImageAsset(bytes, contentTypeHeader) {
|
|
26
|
+
var _a;
|
|
27
|
+
const contentType = (_a = contentTypeHeader === null || contentTypeHeader === void 0 ? void 0 : contentTypeHeader.split(';')[0]) === null || _a === void 0 ? void 0 : _a.trim().toLowerCase();
|
|
28
|
+
if (bytes.byteLength === 0 || !contentType || !SUPPORTED_IMAGE_CONTENT_TYPES.has(contentType)) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return { bytes, contentType };
|
|
32
|
+
}
|
|
33
|
+
export function toOgFontAsset(bytes) {
|
|
34
|
+
const contentType = getFontContentType(bytes);
|
|
35
|
+
return contentType ? { bytes, contentType } : null;
|
|
36
|
+
}
|
|
37
|
+
const resolveInside = (rootDir, src) => {
|
|
38
|
+
const filePath = path.resolve(rootDir, `.${path.posix.normalize(`/${src}`)}`);
|
|
39
|
+
return filePath.startsWith(`${rootDir}${path.sep}`) ? filePath : null;
|
|
40
|
+
};
|
|
41
|
+
const readInside = (rootDir, src) => __awaiter(void 0, void 0, void 0, function* () {
|
|
42
|
+
const filePath = resolveInside(rootDir, src);
|
|
43
|
+
if (!filePath)
|
|
44
|
+
return null;
|
|
45
|
+
try {
|
|
46
|
+
const bytes = new Uint8Array(yield readFile(filePath));
|
|
47
|
+
return bytes.byteLength === 0 || bytes.byteLength > MAX_OG_ASSET_BYTES ? null : bytes;
|
|
48
|
+
}
|
|
49
|
+
catch (_a) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
export function createLocalAssetLoaders(root, remote) {
|
|
54
|
+
const rootDir = path.resolve(root);
|
|
55
|
+
const image = (src) => __awaiter(this, void 0, void 0, function* () {
|
|
56
|
+
if (isAbsoluteUrl(src))
|
|
57
|
+
return remote.image(src);
|
|
58
|
+
const contentType = IMAGE_CONTENT_TYPES[path.extname(src).toLowerCase()];
|
|
59
|
+
if (!contentType)
|
|
60
|
+
return null;
|
|
61
|
+
const bytes = yield readInside(rootDir, src);
|
|
62
|
+
return bytes && toOgImageAsset(bytes, contentType);
|
|
63
|
+
});
|
|
64
|
+
const font = (src) => __awaiter(this, void 0, void 0, function* () {
|
|
65
|
+
if (isAbsoluteUrl(src))
|
|
66
|
+
return remote.font(src);
|
|
67
|
+
const bytes = yield readInside(rootDir, src);
|
|
68
|
+
return bytes && toOgFontAsset(bytes);
|
|
69
|
+
});
|
|
70
|
+
return { image, font };
|
|
71
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { DivisionType, DocsConfig } from '@mintlify/validation';
|
|
2
|
+
type NavigationNode = Record<string, unknown>;
|
|
3
|
+
export declare const findPageAncestors: (path: string, docsConfig: DocsConfig) => NavigationNode[];
|
|
4
|
+
export declare const findPageGroupName: (path: string, docsConfig: DocsConfig) => string;
|
|
5
|
+
export declare const findPageDivisionName: (path: string, docsConfig: DocsConfig, division: DivisionType) => string | undefined;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { isEqualIgnoringLeadingSlash } from '@mintlify/common';
|
|
2
|
+
const DEFAULT_GROUP_NAME = 'Documentation';
|
|
3
|
+
const isNavigationNode = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
4
|
+
const isPage = (value, path) => typeof value === 'string' && isEqualIgnoringLeadingSlash(value, path);
|
|
5
|
+
const searchAncestors = (value, path, ancestors, includeRoot) => {
|
|
6
|
+
if (Array.isArray(value)) {
|
|
7
|
+
for (const item of value) {
|
|
8
|
+
const found = searchAncestors(item, path, ancestors, includeRoot);
|
|
9
|
+
if (found)
|
|
10
|
+
return found;
|
|
11
|
+
}
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
if (!isNavigationNode(value))
|
|
15
|
+
return null;
|
|
16
|
+
const lineage = [value, ...ancestors];
|
|
17
|
+
for (const [key, child] of Object.entries(value)) {
|
|
18
|
+
if (includeRoot && key === 'root' && isPage(child, path))
|
|
19
|
+
return lineage;
|
|
20
|
+
if (key === 'pages' && Array.isArray(child)) {
|
|
21
|
+
for (const page of child) {
|
|
22
|
+
if (isPage(page, path))
|
|
23
|
+
return lineage;
|
|
24
|
+
const found = searchAncestors(page, path, lineage, includeRoot);
|
|
25
|
+
if (found)
|
|
26
|
+
return found;
|
|
27
|
+
}
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const found = searchAncestors(child, path, lineage, includeRoot);
|
|
31
|
+
if (found)
|
|
32
|
+
return found;
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
};
|
|
36
|
+
export const findPageAncestors = (path, docsConfig) => { var _a; return (_a = searchAncestors(docsConfig.navigation, path, [], true)) !== null && _a !== void 0 ? _a : []; };
|
|
37
|
+
export const findPageGroupName = (path, docsConfig) => {
|
|
38
|
+
var _a;
|
|
39
|
+
const [parent] = (_a = searchAncestors(docsConfig.navigation, path, [], false)) !== null && _a !== void 0 ? _a : [];
|
|
40
|
+
const group = parent === null || parent === void 0 ? void 0 : parent.group;
|
|
41
|
+
return typeof group === 'string' && group ? group : DEFAULT_GROUP_NAME;
|
|
42
|
+
};
|
|
43
|
+
export const findPageDivisionName = (path, docsConfig, division) => findPageAncestors(path, docsConfig)
|
|
44
|
+
.map((node) => node[division])
|
|
45
|
+
.find((name) => typeof name === 'string');
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { DocsConfig } from '@mintlify/validation';
|
|
2
|
+
export type OgPageParams = {
|
|
3
|
+
division?: string;
|
|
4
|
+
title?: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
thumbnail?: string;
|
|
7
|
+
};
|
|
8
|
+
export type OgPageInput = {
|
|
9
|
+
path: string;
|
|
10
|
+
title: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
thumbnail?: unknown;
|
|
13
|
+
};
|
|
14
|
+
export declare function getOgPageParams(page: OgPageInput, docsConfig: DocsConfig): OgPageParams;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { findPageDivisionName, findPageGroupName } from './navigation.js';
|
|
2
|
+
import { resolvePageThumbnailOverride, serializeCardThemeOverride } from './thumbnail-override.js';
|
|
3
|
+
const MAX_DESCRIPTION_LENGTH = 160;
|
|
4
|
+
const resolveEyebrow = (path, docsConfig, group) => {
|
|
5
|
+
var _a, _b, _c, _d;
|
|
6
|
+
const source = (_c = (_b = (_a = docsConfig.thumbnails) === null || _a === void 0 ? void 0 : _a.eyebrow) === null || _b === void 0 ? void 0 : _b.source) !== null && _c !== void 0 ? _c : 'group';
|
|
7
|
+
if (source === 'group')
|
|
8
|
+
return group;
|
|
9
|
+
if (source === 'none')
|
|
10
|
+
return '';
|
|
11
|
+
return (_d = findPageDivisionName(path, docsConfig, source)) !== null && _d !== void 0 ? _d : group;
|
|
12
|
+
};
|
|
13
|
+
export function getOgPageParams(page, docsConfig) {
|
|
14
|
+
var _a, _b;
|
|
15
|
+
const { path, title } = page;
|
|
16
|
+
const group = findPageGroupName(path, docsConfig);
|
|
17
|
+
const override = resolvePageThumbnailOverride(page.thumbnail, docsConfig, path);
|
|
18
|
+
const thumbnail = serializeCardThemeOverride(override);
|
|
19
|
+
const description = page.description
|
|
20
|
+
? Array.from(page.description).slice(0, MAX_DESCRIPTION_LENGTH).join('')
|
|
21
|
+
: page.description;
|
|
22
|
+
if (title.toLowerCase() === 'index') {
|
|
23
|
+
return { division: (_a = override.eyebrow) !== null && _a !== void 0 ? _a : '', title: group, description, thumbnail };
|
|
24
|
+
}
|
|
25
|
+
const division = (_b = override.eyebrow) !== null && _b !== void 0 ? _b : resolveEyebrow(path, docsConfig, group);
|
|
26
|
+
return { division, title, description, thumbnail };
|
|
27
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { DocsConfig } from '@mintlify/validation';
|
|
2
|
+
import type { OgPresetName } from '../layouts/types.js';
|
|
3
|
+
import type { OgEyebrowStyle, OgThemeInput } from '../types.js';
|
|
4
|
+
export type OgThemeParams = OgThemeInput & {
|
|
5
|
+
background?: string;
|
|
6
|
+
eyebrowStyle?: OgEyebrowStyle;
|
|
7
|
+
fontFamily?: string;
|
|
8
|
+
fontSource?: string;
|
|
9
|
+
logoDark?: string;
|
|
10
|
+
logoLight?: string;
|
|
11
|
+
preset?: OgPresetName;
|
|
12
|
+
};
|
|
13
|
+
export declare function getOgThemeRevision(themeParams: OgThemeParams, options?: {
|
|
14
|
+
includeRendererRevision?: boolean;
|
|
15
|
+
}): Promise<string>;
|
|
16
|
+
export declare function deriveOgThemeParams(docsConfig: DocsConfig): OgThemeParams;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { getBackgroundColors } from '@mintlify/common/colors';
|
|
11
|
+
import { OG_RENDERER_REVISION } from '../constants.js';
|
|
12
|
+
export function getOgThemeRevision(themeParams_1) {
|
|
13
|
+
return __awaiter(this, arguments, void 0, function* (themeParams, options = {}) {
|
|
14
|
+
const revisionParams = options.includeRendererRevision === false
|
|
15
|
+
? themeParams
|
|
16
|
+
: Object.assign(Object.assign({}, themeParams), { rendererRevision: OG_RENDERER_REVISION });
|
|
17
|
+
const input = new TextEncoder().encode(JSON.stringify(revisionParams));
|
|
18
|
+
const digest = yield globalThis.crypto.subtle.digest('SHA-256', input);
|
|
19
|
+
return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, '0'))
|
|
20
|
+
.join('')
|
|
21
|
+
.slice(0, 24);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
export function deriveOgThemeParams(docsConfig) {
|
|
25
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
26
|
+
const { lightHex, darkHex, background } = getBackgroundColors(docsConfig);
|
|
27
|
+
const logoLight = typeof docsConfig.logo === 'string' ? docsConfig.logo : (_a = docsConfig.logo) === null || _a === void 0 ? void 0 : _a.light;
|
|
28
|
+
const logoDark = typeof docsConfig.logo === 'string' ? docsConfig.logo : (_b = docsConfig.logo) === null || _b === void 0 ? void 0 : _b.dark;
|
|
29
|
+
const { thumbnails } = docsConfig;
|
|
30
|
+
return {
|
|
31
|
+
appearance: (_c = thumbnails === null || thumbnails === void 0 ? void 0 : thumbnails.appearance) !== null && _c !== void 0 ? _c : (_d = docsConfig.appearance) === null || _d === void 0 ? void 0 : _d.default,
|
|
32
|
+
background,
|
|
33
|
+
backgroundDark: darkHex,
|
|
34
|
+
backgroundLight: lightHex,
|
|
35
|
+
eyebrowColor: (_e = thumbnails === null || thumbnails === void 0 ? void 0 : thumbnails.eyebrow) === null || _e === void 0 ? void 0 : _e.color,
|
|
36
|
+
eyebrowStyle: (_f = thumbnails === null || thumbnails === void 0 ? void 0 : thumbnails.eyebrow) === null || _f === void 0 ? void 0 : _f.style,
|
|
37
|
+
fontFamily: (_g = thumbnails === null || thumbnails === void 0 ? void 0 : thumbnails.fonts) === null || _g === void 0 ? void 0 : _g.family,
|
|
38
|
+
fontSource: (_h = thumbnails === null || thumbnails === void 0 ? void 0 : thumbnails.fonts) === null || _h === void 0 ? void 0 : _h.source,
|
|
39
|
+
lightColor: docsConfig.colors.light,
|
|
40
|
+
logoDark,
|
|
41
|
+
logoLight,
|
|
42
|
+
preset: thumbnails === null || thumbnails === void 0 ? void 0 : thumbnails.preset,
|
|
43
|
+
primaryColor: docsConfig.colors.primary,
|
|
44
|
+
surface: thumbnails === null || thumbnails === void 0 ? void 0 : thumbnails.surface,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type DocsConfig, type ThumbnailOverride } from '@mintlify/validation';
|
|
2
|
+
import type { OgThemeParams } from './theme-params.js';
|
|
3
|
+
export type CardThemeOverride = Omit<ThumbnailOverride, 'eyebrow'>;
|
|
4
|
+
export declare function resolvePageThumbnailOverride(pageThumbnail: unknown, docsConfig: DocsConfig, path: string): ThumbnailOverride;
|
|
5
|
+
export declare function serializeCardThemeOverride({ eyebrow: _, ...override }: ThumbnailOverride): string | undefined;
|
|
6
|
+
export declare function applyCardThemeOverride(themeParams: OgThemeParams, serialized: string | undefined): OgThemeParams;
|
|
7
|
+
export declare function applyThumbnailOverride(themeParams: OgThemeParams, { preset, appearance, surface, background }: CardThemeOverride): OgThemeParams;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { thumbnailOverrideSchema, } from '@mintlify/validation';
|
|
13
|
+
import { findPageAncestors } from './navigation.js';
|
|
14
|
+
const cardThemeOverrideSchema = thumbnailOverrideSchema.omit({ eyebrow: true });
|
|
15
|
+
const parseOverride = (value) => {
|
|
16
|
+
const result = thumbnailOverrideSchema.safeParse(value);
|
|
17
|
+
return result.success ? result.data : {};
|
|
18
|
+
};
|
|
19
|
+
export function resolvePageThumbnailOverride(pageThumbnail, docsConfig, path) {
|
|
20
|
+
const layers = [
|
|
21
|
+
parseOverride(pageThumbnail),
|
|
22
|
+
...findPageAncestors(path, docsConfig).map((node) => parseOverride(node.thumbnail)),
|
|
23
|
+
];
|
|
24
|
+
const pick = (key) => { var _a; return (_a = layers.find((layer) => layer[key] !== undefined)) === null || _a === void 0 ? void 0 : _a[key]; };
|
|
25
|
+
return {
|
|
26
|
+
preset: pick('preset'),
|
|
27
|
+
appearance: pick('appearance'),
|
|
28
|
+
surface: pick('surface'),
|
|
29
|
+
background: pick('background'),
|
|
30
|
+
eyebrow: pick('eyebrow'),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export function serializeCardThemeOverride(_a) {
|
|
34
|
+
var { eyebrow: _ } = _a, override = __rest(_a, ["eyebrow"]);
|
|
35
|
+
const serialized = JSON.stringify(override);
|
|
36
|
+
return serialized === '{}' ? undefined : serialized;
|
|
37
|
+
}
|
|
38
|
+
const parseCardThemeOverride = (serialized) => {
|
|
39
|
+
try {
|
|
40
|
+
const result = cardThemeOverrideSchema.safeParse(JSON.parse(serialized));
|
|
41
|
+
return result.success ? result.data : {};
|
|
42
|
+
}
|
|
43
|
+
catch (_a) {
|
|
44
|
+
return {};
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
export function applyCardThemeOverride(themeParams, serialized) {
|
|
48
|
+
return serialized
|
|
49
|
+
? applyThumbnailOverride(themeParams, parseCardThemeOverride(serialized))
|
|
50
|
+
: themeParams;
|
|
51
|
+
}
|
|
52
|
+
export function applyThumbnailOverride(themeParams, { preset, appearance, surface, background }) {
|
|
53
|
+
return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, themeParams), (preset && { preset })), (appearance && { appearance })), (surface && { surface })), (background !== undefined && { background }));
|
|
54
|
+
}
|
package/dist/constants.d.ts
CHANGED
|
@@ -7,4 +7,4 @@ export declare const DEFAULT_FONT_FAMILY = "Inter";
|
|
|
7
7
|
export declare const MONO_FONT_FAMILY = "Paper Mono";
|
|
8
8
|
export declare const LOGO_HEIGHT = 56;
|
|
9
9
|
export declare const MAX_BRAND_GLOW_ALPHA: Record<OgAppearance, number>;
|
|
10
|
-
export declare const OG_RENDERER_REVISION =
|
|
10
|
+
export declare const OG_RENDERER_REVISION = 4;
|
package/dist/constants.js
CHANGED
|
@@ -7,4 +7,4 @@ export const MONO_FONT_FAMILY = 'Paper Mono';
|
|
|
7
7
|
export const LOGO_HEIGHT = 56;
|
|
8
8
|
export const MAX_BRAND_GLOW_ALPHA = { light: 0.32, dark: 0.52 };
|
|
9
9
|
// Bump whenever rendered output changes for the same input, so cards get new hashes.
|
|
10
|
-
export const OG_RENDERER_REVISION =
|
|
10
|
+
export const OG_RENDERER_REVISION = 4;
|
package/dist/inputs/palette.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export type OgPalette = {
|
|
|
15
15
|
badgeText: string;
|
|
16
16
|
};
|
|
17
17
|
export declare const luminance: (hex: string) => number;
|
|
18
|
+
export declare const contrast: (a: string, b: string) => number;
|
|
18
19
|
export declare const isBrandSurface: (theme: OgThemeInput) => boolean;
|
|
19
20
|
export declare const resolveOgPalette: (theme: OgThemeInput) => OgPalette;
|
|
20
21
|
export declare const resolveOgLogoAppearance: (theme: OgThemeInput, preset: OgPreset) => OgAppearance;
|
package/dist/inputs/palette.js
CHANGED
|
@@ -37,7 +37,7 @@ export const luminance = (hex) => {
|
|
|
37
37
|
const { red, green, blue } = hexRgb(hex);
|
|
38
38
|
return 0.2126 * toLinear(red) + 0.7152 * toLinear(green) + 0.0722 * toLinear(blue);
|
|
39
39
|
};
|
|
40
|
-
const contrast = (a, b) => {
|
|
40
|
+
export const contrast = (a, b) => {
|
|
41
41
|
const [first, second] = [luminance(a), luminance(b)];
|
|
42
42
|
return (Math.max(first, second) + 0.05) / (Math.min(first, second) + 0.05);
|
|
43
43
|
};
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { OgPalette } from '../inputs/palette.js';
|
|
2
2
|
import type { BrandGlow } from './brand-glow.js';
|
|
3
|
+
export declare const CLASSIC_CONTENT_BOUNDS: {
|
|
4
|
+
top: number;
|
|
5
|
+
bottom: number;
|
|
6
|
+
};
|
|
3
7
|
export declare const ClassicFrame: ({ palette, glow, backgroundImageUrl, }: {
|
|
4
8
|
palette: OgPalette;
|
|
5
9
|
glow: BrandGlow | null;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { OG_HEIGHT, OG_WIDTH } from '../constants.js';
|
|
3
3
|
import { getFrameColors } from '../inputs/frame-colors.js';
|
|
4
|
-
const
|
|
4
|
+
export const CLASSIC_CONTENT_BOUNDS = { top: 231.482, bottom: 565.189 };
|
|
5
|
+
const FRAME = { left: 62.938, width: 1074.12, dividers: Object.values(CLASSIC_CONTENT_BOUNDS) };
|
|
5
6
|
export const ClassicFrame = ({ palette, glow, backgroundImageUrl, }) => {
|
|
6
7
|
var _a;
|
|
7
8
|
if (backgroundImageUrl) {
|
package/dist/layouts/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OgEyebrowStyle } from '../types.js';
|
|
1
|
+
import type { OgEyebrowStyle, OgPreset } from '../types.js';
|
|
2
2
|
import type { OgPresetName } from './types.js';
|
|
3
3
|
export declare const OG_LAYOUTS: {
|
|
4
4
|
legacy: ({ content, theme, logo, fontFamily }: import("./types.js").OgLayoutProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -6,3 +6,7 @@ export declare const OG_LAYOUTS: {
|
|
|
6
6
|
export declare const PRESET_EYEBROW_STYLES: Record<OgPresetName, OgEyebrowStyle>;
|
|
7
7
|
export { LegacyLayout } from './legacy.js';
|
|
8
8
|
export { PresetLayout } from './preset.js';
|
|
9
|
+
export declare const getTitleBox: (preset: OgPreset) => {
|
|
10
|
+
width: number;
|
|
11
|
+
size: number;
|
|
12
|
+
};
|
package/dist/layouts/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { LegacyLayout } from './legacy.js';
|
|
1
|
+
import { LEGACY_TITLE_BOX, LegacyLayout } from './legacy.js';
|
|
2
|
+
import { getPresetTitleBox } from './preset.js';
|
|
2
3
|
export const OG_LAYOUTS = { legacy: LegacyLayout };
|
|
3
4
|
export const PRESET_EYEBROW_STYLES = {
|
|
4
5
|
classic: 'bar',
|
|
@@ -7,3 +8,4 @@ export const PRESET_EYEBROW_STYLES = {
|
|
|
7
8
|
};
|
|
8
9
|
export { LegacyLayout } from './legacy.js';
|
|
9
10
|
export { PresetLayout } from './preset.js';
|
|
11
|
+
export const getTitleBox = (preset) => preset === 'legacy' ? LEGACY_TITLE_BOX : getPresetTitleBox(preset);
|
package/dist/layouts/legacy.d.ts
CHANGED
package/dist/layouts/legacy.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { LOGO_HEIGHT } from '../constants.js';
|
|
3
3
|
import { getFontStack } from './font-stack.js';
|
|
4
|
-
const
|
|
4
|
+
export const LEGACY_TITLE_BOX = { width: 1000, size: 72 };
|
|
5
|
+
const TEXT_MAX_WIDTH = `${LEGACY_TITLE_BOX.width}px`;
|
|
5
6
|
const DESCRIPTION_WIDTH = '750px';
|
|
6
7
|
const MAX_TEXT_LINES = 2;
|
|
7
8
|
const TEXT_COLORS = {
|
|
@@ -45,7 +46,7 @@ const styles = {
|
|
|
45
46
|
overflow: 'hidden',
|
|
46
47
|
lineClamp: MAX_TEXT_LINES,
|
|
47
48
|
marginTop: '12px',
|
|
48
|
-
fontSize:
|
|
49
|
+
fontSize: `${LEGACY_TITLE_BOX.size}px`,
|
|
49
50
|
fontWeight: 600,
|
|
50
51
|
lineHeight: 1.15,
|
|
51
52
|
letterSpacing: '-0.025em',
|
package/dist/layouts/preset.d.ts
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
|
-
import type { OgPresetLayoutProps } from './types.js';
|
|
1
|
+
import type { OgPresetLayoutProps, OgPresetName } from './types.js';
|
|
2
|
+
export declare const getPresetTitleBox: (preset: OgPresetName) => {
|
|
3
|
+
width: number;
|
|
4
|
+
size: number;
|
|
5
|
+
};
|
|
2
6
|
export declare const PresetLayout: ({ preset, content, palette, backgroundImageUrl, eyebrowStyle, logo, fontFamily, fitText, }: OgPresetLayoutProps) => import("react/jsx-runtime").JSX.Element;
|
package/dist/layouts/preset.js
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { MONO_FONT_FAMILY, OG_HEIGHT, OG_WIDTH } from '../constants.js';
|
|
3
3
|
import { getBrandGlow, getBrandGlowCss } from './brand-glow.js';
|
|
4
|
-
import { ClassicFrame } from './classic-frame.js';
|
|
4
|
+
import { CLASSIC_CONTENT_BOUNDS, ClassicFrame } from './classic-frame.js';
|
|
5
5
|
import { getFontStack } from './font-stack.js';
|
|
6
|
+
const PANEL = { left: 89.386, top: 435.728, width: 1021.23, radius: 48 };
|
|
7
|
+
const LOGO_BOX = {
|
|
8
|
+
height: 56,
|
|
9
|
+
topLeft: { left: 89.649, top: 82.788 },
|
|
10
|
+
bottomRight: { bottom: 80.924, right: 86 },
|
|
11
|
+
bottomCenter: { bottom: 56.924 },
|
|
12
|
+
};
|
|
6
13
|
const PRESET_SETTINGS = {
|
|
7
14
|
classic: {
|
|
8
15
|
align: 'start',
|
|
9
16
|
contentLeft: 89.649,
|
|
10
17
|
contentTop: 264,
|
|
18
|
+
contentBounds: CLASSIC_CONTENT_BOUNDS,
|
|
11
19
|
eyebrowGap: 22.741,
|
|
12
20
|
textGap: 32,
|
|
13
21
|
titleSize: 77.868,
|
|
@@ -24,6 +32,7 @@ const PRESET_SETTINGS = {
|
|
|
24
32
|
align: 'start',
|
|
25
33
|
contentLeft: 106,
|
|
26
34
|
contentTop: 88.117,
|
|
35
|
+
contentBounds: { top: 0, bottom: OG_HEIGHT - LOGO_BOX.bottomRight.bottom - LOGO_BOX.height },
|
|
27
36
|
eyebrowGap: 52,
|
|
28
37
|
textGap: 32,
|
|
29
38
|
titleSize: 77.868,
|
|
@@ -40,6 +49,7 @@ const PRESET_SETTINGS = {
|
|
|
40
49
|
align: 'center',
|
|
41
50
|
contentLeft: 0,
|
|
42
51
|
contentTop: 81.032,
|
|
52
|
+
contentBounds: { top: 0, bottom: PANEL.top },
|
|
43
53
|
eyebrowGap: 52,
|
|
44
54
|
textGap: 28,
|
|
45
55
|
titleSize: 70,
|
|
@@ -53,13 +63,10 @@ const PRESET_SETTINGS = {
|
|
|
53
63
|
textLines: 3,
|
|
54
64
|
},
|
|
55
65
|
};
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
bottomRight: { bottom: 80.924, right: 86 },
|
|
61
|
-
bottomCenter: { bottom: 56.924 },
|
|
62
|
-
};
|
|
66
|
+
export const getPresetTitleBox = (preset) => ({
|
|
67
|
+
width: PRESET_SETTINGS[preset].titleWidth,
|
|
68
|
+
size: PRESET_SETTINGS[preset].titleSize,
|
|
69
|
+
});
|
|
63
70
|
const BAR = { border: 2, padding: 25.229 };
|
|
64
71
|
const PILL = { paddingBlock: 5.5, paddingInline: 15 };
|
|
65
72
|
const TRACKING = -0.02;
|
|
@@ -141,6 +148,13 @@ export const PresetLayout = ({ preset, content, palette, backgroundImageUrl, eye
|
|
|
141
148
|
maxLines: Math.min(MAX_TEXT_LINES, settings.textLines - titleLines.length),
|
|
142
149
|
};
|
|
143
150
|
const descriptionLines = description ? fitText(description, descriptionBox) : [];
|
|
151
|
+
const textGap = titleLines.length > 0 && descriptionLines.length > 0 ? settings.textGap : 0;
|
|
152
|
+
const contentHeight = titleLines.length * settings.titleSize +
|
|
153
|
+
descriptionLines.length * DESCRIPTION.lineHeight +
|
|
154
|
+
textGap;
|
|
155
|
+
const contentTop = eyebrow
|
|
156
|
+
? settings.contentTop
|
|
157
|
+
: Math.max(settings.contentTop, (settings.contentBounds.top + settings.contentBounds.bottom - contentHeight) / 2);
|
|
144
158
|
const glow = getBrandGlow(preset, palette);
|
|
145
159
|
const glowGradient = getBrandGlowCss(glow);
|
|
146
160
|
return (_jsxs("div", { style: Object.assign(Object.assign({ position: 'relative', display: 'flex', width: `${OG_WIDTH}px`, height: `${OG_HEIGHT}px`, backgroundColor: palette.background }, (backgroundImageUrl
|
|
@@ -149,7 +163,7 @@ export const PresetLayout = ({ preset, content, palette, backgroundImageUrl, eye
|
|
|
149
163
|
borderTop: `1px solid ${palette.panelBorder}`,
|
|
150
164
|
borderLeft: `1px solid ${palette.panelBorder}`,
|
|
151
165
|
borderRight: `1px solid ${palette.panelBorder}`,
|
|
152
|
-
})) })), logo && (_jsx("div", { style: getLogoBoxStyle(settings.logo), children: _jsx(Logo, { logo: logo }) })), _jsxs("div", { style: Object.assign(Object.assign({ position: 'absolute', top: `${
|
|
166
|
+
})) })), logo && (_jsx("div", { style: getLogoBoxStyle(settings.logo), children: _jsx(Logo, { logo: logo }) })), _jsxs("div", { style: Object.assign(Object.assign({ position: 'absolute', top: `${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: {
|
|
153
167
|
display: 'flex',
|
|
154
168
|
flexDirection: 'column',
|
|
155
169
|
alignItems: isCentered ? 'center' : 'flex-start',
|