mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-11-02 02:09:55 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9ef655c946 |
@@ -1,8 +0,0 @@
|
|||||||
# Changesets
|
|
||||||
|
|
||||||
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
|
|
||||||
with multi-package repos, or single-package repos to help you version and publish your code. You can
|
|
||||||
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
|
|
||||||
|
|
||||||
We have a quick list of common questions to get you started engaging with this project in
|
|
||||||
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://unpkg.com/@changesets/config@3.0.2/schema.json",
|
|
||||||
"changelog": "@changesets/cli/changelog",
|
|
||||||
"commit": false,
|
|
||||||
"fixed": [],
|
|
||||||
"linked": [],
|
|
||||||
"access": "restricted",
|
|
||||||
"baseBranch": "main",
|
|
||||||
"updateInternalDependencies": "patch",
|
|
||||||
"ignore": []
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
name: Publish
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
publish:
|
|
||||||
name: Publish
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout Repo
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
# This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
|
|
||||||
fetch-depth: 0
|
|
||||||
- name: Setup bun
|
|
||||||
uses: oven-sh/setup-bun@v1
|
|
||||||
with:
|
|
||||||
bun-version: 1.1.18
|
|
||||||
- name: Install dependencies
|
|
||||||
run: bun install --frozen-lockfile
|
|
||||||
env:
|
|
||||||
PUPPETEER_SKIP_DOWNLOAD: 1
|
|
||||||
- name: Create Release Pull Request or Publish to npm
|
|
||||||
id: changesets
|
|
||||||
uses: changesets/action@v1
|
|
||||||
with:
|
|
||||||
publish: npm run release
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
||||||
+3
-8
@@ -129,7 +129,7 @@ const testCases: TestsCase[] = [
|
|||||||
|
|
||||||
// It should keep the current page path, i.e "reference/api-reference/pets" when navigating to the new variant
|
// It should keep the current page path, i.e "reference/api-reference/pets" when navigating to the new variant
|
||||||
await page.waitForURL(
|
await page.waitForURL(
|
||||||
'https://gitbook-open-e2e-sites.gitbook.io/api-multi-versions/v/2.0/reference/api-reference/pets?fallback=true',
|
'https://gitbook-open-e2e-sites.gitbook.io/api-multi-versions/v/2.0/reference/api-reference/pets',
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -152,7 +152,7 @@ const testCases: TestsCase[] = [
|
|||||||
|
|
||||||
// It should keep the current page path, i.e "reference/api-reference/pets" when navigating to the new variant
|
// It should keep the current page path, i.e "reference/api-reference/pets" when navigating to the new variant
|
||||||
await page.waitForURL(
|
await page.waitForURL(
|
||||||
'https://gitbook-open-e2e-sites.gitbook.io/api-multi-versions-share-links/bRfQbzwsK8rbN1GRxx7K/v/2.0/reference/api-reference/pets?fallback=true',
|
'https://gitbook-open-e2e-sites.gitbook.io/api-multi-versions-share-links/bRfQbzwsK8rbN1GRxx7K/v/2.0/reference/api-reference/pets',
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -187,7 +187,7 @@ const testCases: TestsCase[] = [
|
|||||||
|
|
||||||
// It should keep the current page path, i.e "reference/api-reference/pets" when navigating to the new variant
|
// It should keep the current page path, i.e "reference/api-reference/pets" when navigating to the new variant
|
||||||
await page.waitForURL(
|
await page.waitForURL(
|
||||||
'https://gitbook-open-e2e-sites.gitbook.io/api-multi-versions-va/v/2.0/reference/api-reference/pets?fallback=true',
|
'https://gitbook-open-e2e-sites.gitbook.io/api-multi-versions-va/v/2.0/reference/api-reference/pets',
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -363,11 +363,6 @@ const testCases: TestsCase[] = [
|
|||||||
name: 'Page options',
|
name: 'Page options',
|
||||||
baseUrl: 'https://gitbook.gitbook.io/test-1-1/',
|
baseUrl: 'https://gitbook.gitbook.io/test-1-1/',
|
||||||
tests: [
|
tests: [
|
||||||
{
|
|
||||||
name: 'Hidden',
|
|
||||||
url: 'page-options/page-hidden',
|
|
||||||
run: waitForCookiesDialog,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'With cover',
|
name: 'With cover',
|
||||||
url: 'page-options/page-with-cover',
|
url: 'page-options/page-with-cover',
|
||||||
|
|||||||
+1
-4
@@ -13,8 +13,6 @@
|
|||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
"unit": "bun test {src,packages}",
|
"unit": "bun test {src,packages}",
|
||||||
"e2e": "playwright test",
|
"e2e": "playwright test",
|
||||||
"changeset": "changeset",
|
|
||||||
"release": "changeset publish",
|
|
||||||
"postinstall": "rm -rf ./public/~gitbook/static/mathjax@3.2.2 && mkdir -p ./public/~gitbook/static/ && cp -R node_modules/mathjax/es5 ./public/~gitbook/static/mathjax@3.2.2"
|
"postinstall": "rm -rf ./public/~gitbook/static/mathjax@3.2.2 && mkdir -p ./public/~gitbook/static/ && cp -R node_modules/mathjax/es5 ./public/~gitbook/static/mathjax@3.2.2"
|
||||||
},
|
},
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
@@ -22,7 +20,7 @@
|
|||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@geist-ui/icons": "^1.0.2",
|
"@geist-ui/icons": "^1.0.2",
|
||||||
"@gitbook/api": "^0.56.0",
|
"@gitbook/api": "^0.53.0",
|
||||||
"@radix-ui/react-checkbox": "^1.0.4",
|
"@radix-ui/react-checkbox": "^1.0.4",
|
||||||
"@radix-ui/react-popover": "^1.0.7",
|
"@radix-ui/react-popover": "^1.0.7",
|
||||||
"@sentry/nextjs": "^7.94.1",
|
"@sentry/nextjs": "^7.94.1",
|
||||||
@@ -67,7 +65,6 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@argos-ci/playwright": "^2.0.0",
|
"@argos-ci/playwright": "^2.0.0",
|
||||||
"@changesets/cli": "^2.27.7",
|
|
||||||
"@cloudflare/next-on-pages": "^1.11.3",
|
"@cloudflare/next-on-pages": "^1.11.3",
|
||||||
"@cloudflare/workers-types": "^4.20231218.0",
|
"@cloudflare/workers-types": "^4.20231218.0",
|
||||||
"@playwright/test": "^1.42.1",
|
"@playwright/test": "^1.42.1",
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
# @gitbook/emoji-codepoints
|
|
||||||
|
|
||||||
## 0.2.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- 57adb3e: Second release to fix publishing with changeset
|
|
||||||
|
|
||||||
## 0.1.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- 5f8a8fe: Initial release
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@gitbook/emoji-codepoints",
|
"name": "@gitbook/emoji-codepoints",
|
||||||
"description": "Optimized mapping of codepoints to the fully qualified emoji codepoints",
|
"description": "Optimized mapping of codepoints to the fully qualified emoji codepoints",
|
||||||
"version": "0.2.0",
|
|
||||||
"private": true,
|
"private": true,
|
||||||
"exports": "./index.ts",
|
"exports": "./index.ts",
|
||||||
"dependencies": {},
|
"dependencies": {},
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
# @gitbook/react-contentkit
|
|
||||||
|
|
||||||
## 0.2.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- 57adb3e: Second release to fix publishing with changeset
|
|
||||||
|
|
||||||
## 0.1.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- 5f8a8fe: Initial release
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "@gitbook/react-contentkit",
|
"name": "@gitbook/react-contentkit",
|
||||||
"version": "0.2.0",
|
|
||||||
"exports": "./src/index.ts",
|
"exports": "./src/index.ts",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
# @gitbook/react-math
|
|
||||||
|
|
||||||
## 0.2.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- 57adb3e: Second release to fix publishing with changeset
|
|
||||||
|
|
||||||
## 0.1.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- 5f8a8fe: Initial release
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@gitbook/react-math",
|
"name": "@gitbook/react-math",
|
||||||
"exports": "./src/index.ts",
|
"exports": "./src/index.ts",
|
||||||
"version": "0.2.0",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"object-hash": "^3.0.0"
|
"object-hash": "^3.0.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
# @gitbook/react-openapi
|
|
||||||
|
|
||||||
## 0.3.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- bd0ca5b: Fix missing react imports
|
|
||||||
|
|
||||||
## 0.2.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- 57adb3e: Second release to fix publishing with changeset
|
|
||||||
|
|
||||||
## 0.1.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- 5f8a8fe: Initial release
|
|
||||||
@@ -1,5 +1,12 @@
|
|||||||
# `@gitbook/react-openapi`
|
# `@gitbook/react-openapi`
|
||||||
|
|
||||||
Style-less React components to render OpenAPI operation blocks.
|
React components to render OpenAPI operations.
|
||||||
|
|
||||||
Note: The package is a TypeScript package and do not expose JS built files.
|
## Features
|
||||||
|
|
||||||
|
- Generate code samples for the request
|
||||||
|
- Support custom cde samples with `x-codeSamples` (Redocly syntax)
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
- Support for trying out the request
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@gitbook/react-openapi",
|
"name": "@gitbook/react-openapi",
|
||||||
"exports": "./src/index.ts",
|
"exports": "./src/index.ts",
|
||||||
"version": "0.3.0",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@scalar/api-client-react": "^0.3.7",
|
"@scalar/api-client-react": "^0.3.7",
|
||||||
"@scalar/oas-utils": "0.1.6",
|
"@scalar/oas-utils": "0.1.6",
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
export function Markdown(props: { source: string; className?: string }) {
|
export function Markdown(props: { source: string; className?: string }) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as React from 'react';
|
import { OpenAPIV3 } from 'openapi-types';
|
||||||
|
|
||||||
import { CodeSampleInput, codeSampleGenerators } from './code-samples';
|
import { CodeSampleInput, codeSampleGenerators } from './code-samples';
|
||||||
import { OpenAPIOperationData, toJSON } from './fetchOpenAPIOperation';
|
import { OpenAPIOperationData, toJSON } from './fetchOpenAPIOperation';
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import { OpenAPIOperationData, toJSON } from './fetchOpenAPIOperation';
|
import { OpenAPIOperationData, toJSON } from './fetchOpenAPIOperation';
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import * as React from 'react';
|
|
||||||
|
|
||||||
import { OpenAPIV3 } from 'openapi-types';
|
import { OpenAPIV3 } from 'openapi-types';
|
||||||
import { OpenAPIRootSchema } from './OpenAPISchema';
|
import { OpenAPIRootSchema } from './OpenAPISchema';
|
||||||
import { noReference } from './utils';
|
import { noReference } from './utils';
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { OpenAPIV3 } from 'openapi-types';
|
import { OpenAPIV3 } from 'openapi-types';
|
||||||
import { OpenAPIRootSchema, OpenAPISchemaProperties } from './OpenAPISchema';
|
import { OpenAPIRootSchema, OpenAPISchemaProperties } from './OpenAPISchema';
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import { InteractiveSection } from './InteractiveSection';
|
import { InteractiveSection } from './InteractiveSection';
|
||||||
import { OpenAPIOperationData } from './fetchOpenAPIOperation';
|
import { OpenAPIOperationData } from './fetchOpenAPIOperation';
|
||||||
import { generateSchemaExample } from './generateSchemaExample';
|
import { generateSchemaExample } from './generateSchemaExample';
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { OpenAPIV3 } from 'openapi-types';
|
import { OpenAPIV3 } from 'openapi-types';
|
||||||
import { noReference } from './utils';
|
import { noReference } from './utils';
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import { OpenAPIV3 } from 'openapi-types';
|
import { OpenAPIV3 } from 'openapi-types';
|
||||||
import { OpenAPIClientContext } from './types';
|
import { OpenAPIClientContext } from './types';
|
||||||
import { InteractiveSection } from './InteractiveSection';
|
import { InteractiveSection } from './InteractiveSection';
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import { OpenAPIV3 } from 'openapi-types';
|
import { OpenAPIV3 } from 'openapi-types';
|
||||||
import { OpenAPIServerURLVariable } from './OpenAPIServerURLVariable';
|
import { OpenAPIServerURLVariable } from './OpenAPIServerURLVariable';
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import * as React from 'react';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { OpenAPIV3 } from 'openapi-types';
|
import { OpenAPIV3 } from 'openapi-types';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interactive component to show the value of a server variable and let the user change it.
|
* Interactive component to show the value of a server variable and let the user change it.
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import * as React from 'react';
|
|
||||||
import { OpenAPIV3 } from 'openapi-types';
|
import { OpenAPIV3 } from 'openapi-types';
|
||||||
|
|
||||||
import { OpenAPIOperationData, fromJSON } from './fetchOpenAPIOperation';
|
import { OpenAPIOperationData, fromJSON } from './fetchOpenAPIOperation';
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import { useScrollToHash } from '@/components/hooks';
|
import { useScrollToHash } from '@/components/hooks';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,29 +9,5 @@ export function PageClientLayout(props: {}) {
|
|||||||
// We use this hook in the page layout to ensure the elements for the blocks
|
// We use this hook in the page layout to ensure the elements for the blocks
|
||||||
// are rendered before we scroll to the hash.
|
// are rendered before we scroll to the hash.
|
||||||
useScrollToHash();
|
useScrollToHash();
|
||||||
|
|
||||||
useStripFallbackQueryParam();
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Strip the fallback query parameter from current URL.
|
|
||||||
*
|
|
||||||
* When the user switches variants using the space dropdown, we pass a fallback=true parameter.
|
|
||||||
* This parameter indicates that we should redirect to the root page if the path from the
|
|
||||||
* previous variant doesn't exist in the new variant. If the path does exist, no redirect occurs,
|
|
||||||
* so we need to remove the fallback parameter.
|
|
||||||
*/
|
|
||||||
function useStripFallbackQueryParam() {
|
|
||||||
const router = useRouter();
|
|
||||||
const pathname = usePathname();
|
|
||||||
const searchParams = useSearchParams();
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (searchParams.has('fallback')) {
|
|
||||||
const params = new URLSearchParams(searchParams.toString());
|
|
||||||
params.delete('fallback');
|
|
||||||
router.push(`${pathname}?${params.toString()}${window.location.hash ?? ''}`);
|
|
||||||
}
|
|
||||||
}, [router, pathname, searchParams]);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import React from 'react';
|
|||||||
import { PageAside } from '@/components/PageAside';
|
import { PageAside } from '@/components/PageAside';
|
||||||
import { PageBody, PageCover } from '@/components/PageBody';
|
import { PageBody, PageCover } from '@/components/PageBody';
|
||||||
import { PageHrefContext, absoluteHref, pageHref } from '@/lib/links';
|
import { PageHrefContext, absoluteHref, pageHref } from '@/lib/links';
|
||||||
import { getPagePath, resolveFirstDocument } from '@/lib/pages';
|
import { getPagePath } from '@/lib/pages';
|
||||||
import { ContentRefContext } from '@/lib/references';
|
import { ContentRefContext } from '@/lib/references';
|
||||||
import { tcls } from '@/lib/tailwind';
|
import { tcls } from '@/lib/tailwind';
|
||||||
import { getContentTitle } from '@/lib/utils';
|
import { getContentTitle } from '@/lib/utils';
|
||||||
@@ -19,12 +19,10 @@ export const runtime = 'edge';
|
|||||||
/**
|
/**
|
||||||
* Fetch and render a page.
|
* Fetch and render a page.
|
||||||
*/
|
*/
|
||||||
export default async function Page(props: {
|
export default async function Page(props: { params: PagePathParams }) {
|
||||||
params: PagePathParams;
|
const { params } = props;
|
||||||
searchParams: { fallback?: string };
|
|
||||||
}) {
|
|
||||||
const { params, searchParams } = props;
|
|
||||||
|
|
||||||
|
const rawPathname = getPathnameParam(params);
|
||||||
const {
|
const {
|
||||||
content: contentPointer,
|
content: contentPointer,
|
||||||
contentTarget,
|
contentTarget,
|
||||||
@@ -34,14 +32,9 @@ export default async function Page(props: {
|
|||||||
pages,
|
pages,
|
||||||
page,
|
page,
|
||||||
document,
|
document,
|
||||||
} = await getPageDataWithFallback({
|
} = await fetchPageData(params);
|
||||||
pagePathParams: params,
|
|
||||||
searchParams,
|
|
||||||
redirectOnFallback: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const linksContext: PageHrefContext = {};
|
const linksContext: PageHrefContext = {};
|
||||||
const rawPathname = getPathnameParam(params);
|
|
||||||
if (!page) {
|
if (!page) {
|
||||||
const pathname = normalizePathname(rawPathname);
|
const pathname = normalizePathname(rawPathname);
|
||||||
if (pathname !== rawPathname) {
|
if (pathname !== rawPathname) {
|
||||||
@@ -121,18 +114,8 @@ export async function generateViewport({ params }: { params: PagePathParams }):
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function generateMetadata({
|
export async function generateMetadata({ params }: { params: PagePathParams }): Promise<Metadata> {
|
||||||
params,
|
const { space, pages, page, customization, parent } = await fetchPageData(params);
|
||||||
searchParams,
|
|
||||||
}: {
|
|
||||||
params: PagePathParams;
|
|
||||||
searchParams: { fallback?: string };
|
|
||||||
}): Promise<Metadata> {
|
|
||||||
const { space, pages, page, customization, parent } = await getPageDataWithFallback({
|
|
||||||
pagePathParams: params,
|
|
||||||
searchParams,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!page) {
|
if (!page) {
|
||||||
notFound();
|
notFound();
|
||||||
}
|
}
|
||||||
@@ -153,34 +136,3 @@ export async function generateMetadata({
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetches the page data matching the requested pathname and fallback to root page when page is not found.
|
|
||||||
*/
|
|
||||||
async function getPageDataWithFallback(args: {
|
|
||||||
pagePathParams: PagePathParams;
|
|
||||||
searchParams: { fallback?: string };
|
|
||||||
redirectOnFallback?: boolean;
|
|
||||||
}) {
|
|
||||||
const { pagePathParams, searchParams, redirectOnFallback = false } = args;
|
|
||||||
|
|
||||||
const { pages, page: targetPage, ...otherPageData } = await fetchPageData(pagePathParams);
|
|
||||||
|
|
||||||
let page = targetPage;
|
|
||||||
const canFallback = !!searchParams.fallback;
|
|
||||||
if (!page && canFallback) {
|
|
||||||
const rootPage = resolveFirstDocument(pages, []);
|
|
||||||
|
|
||||||
if (redirectOnFallback && rootPage?.page) {
|
|
||||||
redirect(pageHref(pages, rootPage?.page));
|
|
||||||
}
|
|
||||||
|
|
||||||
page = rootPage?.page;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...otherPageData,
|
|
||||||
pages,
|
|
||||||
page,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const runtime = 'edge';
|
|||||||
*/
|
*/
|
||||||
export async function GET(req: NextRequest) {
|
export async function GET(req: NextRequest) {
|
||||||
const { pages: rootPages } = await getSpaceContentData(getContentPointer());
|
const { pages: rootPages } = await getSpaceContentData(getContentPointer());
|
||||||
const pages = flattenPages(rootPages, (page) => !page.hidden);
|
const pages = flattenPages(rootPages);
|
||||||
const urls = pages.map(({ page, depth }) => {
|
const urls = pages.map(({ page, depth }) => {
|
||||||
// Decay priority with depth
|
// Decay priority with depth
|
||||||
const priority = Math.pow(2, -0.25 * depth);
|
const priority = Math.pow(2, -0.25 * depth);
|
||||||
@@ -66,16 +66,13 @@ export async function GET(req: NextRequest) {
|
|||||||
|
|
||||||
type FlatPageEntry = { page: RevisionPageDocument; depth: number };
|
type FlatPageEntry = { page: RevisionPageDocument; depth: number };
|
||||||
|
|
||||||
function flattenPages(
|
function flattenPages(rootPags: RevisionPage[]): FlatPageEntry[] {
|
||||||
rootPags: RevisionPage[],
|
|
||||||
filter: (page: RevisionPageDocument) => boolean,
|
|
||||||
): FlatPageEntry[] {
|
|
||||||
const flattenPage = (
|
const flattenPage = (
|
||||||
page: RevisionPageDocument | RevisionPageGroup,
|
page: RevisionPageDocument | RevisionPageGroup,
|
||||||
depth: number,
|
depth: number,
|
||||||
): FlatPageEntry[] => {
|
): FlatPageEntry[] => {
|
||||||
return [
|
return [
|
||||||
...(page.type === 'document' && filter(page) ? [{ page, depth }] : []),
|
...(page.type === 'document' ? [{ page, depth }] : []),
|
||||||
...page.pages.flatMap((child) =>
|
...page.pages.flatMap((child) =>
|
||||||
child.type === 'link' ? [] : flattenPage(child, depth + 1),
|
child.type === 'link' ? [] : flattenPage(child, depth + 1),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {
|
|||||||
Revision,
|
Revision,
|
||||||
RevisionPageDocument,
|
RevisionPageDocument,
|
||||||
RevisionPageGroup,
|
RevisionPageGroup,
|
||||||
RevisionPageLink,
|
|
||||||
SiteCustomizationSettings,
|
SiteCustomizationSettings,
|
||||||
Space,
|
Space,
|
||||||
} from '@gitbook/api';
|
} from '@gitbook/api';
|
||||||
@@ -314,17 +313,9 @@ function selectPages(
|
|||||||
): FlatPageEntry[] => {
|
): FlatPageEntry[] => {
|
||||||
return [
|
return [
|
||||||
{ page, depth },
|
{ page, depth },
|
||||||
...page.pages.flatMap((child) => {
|
...page.pages.flatMap((child) =>
|
||||||
if (child.type === 'link') {
|
child.type === 'link' ? [] : flattenPage(child, depth + 1),
|
||||||
return [];
|
),
|
||||||
}
|
|
||||||
|
|
||||||
if (child.hidden) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return flattenPage(child, depth + 1);
|
|
||||||
}),
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -349,16 +340,8 @@ function selectPages(
|
|||||||
return limitTo(flattenPage(found.page, 0));
|
return limitTo(flattenPage(found.page, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
const allPages = rootPages.flatMap((page) => {
|
const allPages = rootPages.flatMap((page) =>
|
||||||
if (page.type === 'link') {
|
page.type === 'link' ? [] : flattenPage(page, 0),
|
||||||
return [];
|
);
|
||||||
}
|
|
||||||
|
|
||||||
if (page.hidden) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return flattenPage(page, 0);
|
|
||||||
});
|
|
||||||
return limitTo(allPages);
|
return limitTo(allPages);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,12 +18,8 @@ export async function BlockContentRef(props: BlockProps<DocumentBlockContentRef>
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isContentInOtherSpace =
|
|
||||||
context.contentRefContext?.space &&
|
|
||||||
'space' in block.data.ref &&
|
|
||||||
context.contentRefContext.space.id !== block.data.ref.space;
|
|
||||||
const kind = block?.data?.ref?.kind;
|
const kind = block?.data?.ref?.kind;
|
||||||
if ((resolved.active && kind === 'space') || isContentInOtherSpace) {
|
if (resolved.active && kind === 'space') {
|
||||||
return <SpaceRefCard {...props} resolved={resolved} />;
|
return <SpaceRefCard {...props} resolved={resolved} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +61,6 @@ async function SpaceRefCard(
|
|||||||
}
|
}
|
||||||
href={resolved.href}
|
href={resolved.href}
|
||||||
title={resolved.text}
|
title={resolved.text}
|
||||||
postTitle={resolved.subText}
|
|
||||||
style={style}
|
style={style}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ function useVariantSpaceHref(variantSpaceUrl: string) {
|
|||||||
const targetUrl = new URL(variantSpaceUrl);
|
const targetUrl = new URL(variantSpaceUrl);
|
||||||
targetUrl.pathname += `/${currentPathname}`;
|
targetUrl.pathname += `/${currentPathname}`;
|
||||||
targetUrl.pathname = targetUrl.pathname.replace(/\/{2,}/g, '/').replace(/\/$/, '');
|
targetUrl.pathname = targetUrl.pathname.replace(/\/{2,}/g, '/').replace(/\/$/, '');
|
||||||
targetUrl.searchParams.set('fallback', 'true');
|
|
||||||
|
|
||||||
return targetUrl.toString();
|
return targetUrl.toString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,17 @@ async function sendSiteTrackPageViewRequest(args: {
|
|||||||
|
|
||||||
let latestPageId: string | undefined | null = null;
|
let latestPageId: string | undefined | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the current page is in an iframe.
|
||||||
|
* This is used to avoid tracking page views in iframes.
|
||||||
|
*/
|
||||||
|
function isInIframe(): boolean {
|
||||||
|
try {
|
||||||
|
return window.self !== window.top || window.frameElement !== null;
|
||||||
|
} catch (e) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Track the page view for the current page to GitBook.
|
* Track the page view for the current page to GitBook.
|
||||||
* We don't use the API client to avoid shipping 80kb of JS to the client.
|
* We don't use the API client to avoid shipping 80kb of JS to the client.
|
||||||
@@ -75,6 +86,11 @@ async function trackPageView(args: {
|
|||||||
pageId: string | undefined;
|
pageId: string | undefined;
|
||||||
}) {
|
}) {
|
||||||
const { apiHost, sitePointer, pageId, spaceId } = args;
|
const { apiHost, sitePointer, pageId, spaceId } = args;
|
||||||
|
|
||||||
|
if (isInIframe()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (pageId === latestPageId) {
|
if (pageId === latestPageId) {
|
||||||
// The hook can be called multiple times, we only want to track once.
|
// The hook can be called multiple times, we only want to track once.
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -96,7 +96,6 @@ export const SearchResults = React.forwardRef(function SearchResults(
|
|||||||
const fetchedResults = await (parent
|
const fetchedResults = await (parent
|
||||||
? searchParentContent(parent, query)
|
? searchParentContent(parent, query)
|
||||||
: searchSpaceContent(spaceId, revisionId, query));
|
: searchSpaceContent(spaceId, revisionId, query));
|
||||||
|
|
||||||
setResults(withAsk ? withQuestionResult(fetchedResults, query) : fetchedResults);
|
setResults(withAsk ? withQuestionResult(fetchedResults, query) : fetchedResults);
|
||||||
}, 250);
|
}, 250);
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import {
|
|||||||
SearchAIAnswer,
|
SearchAIAnswer,
|
||||||
SearchPageResult,
|
SearchPageResult,
|
||||||
Site,
|
Site,
|
||||||
SiteSpace,
|
|
||||||
Space,
|
Space,
|
||||||
} from '@gitbook/api';
|
} from '@gitbook/api';
|
||||||
|
import { headers } from 'next/headers';
|
||||||
|
|
||||||
import { getContentPointer } from '@/app/(space)/fetch';
|
import { getContentPointer } from '@/app/(space)/fetch';
|
||||||
import { streamResponse } from '@/lib/actions';
|
import { streamResponse } from '@/lib/actions';
|
||||||
@@ -52,61 +52,6 @@ export interface AskAnswerResult {
|
|||||||
hasAnswer: boolean;
|
hasAnswer: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function searchSiteContent(args: {
|
|
||||||
query: string;
|
|
||||||
siteSpaceIds?: string[];
|
|
||||||
cacheBust?: string;
|
|
||||||
}): Promise<OrderedComputedResult[]> {
|
|
||||||
const { siteSpaceIds, query, cacheBust } = args;
|
|
||||||
const pointer = getContentPointer();
|
|
||||||
|
|
||||||
if (siteSpaceIds?.length === 0) {
|
|
||||||
// if we have no siteSpaces to search in then we won't find anything. skip the call.
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('siteId' in pointer && 'organizationId' in pointer) {
|
|
||||||
const [searchResults, allSiteSpaces] = await Promise.all([
|
|
||||||
api.searchSiteContent(
|
|
||||||
pointer.organizationId,
|
|
||||||
pointer.siteId,
|
|
||||||
query,
|
|
||||||
siteSpaceIds,
|
|
||||||
cacheBust,
|
|
||||||
),
|
|
||||||
siteSpaceIds
|
|
||||||
? null
|
|
||||||
: api.getSiteSpaces({
|
|
||||||
organizationId: pointer.organizationId,
|
|
||||||
siteId: pointer.siteId,
|
|
||||||
siteShareKey: pointer.siteShareKey,
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (!siteSpaceIds) {
|
|
||||||
// We are searching all of this Site's content
|
|
||||||
return searchResults.items
|
|
||||||
.map((spaceItem) => {
|
|
||||||
const siteSpace = allSiteSpaces?.find(
|
|
||||||
(siteSpace) => siteSpace.space.id === spaceItem.id,
|
|
||||||
);
|
|
||||||
|
|
||||||
return spaceItem.pages.map((item) => transformSitePageResult(item, siteSpace));
|
|
||||||
})
|
|
||||||
.flat(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
return searchResults.items
|
|
||||||
.map((spaceItem) => {
|
|
||||||
return spaceItem.pages.map((item) => transformPageResult(item));
|
|
||||||
})
|
|
||||||
.flat(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This should never happen
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Server action to search content in a space
|
* Server action to search content in a space
|
||||||
*/
|
*/
|
||||||
@@ -115,16 +60,6 @@ export async function searchSpaceContent(
|
|||||||
revisionId: string,
|
revisionId: string,
|
||||||
query: string,
|
query: string,
|
||||||
): Promise<OrderedComputedResult[]> {
|
): Promise<OrderedComputedResult[]> {
|
||||||
const pointer = getContentPointer();
|
|
||||||
|
|
||||||
if ('siteId' in pointer && 'organizationId' in pointer) {
|
|
||||||
const siteSpaceIds = pointer.siteSpaceId ? [pointer.siteSpaceId] : []; // if we don't have a siteSpaceID search all content
|
|
||||||
|
|
||||||
// This is a site so use a different function which we can eventually call directly
|
|
||||||
// We also want to break cache for this specific space if the revisionId is different so use it as a cache busting key
|
|
||||||
return await searchSiteContent({ siteSpaceIds, query, cacheBust: revisionId });
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await api.searchSpaceContent(spaceId, revisionId, query);
|
const data = await api.searchSpaceContent(spaceId, revisionId, query);
|
||||||
return data.items.map((item) => transformPageResult(item, undefined)).flat();
|
return data.items.map((item) => transformPageResult(item, undefined)).flat();
|
||||||
}
|
}
|
||||||
@@ -137,18 +72,36 @@ export async function searchParentContent(
|
|||||||
query: string,
|
query: string,
|
||||||
): Promise<OrderedComputedResult[]> {
|
): Promise<OrderedComputedResult[]> {
|
||||||
const pointer = getContentPointer();
|
const pointer = getContentPointer();
|
||||||
const isSite = 'siteId' in pointer;
|
|
||||||
|
|
||||||
if (isSite) {
|
const [data, collectionSpaces, siteSpaces] = await Promise.all([
|
||||||
return searchSiteContent({ query });
|
|
||||||
}
|
|
||||||
|
|
||||||
const [data, collectionSpaces] = await Promise.all([
|
|
||||||
api.searchParentContent(parent.id, query),
|
api.searchParentContent(parent.id, query),
|
||||||
parent.object === 'collection' ? api.getCollectionSpaces(parent.id) : null,
|
parent.object === 'collection' ? api.getCollectionSpaces(parent.id) : null,
|
||||||
|
parent.object === 'site' && 'organizationId' in pointer
|
||||||
|
? api.getSiteSpaces({
|
||||||
|
organizationId: pointer.organizationId,
|
||||||
|
siteId: parent.id,
|
||||||
|
siteShareKey: pointer.siteShareKey,
|
||||||
|
})
|
||||||
|
: null,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let spaces: Space[] = collectionSpaces ? collectionSpaces : [];
|
let spaces: Space[] = [];
|
||||||
|
|
||||||
|
if (collectionSpaces) {
|
||||||
|
spaces = collectionSpaces;
|
||||||
|
} else if (siteSpaces) {
|
||||||
|
spaces = Object.values(
|
||||||
|
siteSpaces.reduce(
|
||||||
|
(acc, siteSpace) => {
|
||||||
|
acc[siteSpace.space.id] = siteSpace.space;
|
||||||
|
// replace the published url for the "space" with the site's published url
|
||||||
|
acc[siteSpace.space.id].urls.published = siteSpace.urls.published;
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{} as Record<string, Space>,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return data.items
|
return data.items
|
||||||
.map((spaceItem) => {
|
.map((spaceItem) => {
|
||||||
@@ -230,24 +183,19 @@ function transformAnswer(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function transformSectionsAndPage(args: {
|
function transformPageResult(item: SearchPageResult, space?: Space) {
|
||||||
item: SearchPageResult;
|
|
||||||
space?: Space;
|
|
||||||
spaceURL?: string;
|
|
||||||
}): [ComputedPageResult, ComputedSectionResult[]] {
|
|
||||||
const { item, space, spaceURL } = args;
|
|
||||||
|
|
||||||
// Resolve a relative path to an absolute URL
|
// Resolve a relative path to an absolute URL
|
||||||
// if the search result is relative to another space, we use the space URL
|
// if the search result is relative to another space, we use the space URL
|
||||||
const getURL = (path: string, spaceURL?: string) => {
|
const getURL = (path: string) => {
|
||||||
if (spaceURL) {
|
if (space) {
|
||||||
if (!spaceURL.endsWith('/')) {
|
let url = space.urls.published ?? space.urls.app;
|
||||||
spaceURL += '/';
|
if (!url.endsWith('/')) {
|
||||||
|
url += '/';
|
||||||
}
|
}
|
||||||
if (path.startsWith('/')) {
|
if (path.startsWith('/')) {
|
||||||
path = path.slice(1);
|
path = path.slice(1);
|
||||||
}
|
}
|
||||||
return spaceURL + path;
|
return url + path;
|
||||||
} else {
|
} else {
|
||||||
return absoluteHref(path);
|
return absoluteHref(path);
|
||||||
}
|
}
|
||||||
@@ -258,7 +206,7 @@ function transformSectionsAndPage(args: {
|
|||||||
type: 'section',
|
type: 'section',
|
||||||
id: item.id + '/' + section.id,
|
id: item.id + '/' + section.id,
|
||||||
title: section.title,
|
title: section.title,
|
||||||
href: getURL(section.path, spaceURL),
|
href: getURL(section.path),
|
||||||
body: section.body,
|
body: section.body,
|
||||||
})) ?? [];
|
})) ?? [];
|
||||||
|
|
||||||
@@ -270,25 +218,5 @@ function transformSectionsAndPage(args: {
|
|||||||
spaceTitle: space?.title,
|
spaceTitle: space?.title,
|
||||||
};
|
};
|
||||||
|
|
||||||
return [page, sections];
|
|
||||||
}
|
|
||||||
|
|
||||||
function transformSitePageResult(item: SearchPageResult, siteSpace?: SiteSpace) {
|
|
||||||
const [page, sections] = transformSectionsAndPage({
|
|
||||||
item,
|
|
||||||
space: siteSpace?.space,
|
|
||||||
spaceURL: siteSpace?.urls.published,
|
|
||||||
});
|
|
||||||
|
|
||||||
return [page, ...sections];
|
|
||||||
}
|
|
||||||
|
|
||||||
function transformPageResult(item: SearchPageResult, space?: Space) {
|
|
||||||
const [page, sections] = transformSectionsAndPage({
|
|
||||||
item,
|
|
||||||
space,
|
|
||||||
spaceURL: space?.urls.published ?? space?.urls.app,
|
|
||||||
});
|
|
||||||
|
|
||||||
return [page, ...sections];
|
return [page, ...sections];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,14 +19,6 @@ export function PagesList(props: {
|
|||||||
return (
|
return (
|
||||||
<ul className={tcls('flex', 'flex-1', 'flex-col', 'gap-y-0.5', style)}>
|
<ul className={tcls('flex', 'flex-1', 'flex-col', 'gap-y-0.5', style)}>
|
||||||
{pages.map((page) => {
|
{pages.map((page) => {
|
||||||
if (page.type === 'link') {
|
|
||||||
return <PageLinkItem key={page.id} page={page} context={context} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (page.hidden) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (page.type === 'group') {
|
if (page.type === 'group') {
|
||||||
return (
|
return (
|
||||||
<PageGroupItem
|
<PageGroupItem
|
||||||
@@ -37,6 +29,8 @@ export function PagesList(props: {
|
|||||||
context={context}
|
context={context}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
} else if (page.type === 'link') {
|
||||||
|
return <PageLinkItem key={page.id} page={page} context={context} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1046,41 +1046,6 @@ export const searchParentContent = cache(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
|
||||||
* Search content in a Site or specific SiteSpaces.
|
|
||||||
*/
|
|
||||||
export const searchSiteContent = cache(
|
|
||||||
'api.searchSiteContent',
|
|
||||||
async (
|
|
||||||
organizationId: string,
|
|
||||||
siteId: string,
|
|
||||||
query: string,
|
|
||||||
siteSpaceIds?: string[],
|
|
||||||
/** A cache bust param to avoid revalidating lot of cache entries by tags */
|
|
||||||
cacheBust?: string,
|
|
||||||
options?: CacheFunctionOptions,
|
|
||||||
) => {
|
|
||||||
const response = await api().orgs.searchSiteContent(
|
|
||||||
organizationId,
|
|
||||||
siteId,
|
|
||||||
{
|
|
||||||
query,
|
|
||||||
siteSpaceIds,
|
|
||||||
},
|
|
||||||
undefined,
|
|
||||||
{
|
|
||||||
...noCacheFetchOptions,
|
|
||||||
signal: options?.signal,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return cacheResponse(response, {
|
|
||||||
ttl: 60 * 60,
|
|
||||||
tags: [],
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of recommended questions in a space.
|
* Get a list of recommended questions in a space.
|
||||||
*/
|
*/
|
||||||
|
|||||||
+3
-14
@@ -75,13 +75,12 @@ export function resolvePageId(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve the next/previous page before another one.
|
* Resolve the next/previous page before another one.
|
||||||
* It ignores hidden pages as this is used for navigation purpose.
|
|
||||||
*/
|
*/
|
||||||
export function resolvePrevNextPages(
|
export function resolvePrevNextPages(
|
||||||
rootPages: Revision['pages'],
|
rootPages: Revision['pages'],
|
||||||
page: RevisionPageDocument,
|
page: RevisionPageDocument,
|
||||||
): { previous?: RevisionPageDocument; next?: RevisionPageDocument } {
|
): { previous?: RevisionPageDocument; next?: RevisionPageDocument } {
|
||||||
const flat = flattenPages(rootPages, (page) => !page.hidden);
|
const flat = flattenPages(rootPages);
|
||||||
|
|
||||||
const currentIndex = flat.findIndex((p) => p.id === page.id);
|
const currentIndex = flat.findIndex((p) => p.id === page.id);
|
||||||
if (currentIndex === -1) {
|
if (currentIndex === -1) {
|
||||||
@@ -153,27 +152,17 @@ function resolvePageDocument(
|
|||||||
return { page, ancestors };
|
return { page, ancestors };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function flattenPages(pages: RevisionPage[]): RevisionPageDocument[] {
|
||||||
* Flatten a list of pages into a list of page documents.
|
|
||||||
*/
|
|
||||||
function flattenPages(
|
|
||||||
pages: RevisionPage[],
|
|
||||||
filter?: (page: RevisionPageDocument | RevisionPageGroup) => boolean,
|
|
||||||
): RevisionPageDocument[] {
|
|
||||||
const result: RevisionPageDocument[] = [];
|
const result: RevisionPageDocument[] = [];
|
||||||
for (const page of pages) {
|
for (const page of pages) {
|
||||||
if (page.type === 'link') {
|
if (page.type === 'link') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter && !filter(page)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (page.type === 'document') {
|
if (page.type === 'document') {
|
||||||
result.push(page);
|
result.push(page);
|
||||||
}
|
}
|
||||||
result.push(...flattenPages(page.pages, filter));
|
result.push(...flattenPages(page.pages));
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
+4
-25
@@ -18,8 +18,6 @@ import { getPagePath, resolvePageId } from './pages';
|
|||||||
export interface ResolvedContentRef {
|
export interface ResolvedContentRef {
|
||||||
/** Text to render in the content ref */
|
/** Text to render in the content ref */
|
||||||
text: string;
|
text: string;
|
||||||
/** Additional sub text to render in the content ref */
|
|
||||||
subText?: string;
|
|
||||||
/** Emoji associated with the reference */
|
/** Emoji associated with the reference */
|
||||||
emoji?: string;
|
emoji?: string;
|
||||||
/** URL to open for the content ref */
|
/** URL to open for the content ref */
|
||||||
@@ -105,14 +103,10 @@ export async function resolveContentRef(
|
|||||||
return resolveContentRefInSpace(contentRef.space, contentRef);
|
return resolveContentRefInSpace(contentRef.space, contentRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
const resolvePageResult =
|
const page =
|
||||||
!contentRef.page || contentRef.page === activePage?.id
|
!contentRef.page || contentRef.page === activePage?.id
|
||||||
? activePage
|
? activePage
|
||||||
? { page: activePage, ancestors: [] }
|
: resolvePageId(pages, contentRef.page)?.page;
|
||||||
: undefined
|
|
||||||
: resolvePageId(pages, contentRef.page);
|
|
||||||
|
|
||||||
const page = resolvePageResult?.page;
|
|
||||||
if (!page) {
|
if (!page) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -140,13 +134,7 @@ export async function resolveContentRef(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const parentPage = (resolvePageResult?.ancestors || []).slice(-1).pop();
|
text = page.title;
|
||||||
// When the looked up ref was a page group we use the page group title as resolved ref text.
|
|
||||||
// Otherwise use the resolved page title.
|
|
||||||
text =
|
|
||||||
parentPage && contentRef.page === parentPage.id && parentPage.type === 'group'
|
|
||||||
? parentPage.title
|
|
||||||
: page.title;
|
|
||||||
emoji = isCurrentPage ? undefined : page.emoji;
|
emoji = isCurrentPage ? undefined : page.emoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,21 +245,12 @@ async function resolveContentRefInSpace(spaceId: string, contentRef: ContentRef)
|
|||||||
baseUrl += '/';
|
baseUrl += '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
const resolved = await resolveContentRef(contentRef, {
|
return resolveContentRef(contentRef, {
|
||||||
space,
|
space,
|
||||||
revisionId: space.revision,
|
revisionId: space.revision,
|
||||||
pages,
|
pages,
|
||||||
baseUrl,
|
baseUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!resolved) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...resolved,
|
|
||||||
subText: space.title,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveContentRefWithFiles(
|
export function resolveContentRefWithFiles(
|
||||||
|
|||||||
Reference in New Issue
Block a user