Merge branch 'main' into ai-page-summaries

This commit is contained in:
Zeno Kapitein
2025-05-06 20:56:40 +02:00
14 changed files with 100 additions and 19 deletions
+8
View File
@@ -0,0 +1,8 @@
---
"@gitbook/react-openapi": patch
"gitbook-v2": patch
"gitbook": patch
"@gitbook/colors": patch
---
Fix code highlighting for HTTP
+5
View File
@@ -0,0 +1,5 @@
---
"gitbook": patch
---
Fix resolution of links in reusable contents
+5
View File
@@ -0,0 +1,5 @@
---
"gitbook": patch
---
Fix invalid sitemap.xml generated with relative URLs instead of absolute ones
+5
View File
@@ -0,0 +1,5 @@
---
"@gitbook/colors": patch
---
Change lightness check for color step 9 to allow input colors with a higher-than-needed contrast
+5
View File
@@ -0,0 +1,5 @@
---
'@gitbook/react-openapi': patch
---
Missing top-level required OpenAPI alternatives
+5
View File
@@ -0,0 +1,5 @@
---
"@gitbook/react-openapi": patch
---
Fix Python code sample "null vs None"
+5 -1
View File
@@ -214,7 +214,11 @@ export function colorScale(
const targetL =
foregroundColor.L * mapping[index] + backgroundColor.L * (1 - mapping[index]);
if (index === 8 && !mix && Math.abs(baseColor.L - targetL) < 0.2) {
if (
index === 8 &&
!mix &&
(darkMode ? targetL - baseColor.L < 0.2 : baseColor.L - targetL < 0.2)
) {
// Original colour is close enough to target, so let's use the original colour as step 9.
result.push(hex);
continue;
@@ -5,7 +5,7 @@
--shiki-token-link: theme("colors.primary.10");
--shiki-token-constant: theme("colors.warning.10");
--shiki-token-string: theme("colors.success.10");
--shiki-token-string: theme("colors.warning.10");
--shiki-token-string-expression: theme("colors.success.10");
--shiki-token-keyword: theme("colors.danger.10");
--shiki-token-parameter: theme("colors.warning.10");
@@ -24,7 +24,7 @@
--shiki-token-link: theme("colors.primary.11");
--shiki-token-constant: theme("colors.warning.11");
--shiki-token-string: theme("colors.success.11");
--shiki-token-string: theme("colors.warning.11");
--shiki-token-string-expression: theme("colors.success.11");
--shiki-token-keyword: theme("colors.danger.11");
--shiki-token-parameter: theme("colors.warning.11");
@@ -41,7 +41,7 @@ html.dark {
--shiki-token-comment: theme("colors.neutral.9");
--shiki-token-constant: theme("colors.warning.11");
--shiki-token-string: theme("colors.success.11");
--shiki-token-string: theme("colors.warning.11");
--shiki-token-string-expression: theme("colors.success.11");
--shiki-token-keyword: theme("colors.danger.11");
--shiki-token-parameter: theme("colors.warning.11");
@@ -46,14 +46,21 @@ export async function ReusableContent(props: BlockProps<DocumentBlockReusableCon
// Create a new context for reusable content block, including
// the data fetcher with the token from the block meta and the correct
// space and revision pointers.
const reusableContentContext: GitBookSpaceContext = {
...context.contentContext,
dataFetcher,
space: resolved.reusableContent.space,
revisionId: resolved.reusableContent.revision,
pages: [],
shareKey: undefined,
};
const reusableContentContext: GitBookSpaceContext =
context.contentContext.space.id === resolved.reusableContent.space.id
? context.contentContext
: {
...context.contentContext,
dataFetcher,
space: resolved.reusableContent.space,
revisionId: resolved.reusableContent.revision,
// When the reusable content is in a different space, we don't resolve relative links to pages
// as this space might not be part of the current site.
// In the future, we might expand the logic to look up the space from the list of all spaces in the site
// and adapt the relative links to point to the correct variant.
pages: [],
shareKey: undefined,
};
return (
<UnwrappedBlocks
-1
View File
@@ -242,7 +242,6 @@ export const getLatestOpenAPISpecVersionContent = cache({
* Resolve a URL to the content to render.
*/
export const getPublishedContentByUrl = cache({
timeout: 30 * 1000,
name: 'api.getPublishedContentByUrl.v7',
tag: (url) => getCacheTagForURL(url),
get: async (
+1 -1
View File
@@ -141,7 +141,7 @@ function getUrlsFromSiteSpaces(context: GitBookSiteContext, siteSpaces: SiteSpac
}
const url = new URL(siteSpace.urls.published);
url.pathname = joinPath(url.pathname, 'sitemap-pages.xml');
return context.linker.toLinkForContent(url.toString());
return context.linker.toAbsoluteURL(context.linker.toLinkForContent(url.toString()));
}, []);
return urls.filter(filterOutNullable);
}
+36 -2
View File
@@ -572,6 +572,9 @@ function flattenAlternatives(
schemasOrRefs: (OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject)[],
ancestors: Set<OpenAPIV3.SchemaObject>
): OpenAPIV3.SchemaObject[] {
// Get the parent schema's required fields from the most recent ancestor
const latestAncestor = Array.from(ancestors).pop();
return schemasOrRefs.reduce<OpenAPIV3.SchemaObject[]>((acc, schemaOrRef) => {
if (checkIsReference(schemaOrRef)) {
return acc;
@@ -580,16 +583,47 @@ function flattenAlternatives(
if (schemaOrRef[alternativeType] && !ancestors.has(schemaOrRef)) {
const schemas = getSchemaAlternatives(schemaOrRef, ancestors);
if (schemas) {
acc.push(...schemas);
acc.push(
...schemas.map((schema) => ({
...schema,
required: mergeRequiredFields(schema, latestAncestor),
}))
);
}
return acc;
}
acc.push(schemaOrRef);
// For direct schemas, handle required fields
const schema = {
...schemaOrRef,
required: mergeRequiredFields(schemaOrRef, latestAncestor),
};
acc.push(schema);
return acc;
}, []);
}
/**
* Merge the required fields of a schema with the required fields of its latest ancestor.
*/
function mergeRequiredFields(
schemaOrRef: OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject,
latestAncestor: OpenAPIV3.SchemaObject | undefined
) {
if (!schemaOrRef.required && !latestAncestor?.required) {
return undefined;
}
if (checkIsReference(schemaOrRef)) {
return latestAncestor?.required;
}
return Array.from(
new Set([...(latestAncestor?.required || []), ...(schemaOrRef.required || [])])
);
}
function getSchemaTitle(schema: OpenAPIV3.SchemaObject): string {
// Otherwise try to infer a nice title
let type = 'any';
@@ -415,13 +415,14 @@ describe('python code sample generator', () => {
key: 'value',
truethy: true,
falsey: false,
nullish: null,
},
};
const output = generator?.generate(input);
expect(output).toBe(
'import requests\n\nresponse = requests.get(\n "https://example.com/path",\n headers={"Content-Type":"application/json"},\n data=json.dumps({"key":"value","truethy":True,"falsey":False})\n)\n\ndata = response.json()'
'import requests\n\nresponse = requests.get(\n "https://example.com/path",\n headers={"Content-Type":"application/json"},\n data=json.dumps({"key":"value","truethy":True,"falsey":False,"nullish":None})\n)\n\ndata = response.json()'
);
});
+5 -2
View File
@@ -30,7 +30,7 @@ export const codeSampleGenerators: CodeSampleGenerator[] = [
{
id: 'http',
label: 'HTTP',
syntax: 'bash',
syntax: 'http',
generate: ({ method, url, headers = {}, body }: CodeSampleInput) => {
const { host, path } = parseHostAndPath(url);
@@ -362,12 +362,15 @@ const BodyGenerators = {
return '$$__TRUE__$$';
case false:
return '$$__FALSE__$$';
case null:
return '$$__NULL__$$';
default:
return value;
}
})
.replaceAll('"$$__TRUE__$$"', 'True')
.replaceAll('"$$__FALSE__$$"', 'False');
.replaceAll('"$$__FALSE__$$"', 'False')
.replaceAll('"$$__NULL__$$"', 'None');
}
return { body, code, headers };