mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-21 10:03:31 +00:00
Tweak link summaries
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
'use client';
|
||||
import { Icon } from '@gitbook/icons';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Loading } from '../primitives';
|
||||
import { streamLinkPageSummary } from './server-actions/streamLinkPageSummary';
|
||||
|
||||
/**
|
||||
@@ -11,8 +13,16 @@ export function AIPageLinkSummary(props: {
|
||||
targetSpaceId: string;
|
||||
targetPageId: string;
|
||||
linkPreview?: string;
|
||||
showTrademark: boolean;
|
||||
}) {
|
||||
const { currentSpaceId, currentPageId, targetSpaceId, targetPageId, linkPreview } = props;
|
||||
const {
|
||||
currentSpaceId,
|
||||
currentPageId,
|
||||
targetSpaceId,
|
||||
targetPageId,
|
||||
linkPreview,
|
||||
showTrademark = true,
|
||||
} = props;
|
||||
|
||||
const [highlight, setHighlight] = useState('');
|
||||
|
||||
@@ -45,5 +55,19 @@ export function AIPageLinkSummary(props: {
|
||||
};
|
||||
}, [currentSpaceId, currentPageId, targetSpaceId, targetPageId]);
|
||||
|
||||
return <p>{highlight.length > 1 ? highlight : "Loading..."}</p>;
|
||||
return (
|
||||
<div>
|
||||
<div className="mb-1 flex items-center gap-1 font-semibold text-tint text-xs uppercase leading-tight tracking-wide">
|
||||
{showTrademark ? (
|
||||
<Loading className="size-4" busy={!highlight || highlight.length === 0} />
|
||||
) : (
|
||||
<Icon icon="sparkle" className="size-3" />
|
||||
)}
|
||||
<h6 className="text-tint">
|
||||
Page highlight
|
||||
</h6>
|
||||
</div>
|
||||
{highlight}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
import { Icon } from '@gitbook/icons';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Link } from '../primitives';
|
||||
import { streamNextRecommendedPages } from './server-actions';
|
||||
@@ -15,7 +16,7 @@ export function AIPageNextRecommendedPages(props: {
|
||||
}) {
|
||||
const { spaceId, revisionId, pageId } = props;
|
||||
|
||||
const [pages, setPages] = useState<{ title: string; href: string }[]>([]);
|
||||
const [pages, setPages] = useState<{ title: string; href: string; icon?: string }[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
let canceled = false;
|
||||
@@ -45,17 +46,11 @@ export function AIPageNextRecommendedPages(props: {
|
||||
}, [spaceId, revisionId, pageId]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={tcls(
|
||||
'flex flex-row flex-wrap gap-2',
|
||||
'max-w-3xl',
|
||||
'mx-auto',
|
||||
'page-api-block:ml-0'
|
||||
)}
|
||||
>
|
||||
<div className={tcls('flex flex-col gap-2')}>
|
||||
{pages.map((page) => {
|
||||
return (
|
||||
<Link key={page.href} href={page.href}>
|
||||
<Link className='flex items-center gap-3' key={page.href} href={page.href}>
|
||||
<Icon className="size-4" icon={page.icon} />
|
||||
{page.title}
|
||||
</Link>
|
||||
);
|
||||
|
||||
@@ -4,15 +4,15 @@ import React from 'react';
|
||||
import { useLanguage } from '@/intl/client';
|
||||
import { tString } from '@/intl/translate';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
import Link from 'next/link';
|
||||
import { Button } from '../primitives';
|
||||
import { AIPageNextRecommendedPages } from './AIPageNextRecommendedPages';
|
||||
import { useAdaptivePane } from './state';
|
||||
|
||||
export function AdaptivePane(props: { spaceId: string; revisionId: string; pageId: string }) {
|
||||
const language = useLanguage();
|
||||
const { opened, open, close } = useAdaptivePane();
|
||||
|
||||
const {spaceId, revisionId, pageId} = props;
|
||||
const { spaceId, revisionId, pageId } = props;
|
||||
|
||||
React.useEffect(() => {
|
||||
if (opened) {
|
||||
@@ -22,10 +22,17 @@ export function AdaptivePane(props: { spaceId: string; revisionId: string; pageI
|
||||
}
|
||||
}, [opened]);
|
||||
|
||||
const pages = [
|
||||
'GitBook Documentation',
|
||||
'GitHub & GitLab Sync',
|
||||
'Troubleshooting',
|
||||
'Version control',
|
||||
];
|
||||
|
||||
return (
|
||||
<div className={tcls('w-80 flex-col', opened ? 'flex' : 'hidden')}>
|
||||
<div className="sticky top-28 flex flex-col gap-4 py-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className={tcls('w-80 flex-col text-sm', opened ? 'flex' : 'hidden')}>
|
||||
<div className="sticky top-28 my-4 flex flex-col gap-2 rounded-md border border-tint-subtle bg-tint-subtle theme-gradient:bg-tint-1/1 p-4">
|
||||
<div className='flex items-center gap-2 border-tint-subtle border-b pb-2'>
|
||||
<h2
|
||||
className={tcls(
|
||||
'flex-1',
|
||||
@@ -35,11 +42,12 @@ export function AdaptivePane(props: { spaceId: string; revisionId: string; pageI
|
||||
'uppercase'
|
||||
)}
|
||||
>
|
||||
{tString(language, 'adaptive_pane_title')}
|
||||
History
|
||||
</h2>
|
||||
<Button
|
||||
iconOnly
|
||||
variant="blank"
|
||||
size="medium"
|
||||
icon={opened ? 'chevrons-right' : 'chevrons-left'}
|
||||
label={
|
||||
opened
|
||||
@@ -52,7 +60,18 @@ export function AdaptivePane(props: { spaceId: string; revisionId: string; pageI
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<AIPageNextRecommendedPages spaceId={spaceId} revisionId={revisionId} pageId={pageId} />
|
||||
<ol className="-mx-2 flex flex-col gap-0.5 text-tint-subtle">
|
||||
{pages.map((page) => (
|
||||
<li key={page}>
|
||||
<Link
|
||||
className="block rounded px-2 py-1.5 last:bg-primary-hover hover:bg-primary-hover"
|
||||
href="#"
|
||||
>
|
||||
{page}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -131,8 +131,6 @@ The content of the target page is:`,
|
||||
fetchServerActionSiteContext(baseContext),
|
||||
]);
|
||||
|
||||
console.log(await response);
|
||||
|
||||
const emitted = new Set<string>();
|
||||
for await (const value of stream) {
|
||||
const highlight = value.highlight;
|
||||
|
||||
@@ -104,6 +104,7 @@ export async function* streamNextRecommendedPages({
|
||||
}
|
||||
|
||||
yield {
|
||||
icon: page.page.icon ?? page.page.emoji,
|
||||
title: page.page.title,
|
||||
href: context.linker.toPathForPage({
|
||||
pages: context.pages,
|
||||
|
||||
@@ -20,7 +20,7 @@ export async function InlineLink(props: InlineProps<DocumentInlineLink>) {
|
||||
})
|
||||
: null;
|
||||
|
||||
if (!resolved) {
|
||||
if (!context.contentContext || !resolved) {
|
||||
return (
|
||||
<span title="Broken link" className="underline">
|
||||
<Inlines
|
||||
@@ -141,20 +141,24 @@ export async function InlineLink(props: InlineProps<DocumentInlineLink>) {
|
||||
</div>
|
||||
|
||||
{!isExternal &&
|
||||
context.contentContext ? (
|
||||
'site' in context.contentContext &&
|
||||
'page' in context.contentContext &&
|
||||
context.contentContext.site.adaptiveContent?.enabled &&
|
||||
inline.data.ref.kind === 'page' ? (
|
||||
<div className="border-tint-subtle border-t bg-tint p-4">
|
||||
<div className="mb-1 flex items-center gap-1 font-semibold text-tint text-xs uppercase leading-tight tracking-wide">
|
||||
<Icon icon="sparkle" className="size-3" />
|
||||
<h6 className="text-tint">AI Summary</h6>
|
||||
</div>
|
||||
<AIPageLinkSummary
|
||||
currentPageId={context.contentContext.page.id}
|
||||
currentSpaceId={context.contentContext.space.id}
|
||||
targetPageId={inline.data.ref.page}
|
||||
targetPageId={
|
||||
inline.data.ref.page ?? context.contentContext.page.id
|
||||
}
|
||||
targetSpaceId={
|
||||
inline.data.ref.space ?? context.contentContext.space.id
|
||||
}
|
||||
linkPreview={`**${resolved.text}**: ${resolved.subText}`}
|
||||
showTrademark={
|
||||
context.contentContext.customization.trademark.enabled
|
||||
}
|
||||
/>
|
||||
|
||||
{/* <div className="-m-2 mt-0 flex flex-col rounded-md p-2 text-sm">
|
||||
|
||||
@@ -13,7 +13,7 @@ import urlJoin from 'url-join';
|
||||
import { getSpaceLanguage, t } from '@/intl/server';
|
||||
import { getDocumentSections } from '@/lib/document-sections';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
import { AIPageNextRecommendedPages } from '../Adaptive';
|
||||
import { Ad } from '../Ads';
|
||||
import { getPDFURLSearchParams } from '../PDF';
|
||||
import { PageFeedbackForm } from '../PageFeedback';
|
||||
@@ -42,12 +42,16 @@ export function PageAside(props: {
|
||||
limit: 100,
|
||||
}).toString()}`
|
||||
);
|
||||
|
||||
const prevPages = ['GitBook Documentation', 'GitHub & GitLab Sync', 'Troubleshooting'];
|
||||
|
||||
return (
|
||||
<aside
|
||||
className={tcls(
|
||||
'group/aside',
|
||||
'hidden',
|
||||
'xl:flex',
|
||||
'text-sm',
|
||||
// 'page-no-toc:lg:flex',
|
||||
'flex-col',
|
||||
'basis-56',
|
||||
@@ -88,146 +92,132 @@ export function PageAside(props: {
|
||||
'page-api-block:xl:max-2xl:rounded-md',
|
||||
'page-api-block:xl:max-2xl:h-auto',
|
||||
'page-api-block:xl:max-2xl:my-8',
|
||||
'page-api-block:p-2',
|
||||
'page-api-block:p-2'
|
||||
|
||||
// When the adaptive pane is open, we hide the aside
|
||||
'adaptive-pane:hidden'
|
||||
// 'adaptive-pane:hidden'
|
||||
)}
|
||||
>
|
||||
{page.layout.outline ? (
|
||||
<>
|
||||
<div
|
||||
className={tcls(
|
||||
'hidden',
|
||||
'page-api-block:xl:max-2xl:flex',
|
||||
'text-xs',
|
||||
'tracking-wide',
|
||||
'font-semibold',
|
||||
'uppercase',
|
||||
<div
|
||||
className={tcls(
|
||||
'overflow-y-auto',
|
||||
'overflow-x-visible',
|
||||
|
||||
'flex-row',
|
||||
'items-center',
|
||||
'gap-2'
|
||||
)}
|
||||
>
|
||||
<Icon icon="block-quote" className={tcls('size-3')} />
|
||||
{t(language, 'on_this_page')}
|
||||
<Icon
|
||||
icon="chevron-down"
|
||||
className={tcls(
|
||||
'size-3',
|
||||
'opacity-6',
|
||||
'ml-auto',
|
||||
'page-api-block:xl:max-2xl:group-hover/aside:hidden'
|
||||
)}
|
||||
/>
|
||||
'flex',
|
||||
'flex-col',
|
||||
'shrink',
|
||||
'pb-12',
|
||||
|
||||
'sticky',
|
||||
'lg:top:0',
|
||||
'site-header:lg:top-16',
|
||||
'site-header-sections:lg:top-[6.75rem]',
|
||||
|
||||
'gap-2',
|
||||
'pt-8',
|
||||
|
||||
'page-api-block:xl:max-2xl:py-0',
|
||||
// Hide it for api page, until hovered
|
||||
'page-api-block:xl:max-2xl:hidden',
|
||||
'page-api-block:xl:max-2xl:group-hover/aside:flex'
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-2 font-semibold text-xs uppercase tracking-wide">
|
||||
<Icon icon="block-quote" className="size-3" />
|
||||
On this page
|
||||
</div>
|
||||
<div
|
||||
className={tcls(
|
||||
'overflow-y-auto',
|
||||
'overflow-x-visible',
|
||||
|
||||
'flex',
|
||||
'flex-col',
|
||||
'shrink',
|
||||
'pb-12',
|
||||
|
||||
'sticky',
|
||||
'lg:top:0',
|
||||
'site-header:lg:top-16',
|
||||
'site-header-sections:lg:top-[6.75rem]',
|
||||
|
||||
'gap-6',
|
||||
'pt-8',
|
||||
|
||||
'page-api-block:xl:max-2xl:py-0',
|
||||
// Hide it for api page, until hovered
|
||||
'page-api-block:xl:max-2xl:hidden',
|
||||
'page-api-block:xl:max-2xl:group-hover/aside:flex'
|
||||
)}
|
||||
>
|
||||
{document ? (
|
||||
{document ? (
|
||||
<div className="-ml-3">
|
||||
<React.Suspense fallback={null}>
|
||||
<PageAsideSections document={document} context={context} />
|
||||
</React.Suspense>
|
||||
) : null}
|
||||
<div
|
||||
className={tcls(
|
||||
'flex',
|
||||
'flex-col',
|
||||
'gap-3',
|
||||
'sidebar-list-default:px-3',
|
||||
'border-t',
|
||||
'first:border-none',
|
||||
'border-tint-subtle',
|
||||
'py-4',
|
||||
'first:pt-0',
|
||||
'page-api-block:xl:max-2xl:px-3',
|
||||
'empty:hidden'
|
||||
)}
|
||||
>
|
||||
{withPageFeedback ? (
|
||||
<React.Suspense fallback={null}>
|
||||
<PageFeedbackForm pageId={page.id} className={tcls('mt-2')} />
|
||||
</React.Suspense>
|
||||
) : null}
|
||||
{customization.git.showEditLink && space.gitSync?.url && page.git ? (
|
||||
<div>
|
||||
<a
|
||||
href={urlJoin(space.gitSync.url, page.git.path)}
|
||||
className={tcls(
|
||||
'flex',
|
||||
'flex-row',
|
||||
'items-center',
|
||||
'text-sm',
|
||||
'hover:text-tint-strong',
|
||||
'links-accent:hover:underline',
|
||||
'links-accent:hover:underline-offset-4',
|
||||
'links-accent:hover:decoration-[3px]',
|
||||
'links-accent:hover:decoration-primary-subtle',
|
||||
'py-2'
|
||||
)}
|
||||
>
|
||||
<Icon
|
||||
icon={
|
||||
space.gitSync.installationProvider === 'gitlab'
|
||||
? 'gitlab'
|
||||
: 'github'
|
||||
}
|
||||
className={tcls('size-4', 'mr-1.5')}
|
||||
/>
|
||||
{t(language, 'edit_on_git', getGitSyncName(space))}
|
||||
</a>
|
||||
</div>
|
||||
) : null}
|
||||
{customization.pdf.enabled ? (
|
||||
<div>
|
||||
<a
|
||||
href={pdfHref}
|
||||
className={tcls(
|
||||
'flex',
|
||||
'flex-row',
|
||||
'items-center',
|
||||
'text-sm',
|
||||
'hover:text-tint-strong',
|
||||
'links-accent:hover:underline',
|
||||
'links-accent:hover:underline-offset-4',
|
||||
'links-accent:hover:decoration-[3px]',
|
||||
'links-accent:hover:decoration-primary-subtle',
|
||||
'py-2'
|
||||
)}
|
||||
>
|
||||
<Icon
|
||||
icon="file-pdf"
|
||||
className={tcls('size-4', 'mr-1.5')}
|
||||
/>
|
||||
{t(language, 'pdf_download')}
|
||||
</a>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className='mt-6 flex items-center gap-2 font-semibold text-xs uppercase tracking-wide'>
|
||||
<Icon icon="sparkle" className="size-3" />
|
||||
Next pages
|
||||
</div>
|
||||
</>
|
||||
<AIPageNextRecommendedPages
|
||||
spaceId={context.space.id}
|
||||
pageId={page.id}
|
||||
revisionId={context.revisionId}
|
||||
/>
|
||||
|
||||
<div
|
||||
className={tcls(
|
||||
'flex',
|
||||
'flex-col',
|
||||
'gap-3',
|
||||
'sidebar-list-default:px-3',
|
||||
'border-t',
|
||||
'first:border-none',
|
||||
'border-tint-subtle',
|
||||
'py-4',
|
||||
'first:pt-0',
|
||||
'page-api-block:xl:max-2xl:px-3',
|
||||
'empty:hidden'
|
||||
)}
|
||||
>
|
||||
{withPageFeedback ? (
|
||||
<React.Suspense fallback={null}>
|
||||
<PageFeedbackForm pageId={page.id} className={tcls('mt-2')} />
|
||||
</React.Suspense>
|
||||
) : null}
|
||||
{customization.git.showEditLink && space.gitSync?.url && page.git ? (
|
||||
<div>
|
||||
<a
|
||||
href={urlJoin(space.gitSync.url, page.git.path)}
|
||||
className={tcls(
|
||||
'flex',
|
||||
'flex-row',
|
||||
'items-center',
|
||||
'text-sm',
|
||||
'hover:text-tint-strong',
|
||||
'links-accent:hover:underline',
|
||||
'links-accent:hover:underline-offset-4',
|
||||
'links-accent:hover:decoration-[3px]',
|
||||
'links-accent:hover:decoration-primary-subtle',
|
||||
'py-2'
|
||||
)}
|
||||
>
|
||||
<Icon
|
||||
icon={
|
||||
space.gitSync.installationProvider === 'gitlab'
|
||||
? 'gitlab'
|
||||
: 'github'
|
||||
}
|
||||
className={tcls('size-4', 'mr-1.5')}
|
||||
/>
|
||||
{t(language, 'edit_on_git', getGitSyncName(space))}
|
||||
</a>
|
||||
</div>
|
||||
) : null}
|
||||
{customization.pdf.enabled ? (
|
||||
<div>
|
||||
<a
|
||||
href={pdfHref}
|
||||
className={tcls(
|
||||
'flex',
|
||||
'flex-row',
|
||||
'items-center',
|
||||
'text-sm',
|
||||
'hover:text-tint-strong',
|
||||
'links-accent:hover:underline',
|
||||
'links-accent:hover:underline-offset-4',
|
||||
'links-accent:hover:decoration-[3px]',
|
||||
'links-accent:hover:decoration-primary-subtle',
|
||||
'py-2'
|
||||
)}
|
||||
>
|
||||
<Icon icon="file-pdf" className={tcls('size-4', 'mr-1.5')} />
|
||||
{t(language, 'pdf_download')}
|
||||
</a>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
<div
|
||||
className={tcls(
|
||||
|
||||
@@ -11,7 +11,6 @@ import { getPagePath } from '@/lib/pages';
|
||||
import { isPageIndexable, isSiteIndexable } from '@/lib/seo';
|
||||
|
||||
import { getResizedImageURL } from '@v2/lib/images';
|
||||
import { AdaptivePane } from '../Adaptive';
|
||||
import { PageClientLayout } from './PageClientLayout';
|
||||
import { type PagePathParams, fetchPageData, getPathnameParam } from './fetch';
|
||||
|
||||
@@ -74,7 +73,6 @@ export async function SitePage(props: SitePageProps) {
|
||||
) : null}
|
||||
{/* We use a flex row reverse to render the aside first because the page is streamed. */}
|
||||
<div className="flex grow flex-row-reverse justify-end">
|
||||
<AdaptivePane spaceId={context.space.id} revisionId={context.revisionId} pageId={page.id} />
|
||||
<PageAside
|
||||
page={page}
|
||||
document={document}
|
||||
|
||||
@@ -32,7 +32,7 @@ const variantClasses = {
|
||||
'text-tint',
|
||||
'ring-0',
|
||||
'shadow-none',
|
||||
'hover:bg-tint-hover',
|
||||
'hover:bg-primary-hover',
|
||||
'hover:text-primary',
|
||||
'hover:scale-1',
|
||||
'hover:shadow-none',
|
||||
|
||||
@@ -2,7 +2,10 @@ import type { SVGProps } from 'react';
|
||||
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
export const Loading = (props: Partial<SVGProps<SVGSVGElement>>) => {
|
||||
export const Loading = ({
|
||||
busy = true,
|
||||
...props
|
||||
}: { busy?: boolean } & SVGProps<SVGSVGElement>) => {
|
||||
return (
|
||||
<svg
|
||||
width="100%"
|
||||
@@ -14,7 +17,7 @@ export const Loading = (props: Partial<SVGProps<SVGSVGElement>>) => {
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
className={tcls('animate-[pathLoading_2s_ease_infinite_forwards]')}
|
||||
className={tcls(busy ? 'animate-[pathLoading_2s_ease_infinite_forwards]' : 'animate-[pathLoading_2s_ease_forwards]' )}
|
||||
d="M6 59.5V56.291C6 45.8865 11.5194 36.263 20.5 31.0091V31.0091L60.9857 7.32407C63.4452 5.88525 66.4843 5.86317 68.9643 7.26611L116 33.8734L70.4183 60.2148C67.9468 61.6431 64.9014 61.6462 62.4269 60.223L29.9772 41.5592C19.3106 35.4242 6 43.1236 6 55.4288V64.8776C6 73.4486 10.5708 81.3691 17.9918 85.6575L54.59 106.807C62.0198 111.1 71.1766 111.1 78.6064 106.807L116.364 84.9874C120.074 82.8432 122.36 78.883 122.36 74.5975V59.2647C122.36 57.7248 120.692 56.7626 119.359 57.5331L72.6023 84.5529C68.8874 86.6996 64.309 86.6996 60.5941 84.5529L26 64.5617"
|
||||
stroke="currentColor"
|
||||
pathLength="100"
|
||||
@@ -27,7 +30,8 @@ export const Loading = (props: Partial<SVGProps<SVGSVGElement>>) => {
|
||||
d="M6 59.5V56.291C6 45.8865 11.5194 36.263 20.5 31.0091V31.0091L60.9857 7.32407C63.4452 5.88525 66.4843 5.86317 68.9643 7.26611L116 33.8734L70.4183 60.2148C67.9468 61.6431 64.9014 61.6462 62.4269 60.223L29.9772 41.5592C19.3106 35.4242 6 43.1236 6 55.4288V64.8776C6 73.4486 10.5708 81.3691 17.9918 85.6575L54.59 106.807C62.0198 111.1 71.1766 111.1 78.6064 106.807L116.364 84.9874C120.074 82.8432 122.36 78.883 122.36 74.5975V59.2647C122.36 57.7248 120.692 56.7626 119.359 57.5331L72.6023 84.5529C68.8874 86.6996 64.309 86.6996 60.5941 84.5529L26 64.5617"
|
||||
stroke="currentColor"
|
||||
pathLength="100"
|
||||
strokeOpacity="0.24"
|
||||
strokeOpacity={busy ? 0.24 : 1}
|
||||
className='transition-opacity duration-1000'
|
||||
fill="none"
|
||||
strokeWidth="11"
|
||||
strokeLinecap="round"
|
||||
|
||||
Reference in New Issue
Block a user