@farming-labs/theme 0.2.104 → 0.2.106

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,6 +4,7 @@ import { emitClientAnalyticsEvent } from "./client-analytics.mjs";
4
4
  import { renderAIResponseMarkdown } from "./ai-markdown.mjs";
5
5
  import { useCallback, useEffect, useRef, useState } from "react";
6
6
  import { createPortal } from "react-dom";
7
+ import { useRouter } from "fumadocs-core/framework";
7
8
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
8
9
 
9
10
  //#region src/ai-search-dialog.tsx
@@ -948,6 +949,7 @@ function AIChat({ api, requestMode, requestHeaders, requestStream = true, messag
948
949
  });
949
950
  }
950
951
  function DocsSearchDialog({ open, onOpenChange, api = "/api/docs", requestMode, requestHeaders, requestStream, suggestedQuestions, aiLabel, loaderVariant, loadingComponentHtml, models, defaultModelId, analytics = false, feedbackEnabled = true }) {
952
+ const router = useRouter();
951
953
  const [tab, setTab] = useState("search");
952
954
  const [searchQuery, setSearchQuery] = useState("");
953
955
  const [searchResults, setSearchResults] = useState([]);
@@ -1064,7 +1066,7 @@ function DocsSearchDialog({ open, onOpenChange, api = "/api/docs", requestMode,
1064
1066
  queryLength: searchQuery.length
1065
1067
  }
1066
1068
  });
1067
- window.location.href = searchResults[activeIndex].url;
1069
+ router.push(searchResults[activeIndex].url);
1068
1070
  }
1069
1071
  };
1070
1072
  if (!open) return null;
@@ -1166,7 +1168,7 @@ function DocsSearchDialog({ open, onOpenChange, api = "/api/docs", requestMode,
1166
1168
  queryLength: searchQuery.length
1167
1169
  }
1168
1170
  });
1169
- window.location.href = result.url;
1171
+ router.push(result.url);
1170
1172
  },
1171
1173
  onMouseEnter: () => setActiveIndex(i),
1172
1174
  className: "fd-ai-result",
@@ -16,13 +16,20 @@ declare global {
16
16
  interface BrowserRootProviderProps extends FumadocsProviderProps {
17
17
  /** Path rendered by the server, used to keep hydration deterministic. */
18
18
  initialPathname?: string;
19
+ /** Framework adapter navigation that preserves the current document shell. */
20
+ navigation?: BrowserNavigationAdapter;
21
+ }
22
+ interface BrowserNavigationAdapter {
23
+ push(url: string): void | Promise<void>;
24
+ refresh(): void | Promise<void>;
19
25
  }
20
26
  /** Framework-neutral provider for server-rendered React documentation adapters. */
21
27
  declare function BrowserRootProvider({
22
28
  children,
23
29
  search,
24
30
  initialPathname,
31
+ navigation,
25
32
  ...props
26
33
  }: BrowserRootProviderProps): react_jsx_runtime0.JSX.Element;
27
34
  //#endregion
28
- export { BrowserDocsLayout, BrowserDocsLayoutProps, BrowserRootProvider, BrowserRootProviderProps };
35
+ export { BrowserDocsLayout, BrowserDocsLayoutProps, BrowserNavigationAdapter, BrowserRootProvider, BrowserRootProviderProps };
package/dist/browser.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { TanstackDocsLayout } from "./tanstack-layout.mjs";
4
- import { useMemo, useSyncExternalStore } from "react";
4
+ import { createContext, useCallback, useContext, useMemo, useSyncExternalStore } from "react";
5
5
  import { FrameworkProvider } from "fumadocs-core/framework";
6
6
  import { jsx } from "react/jsx-runtime";
7
7
  import { RootProvider } from "fumadocs-ui/provider/base";
@@ -14,6 +14,15 @@ function BrowserDocsLayout(props) {
14
14
  browserRuntime: true
15
15
  });
16
16
  }
17
+ const defaultBrowserNavigation = {
18
+ push(url) {
19
+ window.location.assign(url);
20
+ },
21
+ refresh() {
22
+ window.location.reload();
23
+ }
24
+ };
25
+ const BrowserNavigationContext = createContext(defaultBrowserNavigation);
17
26
  function patchHistoryEvents() {
18
27
  if (typeof window === "undefined" || window.__fdBrowserHistoryPatched) return;
19
28
  for (const method of ["pushState", "replaceState"]) {
@@ -40,39 +49,71 @@ function getBrowserPathname() {
40
49
  return typeof window === "undefined" ? "/" : window.location.pathname;
41
50
  }
42
51
  function useBrowserRouter() {
52
+ const navigation = useContext(BrowserNavigationContext);
43
53
  return useMemo(() => ({
44
54
  push(url) {
45
- window.location.assign(url);
55
+ return navigation.push(url);
46
56
  },
47
57
  refresh() {
48
- window.location.reload();
58
+ return navigation.refresh();
49
59
  }
50
- }), []);
60
+ }), [navigation]);
51
61
  }
