Skip to content

Commit 3119066

Browse files
authored
Support for reusable content across spaces. (#3173)
1 parent 326e28e commit 3119066

File tree

10 files changed

+302
-152
lines changed

10 files changed

+302
-152
lines changed

.changeset/polite-falcons-agree.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"gitbook-v2": minor
3+
"gitbook": minor
4+
---
5+
6+
Add support for reusable content across spaces.

bun.lock

+2-2
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@
259259
},
260260
"overrides": {
261261
"@codemirror/state": "6.4.1",
262-
"@gitbook/api": "0.111.0",
262+
"@gitbook/api": "0.113.0",
263263
"react": "^19.0.0",
264264
"react-dom": "^19.0.0",
265265
},
@@ -624,7 +624,7 @@
624624

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

627-
"@gitbook/api": ["@gitbook/api@0.111.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-E5Pk28kPD4p6XNWdwFM9pgDijdByseIZQqcFK+/hoW5tEZa5Yw/plRKJyN1hmwfPL6SKq6Maf0fbIzTQiVXyQQ=="],
627+
"@gitbook/api": ["@gitbook/api@0.113.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-PWMeAkdm4bHSl3b5OmtcmskZ6qRkkDhauCPybo8sGnjS03O14YAUtubAQiNCKX/uwbs+yiQ8KRPyeIwn+g42yw=="],
628628

629629
"@gitbook/cache-do": ["@gitbook/cache-do@workspace:packages/cache-do"],
630630

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"packageManager": "[email protected]",
1111
"overrides": {
1212
"@codemirror/state": "6.4.1",
13-
"@gitbook/api": "0.111.0",
13+
"@gitbook/api": "0.113.0",
1414
"react": "^19.0.0",
1515
"react-dom": "^19.0.0"
1616
},

packages/gitbook/src/components/DocumentView/Integration/contentkit.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ export const contentKitServerContext: ContentKitServerContext = {
2020
'link-external': (props) => <Icon icon="arrow-up-right-from-square" {...props} />,
2121
eye: (props) => <Icon icon="eye" {...props} />,
2222
lock: (props) => <Icon icon="lock" {...props} />,
23+
check: (props) => <Icon icon="check" {...props} />,
24+
'check-circle': (props) => <Icon icon="check-circle" {...props} />,
2325
},
2426
codeBlock: (props) => {
2527
return <PlainCodeBlock code={props.code} syntax={props.syntax} />;

packages/gitbook/src/components/DocumentView/ReusableContent.tsx

+18-5
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,28 @@ export async function ReusableContent(props: BlockProps<DocumentBlockReusableCon
1313
throw new Error('Expected a content context to render a reusable content block');
1414
}
1515

16-
const resolved = await resolveContentRef(block.data.ref, context.contentContext);
17-
if (!resolved?.reusableContent?.document) {
16+
const dataFetcher = block.meta?.token
17+
? context.contentContext.dataFetcher.withToken({ apiToken: block.meta.token })
18+
: context.contentContext.dataFetcher;
19+
20+
const resolved = await resolveContentRef(block.data.ref, {
21+
...context.contentContext,
22+
dataFetcher,
23+
});
24+
25+
if (!resolved?.reusableContent) {
26+
return null;
27+
}
28+
29+
const reusableContent = resolved.reusableContent.revisionReusableContent;
30+
if (!reusableContent.document) {
1831
return null;
1932
}
2033

2134
const document = await getDataOrNull(
22-
context.contentContext.dataFetcher.getDocument({
23-
spaceId: context.contentContext.space.id,
24-
documentId: resolved.reusableContent.document,
35+
dataFetcher.getDocument({
36+
spaceId: resolved.reusableContent.space,
37+
documentId: reusableContent.document,
2538
})
2639
);
2740

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

+24
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
2828
],
2929
},
3030
],
31+
permissions: {
32+
edit: false,
33+
},
3134
},
3235

3336
multiWeight: {
@@ -81,6 +84,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
8184
],
8285
},
8386
],
87+
permissions: {
88+
edit: false,
89+
},
8490
},
8591

8692
multiSource: {
@@ -99,6 +105,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
99105
],
100106
},
101107
],
108+
permissions: {
109+
edit: false,
110+
},
102111
},
103112

104113
missingFormat: {
@@ -117,13 +126,19 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
117126
],
118127
},
119128
],
129+
permissions: {
130+
edit: false,
131+
},
120132
},
121133

122134
empty: {
123135
id: 'empty-font',
124136
custom: true,
125137
fontFamily: 'Empty Font',
126138
fontFaces: [],
139+
permissions: {
140+
edit: false,
141+
},
127142
},
128143

