From 765141a4d8a5612387b4f1c45ae4a0e75ae7419f Mon Sep 17 00:00:00 2001 From: Zeno Kapitein Date: Fri, 18 Jul 2025 20:46:11 +0200 Subject: [PATCH 1/3] Make "last updated" section optional (#3486) --- .../src/components/PageBody/PageBody.tsx | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/packages/gitbook/src/components/PageBody/PageBody.tsx b/packages/gitbook/src/components/PageBody/PageBody.tsx index 37492c1c7..0d5171451 100644 --- a/packages/gitbook/src/components/PageBody/PageBody.tsx +++ b/packages/gitbook/src/components/PageBody/PageBody.tsx @@ -93,19 +93,28 @@ export function PageBody(props: { ) : null} -
- {updatedAt ? ( -

- {t(language, 'page_last_modified', )} -

- ) : null} - {withPageFeedback ? ( - - ) : null} -
+ { + // TODO: after 25/07/2025, we can chage it to a true check as the cache will be updated + page.layout.metadata !== false ? ( +
+ {updatedAt ? ( +

+ {t( + language, + 'page_last_modified', + + )} +

+ ) : null} + {withPageFeedback ? ( + + ) : null} +
+ ) : null + } From 2fe58e8cbb45bc1b5cb01b762dd075b726ea6c3a Mon Sep 17 00:00:00 2001 From: Zeno Kapitein Date: Fri, 18 Jul 2025 21:10:38 +0200 Subject: [PATCH 2/3] Page actions customisation (#3485) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Samy Pessé --- packages/gitbook/e2e/internal.spec.ts | 16 ++++++ .../AIActions/AIActionsDropdown.tsx | 52 ++++++++++++------- .../src/components/PageBody/PageHeader.tsx | 14 +++-- 3 files changed, 59 insertions(+), 23 deletions(-) diff --git a/packages/gitbook/e2e/internal.spec.ts b/packages/gitbook/e2e/internal.spec.ts index 002fb36ff..0db15e051 100644 --- a/packages/gitbook/e2e/internal.spec.ts +++ b/packages/gitbook/e2e/internal.spec.ts @@ -932,6 +932,22 @@ const testCases: TestsCase[] = [ }, ]), }, + { + name: 'Page actions', + contentBaseURL: 'https://gitbook.gitbook.io/test-gitbook-open/', + tests: [ + { + name: 'Without page actions', + url: getCustomizationURL({ + pageActions: { + markdown: false, + externalAI: false, + }, + }), + run: waitForCookiesDialog, + }, + ], + }, { name: 'Ads', contentBaseURL: 'https://gitbook.gitbook.io/test-gitbook-open/', diff --git a/packages/gitbook/src/components/AIActions/AIActionsDropdown.tsx b/packages/gitbook/src/components/AIActions/AIActionsDropdown.tsx index d10d2abfe..a08afc091 100644 --- a/packages/gitbook/src/components/AIActions/AIActionsDropdown.tsx +++ b/packages/gitbook/src/components/AIActions/AIActionsDropdown.tsx @@ -8,6 +8,7 @@ import { } from '@/components/AIActions/AIActions'; import { Button } from '@/components/primitives/Button'; import { DropdownMenu } from '@/components/primitives/DropdownMenu'; +import type { SiteCustomizationSettings } from '@gitbook/api'; import { Icon } from '@gitbook/icons'; import { useRef } from 'react'; @@ -16,10 +17,7 @@ interface AIActionsDropdownProps { markdownPageUrl: string; withAIChat?: boolean; trademark: boolean; - /** - * Whether to include the "Open in LLM" entries in the dropdown menu. - */ - withLLMActions?: boolean; + actions: SiteCustomizationSettings['pageActions']; } /** @@ -59,7 +57,7 @@ export function AIActionsDropdown(props: AIActionsDropdownProps) { * The content of the dropdown menu. */ function AIActionsDropdownMenuContent(props: AIActionsDropdownProps) { - const { markdownPageUrl, withAIChat, trademark, withLLMActions } = props; + const { markdownPageUrl, withAIChat, trademark, actions } = props; return ( <> @@ -67,14 +65,18 @@ function AIActionsDropdownMenuContent(props: AIActionsDropdownProps) { ) : null} - - + {actions.markdown ? ( + <> + + + + ) : null} - {withLLMActions ? ( + {actions.externalAI ? ( <> @@ -88,17 +90,27 @@ function AIActionsDropdownMenuContent(props: AIActionsDropdownProps) { * A default action shown as a quick-access button beside the dropdown menu */ function DefaultAction(props: AIActionsDropdownProps) { - const { markdownPageUrl, withAIChat, trademark } = props; + const { markdownPageUrl, withAIChat, trademark, actions } = props; if (withAIChat) { return ; } - return ( - - ); + if (actions.markdown) { + return ( + + ); + } + + if (actions.externalAI) { + return ( + <> + + + ); + } } diff --git a/packages/gitbook/src/components/PageBody/PageHeader.tsx b/packages/gitbook/src/components/PageBody/PageHeader.tsx index b5e0f1bf3..4797f6b75 100644 --- a/packages/gitbook/src/components/PageBody/PageHeader.tsx +++ b/packages/gitbook/src/components/PageBody/PageHeader.tsx @@ -3,7 +3,7 @@ import { isAIChatEnabled } from '@/components/utils/isAIChatEnabled'; import type { GitBookSiteContext } from '@/lib/context'; import type { AncestorRevisionPage } from '@/lib/pages'; import { tcls } from '@/lib/tailwind'; -import { type RevisionPageDocument, SiteVisibility } from '@gitbook/api'; +import type { RevisionPageDocument } from '@gitbook/api'; import { Icon } from '@gitbook/icons'; import { PageIcon } from '../PageIcon'; import { StyledLink } from '../primitives'; @@ -22,6 +22,12 @@ export async function PageHeader(props: { const withAIChat = isAIChatEnabled(context); + const pageActions = context.customization.pageActions ?? { + // TODO: After 25/07/2025, we can remove this default values as the cache will be updated + markdown: true, + externalAI: true, + }; + return (
- {page.layout.tableOfContents ? ( + {page.layout.tableOfContents && + // Show page actions if *any* of the actions are enabled + (withAIChat || pageActions.markdown || pageActions.externalAI) ? (
) : null} From 39c4f76aea9fe6a71a709c9a8c56a1d22f3070d1 Mon Sep 17 00:00:00 2001 From: Zeno Kapitein Date: Mon, 21 Jul 2025 11:20:17 +0200 Subject: [PATCH 3/3] Add fullwidth page option (#3344) --- .changeset/fuzzy-bulldogs-listen.md | 5 +++++ .../src/components/DocumentView/Blocks.tsx | 22 +++++++++++++++++-- .../src/components/DocumentView/Divider.tsx | 2 +- .../src/components/DocumentView/Heading.tsx | 1 + .../gitbook/src/components/Footer/Footer.tsx | 12 +++++----- .../src/components/PageAside/PageAside.tsx | 2 +- .../src/components/PageBody/PageBody.tsx | 10 ++++----- .../PageBody/PageBodyBlankslate.tsx | 4 +++- .../src/components/PageBody/PageCover.tsx | 2 +- .../PageBody/PageFooterNavigation.tsx | 2 +- .../src/components/PageBody/PageHeader.tsx | 2 +- .../PageBody/PreservePageLayout.tsx | 18 +++++++-------- .../components/SitePage/SitePageSkeleton.tsx | 2 +- .../components/SpaceLayout/SpaceLayout.tsx | 2 +- packages/gitbook/src/components/layout.ts | 1 - packages/gitbook/tailwind.config.ts | 6 ++--- 16 files changed, 59 insertions(+), 34 deletions(-) create mode 100644 .changeset/fuzzy-bulldogs-listen.md diff --git a/.changeset/fuzzy-bulldogs-listen.md b/.changeset/fuzzy-bulldogs-listen.md new file mode 100644 index 000000000..e58b92c18 --- /dev/null +++ b/.changeset/fuzzy-bulldogs-listen.md @@ -0,0 +1,5 @@ +--- +"gitbook": patch +--- + +Add fullwidth page option diff --git a/packages/gitbook/src/components/DocumentView/Blocks.tsx b/packages/gitbook/src/components/DocumentView/Blocks.tsx index 4bbe605d8..be09f2b42 100644 --- a/packages/gitbook/src/components/DocumentView/Blocks.tsx +++ b/packages/gitbook/src/components/DocumentView/Blocks.tsx @@ -47,6 +47,23 @@ type UnwrappedBlocksProps = DocumentContextProps & isOffscreen?: boolean; }; +/* Blocks that can be full width are automatically expanded on full-width pages. + * Ideally we'd rely on the block type to determine if it can be full width, but + * the block's `fullWidth` property does not differentiate between `undefined` and `false`. + * So instead we hardcode a list of blocks that can be full width. */ +const FULL_WIDTH_BLOCKS = [ + 'table', + 'tabs', + 'integration', + 'openapi', + 'images', + 'embed', + 'columns', + 'code', + 'content-ref', + 'hint', +]; + /** * Renders a list of blocks without a wrapper element. */ @@ -68,10 +85,11 @@ export function UnwrappedBlocks(props: UnwrappedBl key={node.key || `${node.type}-${index}`} block={node} style={[ - 'mx-auto w-full decoration-primary/6', + 'mx-auto page-width-wide:mx-0 w-full decoration-primary/6', node.data && 'fullWidth' in node.data && node.data.fullWidth ? 'max-w-screen-2xl' - : 'page-full-width:ml-0 max-w-3xl', + : 'max-w-3xl', + FULL_WIDTH_BLOCKS.includes(node.type) && 'page-width-wide:max-w-screen-2xl', blockStyle, ]} isEstimatedOffscreen={isOffscreen} diff --git a/packages/gitbook/src/components/DocumentView/Divider.tsx b/packages/gitbook/src/components/DocumentView/Divider.tsx index f787d044b..15302df47 100644 --- a/packages/gitbook/src/components/DocumentView/Divider.tsx +++ b/packages/gitbook/src/components/DocumentView/Divider.tsx @@ -7,5 +7,5 @@ import type { BlockProps } from './Block'; export function Divider(props: BlockProps) { const { style } = props; - return
; + return
; } diff --git a/packages/gitbook/src/components/DocumentView/Heading.tsx b/packages/gitbook/src/components/DocumentView/Heading.tsx index 5ca93c148..769bb66ba 100644 --- a/packages/gitbook/src/components/DocumentView/Heading.tsx +++ b/packages/gitbook/src/components/DocumentView/Heading.tsx @@ -27,6 +27,7 @@ export function Heading(props: BlockProps) { 'flex', 'items-baseline', 'scroll-m-12', + getTextAlignment(block.data.align), hashLinkButtonWrapperStyles, style, textStyle.marginTop diff --git a/packages/gitbook/src/components/Footer/Footer.tsx b/packages/gitbook/src/components/Footer/Footer.tsx index eaeb6cc95..31c2c5fd5 100644 --- a/packages/gitbook/src/components/Footer/Footer.tsx +++ b/packages/gitbook/src/components/Footer/Footer.tsx @@ -36,16 +36,16 @@ export function Footer(props: { context: GitBookSiteContext }) {
{ @@ -106,7 +106,7 @@ export function Footer(props: { context: GitBookSiteContext }) { 'col-span-2 page-has-toc:lg:col-span-1 page-has-toc:lg:col-start-2 page-no-toc:xl:col-span-1 page-no-toc:xl:col-start-2' )} > -
+
{partition(customization.footer.groups, FOOTER_COLUMNS).map( (column, columnIndex) => (
- + {page.cover && page.layout.cover && page.layout.coverSize === 'hero' ? ( ) : null} diff --git a/packages/gitbook/src/components/PageBody/PageBodyBlankslate.tsx b/packages/gitbook/src/components/PageBody/PageBodyBlankslate.tsx index 1bf4a8428..6eb6c039b 100644 --- a/packages/gitbook/src/components/PageBody/PageBodyBlankslate.tsx +++ b/packages/gitbook/src/components/PageBody/PageBodyBlankslate.tsx @@ -71,11 +71,13 @@ export async function PageBodyBlankslate(props: { className={tcls( 'grid', 'max-w-3xl', + 'page-width-wide:max-w-screen-2xl', 'w-full', 'mx-auto', 'gap-4', 'grid-cols-1', - 'sm:grid-cols-2' + 'sm:grid-cols-2', + 'page-width-wide:md:grid-cols-3' )} > {pageElements} diff --git a/packages/gitbook/src/components/PageBody/PageCover.tsx b/packages/gitbook/src/components/PageBody/PageCover.tsx index 9c8113e8d..5e52ddf8a 100644 --- a/packages/gitbook/src/components/PageBody/PageCover.tsx +++ b/packages/gitbook/src/components/PageBody/PageCover.tsx @@ -95,7 +95,7 @@ export async function PageCover(props: { : [ 'sm:mx-auto', 'max-w-3xl ', - 'page-full-width:max-w-screen-2xl', + 'page-width-wide:max-w-screen-2xl', 'sm:rounded-md', 'mb-8', ] diff --git a/packages/gitbook/src/components/PageBody/PageFooterNavigation.tsx b/packages/gitbook/src/components/PageBody/PageFooterNavigation.tsx index 465eb81ca..1e9df3be2 100644 --- a/packages/gitbook/src/components/PageBody/PageFooterNavigation.tsx +++ b/packages/gitbook/src/components/PageBody/PageFooterNavigation.tsx @@ -34,7 +34,7 @@ export async function PageFooterNavigation(props: { 'mt-6', 'gap-2', 'max-w-3xl', - 'page-full-width:max-w-screen-2xl', + 'page-width-wide:max-w-screen-2xl', 'mx-auto', 'text-tint' )} diff --git a/packages/gitbook/src/components/PageBody/PageHeader.tsx b/packages/gitbook/src/components/PageBody/PageHeader.tsx index 4797f6b75..6b96faac0 100644 --- a/packages/gitbook/src/components/PageBody/PageHeader.tsx +++ b/packages/gitbook/src/components/PageBody/PageHeader.tsx @@ -32,7 +32,7 @@ export async function PageHeader(props: {
{ // We use the header as it's an element preserved between page transitions @@ -23,12 +23,12 @@ export function PreservePageLayout(props: { asFullWidth: boolean }) { return; } - if (asFullWidth) { - header.classList.add('site-full-width'); + if (siteWidthWide) { + header.classList.add('site-width-wide'); } else { - header.classList.remove('site-full-width'); + header.classList.remove('site-width-wide'); } - }, [asFullWidth]); + }, [siteWidthWide]); return null; } diff --git a/packages/gitbook/src/components/SitePage/SitePageSkeleton.tsx b/packages/gitbook/src/components/SitePage/SitePageSkeleton.tsx index 7b840afbb..2f18c0b8c 100644 --- a/packages/gitbook/src/components/SitePage/SitePageSkeleton.tsx +++ b/packages/gitbook/src/components/SitePage/SitePageSkeleton.tsx @@ -19,7 +19,7 @@ export function SitePageSkeleton() { 'lg:items-start' )} > -
+
diff --git a/packages/gitbook/src/components/SpaceLayout/SpaceLayout.tsx b/packages/gitbook/src/components/SpaceLayout/SpaceLayout.tsx index 0c0efdb1e..f57aec64c 100644 --- a/packages/gitbook/src/components/SpaceLayout/SpaceLayout.tsx +++ b/packages/gitbook/src/components/SpaceLayout/SpaceLayout.tsx @@ -97,7 +97,7 @@ export function SpaceLayout(props: { 'flex-col', 'lg:flex-row', CONTAINER_STYLE, - 'site-full-width:max-w-full', + 'site-width-wide:max-w-full', // Ensure the footer is display below the viewport even if the content is not enough withFooter && 'min-h-[calc(100vh-64px)]', diff --git a/packages/gitbook/src/components/layout.ts b/packages/gitbook/src/components/layout.ts index 6a9b39a98..4f6579ab6 100644 --- a/packages/gitbook/src/components/layout.ts +++ b/packages/gitbook/src/components/layout.ts @@ -14,7 +14,6 @@ export const CONTAINER_STYLE: ClassValue = [ 'md:px-8', 'max-w-screen-2xl', 'mx-auto', - // 'site-full-width:max-w-full', ]; /** diff --git a/packages/gitbook/tailwind.config.ts b/packages/gitbook/tailwind.config.ts index a895b5271..fa5266945 100644 --- a/packages/gitbook/tailwind.config.ts +++ b/packages/gitbook/tailwind.config.ts @@ -564,9 +564,9 @@ const config: Config = { /** * Variant when the page contains a block that will be rendered in full-width mode. */ - addVariant('site-full-width', 'body:has(.site-full-width) &'); - addVariant('site-default-width', 'body:has(.site-default-width) &'); - addVariant('page-full-width', 'body:has(.page-full-width) &'); + addVariant('site-width-wide', 'body:has(.site-width-wide) &'); + addVariant('site-width-default', 'body:has(.site-width-default) &'); + addVariant('page-width-wide', 'body:has(.page-width-wide) &'); /** * Variant when the page is configured to hide the table of content.