Add section description (#2814)

This commit is contained in:
Brett Jephson
2025-02-06 16:53:48 +00:00
committed by GitHub
parent 92b7668bf3
commit 0510b6f29e
10 changed files with 64 additions and 40 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'gitbook': patch
---
Add section description to SectionGroupTile
+3 -1
View File
@@ -33,7 +33,7 @@
"name": "gitbook",
"version": "0.5.0",
"dependencies": {
"@gitbook/api": "^0.90.0",
"@gitbook/api": "^0.92.0",
"@gitbook/cache-do": "workspace:*",
"@gitbook/emoji-codepoints": "workspace:*",
"@gitbook/icons": "workspace:*",
@@ -4615,6 +4615,8 @@
"gaxios/https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="],
"gitbook/@gitbook/api": ["@gitbook/api@0.92.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-fZHECoW8k6oqMK66QlT0W8EMWQaSEULzqgkI+tZ/gmA/n7Ek5nQwT1TUenEq4JtXPtbKSYG7du0b1AfiOR+FOA=="],
"gitbook-v2/next": ["next@15.2.0-canary.33", "", { "dependencies": { "@next/env": "15.2.0-canary.33", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.2.0-canary.33", "@next/swc-darwin-x64": "15.2.0-canary.33", "@next/swc-linux-arm64-gnu": "15.2.0-canary.33", "@next/swc-linux-arm64-musl": "15.2.0-canary.33", "@next/swc-linux-x64-gnu": "15.2.0-canary.33", "@next/swc-linux-x64-musl": "15.2.0-canary.33", "@next/swc-win32-arm64-msvc": "15.2.0-canary.33", "@next/swc-win32-x64-msvc": "15.2.0-canary.33", "sharp": "^0.33.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-WF8QLeYkakuYwksdWY/F+Bi8tNJfIbiSYk9hCmldn9sNp1lU3lqI1hrW1ynbcMSaXC+qQEr7yol2OdvVZ4nZYQ=="],
"glob/minimatch": ["minimatch@10.0.1", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ=="],
+1 -1
View File
@@ -16,7 +16,7 @@
"clean": "rm -rf ./.next && rm -rf ./public/~gitbook/static"
},
"dependencies": {
"@gitbook/api": "^0.90.0",
"@gitbook/api": "^0.92.0",
"@gitbook/cache-do": "workspace:*",
"@gitbook/emoji-codepoints": "workspace:*",
"@gitbook/icons": "workspace:*",
@@ -1,6 +1,6 @@
import { SiteSection, SiteSpace, SiteStructure } from '@gitbook/api';
import assertNever from 'assert-never';
import { Heading, ListItem, Paragraph, Root, RootContent } from 'mdast';
import { ListItem, Paragraph, Root, RootContent } from 'mdast';
import { toMarkdown } from 'mdast-util-to-markdown';
import { NextRequest } from 'next/server';
@@ -10,6 +10,7 @@ import { getPagePath } from '@/lib/pages';
import { joinPath } from '@/lib/paths';
import { checkIsRootPointer, getSiteContentPointer } from '@/lib/pointer';
import { getIndexablePages } from '@/lib/sitemap';
import { getSiteStructureSections } from '@/lib/utils';
export const runtime = 'edge';
@@ -59,7 +60,7 @@ export async function GET(req: NextRequest) {
async function getNodesFromSiteStructure(siteStructure: SiteStructure): Promise<RootContent[]> {
switch (siteStructure.type) {
case 'sections':
return getNodesFromSections(siteStructure.structure);
return getNodesFromSections(getSiteStructureSections(siteStructure));
case 'siteSpaces':
return getNodesFromSiteSpaces(siteStructure.structure, { heading: true });
default:
@@ -2,11 +2,11 @@ import { SiteSection, SiteSpace, SiteStructure } from '@gitbook/api';
import assertNever from 'assert-never';
import jsontoxml from 'jsontoxml';
import { getPublishedContentSite, SiteContentPointer } from '@/lib/api';
import { getAbsoluteHref } from '@/lib/links';
import { getPublishedContentSite } from '@/lib/api';
import { joinPath } from '@/lib/paths';
import { checkIsRootPointer, getSiteContentPointer } from '@/lib/pointer';
import { filterOutNullable } from '@/lib/typescript';
import { getSiteStructureSections } from '@/lib/utils';
export const runtime = 'edge';
@@ -62,7 +62,7 @@ export async function GET() {
async function getUrlsFromSiteStructure(siteStructure: SiteStructure): Promise<string[]> {
switch (siteStructure.type) {
case 'sections':
return getUrlsFromSiteSections(siteStructure.structure);
return getUrlsFromSiteSections(getSiteStructureSections(siteStructure));
case 'siteSpaces':
return getUrlsFromSiteSpaces(siteStructure.structure);
default:
@@ -1,7 +1,6 @@
'use server';
import { RevisionPage, SearchAIAnswer, SearchPageResult, SiteSpace, Space } from '@gitbook/api';
import { captureException } from '@sentry/nextjs';
import * as React from 'react';
import { assert } from 'ts-essentials';
@@ -10,6 +9,7 @@ import * as api from '@/lib/api';
import { getAbsoluteHref, getPageHref } from '@/lib/links';
import { resolvePageId } from '@/lib/pages';
import { filterOutNullable } from '@/lib/typescript';
import { getSiteStructureSections } from '@/lib/utils';
import { DocumentView } from '../DocumentView';
@@ -78,7 +78,7 @@ async function searchSiteContent(args: {
const siteSpaces = siteStructure
? siteStructure.type === 'siteSpaces'
? siteStructure.structure
: siteStructure.structure.reduce<SiteSpace[]>((prev, section) => {
: getSiteStructureSections(siteStructure).reduce<SiteSpace[]>((prev, section) => {
const sectionSiteSpaces = section.siteSpaces.map((siteSpace) => ({
...siteSpace,
space: {
@@ -70,30 +70,34 @@ export function SiteSectionTabs(props: { sections: SectionsList }) {
return (
<NavigationMenu.Item key={id} value={id}>
{isGroup ? (
<>
<NavigationMenu.Trigger
ref={(node) =>
onNodeUpdate(
node,
id,
sectionOrGroup.sections.length,
)
}
asChild
>
<SectionGroupTab
isActive={isActive}
title={title}
icon={icon as IconName}
/>
</NavigationMenu.Trigger>
<NavigationMenu.Content className="absolute z-20 left-0 top-0 w-full md:w-max data-[motion=from-end]:motion-safe:animate-enterFromRight data-[motion=from-start]:motion-safe:animate-enterFromLeft data-[motion=to-end]:motion-safe:animate-exitToRight data-[motion=to-start]:motion-safe:animate-exitToLeft">
<SectionGroupTileList
sections={sectionOrGroup.sections as SiteSection[]}
currentSection={currentSection}
/>
</NavigationMenu.Content>
</>
sectionOrGroup.sections.length > 0 ? (
<>
<NavigationMenu.Trigger
ref={(node) =>
onNodeUpdate(
node,
id,
sectionOrGroup.sections.length,
)
}
asChild
>
<SectionGroupTab
isActive={isActive}
title={title}
icon={icon as IconName}
/>
</NavigationMenu.Trigger>
<NavigationMenu.Content className="absolute z-20 left-0 top-0 w-full md:w-max data-[motion=from-end]:motion-safe:animate-enterFromRight data-[motion=from-start]:motion-safe:animate-enterFromLeft data-[motion=to-end]:motion-safe:animate-exitToRight data-[motion=to-start]:motion-safe:animate-exitToLeft">
<SectionGroupTileList
sections={
sectionOrGroup.sections as SiteSection[]
}
currentSection={currentSection}
/>
</NavigationMenu.Content>
</>
) : null
) : (
<NavigationMenu.Link asChild>
<SectionTab
@@ -250,7 +254,7 @@ function SectionGroupTile(props: { section: SiteSection; isActive: boolean }) {
{icon ? <SectionIcon isActive={false} icon={icon as IconName} /> : null}
<span className="truncate min-w-0">{title}</span>
</div>
<p className="text min-h-[2lh]">{/* TODO - add section description */}</p>
<p className="text-tint-subtle min-h-[2lh]">{section.description}</p>
</Link>
</li>
);
+2 -1
View File
@@ -3,6 +3,7 @@ import { headers } from 'next/headers';
import { assert } from 'ts-essentials';
import { SiteContentPointer, SpaceContentPointer } from './api';
import { getSiteStructureSections } from './utils';
/**
* Get the current site content pointer from the headers
@@ -52,7 +53,7 @@ export function checkIsRootPointer(
): boolean {
switch (siteStructure.type) {
case 'sections': {
return siteStructure.structure.some(
return getSiteStructureSections(siteStructure).some(
(structure) =>
structure.default &&
structure.id === pointer.siteSectionId &&
+8 -5
View File
@@ -1,6 +1,5 @@
import {
ContentRef,
ContentRefSpace,
Revision,
RevisionFile,
RevisionPageDocument,
@@ -31,6 +30,7 @@ import { getBlockById, getBlockTitle } from './document';
import { getGitbookAppHref, getPageHref, PageHrefContext } from './links';
import { getPagePath, resolvePageId } from './pages';
import { ClassValue } from './tailwind';
import { getSiteStructureSections } from './utils';
export interface ResolvedContentRef {
/** Text to render in the content ref */
@@ -317,10 +317,13 @@ async function getBestTargetSpace(
const siteSpaces =
publishedContentSite.structure.type === 'siteSpaces'
? publishedContentSite.structure.structure
: publishedContentSite.structure.structure.reduce<SiteSpace[]>((acc, section) => {
acc.push(...section.siteSpaces);
return acc;
}, []);
: getSiteStructureSections(publishedContentSite.structure).reduce<SiteSpace[]>(
(acc, section) => {
acc.push(...section.siteSpaces);
return acc;
},
[],
);
const spaces = parseSpacesFromSiteSpaces(siteSpaces);
const foundSpace = spaces.find((space) => space.id === spaceId);
if (foundSpace) {
+8
View File
@@ -72,3 +72,11 @@ export function defaultCustomizationForSpace(): api.CustomizationSettings {
inherit: false,
};
}
export function getSiteStructureSections(siteStructure: api.SiteStructure) {
return siteStructure.type === 'sections'
? siteStructure.structure.flatMap((item) =>
item.object === 'site-section-group' ? item.sections : item,
)
: [];
}