@farming-labs/theme 0.1.45 → 0.1.48
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/docs-layout.mjs +36 -1
- package/dist/docs-page-client.d.mts +12 -0
- package/dist/docs-page-client.mjs +29 -9
- package/dist/pixel-border/index.mjs +2 -2
- package/dist/reading-time.mjs +39 -0
- package/dist/tanstack-layout.d.mts +2 -0
- package/dist/tanstack-layout.mjs +5 -1
- package/package.json +2 -2
- package/styles/base.css +43 -0
package/dist/docs-layout.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { withLangInUrl } from "./i18n.mjs";
|
|
|
3
3
|
import { DocsPageClient } from "./docs-page-client.mjs";
|
|
4
4
|
import { DocsAIFeatures } from "./docs-ai-features.mjs";
|
|
5
5
|
import { DocsCommandSearch } from "./docs-command-search.mjs";
|
|
6
|
+
import { resolvePageReadingTime, resolveReadingTimeOptions } from "./reading-time.mjs";
|
|
6
7
|
import { SidebarSearchWithAI } from "./sidebar-search-ai.mjs";
|
|
7
8
|
import { LocaleThemeControl } from "./locale-theme-control.mjs";
|
|
8
9
|
import fs from "node:fs";
|
|
@@ -10,7 +11,7 @@ import path from "node:path";
|
|
|
10
11
|
import matter from "gray-matter";
|
|
11
12
|
import { DocsLayout } from "fumadocs-ui/layouts/docs";
|
|
12
13
|
import { Suspense } from "react";
|
|
13
|
-
import { buildPageOpenGraph, buildPageTwitter, resolveChangelogConfig } from "@farming-labs/docs";
|
|
14
|
+
import { buildPageOpenGraph, buildPageTwitter, resolveChangelogConfig, resolveDocsAgentMdxContent } from "@farming-labs/docs";
|
|
14
15
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
15
16
|
|
|
16
17
|
//#region src/docs-layout.tsx
|
|
@@ -340,6 +341,30 @@ function buildDescriptionMap(config, ctx) {
|
|
|
340
341
|
scan(docsDir, []);
|
|
341
342
|
return map;
|
|
342
343
|
}
|
|
344
|
+
function buildReadingTimeMap(config, ctx, options) {
|
|
345
|
+
const docsDir = ctx.docsDir;
|
|
346
|
+
const map = {};
|
|
347
|
+
const excludedDirs = getExcludedDocsDirs(config, ctx);
|
|
348
|
+
function scan(dir, slugParts) {
|
|
349
|
+
if (!fs.existsSync(dir)) return;
|
|
350
|
+
if (isExcludedDir(dir, excludedDirs)) return;
|
|
351
|
+
const pagePath = path.join(dir, "page.mdx");
|
|
352
|
+
if (fs.existsSync(pagePath)) {
|
|
353
|
+
const { data, content } = matter(fs.readFileSync(pagePath, "utf-8"));
|
|
354
|
+
const minutes = resolvePageReadingTime(data, resolveDocsAgentMdxContent(content, "human"), options);
|
|
355
|
+
if (typeof minutes === "number") {
|
|
356
|
+
const url = slugParts.length === 0 ? `/${ctx.entryPath}` : `/${ctx.entryPath}/${slugParts.join("/")}`;
|
|
357
|
+
map[url] = minutes;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
for (const name of fs.readdirSync(dir)) {
|
|
361
|
+
const full = path.join(dir, name);
|
|
362
|
+
if (fs.statSync(full).isDirectory()) scan(full, [...slugParts, name]);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
scan(docsDir, []);
|
|
366
|
+
return map;
|
|
367
|
+
}
|
|
343
368
|
/**
|
|
344
369
|
* Build a Next.js Metadata object from the docs config.
|
|
345
370
|
*
|
|
@@ -556,6 +581,9 @@ function createDocsLayout(config, options) {
|
|
|
556
581
|
const lastUpdatedRaw = config.lastUpdated;
|
|
557
582
|
const lastUpdatedEnabled = lastUpdatedRaw !== false && (typeof lastUpdatedRaw !== "object" || lastUpdatedRaw.enabled !== false);
|
|
558
583
|
const lastUpdatedPosition = typeof lastUpdatedRaw === "object" ? lastUpdatedRaw.position ?? "footer" : "footer";
|
|
584
|
+
const readingTimeOptions = resolveReadingTimeOptions(config.readingTime);
|
|
585
|
+
const readingTimeEnabledByDefault = readingTimeOptions.enabled;
|
|
586
|
+
const readingTimeWordsPerMinute = readingTimeOptions.wordsPerMinute ?? 220;
|
|
559
587
|
const llmsTxtEnabled = resolveBool(config.llmsTxt);
|
|
560
588
|
const feedbackConfig = resolveFeedbackConfig(config.feedback);
|
|
561
589
|
const openDocsProviders = (typeof pageActions?.openDocs === "object" && pageActions.openDocs.providers ? pageActions.openDocs.providers : void 0)?.map((p) => ({
|
|
@@ -588,6 +616,11 @@ function createDocsLayout(config, options) {
|
|
|
588
616
|
}
|
|
589
617
|
const lastModifiedMap = buildLastModifiedMap(config, localeContext);
|
|
590
618
|
const descriptionMap = buildDescriptionMap(config, localeContext);
|
|
619
|
+
const readingTimeMap = buildReadingTimeMap(config, localeContext, {
|
|
620
|
+
enabledByDefault: readingTimeEnabledByDefault,
|
|
621
|
+
wordsPerMinute: readingTimeWordsPerMinute
|
|
622
|
+
});
|
|
623
|
+
const readingTimeEnabled = readingTimeEnabledByDefault || Object.keys(readingTimeMap).length > 0;
|
|
591
624
|
return function DocsLayoutWrapper({ children }) {
|
|
592
625
|
const tree = buildTree(config, localeContext, !!sidebarFlat);
|
|
593
626
|
const localizedTree = i18n ? localizeTreeUrls(tree, activeLocale) : tree;
|
|
@@ -680,6 +713,8 @@ function createDocsLayout(config, options) {
|
|
|
680
713
|
lastModifiedMap,
|
|
681
714
|
lastUpdatedEnabled,
|
|
682
715
|
lastUpdatedPosition,
|
|
716
|
+
readingTimeEnabled,
|
|
717
|
+
readingTimeMap,
|
|
683
718
|
llmsTxtEnabled,
|
|
684
719
|
descriptionMap,
|
|
685
720
|
feedbackEnabled: feedbackConfig.enabled,
|
|
@@ -39,6 +39,15 @@ interface DocsPageClientProps {
|
|
|
39
39
|
lastModifiedMap?: Record<string, string>;
|
|
40
40
|
/** Direct last-modified value override for the current page. */
|
|
41
41
|
lastModified?: string;
|
|
42
|
+
/** Map of pathname → reading time in minutes */
|
|
43
|
+
readingTimeMap?: Record<string, number>;
|
|
44
|
+
/** Direct reading-time override for the current page. */
|
|
45
|
+
readingTime?: number | null;
|
|
46
|
+
/**
|
|
47
|
+
* Whether path-based reading time values should render by default.
|
|
48
|
+
* Explicit `readingTime` overrides can still render when this is false.
|
|
49
|
+
*/
|
|
50
|
+
readingTimeEnabled?: boolean;
|
|
42
51
|
/** Whether to show "Last updated" at all */
|
|
43
52
|
lastUpdatedEnabled?: boolean;
|
|
44
53
|
/** Where to show the "Last updated" date: "footer" (next to Edit on GitHub) or "below-title" */
|
|
@@ -78,6 +87,9 @@ declare function DocsPageClient({
|
|
|
78
87
|
editOnGithubUrl,
|
|
79
88
|
lastModifiedMap,
|
|
80
89
|
lastModified: lastModifiedProp,
|
|
90
|
+
readingTimeMap,
|
|
91
|
+
readingTime: readingTimeProp,
|
|
92
|
+
readingTimeEnabled,
|
|
81
93
|
lastUpdatedEnabled,
|
|
82
94
|
lastUpdatedPosition,
|
|
83
95
|
llmsTxtEnabled,
|
|
@@ -97,6 +97,9 @@ function decodeHashTarget(hash) {
|
|
|
97
97
|
return value;
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
|
+
function formatReadingTimeLabel(minutes) {
|
|
101
|
+
return `${Math.max(1, Math.ceil(minutes))} min read`;
|
|
102
|
+
}
|
|
100
103
|
function escapeIdSelector(value) {
|
|
101
104
|
if (typeof CSS !== "undefined" && typeof CSS.escape === "function") return CSS.escape(value);
|
|
102
105
|
return value.replace(/["\\.#:[\]>+~(){}^$|*?=!'`\s]/g, "\\$&");
|
|
@@ -116,7 +119,7 @@ function injectTitleDecorations(node, { description, belowTitle }) {
|
|
|
116
119
|
inserted: false
|
|
117
120
|
};
|
|
118
121
|
let inserted = false;
|
|
119
|
-
const extras = [description, belowTitle].filter(Boolean);
|
|
122
|
+
const extras = Children.toArray([description, belowTitle].filter(Boolean));
|
|
120
123
|
if (extras.length === 0) return {
|
|
121
124
|
node,
|
|
122
125
|
inserted: false
|
|
@@ -131,7 +134,7 @@ function injectTitleDecorations(node, { description, belowTitle }) {
|
|
|
131
134
|
if (!isValidElement(current)) return current;
|
|
132
135
|
if (typeof current.type === "string" && current.type === "h1") {
|
|
133
136
|
inserted = true;
|
|
134
|
-
return [current, ...extras];
|
|
137
|
+
return Children.toArray([current, ...extras]);
|
|
135
138
|
}
|
|
136
139
|
const childProps = current.props ?? null;
|
|
137
140
|
if (childProps?.children === void 0) return current;
|
|
@@ -156,9 +159,9 @@ function injectTitleDecorations(node, { description, belowTitle }) {
|
|
|
156
159
|
}
|
|
157
160
|
function TitleDecorations({ description, belowTitle }) {
|
|
158
161
|
if (!description && !belowTitle) return null;
|
|
159
|
-
return /* @__PURE__ */
|
|
162
|
+
return /* @__PURE__ */ jsx(Fragment, { children: Children.toArray([description, belowTitle].filter(Boolean)) });
|
|
160
163
|
}
|
|
161
|
-
function DocsPageClient({ tocEnabled, tocStyle = "default", breadcrumbEnabled = true, changelogBasePath, entry = "docs", locale, copyMarkdown = false, openDocs = false, openDocsProviders, pageActionsPosition = "below-title", pageActionsAlignment = "left", githubUrl, contentDir, githubBranch = "main", githubDirectory, editOnGithubUrl, lastModifiedMap, lastModified: lastModifiedProp, lastUpdatedEnabled = true, lastUpdatedPosition = "footer", llmsTxtEnabled = false, descriptionMap, description, feedbackEnabled = false, feedbackQuestion, feedbackPlaceholder, feedbackPositiveLabel, feedbackNegativeLabel, feedbackSubmitLabel, feedbackOnFeedback, children }) {
|
|
164
|
+
function DocsPageClient({ tocEnabled, tocStyle = "default", breadcrumbEnabled = true, changelogBasePath, entry = "docs", locale, copyMarkdown = false, openDocs = false, openDocsProviders, pageActionsPosition = "below-title", pageActionsAlignment = "left", githubUrl, contentDir, githubBranch = "main", githubDirectory, editOnGithubUrl, lastModifiedMap, lastModified: lastModifiedProp, readingTimeMap, readingTime: readingTimeProp, readingTimeEnabled = false, lastUpdatedEnabled = true, lastUpdatedPosition = "footer", llmsTxtEnabled = false, descriptionMap, description, feedbackEnabled = false, feedbackQuestion, feedbackPlaceholder, feedbackPositiveLabel, feedbackNegativeLabel, feedbackSubmitLabel, feedbackOnFeedback, children }) {
|
|
162
165
|
const fdTocStyle = tocStyle === "directional" ? "clerk" : void 0;
|
|
163
166
|
const [toc, setToc] = useState([]);
|
|
164
167
|
const [titlePortalHost, setTitlePortalHost] = useState(null);
|
|
@@ -169,6 +172,8 @@ function DocsPageClient({ tocEnabled, tocStyle = "default", breadcrumbEnabled =
|
|
|
169
172
|
const pageDescription = description ?? descriptionMap?.[pathname.replace(/\/$/, "") || "/"];
|
|
170
173
|
const normalizedPath = (browserPath ?? pathname).replace(/\/$/, "") || "/";
|
|
171
174
|
const isChangelogRoute = !!(changelogBasePath && (normalizedPath === changelogBasePath || normalizedPath.startsWith(`${changelogBasePath}/`)));
|
|
175
|
+
const matchedReadingTime = readingTimeMap?.[normalizedPath];
|
|
176
|
+
const resolvedReadingTime = !isChangelogRoute ? readingTimeProp !== void 0 ? readingTimeProp : readingTimeEnabled ? matchedReadingTime : void 0 : void 0;
|
|
172
177
|
const effectiveTocEnabled = isChangelogRoute ? false : tocEnabled;
|
|
173
178
|
const effectiveBreadcrumbEnabled = isChangelogRoute ? false : breadcrumbEnabled;
|
|
174
179
|
useEffect(() => {
|
|
@@ -246,11 +251,24 @@ function DocsPageClient({ tocEnabled, tocStyle = "default", breadcrumbEnabled =
|
|
|
246
251
|
const showLastUpdatedBelowTitle = !!lastModified && lastUpdatedPosition === "below-title";
|
|
247
252
|
const showLastUpdatedInFooter = !!lastModified && lastUpdatedPosition === "footer";
|
|
248
253
|
const showFooter = !isChangelogRoute && (!!githubFileUrl || showLastUpdatedInFooter || llmsTxtEnabled);
|
|
254
|
+
const readingTimeBlock = typeof resolvedReadingTime === "number" ? /* @__PURE__ */ jsxs("div", {
|
|
255
|
+
className: "fd-page-meta not-prose",
|
|
256
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
257
|
+
className: "fd-page-meta-dot",
|
|
258
|
+
"aria-hidden": "true",
|
|
259
|
+
children: "·"
|
|
260
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
261
|
+
className: "fd-page-meta-item",
|
|
262
|
+
children: formatReadingTimeLabel(resolvedReadingTime)
|
|
263
|
+
})]
|
|
264
|
+
}) : void 0;
|
|
249
265
|
const titleDescription = pageDescription ? /* @__PURE__ */ jsx("p", {
|
|
250
266
|
className: "fd-page-description",
|
|
251
267
|
children: pageDescription
|
|
252
268
|
}) : void 0;
|
|
253
|
-
const
|
|
269
|
+
const showReadingTimeAboveTitle = !!readingTimeBlock && showActionsAboveTitle;
|
|
270
|
+
const showReadingTimeBelowTitle = !!readingTimeBlock && !showReadingTimeAboveTitle && (showActionsBelowTitle || showLastUpdatedBelowTitle || !showActions && pageActionsPosition === "below-title");
|
|
271
|
+
const belowTitleBlock = showLastUpdatedBelowTitle || showActionsBelowTitle || showReadingTimeBelowTitle ? /* @__PURE__ */ jsxs("div", {
|
|
254
272
|
className: "fd-below-title-block not-prose",
|
|
255
273
|
children: [
|
|
256
274
|
showLastUpdatedBelowTitle && /* @__PURE__ */ jsxs("p", {
|
|
@@ -268,7 +286,8 @@ function DocsPageClient({ tocEnabled, tocStyle = "default", breadcrumbEnabled =
|
|
|
268
286
|
alignment: pageActionsAlignment,
|
|
269
287
|
githubFileUrl
|
|
270
288
|
})
|
|
271
|
-
})
|
|
289
|
+
}),
|
|
290
|
+
showReadingTimeBelowTitle && readingTimeBlock
|
|
272
291
|
]
|
|
273
292
|
}) : void 0;
|
|
274
293
|
const { node: decoratedChildren, inserted: titleDecorationsInserted } = injectTitleDecorations(children, {
|
|
@@ -318,9 +337,9 @@ function DocsPageClient({ tocEnabled, tocStyle = "default", breadcrumbEnabled =
|
|
|
318
337
|
entry,
|
|
319
338
|
locale: activeLocale
|
|
320
339
|
}),
|
|
321
|
-
showActionsAboveTitle && /* @__PURE__ */
|
|
340
|
+
showActionsAboveTitle && /* @__PURE__ */ jsxs("div", {
|
|
322
341
|
className: "fd-below-title-block not-prose",
|
|
323
|
-
children: /* @__PURE__ */ jsx("div", {
|
|
342
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
324
343
|
className: "fd-actions-portal",
|
|
325
344
|
"data-actions-alignment": pageActionsAlignment,
|
|
326
345
|
children: /* @__PURE__ */ jsx(PageActions, {
|
|
@@ -330,8 +349,9 @@ function DocsPageClient({ tocEnabled, tocStyle = "default", breadcrumbEnabled =
|
|
|
330
349
|
alignment: pageActionsAlignment,
|
|
331
350
|
githubFileUrl
|
|
332
351
|
})
|
|
333
|
-
})
|
|
352
|
+
}), readingTimeBlock]
|
|
334
353
|
}),
|
|
354
|
+
!showReadingTimeAboveTitle && !showReadingTimeBelowTitle ? readingTimeBlock : null,
|
|
335
355
|
/* @__PURE__ */ jsxs(DocsBody, {
|
|
336
356
|
style: {
|
|
337
357
|
display: "flex",
|
|
@@ -24,8 +24,8 @@ const PixelBorderUIDefaults = {
|
|
|
24
24
|
},
|
|
25
25
|
typography: { font: {
|
|
26
26
|
style: {
|
|
27
|
-
sans: "var(--font-
|
|
28
|
-
mono: "var(--font-
|
|
27
|
+
sans: "var(--font-sans, system-ui, -apple-system, sans-serif)",
|
|
28
|
+
mono: "var(--font-mono, ui-monospace, monospace)"
|
|
29
29
|
},
|
|
30
30
|
h1: {
|
|
31
31
|
size: "2.25rem",
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import matter from "gray-matter";
|
|
2
|
+
|
|
3
|
+
//#region src/reading-time.ts
|
|
4
|
+
function hasExplicitReadingTime(frontmatter) {
|
|
5
|
+
return Object.prototype.hasOwnProperty.call(frontmatter ?? {}, "readingTime");
|
|
6
|
+
}
|
|
7
|
+
function normalizeWordsPerMinute(wordsPerMinute) {
|
|
8
|
+
if (typeof wordsPerMinute !== "number" || !Number.isFinite(wordsPerMinute)) return 220;
|
|
9
|
+
return Math.max(1, Math.floor(wordsPerMinute));
|
|
10
|
+
}
|
|
11
|
+
function stripNonReadingContent(content) {
|
|
12
|
+
return content.replace(/(`{3,})[^\n]*\n[\s\S]*?\1/g, " ").replace(/(~{3,})[^\n]*\n[\s\S]*?\1/g, " ").replace(/`[^`\n]+`/g, " ").replace(/!\[[^\]]*\]\([^)]+\)/g, " ").replace(/\[([^\]]+)\]\([^)]+\)/g, " $1 ").replace(/<[^>]+>/g, " ").replace(/\{[^{}]*\}/g, " ").replace(/https?:\/\/\S+/g, " ").replace(/[#>*_~|]/g, " ");
|
|
13
|
+
}
|
|
14
|
+
function estimateReadingTimeMinutes(content, wordsPerMinute) {
|
|
15
|
+
const wordCount = stripNonReadingContent(content).match(/\b[\p{L}\p{N}][\p{L}\p{N}'’-]*\b/gu)?.length ?? 0;
|
|
16
|
+
return Math.max(1, Math.ceil(wordCount / normalizeWordsPerMinute(wordsPerMinute)));
|
|
17
|
+
}
|
|
18
|
+
function resolveReadingTimeOptions(readingTime) {
|
|
19
|
+
if (readingTime === true) return { enabled: true };
|
|
20
|
+
if (readingTime === false || readingTime === void 0 || readingTime === null) return { enabled: false };
|
|
21
|
+
if (typeof readingTime !== "object") return { enabled: false };
|
|
22
|
+
return {
|
|
23
|
+
enabled: readingTime.enabled !== false,
|
|
24
|
+
wordsPerMinute: typeof readingTime.wordsPerMinute === "number" && Number.isFinite(readingTime.wordsPerMinute) ? readingTime.wordsPerMinute : void 0
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function resolveReadingTimeFromContent(frontmatter, content, wordsPerMinute) {
|
|
28
|
+
const pageData = frontmatter ?? {};
|
|
29
|
+
if (pageData.readingTime === false) return null;
|
|
30
|
+
if (typeof pageData.readingTime === "number" && Number.isFinite(pageData.readingTime)) return Math.max(1, Math.ceil(pageData.readingTime));
|
|
31
|
+
return estimateReadingTimeMinutes(content, wordsPerMinute);
|
|
32
|
+
}
|
|
33
|
+
function resolvePageReadingTime(frontmatter, content, options) {
|
|
34
|
+
if (!(options?.enabledByDefault ?? false) && !hasExplicitReadingTime(frontmatter)) return;
|
|
35
|
+
return resolveReadingTimeFromContent(frontmatter, content, options?.wordsPerMinute);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
//#endregion
|
|
39
|
+
export { resolvePageReadingTime, resolveReadingTimeOptions };
|
|
@@ -28,6 +28,7 @@ interface TanstackDocsLayoutProps {
|
|
|
28
28
|
tree: TreeRoot;
|
|
29
29
|
locale?: string;
|
|
30
30
|
description?: string;
|
|
31
|
+
readingTime?: number | null;
|
|
31
32
|
lastModified?: string;
|
|
32
33
|
editOnGithubUrl?: string;
|
|
33
34
|
children: ReactNode;
|
|
@@ -37,6 +38,7 @@ declare function TanstackDocsLayout({
|
|
|
37
38
|
tree,
|
|
38
39
|
locale,
|
|
39
40
|
description,
|
|
41
|
+
readingTime,
|
|
40
42
|
lastModified,
|
|
41
43
|
editOnGithubUrl,
|
|
42
44
|
children
|
package/dist/tanstack-layout.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { withLangInUrl } from "./i18n.mjs";
|
|
|
2
2
|
import { DocsPageClient } from "./docs-page-client.mjs";
|
|
3
3
|
import { DocsAIFeatures } from "./docs-ai-features.mjs";
|
|
4
4
|
import { DocsCommandSearch } from "./docs-command-search.mjs";
|
|
5
|
+
import { resolveReadingTimeOptions } from "./reading-time.mjs";
|
|
5
6
|
import { SidebarSearchWithAI } from "./sidebar-search-ai.mjs";
|
|
6
7
|
import { LocaleThemeControl } from "./locale-theme-control.mjs";
|
|
7
8
|
import { DocsLayout } from "fumadocs-ui/layouts/docs";
|
|
@@ -205,7 +206,7 @@ function resolveFeedbackConfig(feedback) {
|
|
|
205
206
|
function ForcedThemeScript({ theme }) {
|
|
206
207
|
return /* @__PURE__ */ jsx("script", { dangerouslySetInnerHTML: { __html: `document.documentElement.classList.remove('light','dark');document.documentElement.classList.add('${theme === "light" || theme === "dark" ? theme : "light"}');` } });
|
|
207
208
|
}
|
|
208
|
-
function TanstackDocsLayout({ config, tree, locale, description, lastModified, editOnGithubUrl, children }) {
|
|
209
|
+
function TanstackDocsLayout({ config, tree, locale, description, readingTime, lastModified, editOnGithubUrl, children }) {
|
|
209
210
|
const tocConfig = config.theme?.ui?.layout?.toc;
|
|
210
211
|
const tocEnabled = tocConfig?.enabled !== false;
|
|
211
212
|
const tocStyle = tocConfig?.style;
|
|
@@ -232,6 +233,7 @@ function TanstackDocsLayout({ config, tree, locale, description, lastModified, e
|
|
|
232
233
|
const lastUpdatedRaw = config.lastUpdated;
|
|
233
234
|
const lastUpdatedEnabled = lastUpdatedRaw !== false && (typeof lastUpdatedRaw !== "object" || lastUpdatedRaw.enabled !== false);
|
|
234
235
|
const lastUpdatedPosition = typeof lastUpdatedRaw === "object" ? lastUpdatedRaw.position ?? "footer" : "footer";
|
|
236
|
+
const readingTimeEnabled = resolveReadingTimeOptions(config.readingTime).enabled;
|
|
235
237
|
const llmsTxtEnabled = resolveBool(config.llmsTxt);
|
|
236
238
|
const feedbackConfig = resolveFeedbackConfig(config.feedback);
|
|
237
239
|
const staticExport = !!config.staticExport;
|
|
@@ -339,6 +341,8 @@ function TanstackDocsLayout({ config, tree, locale, description, lastModified, e
|
|
|
339
341
|
lastUpdatedEnabled,
|
|
340
342
|
lastUpdatedPosition,
|
|
341
343
|
lastModified,
|
|
344
|
+
readingTimeEnabled,
|
|
345
|
+
readingTime: typeof readingTime === "number" ? readingTime : void 0,
|
|
342
346
|
llmsTxtEnabled,
|
|
343
347
|
description,
|
|
344
348
|
feedbackEnabled: feedbackConfig.enabled,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@farming-labs/theme",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.48",
|
|
4
4
|
"description": "Theme package for @farming-labs/docs — layout, provider, MDX components, and styles",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"docs",
|
|
@@ -133,7 +133,7 @@
|
|
|
133
133
|
"tsdown": "^0.20.3",
|
|
134
134
|
"typescript": "^5.9.3",
|
|
135
135
|
"vitest": "^3.2.4",
|
|
136
|
-
"@farming-labs/docs": "0.1.
|
|
136
|
+
"@farming-labs/docs": "0.1.48"
|
|
137
137
|
},
|
|
138
138
|
"peerDependencies": {
|
|
139
139
|
"@farming-labs/docs": ">=0.0.1",
|
package/styles/base.css
CHANGED
|
@@ -171,6 +171,49 @@ figure.shiki:has(figcaption) figcaption {
|
|
|
171
171
|
|
|
172
172
|
/* ─── Page description (frontmatter) ─────────────────────────────────── */
|
|
173
173
|
|
|
174
|
+
.fd-page-meta {
|
|
175
|
+
display: flex;
|
|
176
|
+
flex-wrap: wrap;
|
|
177
|
+
align-items: center;
|
|
178
|
+
gap: 0.375rem;
|
|
179
|
+
margin-bottom: 0.75rem;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.fd-page-meta-dot {
|
|
183
|
+
display: inline-flex;
|
|
184
|
+
align-items: center;
|
|
185
|
+
justify-content: center;
|
|
186
|
+
font-family: var(
|
|
187
|
+
--fd-font-mono,
|
|
188
|
+
ui-monospace,
|
|
189
|
+
SFMono-Regular,
|
|
190
|
+
"SF Mono",
|
|
191
|
+
Menlo,
|
|
192
|
+
Consolas,
|
|
193
|
+
monospace
|
|
194
|
+
);
|
|
195
|
+
font-size: 0.8rem;
|
|
196
|
+
line-height: 1;
|
|
197
|
+
color: color-mix(in srgb, var(--color-fd-muted-foreground) 78%, transparent);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.fd-page-meta-item {
|
|
201
|
+
font-family: var(
|
|
202
|
+
--fd-font-mono,
|
|
203
|
+
ui-monospace,
|
|
204
|
+
SFMono-Regular,
|
|
205
|
+
"SF Mono",
|
|
206
|
+
Menlo,
|
|
207
|
+
Consolas,
|
|
208
|
+
monospace
|
|
209
|
+
);
|
|
210
|
+
font-size: 0.6875rem;
|
|
211
|
+
font-weight: 500;
|
|
212
|
+
letter-spacing: 0;
|
|
213
|
+
text-transform: uppercase;
|
|
214
|
+
color: var(--color-fd-muted-foreground);
|
|
215
|
+
}
|
|
216
|
+
|
|
174
217
|
.fd-page-description {
|
|
175
218
|
margin-bottom: 1rem;
|
|
176
219
|
font-size: 1.125rem;
|