@farming-labs/theme 0.0.2-beta.26 → 0.0.2-beta.28

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) {
@@ -452,4 +508,4 @@ function ForcedThemeScript({ theme }) {
452
508
  }
453
509
 
454
510
  //#endregion
455
- export { createDocsLayout, createDocsMetadata };
511
+ export { createDocsLayout, createDocsMetadata, createPageMetadata };
@@ -53,15 +53,15 @@ function PathBreadcrumb({ pathname, entry }) {
53
53
  * (Copy Markdown, Open in LLM). Re-scans when the route changes.
54
54
  */
55
55
  /**
56
- * Build the GitHub URL for the current page's source file.
56
+ * Build the GitHub URL for the current page's source file (edit view).
57
57
  *
58
58
  * Examples:
59
- * No directory: https://github.com/user/repo/tree/main/app/docs/cli/page.mdx
60
- * With directory: https://github.com/farming-labs/docs/tree/main/website/app/docs/cli/page.mdx
59
+ * No directory: https://github.com/user/repo/edit/main/app/docs/cli/page.mdx
60
+ * With directory: https://github.com/farming-labs/docs/edit/main/website/app/docs/cli/page.mdx
61
61
  */
62
62
  function buildGithubFileUrl(githubUrl, branch, pathname, directory) {
63
63
  const segments = pathname.replace(/^\//, "").replace(/\/$/, "");
64
- return `${githubUrl}/tree/${branch}/${directory ? `${directory}/` : ""}app/${segments}/page.mdx`;
64
+ return `${githubUrl}/edit/${branch}/${`${directory ? `${directory}/` : ""}app/${segments}/page.mdx`}`;
65
65
  }
66
66
  function DocsPageClient({ tocEnabled, tocStyle = "default", breadcrumbEnabled = true, entry = "docs", copyMarkdown = false, openDocs = false, openDocsProviders, pageActionsPosition = "below-title", pageActionsAlignment = "left", githubUrl, githubBranch = "main", githubDirectory, lastModifiedMap, lastUpdatedEnabled = true, lastUpdatedPosition = "footer", llmsTxtEnabled = false, descriptionMap, description, children }) {
67
67
  const fdTocStyle = tocStyle === "directional" ? "clerk" : void 0;
@@ -176,7 +176,8 @@ function DocsPageClient({ tocEnabled, tocStyle = "default", breadcrumbEnabled =
176
176
  showActions && actionsPortalTarget && createPortal(/* @__PURE__ */ jsx(PageActions, {
177
177
  copyMarkdown,
178
178
  openDocs,
179
- providers: openDocsProviders
179
+ providers: openDocsProviders,
180
+ githubFileUrl
180
181
  }), actionsPortalTarget),
181
182
  /* @__PURE__ */ jsxs(DocsBody, {
182
183
  style: {
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 };
@@ -11,11 +11,14 @@ interface PageActionsProps {
11
11
  copyMarkdown?: boolean;
12
12
  openDocs?: boolean;
13
13
  providers?: SerializedProvider[];
14
+ /** GitHub file URL (edit view) for the current page. Used when urlTemplate contains {githubUrl}. */
15
+ githubFileUrl?: string | null;
14
16
  }
15
17
  declare function PageActions({
16
18
  copyMarkdown,
17
19
  openDocs,
18
- providers
20
+ providers,
21
+ githubFileUrl
19
22
  }: PageActionsProps): react_jsx_runtime0.JSX.Element | null;
20
23
  //#endregion
21
24
  export { PageActions };
@@ -72,7 +72,7 @@ const DEFAULT_PROVIDERS = [{
72
72
  name: "Claude",
73
73
  urlTemplate: "https://claude.ai/new?q=Read+{mdxUrl},+I+want+to+ask+questions+about+it."
74
74
  }];
75
- function PageActions({ copyMarkdown, openDocs, providers }) {
75
+ function PageActions({ copyMarkdown, openDocs, providers, githubFileUrl }) {
76
76
  const [copied, setCopied] = useState(false);
77
77
  const [dropdownOpen, setDropdownOpen] = useState(false);
78
78
  const dropdownRef = useRef(null);
@@ -89,12 +89,16 @@ function PageActions({ copyMarkdown, openDocs, providers }) {
89
89
  } catch {}
90
90
  }, []);
91
91
  const handleOpen = useCallback((template) => {
92
+ if (/\{githubUrl\}/.test(template) && !githubFileUrl) {
93
+ setDropdownOpen(false);
94
+ return;
95
+ }
92
96
  const pageUrl = window.location.href;
93
97
  const mdxUrl = `${window.location.origin}${pathname}.mdx`;
94
- const url = template.replace(/\{url\}/g, encodeURIComponent(pageUrl)).replace(/\{mdxUrl\}/g, encodeURIComponent(mdxUrl));
98
+ let url = template.replace(/\{url\}/g, encodeURIComponent(pageUrl)).replace(/\{mdxUrl\}/g, encodeURIComponent(mdxUrl)).replace(/\{githubUrl\}/g, githubFileUrl ?? "");
95
99
  window.open(url, "_blank", "noopener,noreferrer");
96
100
  setDropdownOpen(false);
97
- }, [pathname]);
101
+ }, [pathname, githubFileUrl]);
98
102
  useEffect(() => {
99
103
  if (!dropdownOpen) return;
100
104
  function handleClick(e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/theme",
3
- "version": "0.0.2-beta.26",
3
+ "version": "0.0.2-beta.28",
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.26"
101
+ "@farming-labs/docs": "0.0.2-beta.28"
102
102
  },
103
103
  "peerDependencies": {
104
104
  "@farming-labs/docs": ">=0.0.1",
@@ -450,11 +450,13 @@ 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 {