diff --git a/packages/gitbook/src/components/Adaptive/AIPageSummary.tsx b/packages/gitbook/src/components/Adaptive/AIPageSummary.tsx index 8f0eae0eb..a430d080c 100644 --- a/packages/gitbook/src/components/Adaptive/AIPageSummary.tsx +++ b/packages/gitbook/src/components/Adaptive/AIPageSummary.tsx @@ -1,15 +1,16 @@ 'use client'; -import { useEffect, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { useVisitedPages } from '../Insights'; import { usePageContext } from '../PageContext'; import { useAdaptiveContext } from './AdaptiveContext'; import { streamPageSummary } from './server-actions/streamPageSummary'; export function AIPageSummary() { - const { open } = useAdaptiveContext(); + const { toggle, setLoading, setToggle } = useAdaptiveContext(); const currentPage = usePageContext(); const visitedPages = useVisitedPages((state) => state.pages); + const visitedPagesRef = useRef(visitedPages); const [summary, setSummary] = useState<{ pageSummary?: string; @@ -17,7 +18,15 @@ export function AIPageSummary() { }>({}); useEffect(() => { + if (!visitedPages?.length) return; + + // Skip if the visited pages haven't changed + if (JSON.stringify(visitedPagesRef.current) === JSON.stringify(visitedPages)) return; + + visitedPagesRef.current = visitedPages; + let canceled = false; + setLoading(true); (async () => { const stream = await streamPageSummary({ @@ -36,28 +45,51 @@ export function AIPageSummary() { setSummary(summary); } - })(); + })().finally(() => { + setLoading(false); + if (!toggle.manual) { + setToggle({ + open: true, + manual: false, + }); + } + }); return () => { canceled = true; }; }, [currentPage, visitedPages]); + const shimmerBlocks = [20, 35, 25, 10, 45, 30, 30, 35, 25, 10, 40, 30]; // Widths in percentages + return ( - open && ( + toggle.open && (
{summary.pageSummary ? (
-
+
Key facts
{summary.pageSummary}
- ) : null} + ) : ( +
+ {shimmerBlocks.map((width, index) => ( +
+ ))} +
+ )} {visitedPages.length > 1 && summary?.bigPicture ? (
-
+
Big Picture
{summary?.bigPicture} diff --git a/packages/gitbook/src/components/Adaptive/AdaptiveContext.tsx b/packages/gitbook/src/components/Adaptive/AdaptiveContext.tsx index 22b883cb8..c999e7d97 100644 --- a/packages/gitbook/src/components/Adaptive/AdaptiveContext.tsx +++ b/packages/gitbook/src/components/Adaptive/AdaptiveContext.tsx @@ -1,88 +1,31 @@ 'use client'; -import React, { useEffect } from 'react'; -import { useVisitedPages } from '../Insights'; -import { usePageContext } from '../PageContext'; - -export type SuggestedPage = { - id: string; - title: string; - href: string; - icon?: string; - emoji?: string; -}; - -export type Journey = { - label: string; - icon?: string; - pages?: Array; -}; +import React from 'react'; type AdaptiveContextType = { - journeys: Journey[]; - selectedJourney: Journey | undefined; - setSelectedJourney: (journey: Journey | undefined) => void; loading: boolean; - open: boolean; - setOpen: (open: boolean) => void; + setLoading: (loading: boolean) => void; + toggle: { + open: boolean; + manual: boolean; + }; + setToggle: (toggle: { open: boolean; manual: boolean }) => void; }; export const AdaptiveContext = React.createContext(null); -export const JOURNEY_COUNT = 4; - /** * Client side context provider to pass information about the current page. */ -export function JourneyContextProvider({ - children, - spaces, -}: { children: React.ReactNode; spaces: { id: string; title: string }[] }) { - const [journeys, setJourneys] = React.useState([]); - const [selectedJourney, setSelectedJourney] = React.useState(undefined); +export function AdaptiveContextProvider({ children }: { children: React.ReactNode }) { const [loading, setLoading] = React.useState(true); - const [open, setOpen] = React.useState(true); - - const currentPage = usePageContext(); - const visitedPages = useVisitedPages((state) => state.pages); - - useEffect(() => { - let canceled = false; - - // setJourneys([]); - - (async () => { - // const stream = await streamPageJourneySuggestions({ - // count: JOURNEY_COUNT, - // currentPage: { - // id: currentPage.pageId, - // title: currentPage.title, - // }, - // currentSpace: { - // id: currentPage.spaceId, - // }, - // allSpaces: spaces, - // visitedPages, - // }); - - // for await (const journey of stream) { - // if (canceled) return; - - // setJourneys((prev) => [...prev, journey]); - // } - - setLoading(false); - })(); - - return () => { - canceled = true; - }; - }, [currentPage.pageId, currentPage.spaceId, currentPage.title, visitedPages, spaces]); + const [toggle, setToggle] = React.useState({ + open: false, + manual: false, + }); return ( - + {children} ); diff --git a/packages/gitbook/src/components/Adaptive/AdaptivePane.tsx b/packages/gitbook/src/components/Adaptive/AdaptivePane.tsx index 628bb4a5a..78e800832 100644 --- a/packages/gitbook/src/components/Adaptive/AdaptivePane.tsx +++ b/packages/gitbook/src/components/Adaptive/AdaptivePane.tsx @@ -5,13 +5,13 @@ import { AIPageSummary } from './AIPageSummary'; import { useAdaptiveContext } from './AdaptiveContext'; import { AdaptivePaneHeader } from './AdaptivePaneHeader'; export function AdaptivePane() { - const { open } = useAdaptiveContext(); + const { toggle } = useAdaptiveContext(); return (
diff --git a/packages/gitbook/src/components/Adaptive/AdaptivePaneHeader.tsx b/packages/gitbook/src/components/Adaptive/AdaptivePaneHeader.tsx index 0141b1316..46b67bd61 100644 --- a/packages/gitbook/src/components/Adaptive/AdaptivePaneHeader.tsx +++ b/packages/gitbook/src/components/Adaptive/AdaptivePaneHeader.tsx @@ -6,7 +6,7 @@ import { Button, Loading } from '../primitives'; import { useAdaptiveContext } from './AdaptiveContext'; export function AdaptivePaneHeader() { - const { loading, open, setOpen } = useAdaptiveContext(); + const { loading, toggle, setToggle } = useAdaptiveContext(); return (
@@ -29,11 +29,16 @@ export function AdaptivePaneHeader() {
); diff --git a/packages/gitbook/src/components/Adaptive/server-actions/streamPageSummary.ts b/packages/gitbook/src/components/Adaptive/server-actions/streamPageSummary.ts index bc9051cb4..5bec0329e 100644 --- a/packages/gitbook/src/components/Adaptive/server-actions/streamPageSummary.ts +++ b/packages/gitbook/src/components/Adaptive/server-actions/streamPageSummary.ts @@ -141,28 +141,23 @@ export async function* streamPageSummary({ For the big picture summary: ALWAYS: - - Use a natural, conversational tone a person would actually use - - Include concrete examples with specific benefits - - Balance being precise with sounding natural - - Use occasional contractions or slightly informal phrasing - - Write as if explaining to a colleague in a friendly way - - NEVER: - - Use empty relationship words like "enhances," "supports," or "integrates with" - - Write in an overly academic or technical style - - Use abstract phrases without concrete meaning - - Sound like marketing copy or documentation - - Lose specificity while trying to sound conversational + - Synthesize specific concepts from across multiple pages into concrete insights + - Highlight practical patterns and workflows that emerge when combining these concepts + - Focus on real capabilities that come from understanding multiple features together + - Use specific examples that show the value of combining these ideas + - Keep the language simple and direct + - Use a conversational tone and short sentences, without commas. POOR EXAMPLES TO AVOID: - ✗ "Markdown enhances content creation by integrating with collaboration features." - ✗ "API components support the documentation workflow through seamless integration." - ✗ "The robust search functionality facilitates efficient information retrieval." + ✗ "GitBook combines content creation, collaboration, and integrations, building on your understanding of identifiers and paginated results for seamless documentation management." + ✗ "The platform's robust features for content organization, versioning, and access control work together to create a powerful documentation ecosystem." + ✗ "By leveraging GitBook's content blocks, permissions system, and API capabilities, you can build comprehensive documentation solutions." GOOD EXAMPLES TO FOLLOW: - ✓ "Markdown tables make API data easier to read, while code blocks let you test examples right in the docs." - ✓ "Webhooks save tons of time by automatically creating PDFs whenever content changes." - ✓ "Version control pins down exactly who changed what text, so you won't waste time on formatting debates."`, + ✓ "Combining Markdown tables with webhook notifications means your API docs stay up-to-date automatically - when you update a parameter, the PDF version refreshes too." + ✓ "Content blocks and version history together solve the biggest docs headache - you can experiment with different layouts while keeping a clean record of what changed and why." + ✓ "The real power comes from linking custom domains with content permissions - your sales team gets branded docs while your developers see the technical details on the same site." + ✓ "With spaces, webhooks, and custom metadata working together, you're not just making docs - you're building a knowledge system that responds to how your team actually works."`, }, { role: AIMessageRole.Developer, diff --git a/packages/gitbook/src/components/RootLayout/CustomizationRootLayout.tsx b/packages/gitbook/src/components/RootLayout/CustomizationRootLayout.tsx index c7d2f35b6..e517c3600 100644 --- a/packages/gitbook/src/components/RootLayout/CustomizationRootLayout.tsx +++ b/packages/gitbook/src/components/RootLayout/CustomizationRootLayout.tsx @@ -34,6 +34,7 @@ import { ClientContexts } from './ClientContexts'; import '@gitbook/icons/style.css'; import './globals.css'; import { GITBOOK_FONTS_URL, GITBOOK_ICONS_TOKEN, GITBOOK_ICONS_URL } from '@v2/lib/env'; +import { AdaptiveContextProvider } from '../Adaptive/AdaptiveContext'; import { AnnouncementDismissedScript } from '../Announcement'; /** @@ -175,7 +176,9 @@ export async function CustomizationRootLayout(props: { : null) || IconStyle.Regular } > - {children} + + {children} + diff --git a/packages/gitbook/src/components/SitePage/SitePage.tsx b/packages/gitbook/src/components/SitePage/SitePage.tsx index 83a9d947d..45f784d21 100644 --- a/packages/gitbook/src/components/SitePage/SitePage.tsx +++ b/packages/gitbook/src/components/SitePage/SitePage.tsx @@ -15,7 +15,6 @@ import { getPagePath } from '@/lib/pages'; import { isPageIndexable, isSiteIndexable } from '@/lib/seo'; import { getResizedImageURL } from '@v2/lib/images'; -import { JourneyContextProvider } from '../Adaptive/AdaptiveContext'; import { PageContextProvider } from '../PageContext'; import { PageClientLayout } from './PageClientLayout'; import { type PagePathParams, fetchPageData, getPathnameParam } from './fetch'; @@ -71,32 +70,30 @@ export async function SitePage(props: SitePageProps) { return ( - - {withFullPageCover && page.cover ? ( - - ) : null} - {/* We use a flex row reverse to render the aside first because the page is streamed. */} -
- - -
- - - -
+ {withFullPageCover && page.cover ? ( + + ) : null} + {/* We use a flex row reverse to render the aside first because the page is streamed. */} +
+ + +
+ + +
); }