diff --git a/.changeset/lemon-plants-vanish.md b/.changeset/lemon-plants-vanish.md new file mode 100644 index 000000000..0887b10be --- /dev/null +++ b/.changeset/lemon-plants-vanish.md @@ -0,0 +1,8 @@ +--- +"@gitbook/react-openapi": patch +"gitbook-v2": patch +"gitbook": patch +"@gitbook/colors": patch +--- + +Fix code highlighting for HTTP diff --git a/.changeset/moody-maps-chew.md b/.changeset/moody-maps-chew.md new file mode 100644 index 000000000..0c06afc2a --- /dev/null +++ b/.changeset/moody-maps-chew.md @@ -0,0 +1,5 @@ +--- +"gitbook": patch +--- + +Fix resolution of links in reusable contents diff --git a/.changeset/real-donkeys-invent.md b/.changeset/real-donkeys-invent.md new file mode 100644 index 000000000..a322af7d7 --- /dev/null +++ b/.changeset/real-donkeys-invent.md @@ -0,0 +1,5 @@ +--- +"gitbook": patch +--- + +Fix invalid sitemap.xml generated with relative URLs instead of absolute ones diff --git a/.changeset/rich-lamps-compare.md b/.changeset/rich-lamps-compare.md new file mode 100644 index 000000000..57b026b7b --- /dev/null +++ b/.changeset/rich-lamps-compare.md @@ -0,0 +1,5 @@ +--- +"@gitbook/colors": patch +--- + +Change lightness check for color step 9 to allow input colors with a higher-than-needed contrast diff --git a/.changeset/shy-plums-deny.md b/.changeset/shy-plums-deny.md new file mode 100644 index 000000000..22193b58e --- /dev/null +++ b/.changeset/shy-plums-deny.md @@ -0,0 +1,5 @@ +--- +'@gitbook/react-openapi': patch +--- + +Missing top-level required OpenAPI alternatives diff --git a/.changeset/twelve-news-joke.md b/.changeset/twelve-news-joke.md new file mode 100644 index 000000000..3c0d08681 --- /dev/null +++ b/.changeset/twelve-news-joke.md @@ -0,0 +1,5 @@ +--- +"@gitbook/react-openapi": patch +--- + +Fix Python code sample "null vs None" diff --git a/packages/colors/src/transformations.ts b/packages/colors/src/transformations.ts index 90cb4482e..8234a460a 100644 --- a/packages/colors/src/transformations.ts +++ b/packages/colors/src/transformations.ts @@ -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; diff --git a/packages/gitbook/src/components/DocumentView/CodeBlock/theme.css b/packages/gitbook/src/components/DocumentView/CodeBlock/theme.css index 21c0fd579..5e4a73a88 100644 --- a/packages/gitbook/src/components/DocumentView/CodeBlock/theme.css +++ b/packages/gitbook/src/components/DocumentView/CodeBlock/theme.css @@ -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"); diff --git a/packages/gitbook/src/components/DocumentView/ReusableContent.tsx b/packages/gitbook/src/components/DocumentView/ReusableContent.tsx index bbf2d23f7..ccb66babd 100644 --- a/packages/gitbook/src/components/DocumentView/ReusableContent.tsx +++ b/packages/gitbook/src/components/DocumentView/ReusableContent.tsx @@ -46,14 +46,21 @@ export async function ReusableContent(props: BlockProps getCacheTagForURL(url), get: async ( diff --git a/packages/gitbook/src/routes/sitemap.ts b/packages/gitbook/src/routes/sitemap.ts index 4a64e1238..a5f5e452f 100644 --- a/packages/gitbook/src/routes/sitemap.ts +++ b/packages/gitbook/src/routes/sitemap.ts @@ -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); } diff --git a/packages/react-openapi/src/OpenAPISchema.tsx b/packages/react-openapi/src/OpenAPISchema.tsx index 007c335cf..7841b1c0f 100644 --- a/packages/react-openapi/src/OpenAPISchema.tsx +++ b/packages/react-openapi/src/OpenAPISchema.tsx @@ -572,6 +572,9 @@ function flattenAlternatives( schemasOrRefs: (OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject)[], ancestors: Set ): OpenAPIV3.SchemaObject[] { + // Get the parent schema's required fields from the most recent ancestor + const latestAncestor = Array.from(ancestors).pop(); + return schemasOrRefs.reduce((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'; diff --git a/packages/react-openapi/src/code-samples.test.ts b/packages/react-openapi/src/code-samples.test.ts index 363baf552..7fd24bb3f 100644 --- a/packages/react-openapi/src/code-samples.test.ts +++ b/packages/react-openapi/src/code-samples.test.ts @@ -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()' ); }); diff --git a/packages/react-openapi/src/code-samples.ts b/packages/react-openapi/src/code-samples.ts index fe44da11d..50c6a9204 100644 --- a/packages/react-openapi/src/code-samples.ts +++ b/packages/react-openapi/src/code-samples.ts @@ -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 };