@farming-labs/theme 0.2.98 → 0.2.101
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.
- package/dist/browser.d.mts +28 -0
- package/dist/browser.mjs +81 -0
- package/dist/docs-client-hooks.mjs +1 -1
- package/dist/docs-command-search.mjs +13 -2
- package/dist/docs-layout.mjs +7 -3
- package/dist/docs-page-client.d.mts +6 -0
- package/dist/docs-page-client.mjs +33 -6
- package/dist/mdx.mjs +1 -1
- package/dist/open-docs-provider-icons.mjs +14 -0
- package/dist/open-docs-providers.mjs +17 -2
- package/dist/page-actions.mjs +3 -8
- package/dist/pixel-border/index.d.mts +1 -0
- package/dist/pixel-border/index.mjs +2 -1
- package/dist/reading-time-options.mjs +27 -0
- package/dist/reading-time.mjs +2 -24
- package/dist/tablet-sidebar-bridge.mjs +64 -0
- package/dist/tanstack-layout.d.mts +5 -0
- package/dist/tanstack-layout.mjs +10 -4
- package/dist/tanstack.d.mts +1 -1
- package/dist/tanstack.mjs +1 -1
- package/package.json +9 -2
- package/styles/bundles/browser-framework.css +10585 -0
- package/styles/bundles/shared-framework.css +3 -3
- package/styles/pixel-border.css +33 -13
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { TanstackDocsLayoutProps } from "./tanstack-layout.mjs";
|
|
2
|
+
import { ComponentPropsWithoutRef } from "react";
|
|
3
|
+
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
4
|
+
import { RootProvider } from "fumadocs-ui/provider/base";
|
|
5
|
+
|
|
6
|
+
//#region src/browser.d.ts
|
|
7
|
+
type BrowserDocsLayoutProps = Omit<TanstackDocsLayoutProps, "browserRuntime">;
|
|
8
|
+
/** Docs layout for framework-neutral browser adapters such as Farm.js. */
|
|
9
|
+
declare function BrowserDocsLayout(props: BrowserDocsLayoutProps): react_jsx_runtime0.JSX.Element;
|
|
10
|
+
type FumadocsProviderProps = ComponentPropsWithoutRef<typeof RootProvider>;
|
|
11
|
+
declare global {
|
|
12
|
+
interface Window {
|
|
13
|
+
__fdBrowserHistoryPatched?: boolean;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
interface BrowserRootProviderProps extends FumadocsProviderProps {
|
|
17
|
+
/** Path rendered by the server, used to keep hydration deterministic. */
|
|
18
|
+
initialPathname?: string;
|
|
19
|
+
}
|
|
20
|
+
/** Framework-neutral provider for server-rendered React documentation adapters. */
|
|
21
|
+
declare function BrowserRootProvider({
|
|
22
|
+
children,
|
|
23
|
+
search,
|
|
24
|
+
initialPathname,
|
|
25
|
+
...props
|
|
26
|
+
}: BrowserRootProviderProps): react_jsx_runtime0.JSX.Element;
|
|
27
|
+
//#endregion
|
|
28
|
+
export { BrowserDocsLayout, BrowserDocsLayoutProps, BrowserRootProvider, BrowserRootProviderProps };
|
package/dist/browser.mjs
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { TanstackDocsLayout } from "./tanstack-layout.mjs";
|
|
4
|
+
import { useMemo, useSyncExternalStore } from "react";
|
|
5
|
+
import { FrameworkProvider } from "fumadocs-core/framework";
|
|
6
|
+
import { jsx } from "react/jsx-runtime";
|
|
7
|
+
import { RootProvider } from "fumadocs-ui/provider/base";
|
|
8
|
+
|
|
9
|
+
//#region src/browser.tsx
|
|
10
|
+
/** Docs layout for framework-neutral browser adapters such as Farm.js. */
|
|
11
|
+
function BrowserDocsLayout(props) {
|
|
12
|
+
return /* @__PURE__ */ jsx(TanstackDocsLayout, {
|
|
13
|
+
...props,
|
|
14
|
+
browserRuntime: true
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
function patchHistoryEvents() {
|
|
18
|
+
if (typeof window === "undefined" || window.__fdBrowserHistoryPatched) return;
|
|
19
|
+
for (const method of ["pushState", "replaceState"]) {
|
|
20
|
+
const original = window.history[method];
|
|
21
|
+
window.history[method] = function patchedHistoryMethod(...args) {
|
|
22
|
+
const result = original.apply(this, args);
|
|
23
|
+
window.dispatchEvent(new Event("fd-location-change"));
|
|
24
|
+
return result;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
window.__fdBrowserHistoryPatched = true;
|
|
28
|
+
}
|
|
29
|
+
function subscribeToLocation(onStoreChange) {
|
|
30
|
+
if (typeof window === "undefined") return () => {};
|
|
31
|
+
patchHistoryEvents();
|
|
32
|
+
window.addEventListener("popstate", onStoreChange);
|
|
33
|
+
window.addEventListener("fd-location-change", onStoreChange);
|
|
34
|
+
return () => {
|
|
35
|
+
window.removeEventListener("popstate", onStoreChange);
|
|
36
|
+
window.removeEventListener("fd-location-change", onStoreChange);
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function getBrowserPathname() {
|
|
40
|
+
return typeof window === "undefined" ? "/" : window.location.pathname;
|
|
41
|
+
}
|
|
42
|
+
function useBrowserRouter() {
|
|
43
|
+
return useMemo(() => ({
|
|
44
|
+
push(url) {
|
|
45
|
+
window.location.assign(url);
|
|
46
|
+
},
|
|
47
|
+
refresh() {
|
|
48
|
+
window.location.reload();
|
|
49
|
+
}
|
|
50
|
+
}), []);
|
|
51
|
+
}
|
|
52
|
+
function useBrowserParams() {
|
|
53
|
+
return useMemo(() => ({}), []);
|
|
54
|
+
}
|
|
55
|
+
function BrowserLink({ prefetch: _prefetch, ...props }) {
|
|
56
|
+
return /* @__PURE__ */ jsx("a", { ...props });
|
|
57
|
+
}
|
|
58
|
+
/** Framework-neutral provider for server-rendered React documentation adapters. */
|
|
59
|
+
function BrowserRootProvider({ children, search, initialPathname = "/", ...props }) {
|
|
60
|
+
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
|
|
76
|
+
})
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
//#endregion
|
|
81
|
+
export { BrowserDocsLayout, BrowserRootProvider };
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { emitClientAnalyticsEvent } from "./client-analytics.mjs";
|
|
4
4
|
import { useEffect } from "react";
|
|
5
|
-
import { emitDocsAnalyticsEvent, resolveDocsAnalyticsConfig } from "@farming-labs/docs";
|
|
5
|
+
import { emitDocsAnalyticsEvent, resolveDocsAnalyticsConfig } from "@farming-labs/docs/browser";
|
|
6
6
|
|
|
7
7
|
//#region src/docs-client-hooks.tsx
|
|
8
8
|
function useWindowHook(key, handler) {
|
|
@@ -256,6 +256,7 @@ function DocsCommandSearch({ api = "/api/docs", locale, analytics = false }) {
|
|
|
256
256
|
const searchApi = useMemo(() => withLangInUrl(api, activeLocale), [activeLocale, api]);
|
|
257
257
|
const inputRef = useRef(null);
|
|
258
258
|
const listRef = useRef(null);
|
|
259
|
+
const restoreFocusRef = useRef(null);
|
|
259
260
|
const searchCacheRef = useRef(/* @__PURE__ */ new Map());
|
|
260
261
|
const setOpenWithAnalytics = useCallback((nextOpen, trigger) => {
|
|
261
262
|
setOpen(nextOpen);
|
|
@@ -404,13 +405,23 @@ function DocsCommandSearch({ api = "/api/docs", locale, analytics = false }) {
|
|
|
404
405
|
searchApi
|
|
405
406
|
]);
|
|
406
407
|
useEffect(() => {
|
|
407
|
-
if (open)
|
|
408
|
-
|
|
408
|
+
if (open) {
|
|
409
|
+
const activeElement = document.activeElement;
|
|
410
|
+
restoreFocusRef.current = activeElement instanceof HTMLElement ? activeElement : null;
|
|
411
|
+
const focusTimer = window.setTimeout(() => inputRef.current?.focus(), 10);
|
|
412
|
+
return () => window.clearTimeout(focusTimer);
|
|
413
|
+
} else {
|
|
409
414
|
setQuery("");
|
|
410
415
|
setResults([]);
|
|
411
416
|
setFilter("all");
|
|
412
417
|
setFilterOpen(false);
|
|
413
418
|
setActiveIndex(0);
|
|
419
|
+
const restoreTarget = restoreFocusRef.current;
|
|
420
|
+
restoreFocusRef.current = null;
|
|
421
|
+
if (restoreTarget?.isConnected) {
|
|
422
|
+
const focusFrame = window.requestAnimationFrame(() => restoreTarget.focus({ preventScroll: true }));
|
|
423
|
+
return () => window.cancelAnimationFrame(focusFrame);
|
|
424
|
+
}
|
|
414
425
|
}
|
|
415
426
|
}, [open]);
|
|
416
427
|
useEffect(() => {
|
package/dist/docs-layout.mjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { serializeIcon } from "./serialize-icon.mjs";
|
|
2
|
+
import { resolveOpenDocsProviders } from "./open-docs-providers.mjs";
|
|
2
3
|
import { withLangInUrl } from "./i18n.mjs";
|
|
3
4
|
import { DocsPageClient } from "./docs-page-client.mjs";
|
|
4
5
|
import { DocsAIFeatures } from "./docs-ai-features.mjs";
|
|
5
6
|
import { resolveDocsCloudAIClientRequest } from "./docs-cloud-ai-client.mjs";
|
|
6
7
|
import { DocsCommandSearch } from "./docs-command-search.mjs";
|
|
7
|
-
import {
|
|
8
|
-
import { resolvePageReadingTime
|
|
8
|
+
import { resolveReadingTimeOptions } from "./reading-time-options.mjs";
|
|
9
|
+
import { resolvePageReadingTime } from "./reading-time.mjs";
|
|
9
10
|
import { SidebarSearchWithAI } from "./sidebar-search-ai.mjs";
|
|
10
11
|
import { LocaleThemeControl } from "./locale-theme-control.mjs";
|
|
11
12
|
import fs from "node:fs";
|
|
@@ -405,7 +406,10 @@ function buildDescriptionMap(config, ctx) {
|
|
|
405
406
|
scan(docsDir, []);
|
|
406
407
|
return map;
|
|
407
408
|
}
|
|
408
|
-
/**
|
|
409
|
+
/**
|
|
410
|
+
* Build titles only for pages whose MDX body does not already author an h1.
|
|
411
|
+
* This lets the page frame render the frontmatter title exactly once.
|
|
412
|
+
*/
|
|
409
413
|
function buildGeneratedTitleMap(config, ctx) {
|
|
410
414
|
const docsDir = ctx.docsDir;
|
|
411
415
|
const map = {};
|
|
@@ -17,6 +17,8 @@ interface SerializedProvider {
|
|
|
17
17
|
}
|
|
18
18
|
interface DocsPageClientProps {
|
|
19
19
|
tocEnabled: boolean;
|
|
20
|
+
/** Expose the llms.txt action in a runtime-owned header slot. */
|
|
21
|
+
showLlmsInHeader?: boolean;
|
|
20
22
|
tocStyle?: "default" | "directional";
|
|
21
23
|
breadcrumbEnabled?: boolean;
|
|
22
24
|
changelogBasePath?: string;
|
|
@@ -84,6 +86,8 @@ interface DocsPageClientProps {
|
|
|
84
86
|
descriptionMap?: Record<string, string>;
|
|
85
87
|
/** Frontmatter description to display below the page title (overrides descriptionMap) */
|
|
86
88
|
description?: string;
|
|
89
|
+
/** The first authored paragraph already contains the frontmatter description. */
|
|
90
|
+
descriptionInBody?: boolean;
|
|
87
91
|
/** Built-in page feedback prompt configuration */
|
|
88
92
|
feedbackEnabled?: boolean;
|
|
89
93
|
feedbackQuestion?: string;
|
|
@@ -100,6 +104,7 @@ interface DocsPageClientProps {
|
|
|
100
104
|
}
|
|
101
105
|
declare function DocsPageClient({
|
|
102
106
|
tocEnabled,
|
|
107
|
+
showLlmsInHeader,
|
|
103
108
|
tocStyle,
|
|
104
109
|
breadcrumbEnabled,
|
|
105
110
|
changelogBasePath,
|
|
@@ -139,6 +144,7 @@ declare function DocsPageClient({
|
|
|
139
144
|
generatedTitleMap,
|
|
140
145
|
descriptionMap,
|
|
141
146
|
description,
|
|
147
|
+
descriptionInBody,
|
|
142
148
|
feedbackEnabled,
|
|
143
149
|
feedbackQuestion,
|
|
144
150
|
feedbackPlaceholder,
|
|
@@ -255,7 +255,7 @@ function findThreadlineTocActionsContainer() {
|
|
|
255
255
|
}
|
|
256
256
|
return toc.parentElement ?? toc;
|
|
257
257
|
}
|
|
258
|
-
function DocsPageClient({ tocEnabled, tocStyle = "default", breadcrumbEnabled = true, changelogBasePath, entry = "docs", publicPath, locale, copyMarkdown = false, copyMarkdownFormat, copyMarkdownIncludeTitle, copyMarkdownLabel, copyMarkdownCopiedLabel, openDocs = false, openDocsProviders, openDocsTarget, openDocsPrompt, 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, feedbackEnabled = false, feedbackQuestion, feedbackPlaceholder, feedbackRequireComment, feedbackPositiveLabel, feedbackNegativeLabel, feedbackSubmitLabel, feedbackSuccessMessage, feedbackErrorMessage, feedbackOnFeedback, analytics = false, children }) {
|
|
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, 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
259
|
const fdTocStyle = tocStyle === "directional" ? "clerk" : void 0;
|
|
260
260
|
const [toc, setToc] = useState([]);
|
|
261
261
|
const [titlePortalHost, setTitlePortalHost] = useState(null);
|
|
@@ -268,6 +268,7 @@ function DocsPageClient({ tocEnabled, tocStyle = "default", breadcrumbEnabled =
|
|
|
268
268
|
const activeLocale = resolveClientLocale(searchParams, locale);
|
|
269
269
|
const resolvedPublicPath = normalizePublicDocsPath(publicPath, entry);
|
|
270
270
|
const llmsLangQuery = activeLocale ? `?lang=${encodeURIComponent(activeLocale)}` : "";
|
|
271
|
+
const shouldShowLlmsInHeader = llmsTxtEnabled && showLlmsInHeader;
|
|
271
272
|
const normalizedPath = (browserPathname || pathname).replace(/\/$/, "") || "/";
|
|
272
273
|
const pageTitle = generatedTitleMap?.[normalizedPath];
|
|
273
274
|
const pageDescription = description ?? descriptionMap?.[normalizedPath];
|
|
@@ -509,13 +510,38 @@ function DocsPageClient({ tocEnabled, tocStyle = "default", breadcrumbEnabled =
|
|
|
509
510
|
}
|
|
510
511
|
const host = document.createElement("div");
|
|
511
512
|
host.className = "fd-title-decorations-host";
|
|
513
|
+
const placeHost = () => {
|
|
514
|
+
let anchor = title;
|
|
515
|
+
if (descriptionInBody) {
|
|
516
|
+
let sibling = title.nextElementSibling;
|
|
517
|
+
while (sibling) {
|
|
518
|
+
if (sibling === host || sibling.matches(".not-prose, .fd-title-decorations-host")) {
|
|
519
|
+
sibling = sibling.nextElementSibling;
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
if (sibling.matches("p")) anchor = sibling;
|
|
523
|
+
break;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
if (anchor.nextElementSibling !== host) anchor.insertAdjacentElement("afterend", host);
|
|
527
|
+
};
|
|
512
528
|
title.insertAdjacentElement("afterend", host);
|
|
529
|
+
placeHost();
|
|
530
|
+
const observer = new MutationObserver(placeHost);
|
|
531
|
+
observer.observe(title.parentElement ?? title, { childList: true });
|
|
532
|
+
const animationFrame = window.requestAnimationFrame(placeHost);
|
|
513
533
|
setTitlePortalHost(host);
|
|
514
534
|
return () => {
|
|
535
|
+
window.cancelAnimationFrame(animationFrame);
|
|
536
|
+
observer.disconnect();
|
|
515
537
|
host.remove();
|
|
516
538
|
setTitlePortalHost(null);
|
|
517
539
|
};
|
|
518
|
-
}, [
|
|
540
|
+
}, [
|
|
541
|
+
descriptionInBody,
|
|
542
|
+
needsTitleDecorationsPortal,
|
|
543
|
+
pathname
|
|
544
|
+
]);
|
|
519
545
|
const titleDecorations = needsTitleDecorationsPortal ? /* @__PURE__ */ jsx(TitleDecorations, {
|
|
520
546
|
description: titleDescription,
|
|
521
547
|
belowTitle: belowTitleBlock
|
|
@@ -560,10 +586,11 @@ function DocsPageClient({ tocEnabled, tocStyle = "default", breadcrumbEnabled =
|
|
|
560
586
|
llmsTxtEnabled && /* @__PURE__ */ jsx("a", {
|
|
561
587
|
href: `/llms.txt${llmsLangQuery}`,
|
|
562
588
|
className: "fd-agent-llms-directive",
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
589
|
+
"data-visible-in-header": shouldShowLlmsInHeader ? "true" : void 0,
|
|
590
|
+
style: shouldShowLlmsInHeader ? void 0 : agentLlmsDirectiveStyle,
|
|
591
|
+
tabIndex: shouldShowLlmsInHeader ? void 0 : -1,
|
|
592
|
+
"aria-hidden": shouldShowLlmsInHeader ? void 0 : true,
|
|
593
|
+
children: shouldShowLlmsInHeader ? "LLMS.TXT" : "llms.txt"
|
|
567
594
|
}, "llms-txt"),
|
|
568
595
|
titleControlsPortal,
|
|
569
596
|
tocActionsPortal,
|
package/dist/mdx.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { HoverLink } from "./hover-link.mjs";
|
|
|
5
5
|
import { extractPromptText } from "./prompt-text.mjs";
|
|
6
6
|
import { Prompt } from "./prompt.mjs";
|
|
7
7
|
import React from "react";
|
|
8
|
-
import { resolveDocsAudienceExposure } from "@farming-labs/docs";
|
|
8
|
+
import { resolveDocsAudienceExposure } from "@farming-labs/docs/browser";
|
|
9
9
|
import { Tab, Tabs } from "fumadocs-ui/components/tabs";
|
|
10
10
|
import { CodeBlockTab, CodeBlockTabs, CodeBlockTabsList, CodeBlockTabsTrigger } from "fumadocs-ui/components/codeblock";
|
|
11
11
|
import defaultMdxComponents from "fumadocs-ui/mdx";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region src/open-docs-provider-icons.ts
|
|
2
|
+
/** Monochrome provider marks used by the built-in Open in… presets. */
|
|
3
|
+
const OPEN_DOCS_PROVIDER_ICONS = {
|
|
4
|
+
chatgpt: "<svg viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M11.248 18.25q-.825 0-1.568-.314a4.3 4.3 0 0 1-1.32-.874 4 4 0 0 1-1.304.214 4 4 0 0 1-2.046-.544 4.27 4.27 0 0 1-1.518-1.485 4 4 0 0 1-.56-2.095q0-.48.131-1.04A4.4 4.4 0 0 1 2.04 10.71a4.07 4.07 0 0 1 .017-3.4 4.2 4.2 0 0 1 1.056-1.418 3.8 3.8 0 0 1 1.6-.842 3.9 3.9 0 0 1 .76-1.683q.593-.759 1.451-1.188a4.04 4.04 0 0 1 1.832-.429q.825 0 1.567.313.742.314 1.32.875a4 4 0 0 1 1.304-.215q1.106 0 2.046.545a4.14 4.14 0 0 1 1.501 1.485q.578.941.578 2.095 0 .48-.132 1.04.66.61 1.023 1.419.363.792.363 1.666 0 .892-.38 1.717a4.3 4.3 0 0 1-1.072 1.435 3.8 3.8 0 0 1-1.584.825 3.8 3.8 0 0 1-.775 1.683 4.06 4.06 0 0 1-1.436 1.188 4.04 4.04 0 0 1-1.832.429m-4.076-2.062q.825 0 1.435-.347l3.103-1.782a.36.36 0 0 0 .164-.313v-1.42L7.881 14.62a.67.67 0 0 1-.726 0l-3.118-1.798a.5.5 0 0 1-.017.115v.198q0 .841.396 1.551.413.693 1.139 1.089a3.2 3.2 0 0 0 1.617.412m.165-2.69a.4.4 0 0 0 .181.05q.083 0 .165-.05l1.238-.71-3.977-2.31a.7.7 0 0 1-.363-.643v-3.58q-.825.362-1.32 1.122a2.9 2.9 0 0 0-.495 1.65q0 .809.413 1.55.412.743 1.072 1.123zm3.91 3.663q.875 0 1.585-.396a2.96 2.96 0 0 0 1.534-2.64v-3.564a.32.32 0 0 0-.165-.297l-1.254-.726v4.604a.7.7 0 0 1-.363.643l-3.119 1.799a3 3 0 0 0 1.783.577m.627-6.039V8.878L10.01 7.822 8.129 8.878v2.244l1.881 1.056zM7.057 5.859a.7.7 0 0 1 .363-.644l3.119-1.798a3 3 0 0 0-1.782-.578q-.874 0-1.584.396A2.96 2.96 0 0 0 6.05 4.324a3.07 3.07 0 0 0-.396 1.551v3.547q0 .199.165.314l1.237.726zm8.383 7.887q.825-.364 1.303-1.123.495-.758.495-1.65a3.15 3.15 0 0 0-.412-1.55q-.413-.743-1.073-1.123l-3.086-1.782q-.099-.065-.181-.049a.3.3 0 0 0-.165.05l-1.238.692 3.993 2.327a.6.6 0 0 1 .264.264.64.64 0 0 1 .1.363zm-3.317-8.382a.63.63 0 0 1 .726 0l3.135 1.831v-.297q0-.792-.396-1.501a2.86 2.86 0 0 0-1.105-1.155q-.71-.43-1.65-.43-.825 0-1.436.347L8.294 5.941a.36.36 0 0 0-.165.314v1.418z\"/></svg>",
|
|
5
|
+
claude: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"m4.7144 15.9555 4.7174-2.6471.079-.2307-.079-.1275h-.2307l-.7893-.0486-2.6956-.0729-2.3375-.0971-2.2646-.1214-.5707-.1215-.5343-.7042.0546-.3522.4797-.3218.686.0608 1.5179.1032 2.2767.1578 1.6514.0972 2.4468.255h.3886l.0546-.1579-.1336-.0971-.1032-.0972L6.973 9.8356l-2.55-1.6879-1.3356-.9714-.7225-.4918-.3643-.4614-.1578-1.0078.6557-.7225.8803.0607.2246.0607.8925.686 1.9064 1.4754 2.4893 1.8336.3643.3035.1457-.1032.0182-.0728-.164-.2733-1.3539-2.4467-1.445-2.4893-.6435-1.032-.17-.6194c-.0607-.255-.1032-.4674-.1032-.7285L6.287.1335 6.6997 0l.9957.1336.419.3642.6192 1.4147 1.0018 2.2282 1.5543 3.0296.4553.8985.2429.8318.091.255h.1579v-.1457l.1275-1.706.2368-2.0947.2307-2.6957.0789-.7589.3764-.9107.7468-.4918.5828.2793.4797.686-.0668.4433-.2853 1.8517-.5586 2.9021-.3643 1.9429h.2125l.2429-.2429.9835-1.3053 1.6514-2.0643.7286-.8196.85-.9046.5464-.4311h1.0321l.759 1.1293-.34 1.1657-1.0625 1.3478-.8804 1.1414-1.2628 1.7-.7893 1.36.0729.1093.1882-.0183 2.8535-.607 1.5421-.2794 1.8396-.3157.8318.3886.091.3946-.3278.8075-1.967.4857-2.3072.4614-3.4364.8136-.0425.0304.0486.0607 1.5482.1457.6618.0364h1.621l3.0175.2247.7892.522.4736.6376-.079.4857-1.2142.6193-1.6393-.3886-3.825-.9107-1.3113-.3279h-.1822v.1093l1.0929 1.0686 2.0035 1.8092 2.5075 2.3314.1275.5768-.3218.4554-.34-.0486-2.2039-1.6575-.85-.7468-1.9246-1.621h-.1275v.17l.4432.6496 2.3436 3.5214.1214 1.0807-.17.3521-.6071.2125-.6679-.1214-1.3721-1.9246L14.38 17.959l-1.1414-1.9428-.1397.079-.674 7.2552-.3156.3703-.7286.2793-.6071-.4614-.3218-.7468.3218-1.4753.3886-1.9246.3157-1.53.2853-1.9004.17-.6314-.0121-.0425-.1397.0182-1.4328 1.9672-2.1796 2.9446-1.7243 1.8456-.4128.164-.7164-.3704.0667-.6618.4008-.5889 2.386-3.0357 1.4389-1.882.929-1.0868-.0062-.1579h-.0546l-6.3385 4.1164-1.1293.1457-.4857-.4554.0608-.7467.2307-.2429 1.9064-1.3114Z\"/></svg>",
|
|
6
|
+
cursor: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M11.503.131 1.891 5.678a.84.84 0 0 0-.42.726v11.188c0 .3.162.575.42.724l9.609 5.55a1 1 0 0 0 .998 0l9.61-5.55a.84.84 0 0 0 .42-.724V6.404a.84.84 0 0 0-.42-.726L12.497.131a1.01 1.01 0 0 0-.996 0M2.657 6.338h18.55c.263 0 .43.287.297.515L12.23 22.918c-.062.107-.229.064-.229-.06V12.335a.59.59 0 0 0-.295-.51l-9.11-5.257c-.109-.063-.064-.23.061-.23\"/></svg>",
|
|
7
|
+
gemini: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M11.04 19.32Q12 21.51 12 24q0-2.49.93-4.68.96-2.19 2.58-3.81t3.81-2.55Q21.51 12 24 12q-2.49 0-4.68-.93a12.3 12.3 0 0 1-3.81-2.58 12.3 12.3 0 0 1-2.58-3.81Q12 2.49 12 0q0 2.49-.96 4.68-.93 2.19-2.55 3.81a12.3 12.3 0 0 1-3.81 2.58Q2.49 12 0 12q2.49 0 4.68.96 2.19.93 3.81 2.55t2.55 3.81\"/></svg>",
|
|
8
|
+
copilot: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M23.922 16.997C23.061 18.492 18.063 22.02 12 22.02 5.937 22.02.939 18.492.078 16.997A.641.641 0 0 1 0 16.741v-2.869a.883.883 0 0 1 .053-.22c.372-.935 1.347-2.292 2.605-2.656.167-.429.414-1.055.644-1.517a10.098 10.098 0 0 1-.052-1.086c0-1.331.282-2.499 1.132-3.368.397-.406.89-.717 1.474-.952C7.255 2.937 9.248 1.98 11.978 1.98c2.731 0 4.767.957 6.166 2.093.584.235 1.077.546 1.474.952.85.869 1.132 2.037 1.132 3.368 0 .368-.014.733-.052 1.086.23.462.477 1.088.644 1.517 1.258.364 2.233 1.721 2.605 2.656a.841.841 0 0 1 .053.22v2.869a.641.641 0 0 1-.078.256Zm-11.75-5.992h-.344a4.359 4.359 0 0 1-.355.508c-.77.947-1.918 1.492-3.508 1.492-1.725 0-2.989-.359-3.782-1.259a2.137 2.137 0 0 1-.085-.104L4 11.746v6.585c1.435.779 4.514 2.179 8 2.179 3.486 0 6.565-1.4 8-2.179v-6.585l-.098-.104s-.033.045-.085.104c-.793.9-2.057 1.259-3.782 1.259-1.59 0-2.738-.545-3.508-1.492a4.359 4.359 0 0 1-.355-.508Zm2.328 3.25c.549 0 1 .451 1 1v2c0 .549-.451 1-1 1-.549 0-1-.451-1-1v-2c0-.549.451-1 1-1Zm-5 0c.549 0 1 .451 1 1v2c0 .549-.451 1-1 1-.549 0-1-.451-1-1v-2c0-.549.451-1 1-1Zm3.313-6.185c.136 1.057.403 1.913.878 2.497.442.544 1.134.938 2.344.938 1.573 0 2.292-.337 2.657-.751.384-.435.558-1.15.558-2.361 0-1.14-.243-1.847-.705-2.319-.477-.488-1.319-.862-2.824-1.025-1.487-.161-2.192.138-2.533.529-.269.307-.437.808-.438 1.578v.021c0 .265.021.562.063.893Zm-1.626 0c.042-.331.063-.628.063-.894v-.02c-.001-.77-.169-1.271-.438-1.578-.341-.391-1.046-.69-2.533-.529-1.505.163-2.347.537-2.824 1.025-.462.472-.705 1.179-.705 2.319 0 1.211.175 1.926.558 2.361.365.414 1.084.751 2.657.751 1.21 0 1.902-.394 2.344-.938.475-.584.742-1.44.878-2.497Z\"/></svg>",
|
|
9
|
+
perplexity: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M22.3977 7.0896h-2.3106V.0676l-7.5094 6.3542V.1577h-1.1554v6.1966L4.4904 0v7.0896H1.6023v10.3976h2.8882V24l6.932-6.3591v6.2005h1.1554v-6.0469l6.9318 6.1807v-6.4879h2.8882V7.0896zm-3.4657-4.531v4.531h-5.355l5.355-4.531zm-13.2862.0676 4.8691 4.4634H5.6458V2.6262zM2.7576 16.332V8.245h7.8476l-6.1149 6.1147v1.9723H2.7576zm2.8882 5.0404v-3.8852h.0001v-2.6488l5.7763-5.7764v7.0111l-5.7764 5.2993zm12.7086.0248-5.7766-5.1509V9.0618l5.7766 5.7766v6.5588zm2.8882-5.0652h-1.733v-1.9723L13.3948 8.245h7.8478v8.087z\"/></svg>",
|
|
10
|
+
github: "<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12\"/></svg>"
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
//#endregion
|
|
14
|
+
export { OPEN_DOCS_PROVIDER_ICONS };
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { OPEN_DOCS_PROVIDER_ICONS } from "./open-docs-provider-icons.mjs";
|
|
2
|
+
|
|
1
3
|
//#region src/open-docs-providers.ts
|
|
2
4
|
const PROMPT_PROVIDER_TEMPLATES = {
|
|
3
5
|
chatgpt: "https://chatgpt.com/?q={prompt}",
|
|
@@ -10,36 +12,43 @@ const PROMPT_PROVIDER_TEMPLATES = {
|
|
|
10
12
|
const OPEN_DOCS_PROVIDER_PRESETS = {
|
|
11
13
|
chatgpt: {
|
|
12
14
|
name: "ChatGPT",
|
|
15
|
+
iconHtml: OPEN_DOCS_PROVIDER_ICONS.chatgpt,
|
|
13
16
|
urlTemplate: "https://chatgpt.com/?q={prompt}",
|
|
14
17
|
promptUrlTemplate: PROMPT_PROVIDER_TEMPLATES.chatgpt
|
|
15
18
|
},
|
|
16
19
|
claude: {
|
|
17
20
|
name: "Claude",
|
|
21
|
+
iconHtml: OPEN_DOCS_PROVIDER_ICONS.claude,
|
|
18
22
|
urlTemplate: "https://claude.ai/new?q={prompt}",
|
|
19
23
|
promptUrlTemplate: PROMPT_PROVIDER_TEMPLATES.claude
|
|
20
24
|
},
|
|
21
25
|
cursor: {
|
|
22
26
|
name: "Cursor",
|
|
27
|
+
iconHtml: OPEN_DOCS_PROVIDER_ICONS.cursor,
|
|
23
28
|
urlTemplate: "https://cursor.com/link/prompt?text={prompt}",
|
|
24
29
|
promptUrlTemplate: PROMPT_PROVIDER_TEMPLATES.cursor
|
|
25
30
|
},
|
|
26
31
|
gemini: {
|
|
27
32
|
name: "Gemini",
|
|
33
|
+
iconHtml: OPEN_DOCS_PROVIDER_ICONS.gemini,
|
|
28
34
|
urlTemplate: "https://gemini.google.com/app?q={prompt}",
|
|
29
35
|
promptUrlTemplate: PROMPT_PROVIDER_TEMPLATES.gemini
|
|
30
36
|
},
|
|
31
37
|
copilot: {
|
|
32
38
|
name: "Copilot",
|
|
39
|
+
iconHtml: OPEN_DOCS_PROVIDER_ICONS.copilot,
|
|
33
40
|
urlTemplate: "https://github.com/copilot?prompt={prompt}",
|
|
34
41
|
promptUrlTemplate: PROMPT_PROVIDER_TEMPLATES.copilot
|
|
35
42
|
},
|
|
36
43
|
perplexity: {
|
|
37
44
|
name: "Perplexity",
|
|
45
|
+
iconHtml: OPEN_DOCS_PROVIDER_ICONS.perplexity,
|
|
38
46
|
urlTemplate: "https://www.perplexity.ai/search/?q={prompt}",
|
|
39
47
|
promptUrlTemplate: PROMPT_PROVIDER_TEMPLATES.perplexity
|
|
40
48
|
},
|
|
41
49
|
github: {
|
|
42
50
|
name: "GitHub",
|
|
51
|
+
iconHtml: OPEN_DOCS_PROVIDER_ICONS.github,
|
|
43
52
|
urlTemplate: "{githubUrl}",
|
|
44
53
|
promptUrlTemplate: "{githubUrl}",
|
|
45
54
|
target: "github"
|
|
@@ -48,6 +57,11 @@ const OPEN_DOCS_PROVIDER_PRESETS = {
|
|
|
48
57
|
function normalizeProviderName(name) {
|
|
49
58
|
return name.trim().toLowerCase();
|
|
50
59
|
}
|
|
60
|
+
function resolveOpenDocsProviderIcon(name) {
|
|
61
|
+
if (!name) return void 0;
|
|
62
|
+
const normalizedName = normalizeProviderName(name);
|
|
63
|
+
return OPEN_DOCS_PROVIDER_PRESETS[normalizedName === "github copilot" ? "copilot" : normalizedName]?.iconHtml;
|
|
64
|
+
}
|
|
51
65
|
function resolveOpenDocsProviders(providers, options = {}) {
|
|
52
66
|
if (!providers || providers.length === 0) return void 0;
|
|
53
67
|
const serialized = providers.map((provider) => resolveOpenDocsProvider(provider, options)).filter((provider) => provider !== void 0);
|
|
@@ -60,6 +74,7 @@ function resolveOpenDocsProvider(provider, options = {}) {
|
|
|
60
74
|
if (!preset) return void 0;
|
|
61
75
|
return {
|
|
62
76
|
name: preset.name,
|
|
77
|
+
iconHtml: preset.iconHtml,
|
|
63
78
|
urlTemplate: preset.urlTemplate,
|
|
64
79
|
promptUrlTemplate: preset.promptUrlTemplate,
|
|
65
80
|
target: preset.target ?? options.target,
|
|
@@ -75,11 +90,11 @@ function resolveOpenDocsProvider(provider, options = {}) {
|
|
|
75
90
|
name,
|
|
76
91
|
urlTemplate,
|
|
77
92
|
promptUrlTemplate: provider.promptUrlTemplate ?? cursorAppTemplate ?? preset?.promptUrlTemplate,
|
|
78
|
-
iconHtml: options.serializeIcon?.(provider.icon) ?? (typeof provider.icon === "string" ? provider.icon : void 0),
|
|
93
|
+
iconHtml: options.serializeIcon?.(provider.icon) ?? (typeof provider.icon === "string" ? provider.icon : void 0) ?? preset?.iconHtml,
|
|
79
94
|
target: provider.target ?? preset?.target ?? options.target ?? (hasCustomUrlTemplate ? "page" : void 0),
|
|
80
95
|
prompt: provider.prompt ?? options.prompt
|
|
81
96
|
};
|
|
82
97
|
}
|
|
83
98
|
|
|
84
99
|
//#endregion
|
|
85
|
-
export { resolveOpenDocsProviders };
|
|
100
|
+
export { resolveOpenDocsProviderIcon, resolveOpenDocsProviders };
|
package/dist/page-actions.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { emitClientAnalyticsEvent } from "./client-analytics.mjs";
|
|
4
|
+
import { resolveOpenDocsProviderIcon, resolveOpenDocsProviders } from "./open-docs-providers.mjs";
|
|
4
5
|
import { sanitizeIconHtml } from "./safe-icon-html.mjs";
|
|
5
6
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
6
7
|
import { usePathname } from "fumadocs-core/framework";
|
|
@@ -107,13 +108,7 @@ const SparklesIcon = () => /* @__PURE__ */ jsxs("svg", {
|
|
|
107
108
|
/* @__PURE__ */ jsx("path", { d: "M19 11.5 20 15l3.5 1-3.5 1-1 3.5-1-3.5-3.5-1 3.5-1Z" })
|
|
108
109
|
]
|
|
109
110
|
});
|
|
110
|
-
const DEFAULT_PROVIDERS = [
|
|
111
|
-
name: "ChatGPT",
|
|
112
|
-
urlTemplate: "https://chatgpt.com/?q={prompt}"
|
|
113
|
-
}, {
|
|
114
|
-
name: "Claude",
|
|
115
|
-
urlTemplate: "https://claude.ai/new?q={prompt}"
|
|
116
|
-
}];
|
|
111
|
+
const DEFAULT_PROVIDERS = resolveOpenDocsProviders(["chatgpt", "claude"]) ?? [];
|
|
117
112
|
const DEFAULT_OPEN_DOCS_TARGET = "markdown";
|
|
118
113
|
const DEFAULT_OPEN_DOCS_PROMPT = "Read this documentation: {url}";
|
|
119
114
|
function pageUrlToMarkdownUrl(pageUrl) {
|
|
@@ -328,7 +323,7 @@ function PageActions({ copyMarkdown, copyMarkdownFormat = "markdown", copyMarkdo
|
|
|
328
323
|
className: "fd-page-action-menu",
|
|
329
324
|
role: "menu",
|
|
330
325
|
children: resolvedProviders.map((provider) => {
|
|
331
|
-
const iconHtml = sanitizeIconHtml(provider.iconHtml);
|
|
326
|
+
const iconHtml = sanitizeIconHtml(provider.iconHtml ?? resolveOpenDocsProviderIcon(provider.name));
|
|
332
327
|
return /* @__PURE__ */ jsxs("button", {
|
|
333
328
|
type: "button",
|
|
334
329
|
role: "menuitem",
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//#region src/reading-time-options.ts
|
|
2
|
+
function resolveReadingTimeOptions(readingTime) {
|
|
3
|
+
if (readingTime === true) return {
|
|
4
|
+
enabled: true,
|
|
5
|
+
format: "long",
|
|
6
|
+
includeCode: false
|
|
7
|
+
};
|
|
8
|
+
if (readingTime === false || readingTime === void 0 || readingTime === null) return {
|
|
9
|
+
enabled: false,
|
|
10
|
+
format: "long",
|
|
11
|
+
includeCode: false
|
|
12
|
+
};
|
|
13
|
+
if (typeof readingTime !== "object") return {
|
|
14
|
+
enabled: false,
|
|
15
|
+
format: "long",
|
|
16
|
+
includeCode: false
|
|
17
|
+
};
|
|
18
|
+
return {
|
|
19
|
+
enabled: readingTime.enabled !== false,
|
|
20
|
+
wordsPerMinute: typeof readingTime.wordsPerMinute === "number" && Number.isFinite(readingTime.wordsPerMinute) ? readingTime.wordsPerMinute : void 0,
|
|
21
|
+
format: readingTime.format === "short" ? "short" : "long",
|
|
22
|
+
includeCode: readingTime.includeCode === true
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
//#endregion
|
|
27
|
+
export { resolveReadingTimeOptions };
|
package/dist/reading-time.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveReadingTimeOptions } from "./reading-time-options.mjs";
|
|
1
2
|
import matter from "gray-matter";
|
|
2
3
|
|
|
3
4
|
//#region src/reading-time.ts
|
|
@@ -16,29 +17,6 @@ function estimateReadingTimeMinutes(content, wordsPerMinute, options) {
|
|
|
16
17
|
const wordCount = stripNonReadingContent(content, options).match(/\b[\p{L}\p{N}][\p{L}\p{N}'’-]*\b/gu)?.length ?? 0;
|
|
17
18
|
return Math.max(1, Math.ceil(wordCount / normalizeWordsPerMinute(wordsPerMinute)));
|
|
18
19
|
}
|
|
19
|
-
function resolveReadingTimeOptions(readingTime) {
|
|
20
|
-
if (readingTime === true) return {
|
|
21
|
-
enabled: true,
|
|
22
|
-
format: "long",
|
|
23
|
-
includeCode: false
|
|
24
|
-
};
|
|
25
|
-
if (readingTime === false || readingTime === void 0 || readingTime === null) return {
|
|
26
|
-
enabled: false,
|
|
27
|
-
format: "long",
|
|
28
|
-
includeCode: false
|
|
29
|
-
};
|
|
30
|
-
if (typeof readingTime !== "object") return {
|
|
31
|
-
enabled: false,
|
|
32
|
-
format: "long",
|
|
33
|
-
includeCode: false
|
|
34
|
-
};
|
|
35
|
-
return {
|
|
36
|
-
enabled: readingTime.enabled !== false,
|
|
37
|
-
wordsPerMinute: typeof readingTime.wordsPerMinute === "number" && Number.isFinite(readingTime.wordsPerMinute) ? readingTime.wordsPerMinute : void 0,
|
|
38
|
-
format: readingTime.format === "short" ? "short" : "long",
|
|
39
|
-
includeCode: readingTime.includeCode === true
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
20
|
function resolveReadingTimeFromContent(frontmatter, content, wordsPerMinute, options) {
|
|
43
21
|
const pageData = frontmatter ?? {};
|
|
44
22
|
if (pageData.readingTime === false) return null;
|
|
@@ -51,4 +29,4 @@ function resolvePageReadingTime(frontmatter, content, options) {
|
|
|
51
29
|
}
|
|
52
30
|
|
|
53
31
|
//#endregion
|
|
54
|
-
export { resolvePageReadingTime
|
|
32
|
+
export { resolvePageReadingTime };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { useSidebar } from "fumadocs-ui/components/sidebar/base";
|
|
6
|
+
|
|
7
|
+
//#region src/tablet-sidebar-bridge.tsx
|
|
8
|
+
const tabletSidebarQuery = "(min-width: 768px) and (max-width: 1023px)";
|
|
9
|
+
/**
|
|
10
|
+
* Extends Fumadocs' sidebar state through the tablet breakpoint without
|
|
11
|
+
* intercepting its trigger or cloning the configured navigation tree.
|
|
12
|
+
*/
|
|
13
|
+
function TabletSidebarBridge() {
|
|
14
|
+
const { open, setOpen } = useSidebar();
|
|
15
|
+
const [isTablet, setIsTablet] = useState(false);
|
|
16
|
+
const closeButtonRef = useRef(null);
|
|
17
|
+
const returnFocusRef = useRef(null);
|
|
18
|
+
const visible = isTablet && open;
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
const media = window.matchMedia(tabletSidebarQuery);
|
|
21
|
+
const update = () => setIsTablet(media.matches);
|
|
22
|
+
update();
|
|
23
|
+
media.addEventListener("change", update);
|
|
24
|
+
return () => media.removeEventListener("change", update);
|
|
25
|
+
}, []);
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (!visible) return;
|
|
28
|
+
returnFocusRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
29
|
+
closeButtonRef.current?.focus();
|
|
30
|
+
const closeOnEscape = (event) => {
|
|
31
|
+
if (event.key !== "Escape") return;
|
|
32
|
+
event.preventDefault();
|
|
33
|
+
setOpen(false);
|
|
34
|
+
};
|
|
35
|
+
window.addEventListener("keydown", closeOnEscape);
|
|
36
|
+
return () => {
|
|
37
|
+
window.removeEventListener("keydown", closeOnEscape);
|
|
38
|
+
returnFocusRef.current?.focus();
|
|
39
|
+
returnFocusRef.current = null;
|
|
40
|
+
};
|
|
41
|
+
}, [setOpen, visible]);
|
|
42
|
+
if (!isTablet) return null;
|
|
43
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
44
|
+
hidden: true,
|
|
45
|
+
"aria-hidden": "true",
|
|
46
|
+
"data-fd-tablet-sidebar-state": visible ? "open" : "closed"
|
|
47
|
+
}), visible && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("button", {
|
|
48
|
+
type: "button",
|
|
49
|
+
tabIndex: -1,
|
|
50
|
+
"aria-hidden": "true",
|
|
51
|
+
className: "fd-tablet-sidebar-backdrop",
|
|
52
|
+
onClick: () => setOpen(false)
|
|
53
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
54
|
+
ref: closeButtonRef,
|
|
55
|
+
type: "button",
|
|
56
|
+
"aria-label": "Close Sidebar",
|
|
57
|
+
className: "fd-tablet-sidebar-close",
|
|
58
|
+
onClick: () => setOpen(false),
|
|
59
|
+
children: /* @__PURE__ */ jsx("span", { "aria-hidden": "true" })
|
|
60
|
+
})] })] });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
//#endregion
|
|
64
|
+
export { TabletSidebarBridge };
|
|
@@ -27,8 +27,11 @@ interface TreeRoot {
|
|
|
27
27
|
interface TanstackDocsLayoutProps {
|
|
28
28
|
config: DocsConfig;
|
|
29
29
|
tree: TreeRoot;
|
|
30
|
+
/** Enables browser-adapter shell affordances that are not part of a theme preset. */
|
|
31
|
+
browserRuntime?: boolean;
|
|
30
32
|
locale?: string;
|
|
31
33
|
description?: string;
|
|
34
|
+
descriptionInBody?: boolean;
|
|
32
35
|
readingTime?: number | null;
|
|
33
36
|
lastModified?: string;
|
|
34
37
|
previousPage?: {
|
|
@@ -46,8 +49,10 @@ interface TanstackDocsLayoutProps {
|
|
|
46
49
|
declare function TanstackDocsLayout({
|
|
47
50
|
config,
|
|
48
51
|
tree,
|
|
52
|
+
browserRuntime,
|
|
49
53
|
locale,
|
|
50
54
|
description,
|
|
55
|
+
descriptionInBody,
|
|
51
56
|
readingTime,
|
|
52
57
|
lastModified,
|
|
53
58
|
previousPage,
|