Add basic support for computed pages types (#2520)

This commit is contained in:
Samy Pessé
2024-10-14 10:04:12 +02:00
committed by GitHub
parent 79f2d8ba76
commit c3da5469ca
16 changed files with 96 additions and 36 deletions
BIN
View File
Binary file not shown.
+1 -1
View File
@@ -8,7 +8,7 @@
},
"packageManager": "bun@1.1.18",
"patchedDependencies": {
"@vercel/next@4.3.6": "patches/@vercel%2Fnext@4.3.6.patch"
"@vercel/next@4.3.15": "patches/@vercel%2Fnext@4.3.15.patch"
},
"private": true,
"scripts": {
+2 -2
View File
@@ -16,7 +16,7 @@
"clean": "rm -rf ./.next && rm -rf ./public/~gitbook/static"
},
"dependencies": {
"@gitbook/api": "0.64.1",
"@gitbook/api": "^0.66.0",
"@gitbook/cache-do": "workspace:*",
"@gitbook/emoji-codepoints": "workspace:*",
"@gitbook/icons": "workspace:*",
@@ -41,7 +41,7 @@
"katex": "^0.16.9",
"mathjax": "^3.2.2",
"memoizee": "^0.4.15",
"next": "^14.2.5",
"next": "14.2.15",
"next-themes": "^0.2.1",
"nuqs": "^1.17.4",
"object-hash": "^3.0.0",
@@ -84,10 +84,12 @@ function flattenPages(
return [
...(page.type === 'document' ? [{ page, depth }] : []),
...page.pages.flatMap((child) =>
child.type === 'link' ? [] : flattenPage(child, depth + 1),
child.type === 'document' ? flattenPage(child, depth + 1) : [],
),
];
};
return rootPags.flatMap((page) => (page.type === 'link' ? [] : flattenPage(page, 0)));
return rootPags.flatMap((page) =>
page.type === 'group' || page.type === 'document' ? flattenPage(page, 0) : [],
);
}
@@ -315,7 +315,7 @@ function selectPages(
return [
{ page, depth },
...page.pages.flatMap((child) => {
if (child.type === 'link') {
if (child.type !== 'document') {
return [];
}
@@ -350,7 +350,7 @@ function selectPages(
}
const allPages = rootPages.flatMap((page) => {
if (page.type === 'link') {
if (page.type !== 'document' && page.type !== 'group') {
return [];
}
@@ -63,7 +63,7 @@ export async function IntegrationBlock(props: BlockProps<DocumentBlockIntegratio
renderIntegrationUi(block.data.integration, initialInput),
true,
);
if (!initialOutput) {
if (!initialOutput || initialOutput.type === 'complete') {
return null;
}
@@ -17,7 +17,9 @@ export async function PageBodyBlankslate(props: {
}) {
const { page, rootPages, context } = props;
const pages = page.pages.filter((child) => (child.type === 'document' ? !child.hidden : true));
const pages = page.pages.filter((child) =>
child.type === RevisionPageType.Document ? !child.hidden : true,
);
if (!pages.length) {
return null;
}
@@ -43,7 +45,11 @@ export async function PageBodyBlankslate(props: {
/>
);
if (child.type === RevisionPageType.Link) {
if (child.type === RevisionPageType.Computed) {
throw new Error(
'Unexpected computed page, it should have been computed in the API',
);
} else if (child.type === RevisionPageType.Link) {
const resolved = await resolveContentRef(child.target, context);
if (!resolved) {
return null;
@@ -1,4 +1,9 @@
import { RevisionPage, RevisionPageDocument, RevisionPageGroup } from '@gitbook/api';
import {
RevisionPage,
RevisionPageDocument,
RevisionPageGroup,
RevisionPageType,
} from '@gitbook/api';
import { ContentRefContext } from '@/lib/references';
import { ClassValue, tcls } from '@/lib/tailwind';
@@ -19,7 +24,13 @@ export function PagesList(props: {
return (
<ul className={tcls('flex', 'flex-1', 'flex-col', 'gap-y-0.5', style)}>
{pages.map((page) => {
if (page.type === 'link') {
if (page.type === RevisionPageType.Computed) {
throw new Error(
'Unexpected computed page, it should have been computed in the API',
);
}
if (page.type === RevisionPageType.Link) {
return <PageLinkItem key={page.id} page={page} context={context} />;
}
@@ -27,7 +38,7 @@ export function PagesList(props: {
return null;
}
if (page.type === 'group') {
if (page.type === RevisionPageType.Group) {
return (
<PageGroupItem
key={page.id}
+13 -7
View File
@@ -1,4 +1,10 @@
import { Revision, RevisionPage, RevisionPageDocument, RevisionPageGroup } from '@gitbook/api';
import {
Revision,
RevisionPage,
RevisionPageDocument,
RevisionPageGroup,
RevisionPageType,
} from '@gitbook/api';
export type AncestorRevisionPage = RevisionPageDocument | RevisionPageGroup;
@@ -14,7 +20,7 @@ export function resolvePagePath(
ancestors: AncestorRevisionPage[],
): { page: RevisionPageDocument; ancestors: AncestorRevisionPage[] } | undefined => {
for (const page of pages) {
if (page.type === 'link') {
if (page.type === RevisionPageType.Link || page.type === RevisionPageType.Computed) {
continue;
}
@@ -56,7 +62,7 @@ export function resolvePageId(
ancestors: AncestorRevisionPage[],
): { page: RevisionPageDocument; ancestors: AncestorRevisionPage[] } | undefined => {
for (const page of pages) {
if (page.type === 'link') {
if (page.type === RevisionPageType.Link || page.type === RevisionPageType.Computed) {
continue;
}
@@ -139,14 +145,14 @@ function resolvePageDocument(
page: RevisionPage,
ancestors: AncestorRevisionPage[],
): { page: RevisionPageDocument; ancestors: AncestorRevisionPage[] } | undefined {
if (page.type === 'group') {
if (page.type === RevisionPageType.Group) {
const firstDocument = resolveFirstDocument(page.pages, [...ancestors, page]);
if (firstDocument) {
return firstDocument;
}
return;
} else if (page.type === 'link') {
} else if (page.type === RevisionPageType.Link || page.type === RevisionPageType.Computed) {
return undefined;
}
@@ -162,7 +168,7 @@ function flattenPages(
): RevisionPageDocument[] {
const result: RevisionPageDocument[] = [];
for (const page of pages) {
if (page.type === 'link') {
if (page.type === RevisionPageType.Link || page.type === RevisionPageType.Computed) {
continue;
}
@@ -170,7 +176,7 @@ function flattenPages(
continue;
}
if (page.type === 'document') {
if (page.type === RevisionPageType.Document) {
result.push(page);
}
result.push(...flattenPages(page.pages, filter));
+2 -1
View File
@@ -35,7 +35,8 @@
"build": "tsc",
"typecheck": "tsc --noEmit",
"dev": "tsc -w",
"clean": "rm -rf ./dist && rm -rf ./src/data"
"clean": "rm -rf ./dist && rm -rf ./src/data",
"unit": "bun test"
},
"bin": {
"gitbook-icons": "./bin/gitbook-icons.js"
+14
View File
@@ -0,0 +1,14 @@
import { it } from 'bun:test';
import { validateIconName } from './icons';
it('should have the GitBook custom icon', () => {
if (!validateIconName('gitbook')) {
const message =
'The GitBook icon is missing. It indicates that the dependencies were installed without the font-awesome custom package.';
if (process.env.CI) {
throw new Error(message);
} else {
console.warn(message);
}
}
});
+4 -1
View File
@@ -14,7 +14,10 @@
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react",
"incremental": true
"incremental": true,
"types": [
"bun-types" // add Bun global
]
},
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": ["node_modules"]
+1 -1
View File
@@ -10,7 +10,7 @@
},
"dependencies": {
"classnames": "^2.5.1",
"@gitbook/api": "0.64.1",
"@gitbook/api": "^0.66.0",
"assert-never": "^1.2.1"
},
"peerDependencies": {
+24 -12
View File
@@ -3,6 +3,7 @@
import type {
ContentKitAction,
ContentKitRenderOutput,
ContentKitRenderOutputElement,
RequestRenderIntegrationUI,
} from '@gitbook/api';
import React from 'react';
@@ -15,7 +16,7 @@ import {
} from './context';
import { resolveDynamicBinding } from './dynamic';
type ContentKitLifecycleMode = ContentKitRenderOutput['element']['type'];
type ContentKitLifecycleMode = ContentKitRenderOutputElement['element']['type'];
/**
* Render a ContentKit component.
@@ -27,7 +28,7 @@ export function ContentKit(props: {
/** Initial input being displayed */
initialInput: RequestRenderIntegrationUI;
/** Initial output being displayed */
initialOutput: ContentKitRenderOutput;
initialOutput: ContentKitRenderOutputElement;
/** Initial state to display */
children?: React.ReactNode;
/** Render a new state */
@@ -37,6 +38,8 @@ export function ContentKit(props: {
}>;
/** Callback when an action is triggered */
onAction?: (action: ContentKitAction) => void;
/** Callback when the flow is completed */
onComplete?: (returnValue: any) => void;
}) {
const {
security,
@@ -45,6 +48,7 @@ export function ContentKit(props: {
children: initialChildren,
render,
onAction,
onComplete,
} = props;
const [current, setCurrent] = React.useState({
@@ -61,7 +65,7 @@ export function ContentKit(props: {
const [subView, setSubView] = React.useState<null | {
mode: ContentKitLifecycleMode;
initialInput: RequestRenderIntegrationUI;
initialOutput: ContentKitRenderOutput;
initialOutput: ContentKitRenderOutputElement;
initialChildren: React.ReactNode;
}>(null);
@@ -85,17 +89,22 @@ export function ContentKit(props: {
console.log('transition to input', newInput);
const result = await render(newInput);
const output = result.output;
console.log('and got output', result.output, 'for', newInput);
if (output.type === 'complete') {
return onComplete?.(output.returnValue);
}
console.log('and got output', output, 'for', newInput);
setCurrent((prev) => ({
input: newInput,
children: result.children,
output: result.output,
output: output,
state: prev.state,
}));
},
[setCurrent, current, render],
[setCurrent, current, render, onComplete],
);
const renderer = React.useMemo<ContentKitClientContextType>(() => {
@@ -131,12 +140,15 @@ export function ContentKit(props: {
// Prefetch the modal content to show a loading in the button opening the button
const result = await render(modalInput);
setSubView({
mode: 'modal',
initialInput: modalInput,
initialOutput: result.output,
initialChildren: result.children,
});
if (result.output.type === 'element' || !result.output.type) {
setSubView({
mode: 'modal',
initialInput: modalInput,
initialOutput: result.output,
initialChildren: result.children,
});
}
break;
}
@@ -13,6 +13,11 @@ export function ContentKitOutput(props: {
output: ContentKitRenderOutput;
}) {
const { output, context } = props;
if (output.type === 'complete') {
return null;
}
return (
<>
{process.env.NODE_ENV === 'development' ? (
@@ -1,5 +1,5 @@
diff --git a/dist/index.js b/dist/index.js
index c5ea5e053c4e6be202914bdd2e0228bc7be8b976..31c24e146fdf50a806f8c1da7eedc1d941270fa9 100644
index 1a8c68c0713d41b0239dc8e3895d31437d3bb2e8..f6fec64252c8f1c6225eb37776f6c0d0e2a42c35 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -10474,7 +10474,7 @@ var import_path = require("path");