@windrun-huaiin/third-ui 20.1.0 → 22.0.0

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.
Files changed (123) hide show
  1. package/LICENSE +1 -1
  2. package/dist/ai/ai-markdown.js +1 -1
  3. package/dist/ai/ai-markdown.mjs +1 -1
  4. package/dist/clerk/clerk-page-context-generator.js +3 -2
  5. package/dist/clerk/clerk-page-context-generator.mjs +3 -2
  6. package/dist/clerk/clerk-page-generator.d.ts +0 -8
  7. package/dist/clerk/clerk-page-generator.js +4 -3
  8. package/dist/clerk/clerk-page-generator.mjs +4 -3
  9. package/dist/fuma/base/custom-header.d.ts +1 -1
  10. package/dist/fuma/base/custom-header.js +38 -36
  11. package/dist/fuma/base/custom-header.mjs +25 -23
  12. package/dist/fuma/base/custom-home-layout.d.ts +1 -1
  13. package/dist/fuma/base/custom-home-layout.js +1 -1
  14. package/dist/fuma/base/custom-home-layout.mjs +1 -1
  15. package/dist/fuma/base/header-theme-switch.d.ts +5 -0
  16. package/dist/fuma/base/header-theme-switch.js +42 -0
  17. package/dist/fuma/base/header-theme-switch.mjs +40 -0
  18. package/dist/fuma/base/index.d.ts +1 -0
  19. package/dist/fuma/base/index.js +7 -0
  20. package/dist/fuma/base/index.mjs +1 -0
  21. package/dist/fuma/base/site-layout.d.ts +116 -0
  22. package/dist/fuma/base/site-layout.js +72 -0
  23. package/dist/fuma/base/site-layout.mjs +65 -0
  24. package/dist/fuma/fuma-banner-suit.js +9 -6
  25. package/dist/fuma/fuma-banner-suit.mjs +10 -7
  26. package/dist/fuma/fuma-page-genarator.d.ts +2 -2
  27. package/dist/fuma/fuma-page-genarator.js +21 -8
  28. package/dist/fuma/fuma-page-genarator.mjs +21 -8
  29. package/dist/fuma/heavy/image-grid.d.ts +6 -0
  30. package/dist/fuma/heavy/image-grid.js +17 -0
  31. package/dist/fuma/heavy/image-grid.mjs +15 -0
  32. package/dist/fuma/heavy/image-zoom.d.ts +22 -0
  33. package/dist/fuma/heavy/image-zoom.js +39 -0
  34. package/dist/fuma/heavy/image-zoom.mjs +37 -0
  35. package/dist/fuma/heavy/index.d.ts +4 -0
  36. package/dist/fuma/heavy/index.js +15 -0
  37. package/dist/fuma/heavy/index.mjs +5 -0
  38. package/dist/fuma/heavy/math.d.ts +17 -0
  39. package/dist/fuma/heavy/math.js +60 -0
  40. package/dist/fuma/heavy/math.mjs +57 -0
  41. package/dist/fuma/heavy/mermaid.d.ts +13 -0
  42. package/dist/fuma/heavy/mermaid.js +360 -0
  43. package/dist/fuma/heavy/mermaid.mjs +358 -0
  44. package/dist/fuma/llm-copy-handler.js +3 -2
  45. package/dist/fuma/llm-copy-handler.mjs +3 -2
  46. package/dist/fuma/mdx/features.d.ts +8 -0
  47. package/dist/fuma/mdx/features.js +92 -0
  48. package/dist/fuma/mdx/features.mjs +85 -0
  49. package/dist/fuma/mdx/index.d.ts +0 -4
  50. package/dist/fuma/mdx/index.js +0 -8
  51. package/dist/fuma/mdx/index.mjs +0 -4
  52. package/dist/fuma/mdx/markdown-component-map.js +7 -1
  53. package/dist/fuma/mdx/markdown-component-map.mjs +7 -1
  54. package/dist/fuma/mdx/math.d.ts +17 -0
  55. package/dist/fuma/mdx/math.js +60 -0
  56. package/dist/fuma/mdx/math.mjs +57 -0
  57. package/dist/fuma/mdx/site-mdx-components.d.ts +13 -0
  58. package/dist/fuma/mdx/site-mdx-components.js +19 -0
  59. package/dist/fuma/mdx/site-mdx-components.mjs +17 -0
  60. package/dist/fuma/mdx/site-mdx-presets.d.ts +13 -0
  61. package/dist/fuma/mdx/site-mdx-presets.js +49 -0
  62. package/dist/fuma/mdx/site-mdx-presets.mjs +45 -0
  63. package/dist/fuma/mdx/toc-clerk-portable.js +9 -5
  64. package/dist/fuma/mdx/toc-clerk-portable.mjs +8 -4
  65. package/dist/fuma/mdx/zia-card.js +1 -1
  66. package/dist/fuma/mdx/zia-card.mjs +1 -1
  67. package/dist/fuma/mdx/zia-file.js +1 -0
  68. package/dist/fuma/mdx/zia-file.mjs +1 -0
  69. package/dist/fuma/server/optional-features.d.ts +8 -0
  70. package/dist/fuma/server/optional-features.js +111 -0
  71. package/dist/fuma/server/optional-features.mjs +104 -0
  72. package/dist/fuma/server/site-mdx-components.d.ts +13 -0
  73. package/dist/fuma/server/site-mdx-components.js +19 -0
  74. package/dist/fuma/server/site-mdx-components.mjs +17 -0
  75. package/dist/fuma/server/site-mdx-presets.d.ts +194 -0
  76. package/dist/fuma/server/site-mdx-presets.js +46 -0
  77. package/dist/fuma/server/site-mdx-presets.mjs +42 -0
  78. package/dist/fuma/share/index.d.ts +1 -0
  79. package/dist/fuma/share/index.js +7 -0
  80. package/dist/fuma/share/index.mjs +1 -0
  81. package/dist/fuma/share/markdown-component-map.d.ts +3 -0
  82. package/dist/fuma/share/markdown-component-map.js +79 -0
  83. package/dist/fuma/share/markdown-component-map.mjs +77 -0
  84. package/dist/lib/fuma-schema-check-util.js +19 -5
  85. package/dist/lib/fuma-schema-check-util.mjs +19 -5
  86. package/dist/lib/seo-metadata.d.ts +10 -0
  87. package/dist/main/x-button.js +2 -2
  88. package/dist/main/x-button.mjs +2 -2
  89. package/package.json +31 -8
  90. package/src/ai/ai-markdown.tsx +1 -1
  91. package/src/clerk/clerk-page-context-generator.tsx +6 -3
  92. package/src/clerk/clerk-page-generator.tsx +7 -13
  93. package/src/fuma/base/custom-header.tsx +32 -35
  94. package/src/fuma/base/custom-home-layout.tsx +2 -2
  95. package/src/fuma/base/header-theme-switch.tsx +88 -0
  96. package/src/fuma/base/index.ts +1 -0
  97. package/src/fuma/base/site-layout.tsx +289 -0
  98. package/src/fuma/fuma-banner-suit.tsx +30 -28
  99. package/src/fuma/fuma-page-genarator.tsx +27 -10
  100. package/src/fuma/{mdx → heavy}/image-grid.tsx +1 -1
  101. package/src/fuma/heavy/index.ts +7 -0
  102. package/src/fuma/heavy/math.tsx +130 -0
  103. package/src/fuma/llm-copy-handler.ts +3 -3
  104. package/src/fuma/mdx/index.ts +0 -4
  105. package/src/fuma/mdx/toc-clerk-portable.tsx +27 -24
  106. package/src/fuma/mdx/zia-card.tsx +1 -0
  107. package/src/fuma/mdx/zia-file.tsx +3 -1
  108. package/src/fuma/server/optional-features.tsx +168 -0
  109. package/src/fuma/server/site-mdx-components.tsx +48 -0
  110. package/src/fuma/server/site-mdx-presets.ts +80 -0
  111. package/src/fuma/share/index.ts +1 -0
  112. package/src/fuma/{mdx → share}/markdown-component-map.tsx +1 -1
  113. package/src/lib/fuma-schema-check-util.ts +22 -6
  114. package/src/lib/seo-metadata.ts +47 -0
  115. package/src/main/language-detector.tsx +0 -8
  116. package/src/main/x-button.tsx +2 -2
  117. package/src/styles/fuma.css +3 -7
  118. package/src/styles/third-ui.css +0 -4
  119. package/dist/main/ads-alert-dialog.d.ts +0 -15
  120. package/dist/main/ads-alert-dialog.js +0 -21
  121. package/dist/main/ads-alert-dialog.mjs +0 -19
  122. /package/src/fuma/{mdx → heavy}/image-zoom.tsx +0 -0
  123. /package/src/fuma/{mdx → heavy}/mermaid.tsx +0 -0
