@farming-labs/theme 0.0.2-beta.24 → 0.0.2-beta.27

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.
@@ -14,20 +14,29 @@ import { DocsConfig } from "@farming-labs/docs";
14
14
  * export const metadata = createDocsMetadata(docsConfig);
15
15
  * ```
16
16
  */
17
- declare function createDocsMetadata(config: DocsConfig): {
18
- twitter?: {
19
- card: "summary" | "summary_large_image";
20
- } | undefined;
21
- description?: string | undefined;
22
- title: {
23
- template: string;
24
- default: string;
25
- };
26
- };
17
+ declare function createDocsMetadata(config: DocsConfig): Record<string, unknown>;
18
+ /**
19
+ * Generate page-level metadata with dynamic OG images.
20
+ *
21
+ * Usage in a docs page or [[...slug]] route:
22
+ * ```ts
23
+ * export function generateMetadata({ params }) {
24
+ * const page = getPage(params.slug);
25
+ * return createPageMetadata(docsConfig, {
26
+ * title: page.data.title,
27
+ * description: page.data.description,
28
+ * });
29
+ * }
30
+ * ```
31
+ */
32
+ declare function createPageMetadata(config: DocsConfig, page: {
33
+ title: string;
34
+ description?: string;
35
+ }): Record<string, unknown>;
27
36
  declare function createDocsLayout(config: DocsConfig): ({
28
37
  children
29
38
  }: {
30
39
  children: ReactNode;
31
40
  }) => react_jsx_runtime0.JSX.Element;
32
41
  //#endregion
33
- export { createDocsLayout, createDocsMetadata };
42
+ export { createDocsLayout, createDocsMetadata, createPageMetadata };
@@ -204,15 +204,71 @@ function buildDescriptionMap(entry) {
204
204
  */
205
205
  function createDocsMetadata(config) {
206
206
  const meta = config.metadata;
207
+ const og = config.og;
207
208
  const template = meta?.titleTemplate ?? "%s";
208
- return {
209
+ const defaultTitle = template.replace("%s", "").replace(/^[\s–—-]+/, "").trim() || "Docs";
210
+ const result = {
209
211
  title: {
210
212
  template,
211
- default: template.replace("%s", "").replace(/^[\s–—-]+/, "").trim() || "Docs"
213
+ default: defaultTitle
212
214
  },
213
215
  ...meta?.description ? { description: meta.description } : {},
214
216
  ...meta?.twitterCard ? { twitter: { card: meta.twitterCard } } : {}
215
217
  };
218
+ if (og?.enabled !== false && og?.endpoint) {
219
+ const ogUrl = `${og.endpoint}?title=${encodeURIComponent(defaultTitle)}${meta?.description ? `&description=${encodeURIComponent(meta.description)}` : ""}`;
220
+ result.openGraph = { images: [{
221
+ url: ogUrl,
222
+ width: 1200,
223
+ height: 630
224
+ }] };
225
+ result.twitter = {
226
+ ...result.twitter,
227
+ card: meta?.twitterCard ?? "summary_large_image",
228
+ images: [ogUrl]
229
+ };
230
+ }
231
+ return result;
232
+ }
233
+ /**
234
+ * Generate page-level metadata with dynamic OG images.
235
+ *
236
+ * Usage in a docs page or [[...slug]] route:
237
+ * ```ts
238
+ * export function generateMetadata({ params }) {
239
+ * const page = getPage(params.slug);
240
+ * return createPageMetadata(docsConfig, {
241
+ * title: page.data.title,
242
+ * description: page.data.description,
243
+ * });
244
+ * }
245
+ * ```
246
+ */
247
+ function createPageMetadata(config, page) {
248
+ const og = config.og;
249
+ const result = {
250
+ title: page.title,
251
+ ...page.description ? { description: page.description } : {}
252
+ };
253
+ if (og?.enabled !== false && og?.endpoint) {
254
+ const ogUrl = `${og.endpoint}?title=${encodeURIComponent(page.title)}${page.description ? `&description=${encodeURIComponent(page.description)}` : ""}`;
255
+ result.openGraph = {
256
+ title: page.title,
257
+ description: page.description,
258
+ images: [{
259
+ url: ogUrl,
260
+ width: 1200,
261
+ height: 630
262
+ }]
263
+ };
264
+ result.twitter = {
265
+ card: "summary_large_image",
266
+ title: page.title,
267
+ description: page.description,
268
+ images: [ogUrl]
269
+ };
270
+ }
271
+ return result;
216
272
  }
217
273
  /** Resolve the themeToggle config into fumadocs-ui's `themeSwitch` prop. */
218
274
  function resolveThemeSwitch(toggle) {
@@ -229,7 +285,7 @@ function resolveSidebar(sidebar) {
229
285
  if (sidebar === false) return { enabled: false };
230
286
  return {
231
287
  enabled: sidebar.enabled !== false,
232
- component: sidebar.component,
288
+ componentFn: typeof sidebar.component === "function" ? sidebar.component : void 0,
233
289
  footer: sidebar.footer,
234
290
  banner: sidebar.banner,
235
291
  collapsible: sidebar.collapsible,
@@ -343,7 +399,8 @@ function createDocsLayout(config) {
343
399
  const forcedTheme = themeSwitch.enabled === false && toggleConfig?.default && toggleConfig.default !== "system" ? toggleConfig.default : void 0;
344
400
  const resolvedSidebar = resolveSidebar(config.sidebar);
345
401
  const sidebarFlat = resolvedSidebar.flat;
346
- const { flat: _sidebarFlat, ...sidebarProps } = resolvedSidebar;
402
+ const sidebarComponentFn = resolvedSidebar.componentFn;
403
+ const { flat: _sidebarFlat, componentFn: _componentFn, ...sidebarProps } = resolvedSidebar;
347
404
  const breadcrumbConfig = config.breadcrumb;
348
405
  const breadcrumbEnabled = breadcrumbConfig === void 0 || breadcrumbConfig === true || typeof breadcrumbConfig === "object" && breadcrumbConfig.enabled !== false;
349
406
  const colors = config.theme?._userColorOverrides;
@@ -380,14 +437,21 @@ function createDocsLayout(config) {
380
437
  const lastModifiedMap = buildLastModifiedMap(config.entry);
381
438
  const descriptionMap = buildDescriptionMap(config.entry);
382
439
  return function DocsLayoutWrapper({ children }) {
440
+ const tree = buildTree(config, !!sidebarFlat);
441
+ const finalSidebarProps = { ...sidebarProps };
442
+ if (sidebarComponentFn) finalSidebarProps.component = sidebarComponentFn({
443
+ tree,
444
+ collapsible: sidebarProps.collapsible !== false,
445
+ flat: !!sidebarFlat
446
+ });
383
447
  return /* @__PURE__ */ jsxs(DocsLayout, {
384
- tree: buildTree(config, !!sidebarFlat),
448
+ tree,
385
449
  nav: {
386
450
  title: navTitle,
387
451
  url: navUrl
388
452
  },
389
453
  themeSwitch,
390
- sidebar: sidebarProps,
454
+ sidebar: finalSidebarProps,
391
455
  ...aiMode === "sidebar-icon" && aiEnabled ? { searchToggle: { components: { lg: /* @__PURE__ */ jsx(SidebarSearchWithAI, {}) } } } : {},
392
456
  children: [
393
457
  /* @__PURE__ */ jsx(ColorStyle, { colors }),
@@ -444,4 +508,4 @@ function ForcedThemeScript({ theme }) {
444
508
  }
445
509
 
446
510
  //#endregion
447
- export { createDocsLayout, createDocsMetadata };
511
+ export { createDocsLayout, createDocsMetadata, createPageMetadata };
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { DefaultUIDefaults, fumadocs } from "./default/index.mjs";
2
2
  import { DocsCommandSearch } from "./docs-command-search.mjs";
3
- import { createDocsLayout, createDocsMetadata } from "./docs-layout.mjs";
3
+ import { createDocsLayout, createDocsMetadata, createPageMetadata } from "./docs-layout.mjs";
4
4
  import { DocsPageClient } from "./docs-page-client.mjs";
5
5
  import { RootProvider } from "./provider.mjs";
6
6
  import { PageActions } from "./page-actions.mjs";
@@ -9,4 +9,4 @@ import { DocsBody, DocsPage } from "fumadocs-ui/layouts/docs/page";
9
9
  import { AIConfig, BreadcrumbConfig, CopyMarkdownConfig, DocsConfig, DocsMetadata, DocsNav, DocsTheme, FontStyle, OGConfig, OpenDocsConfig, OpenDocsProvider, PageActionsConfig, PageFrontmatter, SidebarConfig, ThemeToggleConfig, TypographyConfig, UIConfig, createTheme, deepMerge, defineDocs, extendTheme } from "@farming-labs/docs";
10
10
  import { Tab, Tabs } from "fumadocs-ui/components/tabs";
11
11
  import { CodeBlock, CodeBlockTab, CodeBlockTabs, CodeBlockTabsList, CodeBlockTabsTrigger, Pre } from "fumadocs-ui/components/codeblock";
12
- export { type AIConfig, type BreadcrumbConfig, CodeBlock, CodeBlockTab, CodeBlockTabs, CodeBlockTabsList, CodeBlockTabsTrigger, type CopyMarkdownConfig, DocsBody, DocsCommandSearch, type DocsConfig, DocsLayout, type DocsMetadata, type DocsNav, DocsPage, DocsPageClient, type DocsTheme, type FontStyle, DefaultUIDefaults as FumadocsUIDefaults, type OGConfig, type OpenDocsConfig, type OpenDocsProvider, PageActions, type PageActionsConfig, type PageFrontmatter, Pre, RootProvider, type SidebarConfig, Tab, Tabs, type ThemeToggleConfig, type TypographyConfig, type UIConfig, createDocsLayout, createDocsMetadata, createTheme, deepMerge, defineDocs, extendTheme, fumadocs };
12
+ export { type AIConfig, type BreadcrumbConfig, CodeBlock, CodeBlockTab, CodeBlockTabs, CodeBlockTabsList, CodeBlockTabsTrigger, type CopyMarkdownConfig, DocsBody, DocsCommandSearch, type DocsConfig, DocsLayout, type DocsMetadata, type DocsNav, DocsPage, DocsPageClient, type DocsTheme, type FontStyle, DefaultUIDefaults as FumadocsUIDefaults, type OGConfig, type OpenDocsConfig, type OpenDocsProvider, PageActions, type PageActionsConfig, type PageFrontmatter, Pre, RootProvider, type SidebarConfig, Tab, Tabs, type ThemeToggleConfig, type TypographyConfig, type UIConfig, createDocsLayout, createDocsMetadata, createPageMetadata, createTheme, deepMerge, defineDocs, extendTheme, fumadocs };
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { DocsCommandSearch } from "./docs-command-search.mjs";
2
2
  import { PageActions } from "./page-actions.mjs";
3
3
  import { DocsPageClient } from "./docs-page-client.mjs";
4
- import { createDocsLayout, createDocsMetadata } from "./docs-layout.mjs";
4
+ import { createDocsLayout, createDocsMetadata, createPageMetadata } from "./docs-layout.mjs";
5
5
  import { RootProvider } from "./provider.mjs";
6
6
  import { DefaultUIDefaults, fumadocs } from "./default/index.mjs";
7
7
  import { DocsLayout } from "fumadocs-ui/layouts/docs";
@@ -10,4 +10,4 @@ import { createTheme, deepMerge, defineDocs, extendTheme } from "@farming-labs/d
10
10
  import { Tab, Tabs } from "fumadocs-ui/components/tabs";
11
11
  import { CodeBlock, CodeBlockTab, CodeBlockTabs, CodeBlockTabsList, CodeBlockTabsTrigger, Pre } from "fumadocs-ui/components/codeblock";
12
12
 
13
- export { CodeBlock, CodeBlockTab, CodeBlockTabs, CodeBlockTabsList, CodeBlockTabsTrigger, DocsBody, DocsCommandSearch, DocsLayout, DocsPage, DocsPageClient, DefaultUIDefaults as FumadocsUIDefaults, PageActions, Pre, RootProvider, Tab, Tabs, createDocsLayout, createDocsMetadata, createTheme, deepMerge, defineDocs, extendTheme, fumadocs };
13
+ export { CodeBlock, CodeBlockTab, CodeBlockTabs, CodeBlockTabsList, CodeBlockTabsTrigger, DocsBody, DocsCommandSearch, DocsLayout, DocsPage, DocsPageClient, DefaultUIDefaults as FumadocsUIDefaults, PageActions, Pre, RootProvider, Tab, Tabs, createDocsLayout, createDocsMetadata, createPageMetadata, createTheme, deepMerge, defineDocs, extendTheme, fumadocs };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/theme",
3
- "version": "0.0.2-beta.24",
3
+ "version": "0.0.2-beta.27",
4
4
  "description": "Theme package for @farming-labs/docs — layout, provider, MDX components, and styles",
5
5
  "keywords": [
6
6
  "docs",
@@ -98,7 +98,7 @@
98
98
  "next": ">=14.0.0",
99
99
  "tsdown": "^0.20.3",
100
100
  "typescript": "^5.9.3",
101
- "@farming-labs/docs": "0.0.2-beta.24"
101
+ "@farming-labs/docs": "0.0.2-beta.27"
102
102
  },
103
103
  "peerDependencies": {
104
104
  "@farming-labs/docs": ">=0.0.1",
package/styles/base.css CHANGED
@@ -390,8 +390,9 @@ figure.shiki:has(figcaption) figcaption {
390
390
 
391
391
  .fd-page-footer {
392
392
  display: flex;
393
+ flex-wrap: wrap;
393
394
  align-items: center;
394
- gap: 1rem;
395
+ gap: 0.75rem 1rem;
395
396
  margin-top: auto;
396
397
  padding-top: 1.5rem;
397
398
  border-top: 1px solid var(--color-fd-border, hsl(0 0% 80% / 50%));
@@ -409,6 +410,13 @@ figure.shiki:has(figcaption) figcaption {
409
410
  margin-left: auto;
410
411
  }
411
412
 
413
+ @media (max-width: 640px) {
414
+ .fd-last-updated-footer {
415
+ margin-left: 0;
416
+ width: 100%;
417
+ }
418
+ }
419
+
412
420
  .fd-llms-txt-links {
413
421
  display: inline-flex;
414
422
  align-items: center;
@@ -427,8 +435,8 @@ figure.shiki:has(figcaption) figcaption {
427
435
  }
428
436
 
429
437
  .fd-llms-txt-link:hover {
430
- /* color: var(--color-fd-foreground, hsl(0 0% 10%));
431
- border-color: var(--color-fd-foreground, hsl(0 0% 10%)); */
438
+ color: var(--color-fd-foreground, hsl(0 0% 10%));
439
+ border-color: var(--color-fd-foreground, hsl(0 0% 10%));
432
440
  text-decoration: none;
433
441
  }
434
442
 
@@ -450,21 +450,29 @@ figure.shiki > div:first-child {
450
450
  }
451
451
 
452
452
  /* ─── Page Actions (pixel-border overrides) ───────────────────────── */
453
-
454
453
  .fd-page-action-btn {
455
454
  border-radius: 0 !important;
456
455
  font-size: 0.75rem;
457
456
  letter-spacing: 0.03em;
457
+ text-transform: uppercase;
458
+ box-shadow: 2px 2px 0 0 var(--color-fd-border, #262626);
459
+ font-family: var(--fd-font-mono, var(--font-geist-mono, ui-monospace, monospace)) !important;
458
460
  }
459
461
 
460
462
  .fd-page-action-menu {
461
463
  border-radius: 0 !important;
462
464
  box-shadow: 0 4px 24px hsl(0 0% 0% / 0.5);
465
+ padding: 0px !important;
466
+ box-shadow: 2px 2px 0 0 var(--color-fd-border, #262626);
463
467
  }
464
468
 
465
469
  .fd-page-action-menu-item {
466
470
  border-radius: 0 !important;
467
- font-size: 0.8rem;
471
+ font-size: 0.68rem;
472
+ border-top: 1px solid var(--color-fd-border, #262626);
473
+ text-transform: uppercase;
474
+ color: var(--color-fd-muted-foreground, hsl(0 0% 45%));
475
+ font-family: var(--fd-font-mono, var(--font-geist-mono, ui-monospace, monospace)) !important;
468
476
  }
469
477
 
470
478
  /* ─── AI Chat (pixel-border — zero radius, pixel-art, monospace) ── */