52
62
  function useBrowserParams() {
53
63
  return useMemo(() => ({}), []);
54
64
  }
55
- function BrowserLink({ prefetch: _prefetch, ...props }) {
56
- return /* @__PURE__ */ jsx("a", { ...props });
65
+ function BrowserLink({ prefetch: _prefetch, href, target, download, onClick, ...props }) {
66
+ const navigation = useContext(BrowserNavigationContext);
67
+ const handleClick = useCallback((event) => {
68
+ onClick?.(event);
69
+ if (event.defaultPrevented || event.button !== 0) return;
70
+ if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
71
+ if (!href || download !== void 0 && download !== false || target && target !== "_self") return;
72
+ let url;
73
+ try {
74
+ url = new URL(href, window.location.href);
75
+ } catch {
76
+ return;
77
+ }
78
+ if (url.origin !== window.location.origin) return;
79
+ event.preventDefault();
80
+ navigation.push(`${url.pathname}${url.search}${url.hash}`);
81
+ }, [
82
+ download,
83
+ href,
84
+ navigation,
85
+ onClick,
86
+ target
87
+ ]);
88
+ return /* @__PURE__ */ jsx("a", {
89
+ ...props,
90
+ href,
91
+ target,
92
+ download,
93
+ onClick: handleClick
94
+ });
57
95
  }
58
96
  /** Framework-neutral provider for server-rendered React documentation adapters. */
59
- function BrowserRootProvider({ children, search, initialPathname = "/", ...props }) {
97
+ function BrowserRootProvider({ children, search, initialPathname = "/", navigation = defaultBrowserNavigation, ...props }) {
60
98
  const useBrowserPathname = () => useSyncExternalStore(subscribeToLocation, getBrowserPathname, () => initialPathname);
61
- return /* @__PURE__ */ jsx(FrameworkProvider, {
62
- Link: BrowserLink,
63
- usePathname: useBrowserPathname,
64
- useParams: useBrowserParams,
65
- useRouter: useBrowserRouter,
66
- children: /* @__PURE__ */ jsx(RootProvider, {
67
- search: {
68
- ...search,
69
- options: {
70
- api: "/api/docs",
71
- ...search?.options
72
- }
73
- },
74
- ...props,
75
- children
99
+ return /* @__PURE__ */ jsx(BrowserNavigationContext.Provider, {
100
+ value: navigation,
101
+ children: /* @__PURE__ */ jsx(FrameworkProvider, {
102
+ Link: BrowserLink,
103
+ usePathname: useBrowserPathname,
104
+ useParams: useBrowserParams,
105
+ useRouter: useBrowserRouter,
106
+ children: /* @__PURE__ */ jsx(RootProvider, {
107
+ search: {
108
+ ...search,
109
+ options: {
110
+ api: "/api/docs",
111
+ ...search?.options
112
+ }
113
+ },
114
+ ...props,
115
+ children
116
+ })
76
117
  })
77
118
  });
78
119
  }
package/dist/docs-api.mjs CHANGED
@@ -4,7 +4,7 @@ import { BoundedRouteCache } from "./bounded-route-cache.mjs";
4
4
  import fs from "node:fs";
5
5
  import path from "node:path";
6
6
  import matter from "gray-matter";
7
- import { DEFAULT_A2A_AGENT_CARD_ROUTE, DEFAULT_AGENT_SKILLS_ARCHIVE_ROUTE_PATTERN, DEFAULT_AGENT_SKILLS_INDEX_ROUTE, DEFAULT_AGENT_SKILLS_ROUTE_PREFIX, DEFAULT_API_CATALOG_ROUTE, DEFAULT_LEGACY_SKILLS_INDEX_ROUTE, DEFAULT_SITEMAP_MD_DOCS_ROUTE, DOCS_AGENT_MANIFEST_FORMAT, DOCS_AGENT_MANIFEST_SCHEMA_URI, DOCS_AGENT_MANIFEST_VERSION, DOCS_CONTENT_CHANGES_FORMAT, DOCS_CONTENT_CHANGES_RESPONSE_VALUE, DOCS_MARKDOWN_SECTION_INDEX_FORMAT, acceptsDocsMarkdown, buildDocsAskAIContext, buildDocsConfigMap, buildDocsDiagnostics, buildDocsSearchFacets, createDocsAgentTraceContext, createDocsAgentTraceId, createDocsCacheableResponse, createDocsContentChangeFeed, createDocsContentChangesHttpResponse, createDocsMarkdownResponse, createDocsRobotsResponse, createDocsSitemapResponse, createDocsStandardsDiscoveryResponse, detectDocsMarkdownAgentRequest, emitDocsAgentTraceEvent, emitDocsAnalyticsEvent, emitDocsTelemetryAgentSurfaceEvent, emitDocsTelemetryProjectEvent, formatDocsAskAIPackageHints, getDocsAgentManifestLinkHeader, getDocsDiscoveryLinkHeader, getDocsLlmsTxtMaxCharsIssue, getDocsMcpProtectedResourceMetadataRoutes, hasDocsMarkdownSignatureAgent, inferDocsTelemetryAgentSurface, isDocsConfigRequest, isDocsContentChangesRequest, isDocsDiagnosticsRequest, normalizeDocsOkfTrustMetadataInput, normalizeDocsRelated, normalizePageAgentFrontmatter, performDocsSearch, performDocsSearchWithMetadata, renderDocsLlmsTxt, renderDocsMarkdownDocument, resolveAskAISearchRequestConfig, resolveChangelogConfig, resolveDocsAgentContractMcpTools, resolveDocsAudienceMdxContent, resolveDocsContentChangesConfig, resolveDocsI18n, resolveDocsLlmsTxtRequest, resolveDocsLlmsTxtSections, resolveDocsLocale, resolveDocsMetadataBaseUrl, resolveDocsPublishedAgentSkill, resolveDocsRequestApiRoute, resolveDocsRetrievalLastModified, resolveDocsSearchAudience, resolveDocsSearchError, resolveDocsSearchRequest, resolveDocsSitemapConfig, resolveDocsStandardsDiscoveryRequest, resolvePageSidebarFolderIndexBehavior, resolveSearchRequestConfig, selectDocsLlmsTxtContent, stripGeneratedAgentProvenance } from "@farming-labs/docs";
7
+ import { DEFAULT_A2A_AGENT_CARD_ROUTE, DEFAULT_AGENT_SKILLS_ARCHIVE_ROUTE_PATTERN, DEFAULT_AGENT_SKILLS_INDEX_ROUTE, DEFAULT_AGENT_SKILLS_ROUTE_PREFIX, DEFAULT_API_CATALOG_ROUTE, DEFAULT_LEGACY_SKILLS_INDEX_ROUTE, DEFAULT_SITEMAP_MD_DOCS_ROUTE, DOCS_AGENT_MANIFEST_FORMAT, DOCS_AGENT_MANIFEST_SCHEMA_URI, DOCS_AGENT_MANIFEST_VERSION, DOCS_CONTENT_CHANGES_FORMAT, DOCS_CONTENT_CHANGES_RESPONSE_VALUE, DOCS_MARKDOWN_SECTION_INDEX_FORMAT, acceptsDocsMarkdown, buildDocsAskAIContext, buildDocsConfigMap, buildDocsDiagnostics, buildDocsSearchFacets, compactDocsAgentDiscoverySpec, createDocsAgentTraceContext, createDocsAgentTraceId, createDocsCacheableResponse, createDocsContentChangeFeed, createDocsContentChangesHttpResponse, createDocsMarkdownResponse, createDocsRobotsResponse, createDocsSitemapResponse, createDocsStandardsDiscoveryResponse, detectDocsMarkdownAgentRequest, emitDocsAgentTraceEvent, emitDocsAnalyticsEvent, emitDocsTelemetryAgentSurfaceEvent, emitDocsTelemetryProjectEvent, formatDocsAskAIPackageHints, getDocsAgentManifestLinkHeader, getDocsDiscoveryLinkHeader, getDocsLlmsTxtMaxCharsIssue, getDocsMcpProtectedResourceMetadataRoutes, hasDocsMarkdownSignatureAgent, inferDocsTelemetryAgentSurface, isDocsConfigRequest, isDocsContentChangesRequest, isDocsDiagnosticsRequest, normalizeDocsOkfTrustMetadataInput, normalizeDocsRelated, normalizePageAgentFrontmatter, performDocsSearch, performDocsSearchWithMetadata, renderDocsLlmsTxt, renderDocsMarkdownDocument, resolveAskAISearchRequestConfig, resolveChangelogConfig, resolveDocsAgentContractMcpTools, resolveDocsAudienceMdxContent, resolveDocsContentChangesConfig, resolveDocsI18n, resolveDocsLlmsTxtRequest, resolveDocsLlmsTxtSections, resolveDocsLocale, resolveDocsMetadataBaseUrl, resolveDocsPublishedAgentSkill, resolveDocsRequestApiRoute, resolveDocsRetrievalLastModified, resolveDocsSearchAudience, resolveDocsSearchError, resolveDocsSearchRequest, resolveDocsSitemapConfig, resolveDocsStandardsDiscoveryRequest, resolvePageSidebarFolderIndexBehavior, resolveSearchRequestConfig, selectDocsLlmsTxtContent, stripGeneratedAgentProvenance } from "@farming-labs/docs";
8
8
  import { buildApiReferenceOpenApiDocumentAsync, createDocsMcpHttpHandler, createFilesystemDocsMcpSource, readDocsSitemapManifest, resolveApiReferenceConfig, resolveApiReferenceOpenApiDiscovery, resolveConfiguredAgentSkills, resolveDocsMcpConfig } from "@farming-labs/docs/server";
9
9
 
10
10
  //#region src/docs-api.ts
@@ -214,6 +214,12 @@ function buildAgentSpec({ origin, entry, apiRoute, apiCatalog, i18n, search, con
214
214
  format: DOCS_AGENT_MANIFEST_FORMAT,
215
215
  version: DOCS_AGENT_MANIFEST_VERSION,
216
216
  name: "@farming-labs/docs",
217
+ profile: "full",
218
+ profiles: {
219
+ default: "full",
220
+ full: DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE,
221
+ compact: `${DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE}?profile=compact`
222
+ },
217
223
  baseUrl: origin,
218
224
  site: {
219
225
  title: llms.siteTitle ?? "Documentation",
@@ -2777,7 +2783,8 @@ function createDocsAPI(options) {
2777
2783
  sitemap: sitemapConfig,
2778
2784
  okf: options?.agent?.okf
2779
2785
  }),
2780
- lastModified: resolveDocsRetrievalLastModified(page, "agent")
2786
+ lastModified: resolveDocsRetrievalLastModified(page, "agent"),
2787
+ access: page.agent?.access
2781
2788
  };
2782
2789
  }
2783
2790
  const fallbackPage = getIndexes(ctx).find((page) => {
@@ -2791,7 +2798,8 @@ function createDocsAPI(options) {
2791
2798
  sitemap: sitemapConfig,
2792
2799
  okf: options?.agent?.okf
2793
2800
  }),
2794
- lastModified: resolveDocsRetrievalLastModified(fallbackPage, "agent")
2801
+ lastModified: resolveDocsRetrievalLastModified(fallbackPage, "agent"),
2802
+ access: fallbackPage.agent?.access
2795
2803
  };
2796
2804
  const requestedSlug = normalizePublicDocsSlug(ctx, normalizedPublicRequest);
2797
2805
  for (const page of getIndexes(ctx)) if (normalizePublicDocsSlug(ctx, page.url) === requestedSlug) return {
@@ -2801,7 +2809,8 @@ function createDocsAPI(options) {
2801
2809
  sitemap: sitemapConfig,
2802
2810
  okf: options?.agent?.okf
2803
2811
  }),
2804
- lastModified: resolveDocsRetrievalLastModified(page, "agent")
2812
+ lastModified: resolveDocsRetrievalLastModified(page, "agent"),
2813
+ access: page.agent?.access
2805
2814
  };
2806
2815
  return null;
2807
2816
  }
@@ -2937,31 +2946,34 @@ function createDocsAPI(options) {
2937
2946
  method: requestMethod
2938
2947
  }
2939
2948
  });
2949
+ const fullSpec = buildAgentSpec({
2950
+ origin: url.origin,
2951
+ entry,
2952
+ apiRoute: requestApiRoute,
2953
+ apiCatalog: apiCatalogEnabled,
2954
+ i18n,
2955
+ search: searchConfig,
2956
+ contentChanges: contentChangesConfig.enabled,
2957
+ mcp: mcpConfig,
2958
+ feedback: agentFeedbackConfig,
2959
+ llms: llmsConfig,
2960
+ sitemap: sitemapConfig,
2961
+ robots: robotsConfig,
2962
+ openapi: openapiDiscovery,
2963
+ publishedSkills: [await resolveDocsPublishedAgentSkill({
2964
+ preferredDocument: readRootSkillDocument(),
2965
+ fallbackDocument: getGeneratedSkillDocument(url.origin, requestApiRoute)
2966
+ }), ...await getPublishedAgentSkills()],
2967
+ agentCard: options?.agent?.a2a
2968
+ });
2969
+ const compact = url.searchParams.get("profile") === "compact";
2970
+ const spec = compact ? compactDocsAgentDiscoverySpec(fullSpec) : fullSpec;
2940
2971
  return createDocsCacheableResponse({
2941
2972
  request,
2942
- content: `${JSON.stringify(buildAgentSpec({
2943
- origin: url.origin,
2944
- entry,
2945
- apiRoute: requestApiRoute,
2946
- apiCatalog: apiCatalogEnabled,
2947
- i18n,
2948
- search: searchConfig,
2949
- contentChanges: contentChangesConfig.enabled,
2950
- mcp: mcpConfig,
2951
- feedback: agentFeedbackConfig,
2952
- llms: llmsConfig,
2953
- sitemap: sitemapConfig,
2954
- robots: robotsConfig,
2955
- openapi: openapiDiscovery,
2956
- publishedSkills: [await resolveDocsPublishedAgentSkill({
2957
- preferredDocument: readRootSkillDocument(),
2958
- fallbackDocument: getGeneratedSkillDocument(url.origin, requestApiRoute)
2959
- }), ...await getPublishedAgentSkills()],
2960
- agentCard: options?.agent?.a2a
2961
- }), null, 2)}\n`,
2973
+ content: `${JSON.stringify(spec, null, compact ? void 0 : 2)}\n`,
2962
2974
  headers: {
2963
2975
  "Content-Type": "application/json; charset=utf-8",
2964
- "Cache-Control": "public, max-age=0, s-maxage=3600",
2976
+ "Cache-Control": "public, max-age=0, s-maxage=3600, stale-while-revalidate=86400",
2965
2977
  Link: agentManifestLinkHeader,
2966
2978
  "X-Robots-Tag": "noindex"
2967
2979
  }
@@ -3187,6 +3199,7 @@ function createDocsAPI(options) {
3187
3199
  canonicalUrl,
3188
3200
  locale: ctx.locale,
3189
3201
  lastModified: representation?.lastModified,
3202
+ access: representation?.access,
3190
3203
  sitemap: sitemapConfig
3191
3204
  });
3192
3205
  }
@@ -5,6 +5,7 @@ import { useWindowSearchParams } from "./client-location.mjs";
5
5
  import { resolveClientLocale, withLangInUrl } from "./i18n.mjs";
6
6
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
7
7
  import { createPortal } from "react-dom";
8
+ import { useRouter } from "fumadocs-core/framework";
8
9
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
9
10
 
10
11
  //#region src/docs-command-search.tsx
@@ -242,6 +243,7 @@ function HistoryIcon() {
242
243
  * so each theme provides its own visual variant.
243
244
  */
244
245
  function DocsCommandSearch({ api = "/api/docs", locale, analytics = false }) {
246
+ const router = useRouter();
245
247
  const [open, setOpen] = useState(false);
246
248
  const [query, setQuery] = useState("");
247
249
  const [debouncedQuery, setDebouncedQuery] = useState("");
@@ -476,11 +478,12 @@ function DocsCommandSearch({ api = "/api/docs", locale, analytics = false }) {
476
478
  saveRecent(item);
477
479
  setOpen(false);
478
480
  if (item.url.startsWith("http")) window.open(item.url, "_blank", "noopener");
479
- else window.location.href = item.url;
481
+ else router.push(item.url);
480
482
  }, [
481
483
  activeLocale,
482
484
  analytics,
483
485
  query.length,
486
+ router,
484
487
  saveRecent
485
488
  ]);
486
489
  const displayItems = useMemo(() => {
@@ -670,7 +670,7 @@ function LayoutStyle({ layout }) {
670
670
  }
671
671
  if (rootVars.length === 0 && desktopRootVars.length === 0) return null;
672
672
  const parts = [];
673
- if (rootVars.length > 0) parts.push(`:root {\n ${rootVars.join("\n ")}\n}`);
673
+ if (rootVars.length > 0) parts.push(`:root,\n#nd-docs-layout {\n ${rootVars.join("\n ")}\n}`);
674
674
  if (desktopRootVars.length > 0) {
675
675
  const inner = [`:root {\n ${desktopRootVars.join("\n ")}\n }`];
676
676
  if (desktopGridVars.length > 0) inner.push(`[style*="fd-sidebar-col"] {\n ${desktopGridVars.join("\n ")}\n }`);
@@ -163,7 +163,7 @@ function isDocsNavigationPath(pathname, entry, publicPath) {
163
163
  if (publicPath === "") return pathname.startsWith("/");
164
164
  return pathname === publicPath || pathname.startsWith(`${publicPath}/`);
165
165
  }
166
- function installDocsPathNavigationGuard(entry, publicPath) {
166
+ function installDocsPathNavigationGuard(entry, publicPath, navigate) {
167
167
  if (publicPath === `/${entry.replace(/^\/+|\/+$/g, "") || "docs"}`) return void 0;
168
168
  function onClick(event) {
169
169
  if (event.defaultPrevented) return;
@@ -184,7 +184,7 @@ function installDocsPathNavigationGuard(entry, publicPath) {
184
184
  const nextHref = `${nextPath}${url.search}${url.hash}`;
185
185
  if (nextHref === `${window.location.pathname}${window.location.search}${window.location.hash}`) return;
186
186
  event.preventDefault();
187
- window.location.assign(nextHref);
187
+ navigate(nextHref);
188
188
  } catch {}
189
189
  }
190
190
  document.addEventListener("click", onClick, true);
@@ -256,6 +256,7 @@ function findThreadlineTocActionsContainer() {
256
256
  return toc.parentElement ?? toc;
257
257
  }
258
258
  function DocsPageClient({ tocEnabled, showLlmsInHeader = false, tocStyle = "default", breadcrumbEnabled = true, changelogBasePath, entry = "docs", publicPath, locale, copyMarkdown = false, copyMarkdownFormat, copyMarkdownIncludeTitle, copyMarkdownLabel, copyMarkdownCopiedLabel, openDocs = false, openDocsProviders, openDocsTarget, openDocsPrompt, connectMcp, installSkills, pageActionsPosition = "below-title", pageActionsAlignment = "left", githubUrl, contentDir, githubBranch = "main", githubDirectory, editOnGithubUrl, lastModifiedMap, lastModified: lastModifiedProp, readingTimeMap, readingTime: readingTimeProp, readingTimeFormat = "long", previousPage, nextPage, structuredDataMap, structuredData: structuredDataProp, readingTimeEnabled = false, lastUpdatedEnabled = true, lastUpdatedLabel = "Last updated", lastUpdatedPosition = "footer", llmsTxtEnabled = false, generatedTitleMap, descriptionMap, description, descriptionInBody = false, feedbackEnabled = false, feedbackQuestion, feedbackPlaceholder, feedbackRequireComment, feedbackPositiveLabel, feedbackNegativeLabel, feedbackSubmitLabel, feedbackSuccessMessage, feedbackErrorMessage, feedbackOnFeedback, analytics = false, children }) {
259
+ const router = useRouter();
259
260
  const fdTocStyle = tocStyle === "directional" ? "clerk" : void 0;
260
261
  const [toc, setToc] = useState([]);
261
262
  const [titlePortalHost, setTitlePortalHost] = useState(null);
@@ -297,8 +298,12 @@ function DocsPageClient({ tocEnabled, showLlmsInHeader = false, tocStyle = "defa
297
298
  normalizedPath
298
299
  ]);
299
300
  useEffect(() => {
300
- return installDocsPathNavigationGuard(entry, resolvedPublicPath);
301
- }, [entry, resolvedPublicPath]);
301
+ return installDocsPathNavigationGuard(entry, resolvedPublicPath, (url) => router.push(url));
302
+ }, [
303
+ entry,
304
+ resolvedPublicPath,
305
+ router
306
+ ]);
302
307
  const resolvedReadingTime = !isChangelogRoute ? readingTimeProp !== void 0 ? readingTimeProp : readingTimeEnabled ? matchedReadingTime : void 0 : void 0;
303
308
  const effectiveTocEnabled = isChangelogRoute ? false : tocEnabled;
304
309
  const effectiveBreadcrumbEnabled = isChangelogRoute ? false : breadcrumbEnabled;
@@ -315,7 +320,11 @@ function DocsPageClient({ tocEnabled, showLlmsInHeader = false, tocStyle = "defa
315
320
  })));
316
321
  });
317
322
  return () => cancelAnimationFrame(timer);
318
- }, [effectiveTocEnabled, pathname]);
323
+ }, [
324
+ children,
325
+ effectiveTocEnabled,
326
+ pathname
327
+ ]);
319
328
  useEffect(() => {
320
329
  const timer = requestAnimationFrame(() => {
321
330
  const root = document.body;
@@ -505,14 +514,19 @@ function DocsPageClient({ tocEnabled, showLlmsInHeader = false, tocStyle = "defa
505
514
  setTitlePortalHost(null);
506
515
  return;
507
516
  }
508
- const title = document.getElementById("nd-page")?.querySelector("h1");
509
- if (!title) {
517
+ const container = document.getElementById("nd-page");
518
+ if (!container) {
510
519
  setTitlePortalHost(null);
511
520
  return;
512
521
  }
513
522
  const host = document.createElement("div");
514
523
  host.className = "fd-title-decorations-host";
515
524
  const placeHost = () => {
525
+ const title = container.querySelector("h1");
526
+ if (!title) {
527
+ host.remove();
528
+ return;
529
+ }
516
530
  let anchor = title;
517
531
  if (descriptionInBody) {
518
532
  let sibling = title.nextElementSibling;
@@ -527,10 +541,12 @@ function DocsPageClient({ tocEnabled, showLlmsInHeader = false, tocStyle = "defa
527
541
  }
528
542
  if (anchor.nextElementSibling !== host) anchor.insertAdjacentElement("afterend", host);
529
543
  };
530
- title.insertAdjacentElement("afterend", host);
531
544
  placeHost();
532
545
  const observer = new MutationObserver(placeHost);
533
- observer.observe(title.parentElement ?? title, { childList: true });
546
+ observer.observe(container, {
547
+ childList: true,
548
+ subtree: true
549
+ });
534
550
  const animationFrame = window.requestAnimationFrame(placeHost);
535
551
  setTitlePortalHost(host);
536
552
  return () => {
@@ -540,6 +556,7 @@ function DocsPageClient({ tocEnabled, showLlmsInHeader = false, tocStyle = "defa
540
556
  setTitlePortalHost(null);
541
557
  };
542
558
  }, [
559
+ children,
543
560
  descriptionInBody,
544
561
  needsTitleDecorationsPortal,
545
562
  pathname
@@ -549,7 +566,11 @@ function DocsPageClient({ tocEnabled, showLlmsInHeader = false, tocStyle = "defa
549
566
  belowTitle: belowTitleBlock
550
567
  }) : null;
551
568
  const titleDecorationsPortal = titleDecorations && titlePortalHost ? createPortal(titleDecorations, titlePortalHost, "title-decorations") : null;
552
- const titleDecorationsFallback = titleDecorations && !titlePortalHost ? titleDecorations : null;
569
+ const titleDecorationsFallback = titleDecorations && !titlePortalHost ? /* @__PURE__ */ jsx("div", {
570
+ className: "fd-title-decorations-fallback",
571
+ hidden: true,
572
+ children: titleDecorations
573
+ }) : null;
553
574
  const generatedPageHeader = pageTitle ? /* @__PURE__ */ jsxs("div", {
554
575
  className: "fd-generated-page-header not-prose",
555
576
  children: [/* @__PURE__ */ jsx("h1", {
@@ -610,7 +631,7 @@ function DocsPageClient({ tocEnabled, showLlmsInHeader = false, tocStyle = "defa
610
631
  style: fdTocStyle
611
632
  },
612
633
  breadcrumb: { enabled: false },
613
- footer: { enabled: !isChangelogRoute },
634
+ footer: { enabled: !isChangelogRoute && !showPageNavigation },
614
635
  children: [
615
636
  effectiveBreadcrumbEnabled && /* @__PURE__ */ jsx(PathBreadcrumb, {
616
637
  pathname,
@@ -643,6 +664,7 @@ function DocsPageClient({ tocEnabled, showLlmsInHeader = false, tocStyle = "defa
643
664
  }, "actions-above-title"),
644
665
  !showReadingTimeAboveTitle && !showReadingTimeBelowTitle ? readingTimeBlock : null,
645
666
  /* @__PURE__ */ jsxs(DocsBody, {
667
+ className: "fd-page-body",
646
668
  style: {
647
669
  display: "flex",
648
670
  flexDirection: "column"
@@ -650,6 +672,7 @@ function DocsPageClient({ tocEnabled, showLlmsInHeader = false, tocStyle = "defa
650
672
  children: [
651
673
  generatedPageHeader,
652
674
  /* @__PURE__ */ jsx("div", {
675
+ className: "fd-docs-content",
653
676
  style: { flex: 1 },
654
677
  children: renderedChildren
655
678
  }, "content"),
@@ -42,6 +42,22 @@ function resolveTreeIcons(tree, registry) {
42
42
  children: tree.children.map(mapNode)
43
43
  };
44
44
  }
45
+ function applyFlatSidebarLayout(tree, flat) {
46
+ if (!flat) return tree;
47
+ function mapNode(node) {
48
+ if (node.type === "page") return node;
49
+ return {
50
+ ...node,
51
+ collapsible: false,
52
+ defaultOpen: true,
53
+ children: node.children.map(mapNode)
54
+ };
55
+ }
56
+ return {
57
+ ...tree,
58
+ children: tree.children.map(mapNode)
59
+ };
60
+ }
45
61
  function localizeTreeUrls(tree, locale) {
46
62
  function mapNode(node) {
47
63
  if (node.type === "page") return {
@@ -173,7 +189,7 @@ function LayoutStyle({ layout }) {
173
189
  }
174
190
  if (rootVars.length === 0 && desktopRootVars.length === 0) return null;
175
191
  const parts = [];
176
- if (rootVars.length > 0) parts.push(`:root {\n ${rootVars.join("\n ")}\n}`);
192
+ if (rootVars.length > 0) parts.push(`:root,\n#nd-docs-layout {\n ${rootVars.join("\n ")}\n}`);
177
193
  if (desktopRootVars.length > 0) {
178
194
  const inner = [`:root {\n ${desktopRootVars.join("\n ")}\n }`];
179
195
  if (desktopGridVars.length > 0) inner.push(`[style*="fd-sidebar-col"] {\n ${desktopGridVars.join("\n ")}\n }`);
@@ -262,7 +278,10 @@ function TanstackDocsLayout({ config, tree, browserRuntime = false, locale, desc
262
278
  const llmsTxtEnabled = resolveEnabledByDefault(config.llmsTxt);
263
279
  const feedbackConfig = resolveFeedbackConfig(config.feedback);
264
280
  const staticExport = !!config.staticExport;
265
- const frameworkContainerProps = browserRuntime ? { "data-fd-framework": "" } : void 0;
281
+ const frameworkContainerProps = browserRuntime ? {
282
+ "data-fd-framework": "",
283
+ "data-fd-browser-adapter": ""
284
+ } : void 0;
266
285
  const openDocsConfig = pageActions?.openDocs && typeof pageActions.openDocs === "object" ? pageActions.openDocs : void 0;
267
286
  const openDocsProviders = resolveOpenDocsProviders(openDocsConfig?.providers, {
268
287
  target: openDocsConfig?.target,
@@ -284,13 +303,13 @@ function TanstackDocsLayout({ config, tree, browserRuntime = false, locale, desc
284
303
  aiDefaultModelId = rawModelConfig.defaultModel ?? rawModelConfig.models?.[0]?.id ?? aiDefaultModelId;
285
304
  }
286
305
  const i18n = config.i18n;
287
- const resolvedTree = resolveTreeIcons(locale ? localizeTreeUrls(applySidebarFolderIndexBehavior(tree, {
306
+ const resolvedTree = resolveTreeIcons(applyFlatSidebarLayout(locale ? localizeTreeUrls(applySidebarFolderIndexBehavior(tree, {
288
307
  sidebar: config.sidebar,
289
308
  defaultBehavior: config.theme?.name === "shadcn" ? "hidden" : "link"
290
309
  }), locale) : applySidebarFolderIndexBehavior(tree, {
291
310
  sidebar: config.sidebar,
292
311
  defaultBehavior: config.theme?.name === "shadcn" ? "hidden" : "link"
293
- }), config.icons);
312
+ }), !!sidebarFlat), config.icons);
294
313
  const finalSidebarProps = { ...sidebarProps };
295
314
  const sidebarFooter = sidebarProps.footer;
296
315
  if (locale && i18n?.locales && i18n.defaultLocale) finalSidebarProps.footer = /* @__PURE__ */ jsxs("div", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/theme",
3
- "version": "0.2.104",
3
+ "version": "0.2.106",
4
4
  "description": "Theme package for @farming-labs/docs — layout, provider, MDX components, and styles",
5
5
  "keywords": [
6
6
  "docs",
@@ -161,7 +161,7 @@
161
161
  "tsdown": "^0.20.3",
162
162
  "typescript": "^5.9.3",
163
163
  "vitest": "^4.1.8",
164
- "@farming-labs/docs": "0.2.104"
164
+ "@farming-labs/docs": "0.2.106"
165
165
  },
166
166
  "peerDependencies": {
167
167
  "@farming-labs/docs": ">=0.0.1",
@@ -7098,7 +7098,7 @@ body:has(#nd-docs-layout[data-fd-framework]) {
7098
7098
  );
7099
7099
  }
7100
7100
 
7101
- #nd-docs-layout[data-fd-framework] {
7101
+ #nd-docs-layout[data-fd-framework]:not([data-fd-browser-adapter]) {
7102
7102
  display: grid;
7103
7103
  grid-template-columns: var(--fd-sidebar-width) 1fr;
7104
7104
  min-height: 100dvh;
@@ -7153,14 +7153,14 @@ body:has(#nd-docs-layout[data-fd-framework]) {
7153
7153
  display: flex;
7154
7154
  }
7155
7155
 
7156
- #nd-docs-layout[data-fd-framework] {
7156
+ #nd-docs-layout[data-fd-framework]:not([data-fd-browser-adapter]) {
7157
7157
  display: flex;
7158
7158
  flex-direction: column;
7159
7159
  }
7160
7160
  }
7161
7161
 
7162
7162
  @media (min-width: 1024px) {
7163
- #nd-docs-layout[data-fd-framework] {
7163
+ #nd-docs-layout[data-fd-framework]:not([data-fd-browser-adapter]) {
7164
7164
  grid-template-columns: var(--fd-sidebar-width) minmax(0, 1fr);
7165
7165
  }
7166
7166
 
@@ -7112,7 +7112,7 @@ body:has(#nd-docs-layout[data-fd-framework]) {
7112
7112
  );
7113
7113
  }
7114
7114
 
7115
- #nd-docs-layout[data-fd-framework] {
7115
+ #nd-docs-layout[data-fd-framework]:not([data-fd-browser-adapter]) {
7116
7116
  display: grid;
7117
7117
  grid-template-columns: var(--fd-sidebar-width) 1fr;
7118
7118
  min-height: 100dvh;
@@ -7167,14 +7167,14 @@ body:has(#nd-docs-layout[data-fd-framework]) {
7167
7167
  display: flex;
7168
7168
  }
7169
7169
 
7170
- #nd-docs-layout[data-fd-framework] {
7170
+ #nd-docs-layout[data-fd-framework]:not([data-fd-browser-adapter]) {
7171
7171
  display: flex;
7172
7172
  flex-direction: column;
7173
7173
  }
7174
7174
  }
7175
7175
 
7176
7176
  @media (min-width: 1024px) {
7177
- #nd-docs-layout[data-fd-framework] {
7177
+ #nd-docs-layout[data-fd-framework]:not([data-fd-browser-adapter]) {
7178
7178
  grid-template-columns: var(--fd-sidebar-width) minmax(0, 1fr);
7179
7179
  }
7180
7180
 
@@ -75,7 +75,7 @@ body:has(#nd-docs-layout[data-fd-framework]) {
75
75
 
76
76
  /* Layout (sidebar + content, TOC lives inside content area) */
77
77
 
78
- #nd-docs-layout[data-fd-framework] {
78
+ #nd-docs-layout[data-fd-framework]:not([data-fd-browser-adapter]) {
79
79
  display: grid;
80
80
  grid-template-columns: var(--fd-sidebar-width) 1fr;
81
81
  min-height: 100dvh;
@@ -132,14 +132,14 @@ body:has(#nd-docs-layout[data-fd-framework]) {
132
132
  display: flex;
133
133
  }
134
134
 
135
- #nd-docs-layout[data-fd-framework] {
135
+ #nd-docs-layout[data-fd-framework]:not([data-fd-browser-adapter]) {
136
136
  display: flex;
137
137
  flex-direction: column;
138
138
  }
139
139
  }
140
140
 
141
141
  @media (min-width: 1024px) {
142
- #nd-docs-layout[data-fd-framework] {
142
+ #nd-docs-layout[data-fd-framework]:not([data-fd-browser-adapter]) {
143
143
  grid-template-columns: var(--fd-sidebar-width) minmax(0, 1fr);
144
144
  }
145
145