129144
specialChars: {
@@ -136,6 +151,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
136151
sources: [{ url: 'https://example.com/fonts/special.woff2', format: 'woff2' }],
137152
},
138153
],
154+
permissions: {
155+
edit: false,
156+
},
139157
},
140158

141159
complex: {
@@ -158,6 +176,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
158176
],
159177
},
160178
],
179+
permissions: {
180+
edit: false,
181+
},
161182
},
162183

163184
variousURLs: {
@@ -174,6 +195,9 @@ const TEST_FONTS: { [key in string]: CustomizationFontDefinition } = {
174195
],
175196
},
176197
],
198+
permissions: {
199+
edit: false,
200+
},
177201
},
178202
};
179203

packages/gitbook/src/fonts/custom.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import type { CustomizationFontDefinition } from '@gitbook/api';
1+
import type { CustomizationFontDefinitionInput } from '@gitbook/api';
22

33
/**
44
* Define the custom font faces and set the --font-custom to the custom font name
55
*/
6-
export function generateFontFacesCSS(customFont: CustomizationFontDefinition): string {
6+
export function generateFontFacesCSS(customFont: CustomizationFontDefinitionInput): string {
77
const { fontFaces } = customFont;
88

99
// Generate font face declarations for all weights
@@ -45,7 +45,7 @@ export function generateFontFacesCSS(customFont: CustomizationFontDefinition): s
4545
/**
4646
* Get a list of font sources to preload (only 400 and 700 weights)
4747
*/
48-
export function getFontSourcesToPreload(customFont: CustomizationFontDefinition) {
48+
export function getFontSourcesToPreload(customFont: CustomizationFontDefinitionInput) {
4949
return customFont.fontFaces.filter(
5050
(face): face is typeof face & { weight: 400 | 700 } =>
5151
face.weight === 400 || face.weight === 700

packages/gitbook/src/lib/api.ts

+1
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ export const getPublishedContentByUrl = cache({
267267

268268
const parsed = parseCacheResponse(response);
269269

270+
// biome-ignore lint/suspicious/noConsole: log the ttl of the token
270271
console.log(
271272
`Parsed ttl: ${parsed.ttl} at ${Date.now()}, for ${'apiToken' in response.data ? response.data.apiToken : '<no-token>'}`
272273
);

packages/gitbook/src/lib/references.tsx

+41-6
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,12 @@ export interface ResolvedContentRef {
4141
file?: RevisionFile;
4242
/** Page document resolved from the content ref */
4343
page?: RevisionPageDocument;
44-
/** Resolved reusable content, if the ref points to reusable content on a revision. */
45-
reusableContent?: RevisionReusableContent;
44+
/** Resolved reusable content, if the ref points to reusable content on a revision. Also contains the space and revision used for resolution. */
45+
reusableContent?: {
46+
revisionReusableContent: RevisionReusableContent;
47+
space: string;
48+
revision: string;
49+
};
4650
/** Resolve OpenAPI spec filesystem. */
4751
openAPIFilesystem?: Filesystem;
4852
}
@@ -231,21 +235,52 @@ export async function resolveContentRef(
231235
}
232236

233237
case 'reusable-content': {
238+
// Figure out which space and revision the reusable content is in.
239+
const container: { space: string; revision: string } | null = await (async () => {
240+
// without a space on the content ref, or if the space is the same as the current one, we can use the current revision.
241+
if (!contentRef.space || contentRef.space === context.space.id) {
242+
return { space: context.space.id, revision: revisionId };
243+
}
244+
245+
const space = await getDataOrNull(
246+
dataFetcher.getSpace({
247+
spaceId: contentRef.space,
248+
shareKey: undefined,
249+
})
250+
);
251+
252+
if (!space) {
253+
return null;
254+
}
255+
256+
return { space: space.id, revision: space.revision };
257+
})();
258+
259+
if (!container) {
260+
return null;
261+
}
262+
234263
const reusableContent = await getDataOrNull(
235264
dataFetcher.getReusableContent({
236-
spaceId: space.id,
237-
revisionId,
265+
spaceId: container.space,
266+
revisionId: container.revision,
238267
reusableContentId: contentRef.reusableContent,
239268
})
240269
);
270+
241271
if (!reusableContent) {
242272
return null;
243273
}
274+
244275
return {
245-
href: getGitBookAppHref(`/s/${space.id}`),
276+
href: getGitBookAppHref(`/s/${container.space}/~/reusable/${reusableContent.id}`),
246277
text: reusableContent.title,
247278
active: false,
248-
reusableContent,
279+
reusableContent: {
280+
revisionReusableContent: reusableContent,
281+
space: container.space,
282+
revision: container.revision,
283+
},
249284
};
250285
}
251286

0 commit comments

Comments
 (0)