Skip to content

Commit 0273e66

Browse files
committed
ogimage
1 parent e244736 commit 0273e66

File tree

8 files changed

+85
-148
lines changed

8 files changed

+85
-148
lines changed

bun.lock

+2-2
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@
254254
},
255255
"overrides": {
256256
"@codemirror/state": "6.4.1",
257-
"@gitbook/api": "0.99.0",
257+
"@gitbook/api": "https://pkg.pr.new/GitbookIO/integrations/@gitbook/api@759",
258258
"react": "18.3.1",
259259
"react-dom": "18.3.1",
260260
},
@@ -615,7 +615,7 @@
615615

616616
"@fortawesome/fontawesome-svg-core": ["@fortawesome/[email protected]", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "6.6.0" } }, "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg=="],
617617

618-
"@gitbook/api": ["@gitbook/api@0.99.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-qaHNCKul6rp0wqKRQ/2/2+X8mNEmy5ZK4vWuZoI9eL306TTqFBWD4DSTHz0WV6X24FVe3HF7qy/mVRd4ja2I2A=="],
618+
"@gitbook/api": ["@gitbook/api@https://pkg.pr.new/GitbookIO/integrations/@gitbook/api@759", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }],
619619

620620
"@gitbook/cache-do": ["@gitbook/cache-do@workspace:packages/cache-do"],
621621

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"@codemirror/state": "6.4.1",
1313
"react": "18.3.1",
1414
"react-dom": "18.3.1",
15-
"@gitbook/api": "0.99.0"
15+
"@gitbook/api": "https://pkg.pr.new/GitbookIO/integrations/@gitbook/api@759"
1616
},
1717
"private": true,
1818
"scripts": {

packages/gitbook/package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,15 @@
3333
"@tailwindcss/container-queries": "^0.1.1",
3434
"@tailwindcss/typography": "^0.5.16",
3535
"@upstash/redis": "^1.27.1",
36+
"ai": "^4.1.46",
3637
"ajv": "^8.12.0",
3738
"assert-never": "^1.2.1",
3839
"bun-types": "^1.1.20",
3940
"classnames": "^2.5.1",
4041
"framer-motion": "^10.16.14",
4142
"js-cookie": "^3.0.5",
4243
"jsontoxml": "^1.0.1",
44+
"jwt-decode": "^4.0.0",
4345
"katex": "^0.16.9",
4446
"mathjax": "^3.2.2",
4547
"mdast-util-to-markdown": "^2.1.2",
@@ -62,9 +64,7 @@
6264
"tailwind-merge": "^2.2.0",
6365
"tailwind-shades": "^1.1.2",
6466
"url-join": "^5.0.0",
65-
"usehooks-ts": "^3.1.0",
66-
"ai": "^4.1.46",
67-
"jwt-decode": "^4.0.0"
67+
"usehooks-ts": "^3.1.0"
6868
},
6969
"devDependencies": {
7070
"@argos-ci/playwright": "^3.10.0",

packages/gitbook/src/components/RootLayout/CustomizationRootLayout.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ import {
2323
import { IconStyle, IconsProvider } from '@gitbook/icons';
2424
import * as ReactDOM from 'react-dom';
2525

26-
import { fontNotoColorEmoji, getFontData } from '@/fonts';
26+
import { getFontData } from '@/fonts';
27+
import { fontNotoColorEmoji } from '@/fonts/default';
2728
import { getSpaceLanguage } from '@/intl/server';
2829
import { getAssetURL } from '@/lib/assets';
2930
import { tcls } from '@/lib/tailwind';

packages/gitbook/src/fonts/custom.test.ts

+11-14
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
import { describe, expect, test } from 'bun:test';
2+
import type { CustomizationFontDefinition } from '@gitbook/api';
23
import stylelint from 'stylelint';
3-
import {
4-
type CustomizationFontDefinition,
5-
generateFontFacesCSS,
6-
getFontSourcesToPreload,
7-
} from './custom';
4+
import { generateFontFacesCSS, getFontSourcesToPreload } from './custom';
85

9-
const TEST_FONTS = {
6+
const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
107
basic: {
118
id: 'open-sans',
129
fontFamily: 'Open Sans',
@@ -30,7 +27,7 @@ const TEST_FONTS = {
3027
],
3128
},
3229
],
33-
} as CustomizationFontDefinition,
30+
},
3431

3532
multiWeight: {
3633
id: 'roboto',
@@ -82,7 +79,7 @@ const TEST_FONTS = {
8279
],
8380
},
8481
],
85-
} as CustomizationFontDefinition,
82+
},
8683

8784
multiSource: {
8885
id: 'lato',
@@ -99,7 +96,7 @@ const TEST_FONTS = {
9996
],
10097
},
10198
],
102-
} as CustomizationFontDefinition,
99+
},
103100

104101
missingFormat: {
105102
id: 'source-sans',
@@ -116,13 +113,13 @@ const TEST_FONTS = {
116113
],
117114
},
118115
],
119-
} as CustomizationFontDefinition,
116+
},
120117

