diff --git a/examples/next/app/global.css b/examples/next/app/global.css index 33cd3a64..570aa881 100644 --- a/examples/next/app/global.css +++ b/examples/next/app/global.css @@ -1,2 +1,2 @@ @import "tailwindcss"; -@import "@farming-labs/theme/hardline/css"; +@import "@farming-labs/theme/threadline/css"; diff --git a/examples/next/docs.config.tsx b/examples/next/docs.config.tsx index 9935537c..363d7c2c 100644 --- a/examples/next/docs.config.tsx +++ b/examples/next/docs.config.tsx @@ -18,7 +18,7 @@ import { Users, Mail, } from "lucide-react"; -import { hardline } from "@farming-labs/theme/hardline"; +import { threadline } from "@farming-labs/theme/threadline"; const typesenseBaseUrl = process.env.TYPESENSE_URL ?? process.env.TYPESENSE_BASE_URL; const typesenseCollection = process.env.TYPESENSE_COLLECTION ?? "docs"; @@ -97,7 +97,7 @@ export default defineDocs({ baseBranch: "main", }, }, - theme: hardline(), + theme: threadline(), ai: { enabled: true, // mode: "sidebar-icon", diff --git a/examples/sveltekit/src/app.css b/examples/sveltekit/src/app.css index 4a7e07a4..752469a4 100644 --- a/examples/sveltekit/src/app.css +++ b/examples/sveltekit/src/app.css @@ -1 +1 @@ -@import "@farming-labs/theme/hardline/css"; +@import "@farming-labs/theme/threadline/css"; diff --git a/examples/sveltekit/src/lib/docs.config.ts b/examples/sveltekit/src/lib/docs.config.ts index 7678348d..bd93ae45 100644 --- a/examples/sveltekit/src/lib/docs.config.ts +++ b/examples/sveltekit/src/lib/docs.config.ts @@ -1,10 +1,10 @@ import { defineDocs } from "@farming-labs/docs"; -import { hardline } from "@farming-labs/svelte-theme/hardline"; +import { threadline } from "@farming-labs/theme/threadline"; export default defineDocs({ entry: "docs", contentDir: "docs", - theme: hardline(), + theme: threadline(), github: { url: "https://github.com/farming-labs/docs", branch: "main", diff --git a/packages/fumadocs/src/docs-page-client.tsx b/packages/fumadocs/src/docs-page-client.tsx index 9c332aed..58bf5d48 100644 --- a/packages/fumadocs/src/docs-page-client.tsx +++ b/packages/fumadocs/src/docs-page-client.tsx @@ -30,6 +30,11 @@ interface TOCItem { depth: number; } +interface PageNavigationItem { + name: string; + url: string; +} + /** Serializable provider — icon is an HTML string, not JSX. */ interface SerializedProvider { name: string; @@ -83,6 +88,8 @@ interface DocsPageClientProps { readingTime?: number | null; /** Reading-time label style. */ readingTimeFormat?: ReadingTimeFormat; + previousPage?: PageNavigationItem | null; + nextPage?: PageNavigationItem | null; /** Map of pathname → serialized Schema.org JSON-LD. */ structuredDataMap?: Record; /** Direct serialized Schema.org JSON-LD override for the current page. */ @@ -517,6 +524,8 @@ export function DocsPageClient({ readingTimeMap, readingTime: readingTimeProp, readingTimeFormat = "long", + previousPage, + nextPage, structuredDataMap, structuredData: structuredDataProp, readingTimeEnabled = false, @@ -785,6 +794,13 @@ export function DocsPageClient({ lastUpdatedLabelText && lastModified ? `${lastUpdatedLabelText} ${lastModified}` : lastModified; const showFooter = !isChangelogRoute && (!!githubFileUrl || showLastUpdatedInFooter || llmsTxtEnabled); + const localizedPreviousPage = previousPage?.url + ? { ...previousPage, url: withLangInUrl(previousPage.url, activeLocale) } + : null; + const localizedNextPage = nextPage?.url + ? { ...nextPage, url: withLangInUrl(nextPage.url, activeLocale) } + : null; + const showPageNavigation = !isChangelogRoute && (!!localizedPreviousPage || !!localizedNextPage); const readingTimeBlock = typeof resolvedReadingTime === "number" ? (
@@ -1033,6 +1049,60 @@ export function DocsPageClient({ )}
)} + {showPageNavigation && ( + + )} diff --git a/packages/fumadocs/src/tanstack-layout.tsx b/packages/fumadocs/src/tanstack-layout.tsx index 57771b9e..1f0cb5fc 100644 --- a/packages/fumadocs/src/tanstack-layout.tsx +++ b/packages/fumadocs/src/tanstack-layout.tsx @@ -101,6 +101,8 @@ export interface TanstackDocsLayoutProps { description?: string; readingTime?: number | null; lastModified?: string; + previousPage?: { name: string; url: string } | null; + nextPage?: { name: string; url: string } | null; structuredData?: string; editOnGithubUrl?: string; children: ReactNode; @@ -343,6 +345,8 @@ export function TanstackDocsLayout({ description, readingTime, lastModified, + previousPage, + nextPage, structuredData, editOnGithubUrl, children, @@ -549,6 +553,8 @@ export function TanstackDocsLayout({ lastUpdatedLabel={lastUpdatedLabel} lastUpdatedPosition={lastUpdatedPosition} lastModified={lastModified} + previousPage={previousPage} + nextPage={nextPage} readingTimeEnabled={readingTimeEnabled} readingTimeFormat={readingTimeOptions.format} readingTime={typeof readingTime === "number" ? readingTime : undefined} diff --git a/packages/fumadocs/styles/threadline.css b/packages/fumadocs/styles/threadline.css index 062764a4..53a6e1f7 100644 --- a/packages/fumadocs/styles/threadline.css +++ b/packages/fumadocs/styles/threadline.css @@ -148,6 +148,38 @@ body:has(#nd-docs-layout) { 0px var(--fd-sidebar-width, 260px) minmax(0, var(--fd-threadline-main-width)) var(--fd-toc-width, 224px) 0px !important; } + + #nd-docs-layout[data-fd-framework] { + width: var(--fd-threadline-frame-width) !important; + max-width: var(--fd-threadline-frame-width) !important; + margin-inline: auto !important; + grid-template-columns: + var(--fd-sidebar-width, 260px) + minmax(0, calc(var(--fd-threadline-main-width) + var(--fd-toc-width, 224px))) !important; + } + + #nd-docs-layout[data-fd-framework] .fd-main { + width: calc(var(--fd-threadline-main-width) + var(--fd-toc-width, 224px)) !important; + } + + #nd-docs-layout[data-fd-framework] .fd-sidebar { + top: var(--docs-header-height, 48px) !important; + height: calc(100dvh - var(--docs-header-height, 48px)) !important; + } + + #nd-docs-layout[data-fd-framework] .fd-page { + justify-content: flex-start !important; + } + + #nd-docs-layout[data-fd-framework] .fd-page:has(.fd-toc) { + grid-template-columns: + minmax(0, var(--fd-threadline-main-width)) + var(--fd-toc-width, 224px) !important; + } + + #nd-docs-layout[data-fd-framework] .fd-page:not(:has(.fd-toc)) { + grid-template-columns: minmax(0, var(--fd-threadline-main-width)) !important; + } } #nd-docs-layout > header, @@ -454,6 +486,53 @@ aside#nd-sidebar-mobile a[data-active="true"]:hover, background: color-mix(in srgb, var(--muted-foreground) 28%, transparent) !important; } +#nd-docs-layout[data-fd-framework] .fd-page-body .fd-callout { + align-items: flex-start !important; + gap: 0.5rem !important; + padding: 0.75rem 0.75rem 0.75rem 0.25rem !important; + overflow: visible !important; + font-size: 0.75rem !important; + line-height: 1.45 !important; +} + +#nd-docs-layout[data-fd-framework] .fd-page-body .fd-callout-indicator { + position: static !important; + width: 0.125rem !important; + min-width: 0.125rem !important; + height: auto !important; + min-height: 1.5rem !important; + align-self: stretch !important; + flex: 0 0 0.125rem !important; + border-radius: 0.25rem !important; + background: color-mix(in srgb, var(--muted-foreground) 28%, transparent) !important; +} + +#nd-docs-layout[data-fd-framework] .fd-page-body .fd-callout-icon { + display: none !important; +} + +#nd-docs-layout[data-fd-framework] .fd-page-body .fd-callout-content { + min-width: 0; + flex: 1; + color: var(--foreground) !important; + font-size: 0.875rem !important; + line-height: 1.7 !important; +} + +#nd-docs-layout[data-fd-framework] .fd-page-body .fd-callout-title, +#nd-docs-layout[data-fd-framework] .fd-page-body .fd-callout-content > p { + display: inline !important; + margin: 0 !important; + color: inherit !important; + font-size: inherit !important; + line-height: inherit !important; +} + +#nd-docs-layout[data-fd-framework] .fd-page-body .fd-callout-title { + margin-inline-end: 0.375rem !important; + font-weight: 600 !important; +} + .fd-docs-content :not(pre) > code, .prose :not(pre) > code { border: 0 !important; @@ -491,6 +570,157 @@ figure.shiki > div:first-child:has(figcaption), font-weight: 500 !important; } +#nd-docs-layout[data-fd-framework] .fd-codeblock.fd-codeblock--titled { + border: 0 !important; + border-radius: 0.75rem !important; + background: var(--fd-assistant-code-bg) !important; + background-color: var(--fd-assistant-code-bg) !important; + overflow: hidden; +} + +#nd-docs-layout[data-fd-framework] .fd-codeblock.fd-codeblock--titled .fd-codeblock-title { + position: relative; + height: 2.375rem; + min-height: 2.375rem !important; + padding: 0 1rem !important; + border: 0 !important; + border-radius: 0 !important; + background: transparent !important; + color: var(--muted-foreground) !important; +} + +#nd-docs-layout[data-fd-framework] .fd-codeblock.fd-codeblock--titled .fd-codeblock-title::before { + content: ""; + position: absolute; + inset: 0; + background: repeating-linear-gradient( + -45deg, + currentColor, + currentColor 1px, + transparent 1px, + transparent 6px + ); + opacity: 0.03; + pointer-events: none; +} + +:is(html.dark, body.dark, .dark) + #nd-docs-layout[data-fd-framework] + .fd-codeblock.fd-codeblock--titled + .fd-codeblock-title::before { + opacity: 0.05; +} + +#nd-docs-layout[data-fd-framework] .fd-codeblock.fd-codeblock--titled .fd-codeblock-content { + padding: 0.875rem 0 !important; + background: transparent !important; + font-size: 0.8125rem !important; + line-height: 1.65 !important; + overflow: auto; +} + +#nd-docs-layout[data-fd-framework] .fd-codeblock.fd-codeblock--titled pre { + margin: 0 !important; + padding: 0.15rem 0.5rem !important; + border: 0 !important; + border-radius: 0 !important; + background: transparent !important; + background-color: transparent !important; + font-size: 0.8125rem !important; + line-height: 1.65 !important; + overflow: visible; +} + +#nd-docs-layout[data-fd-framework] .fd-codeblock.fd-codeblock--titled.shiki { + background: var(--fd-assistant-code-bg) !important; + background-color: var(--fd-assistant-code-bg) !important; +} + +:is(html.dark, body.dark, .dark) + #nd-docs-layout[data-fd-framework] + .fd-codeblock.fd-codeblock--titled.shiki { + background: var(--fd-assistant-code-bg) !important; + background-color: var(--fd-assistant-code-bg) !important; +} + +#nd-docs-layout[data-fd-framework] .fd-codeblock.fd-codeblock--titled pre.shiki, +:is(html.dark, body.dark, .dark) + #nd-docs-layout[data-fd-framework] + .fd-codeblock.fd-codeblock--titled + pre.shiki { + background: transparent !important; + background-color: transparent !important; +} + +#nd-docs-layout[data-fd-framework] .fd-codeblock.fd-codeblock--titled pre code { + font-size: inherit !important; + line-height: inherit !important; +} + +#nd-docs-layout[data-fd-framework] .fd-codeblock:not(.fd-codeblock--titled) { + background: var(--fd-assistant-code-bg) !important; + background-color: var(--fd-assistant-code-bg) !important; +} + +:is(html.dark, body.dark, .dark) + #nd-docs-layout[data-fd-framework] + .fd-codeblock:not(.fd-codeblock--titled) { + background: var(--fd-assistant-code-bg) !important; + background-color: var(--fd-assistant-code-bg) !important; +} + +#nd-docs-layout[data-fd-framework] .fd-codeblock:not(.fd-codeblock--titled) .fd-codeblock-content { + padding: 0.875rem 0 !important; + background: transparent !important; + font-size: 0.8125rem !important; + line-height: 1.65 !important; + overflow: auto; +} + +#nd-docs-layout[data-fd-framework] .fd-codeblock:not(.fd-codeblock--titled) pre { + margin: 0 !important; + padding: 0.15rem 0.5rem !important; + border: 0 !important; + border-radius: 0 !important; + background: transparent !important; + background-color: transparent !important; + font-size: 0.8125rem !important; + line-height: 1.65 !important; + overflow: visible; +} + +#nd-docs-layout[data-fd-framework] .fd-codeblock:not(.fd-codeblock--titled) pre.shiki, +:is(html.dark, body.dark, .dark) + #nd-docs-layout[data-fd-framework] + .fd-codeblock:not(.fd-codeblock--titled) + pre.shiki { + background: transparent !important; + background-color: transparent !important; +} + +#nd-docs-layout[data-fd-framework] .fd-codeblock:not(.fd-codeblock--titled) pre code { + font-size: inherit !important; + line-height: inherit !important; +} + +#nd-docs-layout[data-fd-framework] .fd-codeblock.fd-codeblock--titled .fd-codeblock-actions { + margin-inline-end: -0.5rem; +} + +#nd-docs-layout[data-fd-framework] .fd-codeblock.fd-codeblock--titled .fd-codeblock-title .fd-copy-btn { + width: 1.5rem; + height: 1.5rem; + border: 0 !important; + background: transparent !important; + color: var(--muted-foreground) !important; + opacity: 0; +} + +#nd-docs-layout[data-fd-framework] .fd-codeblock.fd-codeblock--titled:hover .fd-codeblock-title .fd-copy-btn, +#nd-docs-layout[data-fd-framework] .fd-codeblock.fd-codeblock--titled .fd-codeblock-title .fd-copy-btn:focus-visible { + opacity: 1; +} + figure.shiki > div[role="region"] { padding-top: 0.875rem !important; padding-bottom: 0.875rem !important; @@ -572,6 +802,16 @@ figure.shiki button[aria-label*="Copy"]:hover { background: transparent !important; } +#nd-docs-layout[data-fd-framework] .fd-page-body th { + font-size: inherit !important; + line-height: inherit !important; +} + +#nd-docs-layout[data-fd-framework] .fd-page-body td { + font-size: 0.875rem !important; + line-height: 1.7 !important; +} + .fd-actions-toc-portal { display: none; } @@ -849,6 +1089,52 @@ figure.shiki button[aria-label*="Copy"]:hover { padding-top: 0.25rem !important; } +@media (min-width: 1280px) { + #nd-docs-layout[data-fd-framework] .fd-toc { + position: sticky !important; + top: var(--docs-header-height, 48px) !important; + width: var(--fd-toc-width, 224px) !important; + height: calc(100dvh - var(--docs-header-height, 48px)) !important; + padding: 3rem 1rem 0.5rem 0 !important; + overflow-y: auto; + } + + #nd-docs-layout[data-fd-framework] .fd-toc-title { + display: inline-flex !important; + align-items: center !important; + gap: 0.375rem !important; + margin: 0 0 0.75rem !important; + color: var(--muted-foreground) !important; + font-size: 0.75rem !important; + font-weight: 500 !important; + line-height: 1.45 !important; + } + + #nd-docs-layout[data-fd-framework] .fd-toc-title svg { + width: 0.875rem !important; + height: 0.875rem !important; + opacity: 1 !important; + } + + #nd-docs-layout[data-fd-framework] .fd-toc-list { + border-left: 0 !important; + } + + #nd-docs-layout[data-fd-framework] .fd-toc-link { + margin: 0 !important; + padding: 0.375rem 0 0.375rem 0.75rem !important; + border-left: 0 !important; + color: var(--muted-foreground) !important; + font-size: 0.875rem !important; + line-height: 1.7 !important; + } + + #nd-docs-layout[data-fd-framework] .fd-toc-link:hover, + #nd-docs-layout[data-fd-framework] .fd-toc-link-active { + color: var(--foreground) !important; + } +} + .fd-page-nav { display: grid !important; grid-template-columns: repeat(2, minmax(0, 1fr)) !important; @@ -1113,6 +1399,12 @@ figure.shiki button[aria-label*="Copy"]:hover { display: none !important; } +#nd-docs-layout[data-fd-framework] .fd-page-footer { + display: block !important; + margin-top: auto !important; + padding-top: 1.5rem !important; +} + @media (min-width: 1280px) { .fd-actions-toc-host { display: block; @@ -1231,6 +1523,61 @@ figure.shiki button[aria-label*="Copy"]:hover { box-shadow: none !important; } +#nd-docs-layout[data-fd-framework] .fd-page-body .fd-tabs { + margin: 1rem 0 !important; + padding: 0 !important; + border: 0 !important; + border-radius: 0.75rem !important; + background: var(--fd-assistant-code-bg) !important; + box-shadow: none !important; + overflow: hidden; +} + +#nd-docs-layout[data-fd-framework] .fd-page-body .fd-tabs-list { + min-height: 2.3125rem !important; + align-items: center !important; + gap: 0 !important; + padding: 0.25rem !important; + border: 0 !important; + border-radius: 0.625rem !important; + background: var(--fd-assistant-code-bg) !important; + overflow-x: auto; +} + +#nd-docs-layout[data-fd-framework] .fd-page-body .fd-tab-trigger { + min-height: 1.8125rem !important; + padding: 0.375rem 0.5rem !important; + border: 0 !important; + border-radius: 0.5rem !important; + color: var(--muted-foreground) !important; + font-size: 0.75rem !important; + font-weight: 500 !important; + line-height: 1.45 !important; +} + +#nd-docs-layout[data-fd-framework] .fd-page-body .fd-tab-trigger.fd-tab-active, +#nd-docs-layout[data-fd-framework] .fd-page-body .fd-tab-trigger[data-state="active"] { + background: var(--background) !important; + color: var(--foreground) !important; + box-shadow: none !important; +} + +#nd-docs-layout[data-fd-framework] .fd-page-body .fd-tab-panel, +#nd-docs-layout[data-fd-framework] .fd-page-body .fd-code-group-panel { + margin: 0 !important; + padding: 0 !important; + background: transparent !important; +} + +#nd-docs-layout[data-fd-framework] .fd-page-body .fd-tabs .fd-codeblock { + margin: 0 !important; + border-radius: 0 0 0.75rem 0.75rem !important; +} + +#nd-docs-layout[data-fd-framework] .fd-page-body .fd-tabs .fd-codeblock pre { + border-radius: 0 !important; +} + .fd-feedback-submit, .fd-ai-send-btn, .fd-ai-fm-send-btn, diff --git a/packages/tanstack-start/src/react.tsx b/packages/tanstack-start/src/react.tsx index e5be3404..1cf0b5f8 100644 --- a/packages/tanstack-start/src/react.tsx +++ b/packages/tanstack-start/src/react.tsx @@ -72,6 +72,8 @@ export function TanstackDocsPage({ description={data.description} readingTime={data.readingTime} lastModified={data.lastModified} + previousPage={data.previousPage} + nextPage={data.nextPage} structuredData={data.structuredData} editOnGithubUrl={data.editOnGithub} >