@farming-labs/theme 0.1.9 → 0.1.11
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/ai-search-dialog.mjs +3 -1
- package/dist/docs-api.mjs +1 -1
- package/dist/docs-layout.mjs +3 -27
- package/dist/serialize-icon.mjs +8 -2
- package/package.json +2 -2
- package/styles/base.css +26 -0
- package/styles/colorful.css +33 -0
|
@@ -22,6 +22,8 @@ function buildCodeBlock(lang, code) {
|
|
|
22
22
|
return `<div class="fd-ai-code-block"><div class="fd-ai-code-header">${lang ? `<div class="fd-ai-code-lang">${escapeHtml(lang)}</div>` : ""}<button class="fd-ai-code-copy" onclick="(function(btn){var code=btn.closest('.fd-ai-code-block').querySelector('code').textContent;navigator.clipboard.writeText(code).then(function(){btn.textContent='Copied!';setTimeout(function(){btn.textContent='Copy'},1500)})})(this)">Copy</button></div><pre><code>${highlighted}</code></pre></div>`;
|
|
23
23
|
}
|
|
24
24
|
function renderMarkdown(text) {
|
|
25
|
+
const codeBlockTokenBoundary = String.fromCharCode(0);
|
|
26
|
+
const codeBlockTokenPattern = new RegExp(`${codeBlockTokenBoundary}CB(\\d+)${codeBlockTokenBoundary}`, "g");
|
|
25
27
|
const codeBlocks = [];
|
|
26
28
|
let processed = text.replace(/```(\w*)\n([\s\S]*?)```/g, (_match, lang, code) => {
|
|
27
29
|
codeBlocks.push(buildCodeBlock(lang, code));
|
|
@@ -51,7 +53,7 @@ function renderMarkdown(text) {
|
|
|
51
53
|
}
|
|
52
54
|
let result = output.join("\n");
|
|
53
55
|
result = result.replace(/`([^`]+)`/g, "<code>$1</code>").replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>").replace(/(?<!\*)\*([^*]+)\*(?!\*)/g, "<em>$1</em>").replace(/\[([^\]]+)\]\(([^)]+)\)/g, "<a href=\"$2\">$1</a>").replace(/^### (.*$)/gm, "<h4>$1</h4>").replace(/^## (.*$)/gm, "<h3>$1</h3>").replace(/^# (.*$)/gm, "<h2>$1</h2>").replace(/^[-*] (.*$)/gm, "<div style=\"display:flex;gap:8px;padding:2px 0\"><span style=\"opacity:0.5\">•</span><span>$1</span></div>").replace(/^(\d+)\. (.*$)/gm, "<div style=\"display:flex;gap:8px;padding:2px 0\"><span style=\"opacity:0.5\">$1.</span><span>$2</span></div>").replace(/\n\n/g, "<div style=\"height:8px\"></div>").replace(/\n/g, "<br>");
|
|
54
|
-
result = result.replace(
|
|
56
|
+
result = result.replace(codeBlockTokenPattern, (_m, idx) => codeBlocks[Number(idx)]);
|
|
55
57
|
return result;
|
|
56
58
|
}
|
|
57
59
|
function escapeHtml(s) {
|
package/dist/docs-api.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { withLangInUrl } from "./i18n.mjs";
|
|
2
2
|
import { getNextAppDir } from "./get-app-dir.mjs";
|
|
3
|
-
import { performDocsSearch, resolveDocsI18n, resolveDocsLocale, resolveSearchRequestConfig } from "@farming-labs/docs";
|
|
4
3
|
import fs from "node:fs";
|
|
5
4
|
import path from "node:path";
|
|
6
5
|
import matter from "gray-matter";
|
|
6
|
+
import { performDocsSearch, resolveDocsI18n, resolveDocsLocale, resolveSearchRequestConfig } from "@farming-labs/docs";
|
|
7
7
|
import { createDocsMcpHttpHandler, createFilesystemDocsMcpSource } from "@farming-labs/docs/server";
|
|
8
8
|
|
|
9
9
|
//#region src/docs-api.ts
|
package/dist/docs-layout.mjs
CHANGED
|
@@ -5,29 +5,16 @@ import { DocsAIFeatures } from "./docs-ai-features.mjs";
|
|
|
5
5
|
import { DocsCommandSearch } from "./docs-command-search.mjs";
|
|
6
6
|
import { SidebarSearchWithAI } from "./sidebar-search-ai.mjs";
|
|
7
7
|
import { LocaleThemeControl } from "./locale-theme-control.mjs";
|
|
8
|
+
import fs from "node:fs";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import matter from "gray-matter";
|
|
8
11
|
import { DocsLayout } from "fumadocs-ui/layouts/docs";
|
|
9
12
|
import { Suspense } from "react";
|
|
10
13
|
import { buildPageOpenGraph, buildPageTwitter } from "@farming-labs/docs";
|
|
11
14
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12
15
|
|
|
13
16
|
//#region src/docs-layout.tsx
|
|
14
|
-
function runtimeRequire(id) {
|
|
15
|
-
const requireFn = eval("require");
|
|
16
|
-
return requireFn(id);
|
|
17
|
-
}
|
|
18
|
-
function getNodeFs() {
|
|
19
|
-
return runtimeRequire("node:fs");
|
|
20
|
-
}
|
|
21
|
-
function getNodePath() {
|
|
22
|
-
return runtimeRequire("node:path");
|
|
23
|
-
}
|
|
24
|
-
function getMatter() {
|
|
25
|
-
const module = runtimeRequire("gray-matter");
|
|
26
|
-
return module.default ?? module;
|
|
27
|
-
}
|
|
28
17
|
function getNextAppDir(root) {
|
|
29
|
-
const fs = getNodeFs();
|
|
30
|
-
const path = getNodePath();
|
|
31
18
|
if (fs.existsSync(path.join(root, "src", "app"))) return "src/app";
|
|
32
19
|
return "app";
|
|
33
20
|
}
|
|
@@ -38,8 +25,6 @@ function resolveIcon(iconKey, registry) {
|
|
|
38
25
|
}
|
|
39
26
|
/** Read frontmatter from a page.mdx file. */
|
|
40
27
|
function readFrontmatter(filePath) {
|
|
41
|
-
const fs = getNodeFs();
|
|
42
|
-
const matter = getMatter();
|
|
43
28
|
try {
|
|
44
29
|
const { data } = matter(fs.readFileSync(filePath, "utf-8"));
|
|
45
30
|
return data;
|
|
@@ -49,8 +34,6 @@ function readFrontmatter(filePath) {
|
|
|
49
34
|
}
|
|
50
35
|
/** Check if a directory has any subdirectories that contain page.mdx. */
|
|
51
36
|
function hasChildPages(dir) {
|
|
52
|
-
const fs = getNodeFs();
|
|
53
|
-
const path = getNodePath();
|
|
54
37
|
if (!fs.existsSync(dir)) return false;
|
|
55
38
|
for (const name of fs.readdirSync(dir)) {
|
|
56
39
|
const full = path.join(dir, name);
|
|
@@ -74,7 +57,6 @@ function resolveDocsLocaleContext(config, locale) {
|
|
|
74
57
|
const entryBase = config.entry ?? "docs";
|
|
75
58
|
const i18n = resolveDocsI18nConfig(getDocsI18n(config));
|
|
76
59
|
const contentDir = config.contentDir;
|
|
77
|
-
const path = getNodePath();
|
|
78
60
|
function resolveContentDir(localeValue) {
|
|
79
61
|
if (!contentDir) {
|
|
80
62
|
const appDir = getNextAppDir(process.cwd());
|
|
@@ -99,8 +81,6 @@ function buildTree(config, ctx, flat = false) {
|
|
|
99
81
|
const icons = config.icons;
|
|
100
82
|
const ordering = config.ordering;
|
|
101
83
|
const rootChildren = [];
|
|
102
|
-
const fs = getNodeFs();
|
|
103
|
-
const path = getNodePath();
|
|
104
84
|
if (fs.existsSync(path.join(docsDir, "page.mdx"))) {
|
|
105
85
|
const data = readFrontmatter(path.join(docsDir, "page.mdx"));
|
|
106
86
|
rootChildren.push({
|
|
@@ -227,8 +207,6 @@ function localizeTreeUrls(tree, locale) {
|
|
|
227
207
|
function buildLastModifiedMap(ctx) {
|
|
228
208
|
const docsDir = ctx.docsDir;
|
|
229
209
|
const map = {};
|
|
230
|
-
const fs = getNodeFs();
|
|
231
|
-
const path = getNodePath();
|
|
232
210
|
function formatDate(date) {
|
|
233
211
|
return date.toLocaleDateString("en-US", {
|
|
234
212
|
year: "numeric",
|
|
@@ -258,8 +236,6 @@ function buildLastModifiedMap(ctx) {
|
|
|
258
236
|
function buildDescriptionMap(ctx) {
|
|
259
237
|
const docsDir = ctx.docsDir;
|
|
260
238
|
const map = {};
|
|
261
|
-
const fs = getNodeFs();
|
|
262
|
-
const path = getNodePath();
|
|
263
239
|
function scan(dir, slugParts) {
|
|
264
240
|
if (!fs.existsSync(dir)) return;
|
|
265
241
|
const pagePath = path.join(dir, "page.mdx");
|
package/dist/serialize-icon.mjs
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
|
|
1
3
|
//#region src/serialize-icon.ts
|
|
4
|
+
/**
|
|
5
|
+
* Server-only helper to convert a ReactNode icon into an HTML string
|
|
6
|
+
* so it can be safely serialized across the server→client boundary.
|
|
7
|
+
*/
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
2
9
|
function serializeIcon(icon) {
|
|
3
10
|
if (!icon) return void 0;
|
|
4
11
|
if (typeof icon === "string") return icon;
|
|
5
12
|
try {
|
|
6
|
-
const
|
|
7
|
-
const { renderToStaticMarkup } = runtimeRequire("react-dom/server");
|
|
13
|
+
const { renderToStaticMarkup } = require("react-dom/server");
|
|
8
14
|
return renderToStaticMarkup(icon);
|
|
9
15
|
} catch {
|
|
10
16
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@farming-labs/theme",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
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.11"
|
|
137
137
|
},
|
|
138
138
|
"peerDependencies": {
|
|
139
139
|
"@farming-labs/docs": ">=0.0.1",
|
package/styles/base.css
CHANGED
|
@@ -650,3 +650,29 @@ figure.shiki:hover > div:first-child button {
|
|
|
650
650
|
width: 16px;
|
|
651
651
|
height: 16px;
|
|
652
652
|
}
|
|
653
|
+
|
|
654
|
+
/* ─── Logical inset compatibility ─────────────────────────────────── */
|
|
655
|
+
|
|
656
|
+
/* Tailwind 4 currently emits `start-*` utilities for some imported Fumadocs
|
|
657
|
+
* logical inset classes, while the rendered docs layout still uses `inset-s-*`.
|
|
658
|
+
* Add the small alias set the layout relies on so sidebar connectors, active
|
|
659
|
+
* markers, and sticky sidebar placement keep working across themes. */
|
|
660
|
+
.inset-s-0 {
|
|
661
|
+
inset-inline-start: 0;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
.inset-s-4 {
|
|
665
|
+
inset-inline-start: calc(var(--spacing) * 4);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
.inset-e-0 {
|
|
669
|
+
inset-inline-end: 0;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
.before\:inset-s-2\.5::before {
|
|
673
|
+
inset-inline-start: calc(var(--spacing) * 2.5);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
.data-\[active\=true\]\:before\:inset-s-2\.5[data-active="true"]::before {
|
|
677
|
+
inset-inline-start: calc(var(--spacing) * 2.5);
|
|
678
|
+
}
|
package/styles/colorful.css
CHANGED
|
@@ -17,6 +17,12 @@
|
|
|
17
17
|
--color-fd-ring: hsl(45, 90%, 55%);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
.fd-sidebar {
|
|
21
|
+
--color-fd-muted: hsl(0 0% 96%);
|
|
22
|
+
--color-fd-secondary: hsl(0 0% 97%);
|
|
23
|
+
--color-fd-muted-foreground: hsl(0 0% 45%);
|
|
24
|
+
}
|
|
25
|
+
|
|
20
26
|
/* ─── Description under title ──────────────────────────────────────── */
|
|
21
27
|
|
|
22
28
|
.fd-page-description {
|
|
@@ -397,3 +403,30 @@
|
|
|
397
403
|
.fd-feedback-status[data-status="success"] {
|
|
398
404
|
color: var(--color-fd-primary);
|
|
399
405
|
}
|
|
406
|
+
|
|
407
|
+
/* ─── Desktop docs layout (colorful theme) ──────────────────────── */
|
|
408
|
+
|
|
409
|
+
@media (min-width: 1024px) {
|
|
410
|
+
#nd-docs-layout.grid {
|
|
411
|
+
grid-template:
|
|
412
|
+
"sidebar header toc"
|
|
413
|
+
"sidebar toc-popover toc"
|
|
414
|
+
"sidebar main toc" 1fr /
|
|
415
|
+
var(--fd-sidebar-col)
|
|
416
|
+
minmax(0, 1fr)
|
|
417
|
+
var(--fd-toc-width) !important;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
#nd-docs-layout [data-sidebar-placeholder] {
|
|
421
|
+
justify-self: stretch;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
#nd-docs-layout article#nd-page {
|
|
425
|
+
max-width: none;
|
|
426
|
+
margin-inline: 0;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
#nd-docs-layout article#nd-page > .prose {
|
|
430
|
+
max-width: none;
|
|
431
|
+
}
|
|
432
|
+
}
|