121118
empty: {
122119
id: 'empty-font',
123120
fontFamily: 'Empty Font',
124121
fontFaces: [],
125-
} as CustomizationFontDefinition,
122+
},
126123

127124
specialChars: {
128125
id: 'special-font',
@@ -133,7 +130,7 @@ const TEST_FONTS = {
133130
sources: [{ url: 'https://example.com/fonts/special.woff2', format: 'woff2' }],
134131
},
135132
],
136-
} as CustomizationFontDefinition,
133+
},
137134

138135
complex: {
139136
id: 'complex-font',
@@ -154,7 +151,7 @@ const TEST_FONTS = {
154151
],
155152
},
156153
],
157-
} as CustomizationFontDefinition,
154+
},
158155

159156
variousURLs: {
160157
id: 'various-urls',
@@ -169,7 +166,7 @@ const TEST_FONTS = {
169166
],
170167
},
171168
],
172-
} as CustomizationFontDefinition,
169+
},
173170
};
174171

175172
// Helper function to validate CSS with stylelint

packages/gitbook/src/fonts/custom.ts

+1-56
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,4 @@
1-
import type { CustomizationDefaultFont } from '@gitbook/api';
2-
3-
/**
4-
* The human-readable font-family name used in CSS (e.g., "Open Sans", "Playfair Display").
5-
* @minLength 1
6-
* @maxLength 50
7-
*/
8-
export type FontFamily = string;
9-
10-
/**
11-
* Numeric representation of the font weight (400=regular, 500=medium, 700=bold, 900=black).
12-
* @min 1
13-
* @max 1000
14-
*/
15-
export type FontWeight = number;
16-
17-
/** A font file referenced within a font-face declaration, specifying the file's location and format. */
18-
export interface FontSource {
19-
/** The absolute or relative URL pointing to the font file. */
20-
url: string;
21-
/** The format of the font file. Prefer 'woff2' for modern browsers. */
22-
format?: 'woff2' | 'woff';
23-
}
24-
25-
/** A single font-face declaration specifying the weight and source files for a particular variation of the font. */
26-
export interface FontFace {
27-
/** Numeric representation of the font weight (400=regular, 500=medium, 700=bold, 900=black). */
28-
weight: FontWeight;
29-
/**
30-
* Font source files provided in supported formats (e.g., woff2, woff).
31-
* @minItems 1
32-
*/
33-
sources: FontSource[];
34-
}
35-
36-
/** Defines a font family along with its various font-face declarations for use in CSS '@font-face' rules. */
37-
export interface CustomizationFontDefinition {
38-
/** A globally unique identifier for the font definition. */
39-
id: string;
40-
/** The human-readable font-family name used in CSS (e.g., "Open Sans", "Playfair Display"). */
41-
fontFamily: FontFamily;
42-
/**
43-
* A list of font-face definitions, specifying variations such as weight and style.
44-
* @minItems 1
45-
*/
46-
fontFaces: FontFace[];
47-
}
48-
49-
/**
50-
* The path of the file in the storage bucket
51-
* @minLength 1
52-
* @maxLength 512
53-
*/
54-
export type StorageFileKey = string;
55-
56-
export type CustomizationFont = CustomizationDefaultFont | CustomizationFontDefinition;
1+
import type { CustomizationFontDefinition, FontSource } from '@gitbook/api';
572

583
/**
594
* Define the custom font faces and set the --font-content to the custom font name

packages/gitbook/src/fonts/index.ts

+3-62
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,22 @@
1-
import type { CustomizationDefaultFont } from '@gitbook/api';
1+
import type { CustomizationFont, FontSource } from '@gitbook/api';
22
import { generateFontFacesCSS, getFontSourcesToPreload } from './custom';
33
import { fonts } from './default';
44

5-
export * from './default';
6-
export * from './custom';
7-
8-
/**
9-
* The human-readable font-family name used in CSS (e.g., "Open Sans", "Playfair Display").
10-
* @minLength 1
11-
* @maxLength 50
12-
*/
13-
export type FontFamily = string;
14-
15-
/**
16-
* Numeric representation of the font weight (400=regular, 500=medium, 700=bold, 900=black).
17-
* @min 1
18-
* @max 1000
19-
*/
20-
export type FontWeight = number;
21-
22-
/** A font file referenced within a font-face declaration, specifying the file's location and format. */
23-
export interface FontSource {
24-
/** The absolute or relative URL pointing to the font file. */
25-
url: string;
26-
/** The format of the font file. Prefer 'woff2' for modern browsers. */
27-
format?: 'woff2' | 'woff';
28-
}
29-
30-
/** A single font-face declaration specifying the weight and source files for a particular variation of the font. */
31-
export interface FontFace {
32-
/** Numeric representation of the font weight (400=regular, 500=medium, 700=bold, 900=black). */
33-
weight: FontWeight;
34-
/**
35-
* Font source files provided in supported formats (e.g., woff2, woff).
36-
* @minItems 1
37-
*/
38-
sources: FontSource[];
39-
}
40-
41-
/** Defines a font family along with its various font-face declarations for use in CSS '@font-face' rules. */
42-
export interface CustomizationFontDefinition {
43-
/** A globally unique identifier for the font definition. */
44-
id: string;
45-
/** The human-readable font-family name used in CSS (e.g., "Open Sans", "Playfair Display"). */
46-
fontFamily: FontFamily;
47-
/**
48-
* A list of font-face definitions, specifying variations such as weight and style.
49-
* @minItems 1
50-
*/
51-
fontFaces: FontFace[];
52-
}
53-
54-
/**
55-
* The path of the file in the storage bucket
56-
* @minLength 1
57-
* @maxLength 512
58-
*/
59-
export type StorageFileKey = string;
60-
61-
export type CustomizationFont = CustomizationDefaultFont | CustomizationFontDefinition;
625
/**
636
* Represents font data for either a default font or a custom font
647
*/
658
type FontData = DefaultFontData | CustomFontData;
669

