@farming-labs/theme 0.1.45 → 0.1.47
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 +32 -1
- package/dist/docs-page-client.d.mts +9 -0
- package/dist/docs-page-client.mjs +28 -9
- package/dist/pixel-border/index.mjs +2 -2
- package/dist/reading-time.mjs +32 -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 { resolveReadingTimeFromContent, 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, wordsPerMinute) {
|
|
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 = resolveReadingTimeFromContent(data, resolveDocsAgentMdxContent(content, "human"), wordsPerMinute);
|
|
355
|
+
if (minutes !== null) {
|
|
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 readingTimeEnabled = 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,7 @@ function createDocsLayout(config, options) {
|
|
|
588
616
|
}
|
|
589
617
|
const lastModifiedMap = buildLastModifiedMap(config, localeContext);
|
|
590
618
|
const descriptionMap = buildDescriptionMap(config, localeContext);
|
|
619
|
+
const readingTimeMap = readingTimeEnabled ? buildReadingTimeMap(config, localeContext, readingTimeWordsPerMinute) : {};
|
|
591
620
|
return function DocsLayoutWrapper({ children }) {
|
|
592
621
|
const tree = buildTree(config, localeContext, !!sidebarFlat);
|
|
593
622
|
const localizedTree = i18n ? localizeTreeUrls(tree, activeLocale) : tree;
|
|
@@ -680,6 +709,8 @@ function createDocsLayout(config, options) {
|
|
|
680
709
|
lastModifiedMap,
|
|
681
710
|
lastUpdatedEnabled,
|
|
682
711
|
lastUpdatedPosition,
|
|
712
|
+
readingTimeEnabled,
|
|
713
|
+
readingTimeMap,
|
|
683
714
|
llmsTxtEnabled,
|
|
684
715
|
descriptionMap,
|
|
685
716
|
feedbackEnabled: feedbackConfig.enabled,
|
|
@@ -39,6 +39,12 @@ 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;
|
|
46
|
+
/** Whether to show estimated reading time at the top of the page. */
|
|
47
|
+
readingTimeEnabled?: boolean;
|
|
42
48
|
/** Whether to show "Last updated" at all */
|
|
43
49
|
lastUpdatedEnabled?: boolean;
|
|
44
50
|
/** Where to show the "Last updated" date: "footer" (next to Edit on GitHub) or "below-title" */
|
|
@@ -78,6 +84,9 @@ declare function DocsPageClient({
|
|
|
78
84
|
editOnGithubUrl,
|
|
79
85
|
lastModifiedMap,
|
|
80
86
|
lastModified: lastModifiedProp,
|
|
87
|
+
readingTimeMap,
|
|
88
|
+
readingTime: readingTimeProp,
|
|
89
|
+
readingTimeEnabled,
|
|
81
90
|
lastUpdatedEnabled,
|
|
82
91
|
lastUpdatedPosition,
|
|
83
92
|
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,7 @@ 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 resolvedReadingTime = !isChangelogRoute && readingTimeEnabled ? readingTimeProp ?? readingTimeMap?.[normalizedPath] : void 0;
|
|
172
176
|
const effectiveTocEnabled = isChangelogRoute ? false : tocEnabled;
|
|
173
177
|
const effectiveBreadcrumbEnabled = isChangelogRoute ? false : breadcrumbEnabled;
|
|
174
178
|
useEffect(() => {
|
|
@@ -246,11 +250,24 @@ function DocsPageClient({ tocEnabled, tocStyle = "default", breadcrumbEnabled =
|
|
|
246
250
|
const showLastUpdatedBelowTitle = !!lastModified && lastUpdatedPosition === "below-title";
|
|
247
251
|
const showLastUpdatedInFooter = !!lastModified && lastUpdatedPosition === "footer";
|
|
248
252
|
const showFooter = !isChangelogRoute && (!!githubFileUrl || showLastUpdatedInFooter || llmsTxtEnabled);
|
|
253
|
+
const readingTimeBlock = typeof resolvedReadingTime === "number" ? /* @__PURE__ */ jsxs("div", {
|
|
254
|
+
className: "fd-page-meta not-prose",
|
|
255
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
256
|
+
className: "fd-page-meta-dot",
|
|
257
|
+
"aria-hidden": "true",
|
|
258
|
+
children: "·"
|
|
259
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
260
|
+
className: "fd-page-meta-item",
|
|
261
|
+
children: formatReadingTimeLabel(resolvedReadingTime)
|
|
262
|
+
})]
|
|
263
|
+
}) : void 0;
|
|
249
264
|
const titleDescription = pageDescription ? /* @__PURE__ */ jsx("p", {
|
|
250
265
|
className: "fd-page-description",
|
|
251
266
|
children: pageDescription
|
|
252
267
|
}) : void 0;
|
|
253
|
-
const
|
|
268
|
+
const showReadingTimeAboveTitle = !!readingTimeBlock && showActionsAboveTitle;
|
|
269
|
+
const showReadingTimeBelowTitle = !!readingTimeBlock && !showReadingTimeAboveTitle && (showActionsBelowTitle || showLastUpdatedBelowTitle || !showActions && pageActionsPosition === "below-title");
|
|
270
|
+
const belowTitleBlock = showLastUpdatedBelowTitle || showActionsBelowTitle || showReadingTimeBelowTitle ? /* @__PURE__ */ jsxs("div", {
|
|
254
271
|
className: "fd-below-title-block not-prose",
|
|
255
272
|
children: [
|
|
256
273
|
showLastUpdatedBelowTitle && /* @__PURE__ */ jsxs("p", {
|
|
@@ -268,7 +285,8 @@ function DocsPageClient({ tocEnabled, tocStyle = "default", breadcrumbEnabled =
|
|
|
268
285
|
alignment: pageActionsAlignment,
|
|
269
286
|
githubFileUrl
|
|
270
287
|
})
|
|
271
|
-
})
|
|
288
|
+
}),
|
|
289
|
+
showReadingTimeBelowTitle && readingTimeBlock
|
|
272
290
|
]
|
|
273
291
|
}) : void 0;
|
|
274
292
|
const { node: decoratedChildren, inserted: titleDecorationsInserted } = injectTitleDecorations(children, {
|
|
@@ -318,9 +336,9 @@ function DocsPageClient({ tocEnabled, tocStyle = "default", breadcrumbEnabled =
|
|
|
318
336
|
entry,
|
|
319
337
|
locale: activeLocale
|
|
320
338
|
}),
|
|
321
|
-
showActionsAboveTitle && /* @__PURE__ */
|
|
339
|
+
showActionsAboveTitle && /* @__PURE__ */ jsxs("div", {
|
|
322
340
|
className: "fd-below-title-block not-prose",
|
|
323
|
-
children: /* @__PURE__ */ jsx("div", {
|
|
341
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
324
342
|
className: "fd-actions-portal",
|
|
325
343
|
"data-actions-alignment": pageActionsAlignment,
|
|
326
344
|
children: /* @__PURE__ */ jsx(PageActions, {
|
|
@@ -330,8 +348,9 @@ function DocsPageClient({ tocEnabled, tocStyle = "default", breadcrumbEnabled =
|
|
|
330
348
|
alignment: pageActionsAlignment,
|
|
331
349
|
githubFileUrl
|
|
332
350
|
})
|
|
333
|
-
})
|
|
351
|
+
}), readingTimeBlock]
|
|
334
352
|
}),
|
|
353
|
+
!showReadingTimeAboveTitle && !showReadingTimeBelowTitle ? readingTimeBlock : null,
|
|
335
354
|
/* @__PURE__ */ jsxs(DocsBody, {
|
|
336
355
|
style: {
|
|
337
356
|
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,32 @@
|
|
|
1
|
+
import matter from "gray-matter";
|
|
2
|
+
|
|
3
|
+
//#region src/reading-time.ts
|
|
4
|
+
function normalizeWordsPerMinute(wordsPerMinute) {
|
|
5
|
+
if (typeof wordsPerMinute !== "number" || !Number.isFinite(wordsPerMinute)) return 220;
|
|
6
|
+
return Math.max(1, Math.floor(wordsPerMinute));
|
|
7
|
+
}
|
|
8
|
+
function stripNonReadingContent(content) {
|
|
9
|
+
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, " ");
|
|
10
|
+
}
|
|
11
|
+
function estimateReadingTimeMinutes(content, wordsPerMinute) {
|
|
12
|
+
const wordCount = stripNonReadingContent(content).match(/\b[\p{L}\p{N}][\p{L}\p{N}'’-]*\b/gu)?.length ?? 0;
|
|
13
|
+
return Math.max(1, Math.ceil(wordCount / normalizeWordsPerMinute(wordsPerMinute)));
|
|
14
|
+
}
|
|
15
|
+
function resolveReadingTimeOptions(readingTime) {
|
|
16
|
+
if (readingTime === true) return { enabled: true };
|
|
17
|
+
if (readingTime === false || readingTime === void 0 || readingTime === null) return { enabled: false };
|
|
18
|
+
if (typeof readingTime !== "object") return { enabled: false };
|
|
19
|
+
return {
|
|
20
|
+
enabled: readingTime.enabled !== false,
|
|
21
|
+
wordsPerMinute: typeof readingTime.wordsPerMinute === "number" && Number.isFinite(readingTime.wordsPerMinute) ? readingTime.wordsPerMinute : void 0
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function resolveReadingTimeFromContent(frontmatter, content, wordsPerMinute) {
|
|
25
|
+
const pageData = frontmatter ?? {};
|
|
26
|
+
if (pageData.readingTime === false) return null;
|
|
27
|
+
if (typeof pageData.readingTime === "number" && Number.isFinite(pageData.readingTime)) return Math.max(1, Math.ceil(pageData.readingTime));
|
|
28
|
+
return estimateReadingTimeMinutes(content, wordsPerMinute);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
//#endregion
|
|
32
|
+
export { resolveReadingTimeFromContent, 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.47",
|
|
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.47"
|
|
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;
|