@@ -13,7 +13,7 @@ interface FumaPageParams {
13
13
  /*
14
14
  * The source of the mdx content
15
15
  */
16
- mdxContentSource: any;
16
+ mdxContentSource: any | (() => Promise<any>);
17
17
  /*
18
18
  * The mdx components handler, refer to fumadocs
19
19
  */
@@ -81,11 +81,20 @@ export function createFumaPage({
81
81
  localePrefixAsNeeded = true,
82
82
  defaultLocale = 'en',
83
83
  }: FumaPageParams) {
84
+ const getSource = async () => {
85
+ if (typeof mdxContentSource === 'function') {
86
+ return await mdxContentSource();
87
+ }
88
+
89
+ return mdxContentSource;
90
+ };
91
+
84
92
  const Page = async function Page({ params }: { params: Promise<{ locale: string; slug?: string[] }> }) {
85
93
  const { slug, locale } = await params;
86
- const page = mdxContentSource.getPage(slug, locale);
94
+ const source = await getSource();
95
+ const page = source.getPage(slug, locale);
87
96
  if (!page) {
88
- console.log('[FumaPage] missing page', { slug, locale, available: mdxContentSource.pageTree?.[locale]?.children?.map((c: any) => c.url) });
97
+ console.log('[FumaPage] missing page', { slug, locale, available: source.pageTree?.[locale]?.children?.map((c: any) => c.url) });
89
98
  return <FallbackPage siteIcon={siteIcon} />;
90
99
  }
91
100
 
@@ -103,7 +112,14 @@ export function createFumaPage({
103
112
  />
104
113
  );
105
114
 
106
- const MDX = page.data.body;
115
+ const content =
116
+ typeof page.data.load === 'function'
117
+ ? await page.data.load(getMDXComponents())
118
+ : {
119
+ body: await page.data.body({ components: getMDXComponents() }),
120
+ toc: page.data.toc ?? [],
121
+ };
122
+
107
123
  return (
108
124
  <DocsPage
109
125
  breadcrumb={{enabled: showBreadcrumb}}
@@ -112,7 +128,7 @@ export function createFumaPage({
112
128
  single: false,
113
129
  component: (
114
130
  <PortableClerkTOC
115
- toc={page.data.toc}
131
+ toc={content.toc}
116
132
  footer={tocFooterElement}
117
133
  />
118
134
  ),
@@ -120,25 +136,26 @@ export function createFumaPage({
120
136
  tableOfContentPopover={{
121
137
  enabled: false,
122
138
  }}
123
- toc={page.data.toc}
124
- article={{ className: 'max-sm:pb-16' }}
139
+ toc={content.toc}
140
+ className="max-sm:pb-16"
125
141
  >
126
142
  <DocsTitle>{page.data.title}</DocsTitle>
127
143
  <DocsDescription className="mb-2">{page.data.description}</DocsDescription>
128
144
  <DocsBody className="text-fd-foreground/80">
129
- <MDX components={getMDXComponents()} />
145
+ {content.body}
130
146
  </DocsBody>
131
147
  </DocsPage>
132
148
  );
133
149
  };
134
150
 
135
151
  function generateStaticParams() {
136
- return mdxContentSource.generateParams('slug', 'locale');
152
+ return getSource().then((source) => source.generateParams('slug', 'locale'));
137
153
  }
138
154
 
139
155
  async function generateMetadata(props: { params: Promise<{ slug?: string[]; locale?: string }> }) {
140
156
  const { slug, locale } = await props.params;
141
- const page = mdxContentSource.getPage(slug, locale);
157
+ const source = await getSource();
158
+ const page = source.getPage(slug, locale);
142
159
  if (!page) {
143
160
  return {
144
161
  title: '404 - Page Not Found',
@@ -32,4 +32,4 @@ export function ImageGrid({
32
32
  ))}
33
33
  </div>
34
34
  );
35
- }
35
+ }
@@ -0,0 +1,7 @@
1
+ 'use client';
2
+
3
+ export * from './image-zoom';
4
+ export * from './image-grid';
5
+ export * from './math';
6
+ export * from './mermaid';
7
+
@@ -0,0 +1,130 @@
1
+ import katex from 'katex';
2
+ import { cn } from '@windrun-huaiin/lib/utils';
3
+ import type { HTMLAttributes, ReactNode } from 'react';
4
+
5
+ type MathSourceProps = {
6
+ children?: ReactNode;
7
+ math?: string;
8
+ formula?: string;
9
+ };
10
+
11
+ type Align = 'left' | 'center' | 'right';
12
+
13
+ export type MathBlockProps = Omit<HTMLAttributes<HTMLDivElement>, 'children'> &
14
+ MathSourceProps & {
15
+ title?: ReactNode;
16
+ titleAlign?: Align;
17
+ };
18
+
19
+ export type InlineMathProps = Omit<HTMLAttributes<HTMLSpanElement>, 'children'> &
20
+ MathSourceProps & {
21
+ align?: Align;
22
+ };
23
+
24
+ const alignClassMap: Record<Align, string> = {
25
+ left: 'text-left justify-start',
26
+ center: 'text-center justify-center',
27
+ right: 'text-right justify-end',
28
+ };
29
+
30
+ const textAlignClassMap: Record<Align, string> = {
31
+ left: 'text-left',
32
+ center: 'text-center',
33
+ right: 'text-right',
34
+ };
35
+
36
+ function getMathSource({ children, math, formula }: MathSourceProps): string {
37
+ if (typeof math === 'string' && math.trim() !== '') return math.trim();
38
+ if (typeof formula === 'string' && formula.trim() !== '') return formula.trim();
39
+ if (typeof children === 'string' && children.trim() !== '') return children.trim();
40
+
41
+ if (Array.isArray(children)) {
42
+ const text = children
43
+ .map((item) => (typeof item === 'string' ? item : ''))
44
+ .join('')
45
+ .trim();
46
+
47
+ if (text !== '') return text;
48
+ }
49
+
50
+ return '';
51
+ }
52
+
53
+ function renderMath(source: string, displayMode: boolean) {
54
+ if (source === '') return '';
55
+
56
+ return katex.renderToString(source, {
57
+ displayMode,
58
+ throwOnError: false,
59
+ output: 'html',
60
+ strict: 'ignore',
61
+ trust: false,
62
+ });
63
+ }
64
+
65
+ export function MathBlock({
66
+ title,
67
+ titleAlign = 'center',
68
+ children,
69
+ math,
70
+ formula,
71
+ className,
72
+ ...props
73
+ }: MathBlockProps) {
74
+ const source = getMathSource({ children, math, formula });
75
+ const html = renderMath(source, true);
76
+
77
+ return (
78
+ <div
79
+ {...props}
80
+ className={cn(
81
+ 'not-prose my-6 overflow-x-auto rounded-xl border bg-fd-card p-4 text-fd-card-foreground',
82
+ className,
83
+ )}
84
+ >
85
+ {title ? (
86
+ <div
87
+ className={cn(
88
+ 'mb-3 text-sm font-medium text-fd-muted-foreground',
89
+ alignClassMap[titleAlign].split(' ')[0],
90
+ )}
91
+ >
92
+ {title}
93
+ </div>
94
+ ) : null}
95
+ {html ? (
96
+ <div
97
+ className="min-w-fit [&_.katex-display]:my-0 [&_.katex-display]:overflow-x-auto [&_.katex-display]:overflow-y-hidden"
98
+ dangerouslySetInnerHTML={{ __html: html }}
99
+ />
100
+ ) : (
101
+ <div className="text-sm text-fd-muted-foreground">Empty math block.</div>
102
+ )}
103
+ </div>
104
+ );
105
+ }
106
+
107
+ export function InlineMath({
108
+ children,
109
+ math,
110
+ formula,
111
+ align = 'center',
112
+ className,
113
+ ...props
114
+ }: InlineMathProps) {
115
+ const source = getMathSource({ children, math, formula });
116
+ const html = renderMath(source, false);
117
+
118
+ return (
119
+ <span
120
+ {...props}
121
+ className={cn(
122
+ 'mx-1 inline-flex max-w-full align-middle rounded-md bg-neutral-200 px-2 py-0.5 text-sm leading-none dark:bg-white/20 [&_.katex]:text-inherit',
123
+ textAlignClassMap[align],
124
+ className,
125
+ )}
126
+ >
127
+ <span dangerouslySetInnerHTML={{ __html: html }} />
128
+ </span>
129
+ );
130
+ }
@@ -42,8 +42,8 @@ export async function LLMCopyHandler(options: LLMCopyHandlerOptions): Promise<{
42
42
  return { error: 'Page file path information missing', status: 500 };
43
43
  }
44
44
 
45
- const title = page.title;
46
- const description = page.description;
45
+ const title = page.data?.title ?? page.title;
46
+ const description = page.data?.description ?? page.description;
47
47
  const relativeMdxFilePath = page.path;
48
48
  const absoluteFilePath = nodePath.join(process.cwd(), sourceDir, relativeMdxFilePath);
49
49
  console.log(`[LLMCopy] Attempting to read MDX content from: ${absoluteFilePath}`);
@@ -90,4 +90,4 @@ export async function LLMCopyHandler(options: LLMCopyHandlerOptions): Promise<{
90
90
  console.error('[LLMCopy] General Error object details:', JSON.stringify(error, Object.getOwnPropertyNames(error), 2));
91
91
  return { error: 'Internal Server Error', status: 500 };
92
92
  }
93
- }
93
+ }
@@ -1,9 +1,6 @@
1
1
  'use client';
2
2
 
3
- export * from './mermaid';
4
- export * from './image-zoom';
5
3
  export * from './trophy-card';
6
- export * from './image-grid';
7
4
  export * from './zia-card';
8
5
  export * from './gradient-button';
9
6
  export * from './toc-base';
@@ -13,4 +10,3 @@ export * from './toc-footer-wrapper';
13
10
  export * from './toc-clerk-portable';
14
11
  export * from './banner';
15
12
  export * from './suno-embed';
16
- export * from './markdown-component-map';
@@ -6,12 +6,11 @@
6
6
  */
7
7
  import * as Primitive from 'fumadocs-core/toc';
8
8
  import {
9
- PageTOC,
10
- PageTOCPopover,
11
- PageTOCPopoverContent,
12
- PageTOCPopoverTrigger,
13
- PageTOCTitle,
14
- } from 'fumadocs-ui/layouts/docs/page';
9
+ TOC as PageTOC,
10
+ TOCPopover as PageTOCPopover,
11
+ type TOCProps as PageTOCProps,
12
+ type TOCPopoverProps as PageTOCPopoverProps,
13
+ } from 'fumadocs-ui/layouts/docs/page/slots/toc';
15
14
  import {
16
15
  type ComponentProps,
17
16
  type ReactNode,
@@ -85,14 +84,20 @@ export function PortableClerkTOC({
85
84
  className,
86
85
  }: PortableClerkTOCProps) {
87
86
  return (
88
- <PageTOC className={className}>
89
- {header}
90
- {title ?? <PageTOCTitle />}
91
- <PortableClerkTOCScrollArea>
92
- <PortableClerkTOCItems toc={toc} emptyLabel={emptyLabel} />
93
- </PortableClerkTOCScrollArea>
94
- {footer}
95
- </PageTOC>
87
+ <PageTOC
88
+ style="clerk"
89
+ container={{ className }}
90
+ header={
91
+ <>
92
+ {header}
93
+ {title ?? null}
94
+ </>
95
+ }
96
+ footer={footer}
97
+ list={{
98
+ children: <PortableClerkTOCItems toc={toc} emptyLabel={emptyLabel} />,
99
+ } as PageTOCProps['list']}
100
+ />
96
101
  );
97
102
  }
98
103
 
@@ -103,16 +108,14 @@ export function PortableClerkTOCPopover({
103
108
  emptyLabel = 'No headings',
104
109
  }: Omit<PortableClerkTOCProps, 'title' | 'className'>) {
105
110
  return (
106
- <PageTOCPopover>
107
- <PageTOCPopoverTrigger />
108
- <PageTOCPopoverContent>
109
- {header}
110
- <PortableClerkTOCScrollArea>
111
- <PortableClerkTOCItems toc={toc} emptyLabel={emptyLabel} />
112
- </PortableClerkTOCScrollArea>
113
- {footer}
114
- </PageTOCPopoverContent>
115
- </PageTOCPopover>
111
+ <PageTOCPopover
112
+ style="clerk"
113
+ header={header}
114
+ footer={footer}
115
+ list={{
116
+ children: <PortableClerkTOCItems toc={toc} emptyLabel={emptyLabel} />,
117
+ } as PageTOCPopoverProps['list']}
118
+ />
116
119
  );
117
120
  }
118
121
 
@@ -22,6 +22,7 @@ export function ZiaCard({ icon, title, description, ...props }: ZiaCardProps) {
22
22
  return (
23
23
  <Link
24
24
  href={props.href!}
25
+ prefetch={false}
25
26
  data-card
26
27
  className={cn(
27
28
  'block rounded-lg border bg-fd-card p-4 text-fd-card-foreground shadow-md transition-colors @max-lg:col-span-full',
@@ -1,3 +1,5 @@
1
+ 'use client';
2
+
1
3
  import { FileIcon, FolderOpenIcon, FolderIcon } from '@windrun-huaiin/base-ui/icons';
2
4
  import { type HTMLAttributes, type ReactNode, useState } from 'react';
3
5
  import { cn } from '@windrun-huaiin/lib/utils';
@@ -81,4 +83,4 @@ export function ZiaFolder({
81
83
  </CollapsibleContent>
82
84
  </Collapsible>
83
85
  );
84
- }
86
+ }
@@ -0,0 +1,168 @@
1
+ import type { MDXComponents, MDXProps } from 'mdx/types';
2
+ import type { ReactNode } from 'react';
3
+ import { CodeBlock, Pre } from 'fumadocs-ui/components/codeblock';
4
+ import { TypeTable } from 'fumadocs-ui/components/type-table';
5
+ import {
6
+ CSSIcon,
7
+ CSVIcon,
8
+ DiffIcon,
9
+ HtmlIcon,
10
+ HttpIcon,
11
+ JavaIcon,
12
+ JsonIcon,
13
+ LogIcon,
14
+ MDXIcon,
15
+ RegexIcon,
16
+ SQLIcon,
17
+ SchemeIcon,
18
+ SquareDashedBottomCodeIcon,
19
+ TxtIcon,
20
+ XMLIcon,
21
+ YamlIcon,
22
+ } from '@windrun-huaiin/base-ui/icons';
23
+ import { baseMarkdownComponents } from '../share/markdown-component-map';
24
+ import { Mermaid, ImageGrid, ImageZoom, MathBlock, InlineMath } from '../heavy';
25
+ import { TrophyCard } from '../mdx/trophy-card';
26
+ import { ZiaCard } from '../mdx/zia-card';
27
+ import { GradientButton } from '../mdx/gradient-button';
28
+ import { ZiaFile, ZiaFolder } from '../mdx/zia-file';
29
+ import { SunoEmbed } from '../mdx/suno-embed';
30
+
31
+ const defaultCodeLanguageIconMap: Record<string, ReactNode> = {
32
+ css: <CSSIcon />,
33
+ csv: <CSVIcon />,
34
+ diff: <DiffIcon />,
35
+ html: <HtmlIcon />,
36
+ http: <HttpIcon />,
37
+ java: <JavaIcon />,
38
+ json: <JsonIcon />,
39
+ jsonc: <SquareDashedBottomCodeIcon />,
40
+ log: <LogIcon />,
41
+ mdx: <MDXIcon />,
42
+ plaintext: <TxtIcon />,
43
+ regex: <RegexIcon />,
44
+ scheme: <SchemeIcon />,
45
+ sql: <SQLIcon />,
46
+ text: <TxtIcon />,
47
+ txt: <TxtIcon />,
48
+ xml: <XMLIcon />,
49
+ yaml: <YamlIcon />,
50
+ yml: <YamlIcon />,
51
+ };
52
+
53
+ function tryToMatchIcon(
54
+ props: Readonly<MDXProps & { 'data-language'?: string; title?: string }>,
55
+ iconMap: Record<string, ReactNode>,
56
+ ): ReactNode | undefined {
57
+ let lang: string | undefined;
58
+
59
+ const dataLanguage = props['data-language'] as string | undefined;
60
+
61
+ if (dataLanguage && dataLanguage.trim() !== '') {
62
+ lang = dataLanguage.trim().toLowerCase();
63
+ } else {
64
+ const title = props.title as string | undefined;
65
+ if (title) {
66
+ const titleParts = title.split('.');
67
+ if (titleParts.length > 1 && titleParts[0] !== '') {
68
+ const extension = titleParts.pop()?.toLowerCase();
69
+ if (extension) {
70
+ lang = extension;
71
+ }
72
+ }
73
+ }
74
+ }
75
+
76
+ if (lang && iconMap[lang]) {
77
+ return iconMap[lang];
78
+ }
79
+
80
+ return undefined;
81
+ }
82
+
83
+ export function createBaseMdxComponents(
84
+ imageFallbackSrc?: string,
85
+ ): MDXComponents {
86
+ return {
87
+ ...baseMarkdownComponents,
88
+ img: (props) => (
89
+ <ImageZoom
90
+ {...(props as any)}
91
+ fallbackSrc={imageFallbackSrc}
92
+ />
93
+ ),
94
+ };
95
+ }
96
+
97
+ export function createCodeMdxComponents(
98
+ iconMap: Record<string, ReactNode> = {},
99
+ ): MDXComponents {
100
+ const mergedIconMap = {
101
+ ...defaultCodeLanguageIconMap,
102
+ ...iconMap,
103
+ };
104
+
105
+ return {
106
+ pre: (props) => {
107
+ const customIcon = tryToMatchIcon(props as MDXProps & { 'data-language'?: string; title?: string }, mergedIconMap);
108
+ return (
109
+ <CodeBlock
110
+ {...props}
111
+ {...(customIcon && { icon: customIcon })}
112
+ >
113
+ <Pre>{props.children}</Pre>
114
+ </CodeBlock>
115
+ );
116
+ },
117
+ CodeBlock,
118
+ Pre,
119
+ };
120
+ }
121
+
122
+ export function createMathMdxComponents(): MDXComponents {
123
+ return {
124
+ MathBlock,
125
+ InlineMath,
126
+ };
127
+ }
128
+
129
+ export function createMermaidMdxComponents(
130
+ watermarkEnabled?: boolean,
131
+ watermarkText?: string,
132
+ ): MDXComponents {
133
+ return {
134
+ Mermaid: (props) => (
135
+ <Mermaid
136
+ {...props}
137
+ watermarkEnabled={watermarkEnabled}
138
+ watermarkText={watermarkText}
139
+ />
140
+ ),
141
+ };
142
+ }
143
+
144
+ export function createTypeTableMdxComponents(): MDXComponents {
145
+ return {
146
+ TypeTable,
147
+ };
148
+ }
149
+
150
+ export function createWidgetMdxComponents(
151
+ cdnBaseUrl?: string,
152
+ imageFallbackSrc?: string,
153
+ ): MDXComponents {
154
+ return {
155
+ TrophyCard,
156
+ ZiaCard,
157
+ GradientButton,
158
+ ZiaFile,
159
+ ZiaFolder,
160
+ SunoEmbed,
161
+ ImageGrid: (props) => (
162
+ <ImageGrid {...props} cdnBaseUrl={cdnBaseUrl} />
163
+ ),
164
+ ImageZoom: (props) => (
165
+ <ImageZoom {...props} fallbackSrc={imageFallbackSrc} />
166
+ ),
167
+ };
168
+ }
@@ -0,0 +1,48 @@
1
+ import type { MDXComponents } from 'mdx/types';
2
+ import type { ReactNode } from 'react';
3
+ import type { SiteMdxFeature } from '@windrun-huaiin/contracts/mdx';
4
+ import {
5
+ composeSiteMdxComponents,
6
+ createSiteFeatureComponentMap,
7
+ DEFAULT_SITE_MDX_FEATURES,
8
+ } from './site-mdx-presets';
9
+
10
+ export interface SiteMdxComponentsOptions {
11
+ imageFallbackSrc?: string;
12
+ cdnBaseUrl?: string;
13
+ watermarkEnabled?: boolean;
14
+ watermarkText?: string;
15
+ additionalComponents?: MDXComponents;
16
+ iconMap?: Record<string, ReactNode>;
17
+ features?: SiteMdxFeature[];
18
+ }
19
+
20
+ export function createSiteMdxComponents(
21
+ options: SiteMdxComponentsOptions,
22
+ ) {
23
+ const {
24
+ additionalComponents,
25
+ cdnBaseUrl,
26
+ features = DEFAULT_SITE_MDX_FEATURES,
27
+ iconMap = {},
28
+ imageFallbackSrc,
29
+ watermarkEnabled,
30
+ watermarkText,
31
+ } = options;
32
+ const featureMap = createSiteFeatureComponentMap({
33
+ cdnBaseUrl,
34
+ iconMap,
35
+ imageFallbackSrc,
36
+ watermarkEnabled,
37
+ watermarkText,
38
+ });
39
+
40
+ return function getMDXComponents(components?: MDXComponents): MDXComponents {
41
+ return composeSiteMdxComponents(
42
+ features,
43
+ featureMap,
44
+ additionalComponents,
45
+ components,
46
+ );
47
+ };
48
+ }
@@ -0,0 +1,80 @@
1
+ import defaultMdxComponents from 'fumadocs-ui/mdx';
2
+ import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
3
+ import { Callout } from 'fumadocs-ui/components/callout';
4
+ import { File, Folder, Files } from 'fumadocs-ui/components/files';
5
+ import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
6
+ import type { MDXComponents } from 'mdx/types';
7
+ import {
8
+ createBaseMdxComponents,
9
+ createCodeMdxComponents,
10
+ createMathMdxComponents,
11
+ createMermaidMdxComponents,
12
+ createTypeTableMdxComponents,
13
+ createWidgetMdxComponents,
14
+ } from './optional-features';
15
+ import { SiteX } from '../site-x';
16
+ import type { SiteMdxComponentsOptions } from './site-mdx-components';
17
+ import type { SiteMdxFeature } from '@windrun-huaiin/contracts/mdx';
18
+
19
+ const defaultFumaUiComponents: MDXComponents = {
20
+ Callout,
21
+ File,
22
+ Folder,
23
+ Files,
24
+ Accordion,
25
+ Accordions,
26
+ Tab,
27
+ Tabs,
28
+ };
29
+
30
+ export const DEFAULT_SITE_MDX_FEATURES: SiteMdxFeature[] = [
31
+ 'base',
32
+ 'code',
33
+ 'math',
34
+ 'mermaid',
35
+ 'type-table',
36
+ ];
37
+
38
+ export function createSiteFeatureComponentMap(
39
+ options: SiteMdxComponentsOptions,
40
+ ) {
41
+ const {
42
+ cdnBaseUrl,
43
+ iconMap = {},
44
+ imageFallbackSrc,
45
+ watermarkEnabled,
46
+ watermarkText,
47
+ } = options;
48
+
49
+ return {
50
+ base: {
51
+ ...defaultFumaUiComponents,
52
+ SiteX,
53
+ ...createBaseMdxComponents(imageFallbackSrc),
54
+ ...createWidgetMdxComponents(cdnBaseUrl, imageFallbackSrc),
55
+ },
56
+ code: createCodeMdxComponents(iconMap),
57
+ math: createMathMdxComponents(),
58
+ mermaid: createMermaidMdxComponents(watermarkEnabled, watermarkText),
59
+ 'type-table': createTypeTableMdxComponents(),
60
+ } satisfies Record<SiteMdxFeature, MDXComponents>;
61
+ }
62
+
63
+ export function composeSiteMdxComponents(
64
+ features: readonly SiteMdxFeature[],
65
+ featureMap: Record<SiteMdxFeature, MDXComponents>,
66
+ additionalComponents?: MDXComponents,
67
+ components?: MDXComponents,
68
+ ): MDXComponents {
69
+ return {
70
+ ...defaultMdxComponents,
71
+ ...features.reduce<MDXComponents>((acc, feature) => {
72
+ return {
73
+ ...acc,
74
+ ...featureMap[feature],
75
+ };
76
+ }, {}),
77
+ ...additionalComponents,
78
+ ...components,
79
+ };
80
+ }
@@ -0,0 +1 @@
1
+ export * from './markdown-component-map';
@@ -1,7 +1,7 @@
1
1
  import { cn } from '@windrun-huaiin/lib/utils';
2
2
  import defaultMdxComponents from 'fumadocs-ui/mdx';
3
3
  import { type ComponentType } from 'react';
4
- import { ImageZoom } from './image-zoom';
4
+ import { ImageZoom } from '../heavy';
5
5
 
6
6
  export type MarkdownComponentMap = Record<string, ComponentType<any>>;
7
7