6710
/**
68-
* Font data for a default font from next/font
11+
* Font data for a default font, currently handle with next/font
6912
*/
7013
interface DefaultFontData {
7114
type: 'default';
7215
variable: string;
7316
}
7417

7518
/**
76-
* Font data for a custom font with @font-face definitions
19+
* Font data for a custom font with @font-face rules
7720
*/
7821
interface CustomFontData {
7922
type: 'custom';
@@ -86,14 +29,12 @@ interface CustomFontData {
8629
*/
8730
export function getFontData(font: CustomizationFont): FontData {
8831
if (typeof font === 'string') {
89-
// Default font from next/font
9032
return {
9133
type: 'default',
9234
variable: fonts[font].variable,
9335
};
9436
}
9537

96-
// Custom font with @font-face rules
9738
return {
9839
type: 'custom',
9940
fontFaceRules: generateFontFacesCSS(font),

packages/gitbook/src/routes/ogimage.tsx

+62-9
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,53 @@ export async function serveOGImage(baseContext: GitBookSiteContext, params: Page
5959
: page.description
6060
: '';
6161

62-
const fontFamily = googleFontsMap[customization.styling.font] ?? 'Inter';
62+
// Load the fonts
63+
const { fontFamily, fonts } = await (async () => {
64+
// google fonts
65+
if (typeof customization.styling.font === 'string') {
66+
const fontFamily = googleFontsMap[customization.styling.font] ?? 'Inter';
6367

64-
const regularText = pageDescription;
65-
const boldText = `${contentTitle}${pageTitle}`;
68+
const regularText = pageDescription;
69+
const boldText = `${contentTitle}${pageTitle}`;
6670

67-
const fonts = (
68-
await Promise.all([
69-
loadGoogleFont({ fontFamily, text: regularText, weight: 400 }),
70-
loadGoogleFont({ fontFamily, text: boldText, weight: 700 }),
71-
])
72-
).filter(filterOutNullable);
71+
const fonts = (
72+
await Promise.all([
73+
loadGoogleFont({ fontFamily, text: regularText, weight: 400 }),
74+
loadGoogleFont({ fontFamily, text: boldText, weight: 700 }),
75+
])
76+
).filter(filterOutNullable);
77+
78+
return { fontFamily, fonts };
79+
}
80+
81+
// custom fonts
82+
const primaryFontSources = customization.styling.font.fontFaces
83+
.filter((face): face is typeof face & { weight: 400 | 700 } => {
84+
return face.weight === 400 || face.weight === 700;
85+
})
86+
.map((face) => {
87+
if (face.sources.length === 0) {
88+
return null;
89+
}
90+
91+
return {
92+
weight: face.weight,
93+
// just load the first source, fromat is not that important here
94+
url: face.sources[0].url,
95+
} as const;
96+
})
97+
.filter(filterOutNullable);
98+
99+
const fonts = (
100+
await Promise.all(
101+
primaryFontSources.map((source) => {
102+
return loadCustomFont({ url: source.url, weight: source.weight });
103+
})
104+
)
105+
).filter(filterOutNullable);
106+
107+
return { fontFamily: 'CustomFont', fonts };
108+
})();
73109

74110
const theme = customization.themes.default;
75111
const useLightTheme = theme === 'light';
@@ -242,3 +278,20 @@ async function loadGoogleFont(input: { fontFamily: string; text: string; weight:
242278
// If for some reason we can't load the font, we'll just use the default one
243279
return null;
244280
}
281+
282+
async function loadCustomFont(input: { url: string; weight: 400 | 700 }) {
283+
const { url, weight } = input;
284+
const response = await fetch(url);
285+
if (!response.ok) {
286+
return null;
287+
}
288+
289+
const data = await response.arrayBuffer();
290+
291+
return {
292+
name: 'CustomFont',
293+
data,
294+
style: 'normal' as const,
295+
weight,
296+
};
297+
}

0 commit comments

Comments
 (0)