@windrun-huaiin/third-ui 23.0.0 → 23.1.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.
package/README.md CHANGED
@@ -1,222 +1,185 @@
1
1
  # @windrun-huaiin/third-ui
2
2
 
3
- Third-party integrated UI components library, including Clerk authentication, Fumadocs documentation, and main application components.
3
+ This README currently documents only the Fuma/MDX component design in `third-ui`.
4
4
 
5
- ## Installation
5
+ Other modules in this package, such as Clerk, main application UI, AI UI, fingerprint, SEO helpers, and shared layout utilities, are intentionally not covered here.
6
6
 
7
- ```bash
8
- pnpm add @windrun-huaiin/third-ui
7
+ ## MDX Component Layer
8
+
9
+ The Fuma/MDX part of `third-ui` provides the rendering component map used by application MDX pages.
10
+
11
+ It is not responsible for reading files or compiling MDX. That belongs to `@windrun-huaiin/fumadocs-local-md`.
12
+
13
+ The split is:
14
+
15
+ - `local-md`: content source, frontmatter/meta parsing, Markdown/MDX compilation, render execution safety.
16
+ - `third-ui`: React components used when MDX is rendered.
17
+ - application: chooses which compiler features and renderer features are imported.
18
+
19
+ ## Design Goals
20
+
21
+ - Keep the base MDX component map useful but small.
22
+ - Make heavyweight rendering features explicit imports.
23
+ - Avoid old aggregate component entries that import every renderer feature at module load time.
24
+ - Let application code be the capability declaration.
25
+ - Preserve a safe fallback path when disabled or unknown MDX components appear in content.
26
+ - Keep renderer-only capabilities separate from compiler-only capabilities.
27
+
28
+ ## Current Entry Model
29
+
30
+ New applications should use the base site MDX entry:
31
+
32
+ ```ts
33
+ @windrun-huaiin/third-ui/fuma/server/site-mdx/base
9
34
  ```
10
35
 
11
- ## TailwindCSS 4.x Config
12
-
13
- - Assume you have a project structure like this:
14
-
15
- ```txt
16
- Your-project/
17
- ├── src/
18
- │ └── app/
19
- │ └── globals.css
20
- ├── node_modules/
21
- │ ├── @windrun-huaiin/
22
- │ │ ├── third-ui/
23
- │ │ │ └── src/ # This is third-ui src
24
- │ │ └── base-ui/
25
- │ │ └── src/ # This is base-ui src
26
- └── package.json
36
+ Optional renderer features live behind physical subpath entries:
37
+
38
+ ```ts
39
+ @windrun-huaiin/third-ui/fuma/server/site-mdx/features/code
40
+ @windrun-huaiin/third-ui/fuma/server/site-mdx/features/math
41
+ @windrun-huaiin/third-ui/fuma/server/site-mdx/features/mermaid
42
+ @windrun-huaiin/third-ui/fuma/server/site-mdx/features/type-table
27
43
  ```
28
44
 
29
- - Then, in your `globals.css` file, you have to configure Tailwind CSS 4.x like this:
45
+ The old aggregate entries have been removed. There is no supported `site-mdx-components` entry and no `optional-features` aggregate entry.
30
46
 
31
- ```css
32
- @import 'tailwindcss';
47
+ ## Base Components
33
48
 
34
- @source "../node_modules/@windrun-huaiin/third-ui/src/**/*.{js,ts,jsx,tsx}";
35
- @source "../node_modules/@windrun-huaiin/base-ui/src/**/*.{js,ts,jsx,tsx}";
36
- @source "./src/**/*.{js,ts,jsx,tsx}";
49
+ The base entry is the minimum component set for normal documentation content.
37
50
 
38
- /* Import styles */
39
- @import '@windrun-huaiin/third-ui/styles/third-ui.css';
40
- ```
51
+ It includes:
41
52
 
53
+ - Fuma UI basics: `Card`, `Cards`, `Callout`, `File`, `Folder`, `Files`, `Accordion`, `Accordions`, `Tab`, `Tabs`.
54
+ - Markdown element renderers: headings, links, blockquote, lists, table, inline code, pre, image, and related primitive tags.
55
+ - Site-level lightweight components: `SiteX`, `TrophyCard`, `ZiaCard`, `GradientButton`, `ZiaFile`, `ZiaFolder`, `SunoEmbed`.
56
+ - Image rendering integration with image fallback and CDN-related options.
57
+ - Feature-specific fallback components for disabled math, Mermaid, type table, and code-tab style components.
42
58
 
43
- ## Usage Example
59
+ It intentionally does not include heavyweight renderer implementations such as Mermaid, KaTeX rendering, Fuma codeblock rendering, or type-table rendering.
44
60
 
45
- Root entry import is not supported. Always import from an explicit subpath such as `@windrun-huaiin/third-ui/clerk` or `@windrun-huaiin/third-ui/main`.
61
+ ## Renderer Feature Matrix
46
62
 
47
- ### Import components by module
48
- ```tsx
49
- // Only import Clerk related components
50
- import { ClerkUser, ClerkOrganization } from '@windrun-huaiin/third-ui/clerk';
63
+ | Capability | Renderer Entry | Compiler Entry Required | Notes |
64
+ | --- | --- | --- | --- |
65
+ | `base` | `site-mdx/base` | `local-md/presets/fuma-docs/base` | Required for normal MDX rendering |
66
+ | `code` | `site-mdx/features/code` | `local-md/presets/fuma-docs/features/code` | Enables Fuma codeblock UI and built-in language icon mapping |
67
+ | `math` | `site-mdx/features/math` | `local-md/presets/fuma-docs/features/math` | Enables `MathBlock` and `InlineMath` rendering |
68
+ | `mermaid` | `site-mdx/features/mermaid` | Not required | Renderer-only component capability |
69
+ | `type-table` | `site-mdx/features/type-table` | Not required | Renderer-only component capability |
70
+ | `npm` | Not required | `local-md/presets/fuma-docs/features/npm` | Compiler-only content transform |
51
71
 
52
- // Only import main application components
53
- import { CTA, Features } from '@windrun-huaiin/third-ui/main';
72
+ This table is the main rule for application integration.
54
73
 
55
- // Only import Fumadocs components
56
- import { FumaPageGenerator, FumaBannerSuit } from '@windrun-huaiin/third-ui/fuma/server';
74
+ `code` and `math` need both compiler and renderer support. `npm` is compiler-only. `mermaid` and `type-table` are renderer-only.
57
75
 
58
- // Shared MDX building blocks
59
- import { TocFooterWrapper, PortableClerkTOC } from '@windrun-huaiin/third-ui/fuma/mdx';
60
- ```
76
+ ## Bundle Cropping Rule
61
77
 
62
- ### Use components
63
- ```tsx
64
- // Clerk user component (need to pass in translations and configuration)
65
- <ClerkUser
66
- locale="zh"
67
- clerkAuthInModal={true}
68
- t={{ signIn: "Sign in" }}
69
- t2={{ terms: "Terms of Service", privacy: "Privacy Policy" }}
70
- />
71
-
72
- // Clerk organization component
73
- <ClerkOrganization locale="zh" className="custom-class" />
74
-
75
- // Main application components
76
- <CTA />
77
- <Features />
78
- ```
78
+ Renderer pruning is based on import boundaries.
79
79
 
80
- ## Design Principles
81
-
82
- 1. **Modularization**: Grouped by functional domain, support import on demand
83
- 2. **Parameterization**: Remove hard-coded dependencies, pass configuration through props
84
- 3. **Type safety**: Full TypeScript support
85
- 4. **Path alias**: Use `@/` alias internally, keep code clean
86
-
87
- ## Dependencies
88
-
89
- - `@windrun-huaiin/base-ui`: Base UI components
90
- - `@windrun-huaiin/lib`: General utility library
91
- - `@clerk/nextjs`: Clerk authentication
92
- - `fumadocs-core`, `fumadocs-ui`: Fumadocs documentation
93
-
94
- ## Notes
95
-
96
- - Components have removed direct `appConfig` dependencies, and configuration is passed through props
97
- - Clerk components need to provide correct translations in the application layer
98
- - Some components may require specific CSS animation classes (e.g. `animate-cta-gradient-wave`)
99
-
100
- ## Component List
101
-
102
- ### Clerk module
103
- - `ClerkProviderClient` - Clerk authentication provider
104
- - `ClerkUser` - User button component
105
- - `ClerkOrganization` - Organization switcher component
106
-
107
- ### Main module
108
- - `CTA` - Call-to-Action component
109
- - `Features` - Feature showcase component
110
- - `Footer` - Footer component
111
- - `Gallery` - Image gallery component
112
- - `GoToTop` - Go to top button
113
- - `SEOContent` - SEO content component
114
- - `Tips` - Tip component
115
-
116
- ### Fuma module
117
- - `getMDXComponents` - MDX component configuration function
118
- - `createMDXComponents` - MDX component factory function
119
- - `TocFooter` - Table of contents footer component
120
- - `FumaBannerSuit` - Fumadocs banner component
121
-
122
- ### Fuma MDX submodule
123
- - `Mermaid` - Flowchart component
124
- - `ImageZoom` - Image zoom component
125
- - `TrophyCard` - Trophy card component
126
- - `ImageGrid` - Image grid component
127
- - `ZiaCard` - Zia card component
128
- - `GradientButton` - Gradient button component
129
-
130
- ## Usage
131
-
132
- ### Clerk components
133
-
134
- ```tsx
135
- import { ClerkProviderClient, ClerkUser } from '@windrun-huaiin/third-ui/clerk';
136
-
137
- // Use in layout.tsx
138
- <ClerkProviderClient
139
- signInUrl="/sign-in"
140
- signUpUrl="/sign-up"
141
- waitlistUrl="/waitlist"
142
- >
143
- {children}
144
- </ClerkProviderClient>
145
-
146
- // Use in navigation bar
147
- <ClerkUser clerkAuthInModal={true} />
148
- ```
80
+ If an application does not import `site-mdx/features/mermaid`, Mermaid renderer code should not be pulled into the base renderer entry. The same rule applies to code, math, and type-table renderer features.
149
81
 
150
- ### Main components
82
+ Do not reintroduce a file that imports all renderer features and then chooses one at runtime. That pattern defeats bundle cropping because static imports run before configuration checks.
151
83
 
152
- ```tsx
153
- import { CTA, Features, Footer } from '@windrun-huaiin/third-ui/main';
84
+ Correct model:
154
85
 
155
- // Use various page components
156
- <Features />
157
- <CTA />
158
- <Footer />
159
- ```
86
+ - base entry imports only base renderers and lightweight fallback components.
87
+ - each optional feature entry imports only its own renderer implementation.
88
+ - application code imports the feature entries it wants.
160
89
 
161
- ### Fumadocs components
162
-
163
- ```tsx
164
- import { createMDXComponents, TocFooter } from '@windrun-huaiin/third-ui/fuma';
165
-
166
- // Create pre-configured MDX components
167
- const getMDXComponents = createMDXComponents({
168
- watermark: {
169
- enabled: true,
170
- text: "Your Watermark"
171
- },
172
- githubBaseUrl: "https://github.com/your-org/your-repo/edit/main/"
173
- });
174
-
175
- // Use in page
176
- const MDX = page.data.body;
177
- <MDX components={getMDXComponents()} />
178
-
179
- // Use TocFooter
180
- <TocFooter
181
- lastModified={page.data.date}
182
- showCopy={true}
183
- editPath="src/docs/your-file.mdx"
184
- githubBaseUrl="https://github.com/your-org/your-repo/edit/main/"
185
- />
186
- ```
90
+ ## Fallback Design
91
+
92
+ There are two fallback layers.
93
+
94
+ The first layer lives in `third-ui`.
95
+
96
+ It provides feature-aware fallbacks for known but disabled MDX capabilities:
97
+
98
+ - `MathBlock`
99
+ - `InlineMath`
100
+ - `Mermaid`
101
+ - `TypeTable`
102
+ - `CodeBlockTab`
103
+ - `CodeBlockTabs`
104
+ - `CodeBlockTabsList`
105
+ - `CodeBlockTabsTrigger`
106
+
107
+ These fallbacks render visible warning blocks instead of silently dropping content.
187
108
 
188
- #### MDX components global configuration
109
+ The second layer lives in `local-md`.
189
110
 
190
- In MDX file:
111
+ It is the final safety net for arbitrary unknown PascalCase components, such as:
191
112
 
192
113
  ```mdx
193
- <!-- Mermaid chart, watermark automatically applied -->
194
- <Mermaid
195
- chart="graph TD; A-->B"
196
- title="My Diagram"
197
- />
198
-
199
- <!-- Image grid, CDN URL automatically applied -->
200
- <ImageGrid
201
- type="url"
202
- images={["image1.webp", "image2.webp"]}
203
- altPrefix="example"
204
- />
205
-
206
- <!-- Image zoom, placeholder image automatically applied -->
207
- <ImageZoom src="/some-image.jpg" alt="Example" />
114
+ <CalloutXXX />
208
115
  ```
209
116
 
210
- All configuration parameters will be automatically obtained from the global configuration, and no need to specify them in each use.
117
+ `local-md` detects missing component references from the compiled MDX output and injects generic fallback components before rendering. This prevents unknown MDX components from crashing the page.
118
+
119
+ The priority order is:
120
+
121
+ - application-provided MDX components
122
+ - enabled `third-ui` renderer feature components
123
+ - `third-ui` known disabled-feature fallbacks
124
+ - `local-md` generic unknown-component fallback
125
+
126
+ ## Heavy Renderer Boundary
127
+
128
+ Heavy renderers are isolated under dedicated feature paths or heavy modules.
129
+
130
+ Examples:
131
+
132
+ - Mermaid rendering is behind the Mermaid feature entry.
133
+ - Math rendering uses the math feature entry and lazy heavy math renderer.
134
+ - Type table rendering is behind the type-table feature entry.
135
+ - Fuma codeblock rendering is behind the code feature entry.
136
+
137
+ The code renderer owns its programming-language icon map internally. Applications should not pass a global icon map into `createCodeMdxComponents()`.
138
+
139
+ This keeps `site-mdx/base` from becoming a hidden dependency sink.
140
+
141
+ ## Application Rules
142
+
143
+ Applications should treat their MDX integration files as the capability declaration.
144
+
145
+ For `apps/ddaas`, the important files are:
146
+
147
+ - `src/lib/content-source.ts`: compiler/source capabilities.
148
+ - `src/components/mdx-components.tsx`: renderer/component capabilities.
149
+
150
+ When enabling a capability:
151
+
152
+ - import the compiler feature only if the capability needs compiler support.
153
+ - import the renderer feature only if the capability needs renderer support.
154
+ - add the imported feature to the matching `features` list.
155
+ - do not import unused feature entries “just in case”.
156
+
157
+ If an application creates its own MDX component and that component imports a heavy package directly, the package can still enter the bundle. That is expected and belongs to the application layer.
158
+
159
+ ## Styling
160
+
161
+ MDX components assume the application includes the package styles and Tailwind source scanning for `third-ui`.
162
+
163
+ The exact global CSS setup is application-specific, but MDX pages need the same base styles used by the rest of the Fuma UI integration.
164
+
165
+ ## Export Map
166
+
167
+ MDX-related exports currently relevant to this design:
211
168
 
169
+ | Export | Purpose |
170
+ | --- | --- |
171
+ | `./fuma/server/site-mdx/base` | Recommended base MDX component factory |
172
+ | `./fuma/server/site-mdx/features/code` | Optional code renderer components |
173
+ | `./fuma/server/site-mdx/features/math` | Optional math renderer components |
174
+ | `./fuma/server/site-mdx/features/mermaid` | Optional Mermaid renderer components |
175
+ | `./fuma/server/site-mdx/features/type-table` | Optional type-table renderer components |
176
+ | `./fuma/mdx` | Shared MDX building blocks used by pages and widgets |
177
+ | `./fuma/heavy` | Heavy renderer exports; avoid importing from app base paths unless intentionally needed |
178
+ | `./fuma/share` | Shared markdown component utilities |
212
179
 
213
- ## Showcase
180
+ ## Non-Goals
214
181
 
215
- - [Free Trivia Game](https://freetrivia.info/)
216
- - [Music Poster](https://musicposter.org/en)
217
- - [Image Narration](https://imagenarration.com/en)
218
- - [Describe Yourself](https://describeyourself.org/en)
219
- - [Newspaper Template](https://newspaper-template.org/en)
220
- - [breathing exercise](https://breathingexercise.net/en)
221
- - [ai directory list](https://aidirectorylist.com/en)
222
- - [reve image directory](https://reveimage.directory/en)
182
+ - This README does not document Clerk, main UI, AI UI, fingerprint, or SEO helpers.
183
+ - The base MDX entry should not become an all-features preset.
184
+ - Runtime feature flags should not be used as the primary pruning mechanism.
185
+ - Unknown MDX components should not crash the page.
@@ -1,3 +1,2 @@
1
1
  import type { MDXComponents } from 'mdx/types';
2
- import { type ReactNode } from 'react';
3
- export declare function createCodeMdxComponents(iconMap?: Record<string, ReactNode>): MDXComponents;
2
+ export declare function createCodeMdxComponents(): MDXComponents;
@@ -51,11 +51,10 @@ function tryToMatchIcon(props, iconMap) {
51
51
  }
52
52
  return undefined;
53
53
  }
54
- function createCodeMdxComponents(iconMap = {}) {
55
- const mergedIconMap = Object.assign(Object.assign({}, defaultCodeLanguageIconMap), iconMap);
54
+ function createCodeMdxComponents() {
56
55
  return {
57
56
  pre: (props) => {
58
- const customIcon = tryToMatchIcon(props, mergedIconMap);
57
+ const customIcon = tryToMatchIcon(props, defaultCodeLanguageIconMap);
59
58
  return (jsxRuntime.jsx(CodeBlock, Object.assign({}, props, (customIcon && { icon: customIcon }), { children: jsxRuntime.jsx(Pre, { children: props.children }) })));
60
59
  },
61
60
  CodeBlock,
@@ -49,11 +49,10 @@ function tryToMatchIcon(props, iconMap) {
49
49
  }
50
50
  return undefined;
51
51
  }
52
- function createCodeMdxComponents(iconMap = {}) {
53
- const mergedIconMap = Object.assign(Object.assign({}, defaultCodeLanguageIconMap), iconMap);
52
+ function createCodeMdxComponents() {
54
53
  return {
55
54
  pre: (props) => {
56
- const customIcon = tryToMatchIcon(props, mergedIconMap);
55
+ const customIcon = tryToMatchIcon(props, defaultCodeLanguageIconMap);
57
56
  return (jsx(CodeBlock, Object.assign({}, props, (customIcon && { icon: customIcon }), { children: jsx(Pre, { children: props.children }) })));
58
57
  },
59
58
  CodeBlock,
@@ -2,13 +2,17 @@
2
2
 
3
3
  var tabs = require('fumadocs-ui/components/tabs');
4
4
  var callout = require('fumadocs-ui/components/callout');
5
+ var card = require('fumadocs-ui/components/card');
5
6
  var files = require('fumadocs-ui/components/files');
6
7
  var accordion = require('fumadocs-ui/components/accordion');
7
8
  var siteX = require('../site-x.js');
8
9
  var base = require('./features/base.js');
9
10
  var widgets = require('./features/widgets.js');
11
+ var siteMdxFallbacks = require('./site-mdx-fallbacks.js');
10
12
 
11
13
  const defaultFumaUiComponents = {
14
+ Card: card.Card,
15
+ Cards: card.Cards,
12
16
  Callout: callout.Callout,
13
17
  File: files.File,
14
18
  Folder: files.Folder,
@@ -24,7 +28,7 @@ function createSiteMdxBaseComponents(options = {}) {
24
28
  function createSiteMdxComponents(options = {}) {
25
29
  const { additionalComponents, baseOptions, features = [], } = options;
26
30
  return function getMDXComponents(components) {
27
- return Object.assign(Object.assign(Object.assign(Object.assign({}, createSiteMdxBaseComponents(baseOptions)), features.reduce((acc, feature) => (Object.assign(Object.assign({}, acc), feature)), {})), additionalComponents), components);
31
+ return siteMdxFallbacks.withMissingMdxComponentFallback(Object.assign(Object.assign(Object.assign(Object.assign({}, createSiteMdxBaseComponents(baseOptions)), features.reduce((acc, feature) => (Object.assign(Object.assign({}, acc), feature)), {})), additionalComponents), components));
28
32
  };
29
33
  }
30
34
 
@@ -1,12 +1,16 @@
1
1
  import { Tabs, Tab } from 'fumadocs-ui/components/tabs';
2
2
  import { Callout } from 'fumadocs-ui/components/callout';
3
+ import { Cards, Card } from 'fumadocs-ui/components/card';
3
4
  import { Files, Folder, File } from 'fumadocs-ui/components/files';
4
5
  import { Accordions, Accordion } from 'fumadocs-ui/components/accordion';
5
6
  import { SiteX } from '../site-x.mjs';
6
7
  import { createBaseMdxComponents } from './features/base.mjs';
7
8
  import { createWidgetMdxComponents } from './features/widgets.mjs';
9
+ import { withMissingMdxComponentFallback } from './site-mdx-fallbacks.mjs';
8
10
 
9
11
  const defaultFumaUiComponents = {
12
+ Card,
13
+ Cards,
10
14
  Callout,
11
15
  File,
12
16
  Folder,
@@ -22,7 +26,7 @@ function createSiteMdxBaseComponents(options = {}) {
22
26
  function createSiteMdxComponents(options = {}) {
23
27
  const { additionalComponents, baseOptions, features = [], } = options;
24
28
  return function getMDXComponents(components) {
25
- return Object.assign(Object.assign(Object.assign(Object.assign({}, createSiteMdxBaseComponents(baseOptions)), features.reduce((acc, feature) => (Object.assign(Object.assign({}, acc), feature)), {})), additionalComponents), components);
29
+ return withMissingMdxComponentFallback(Object.assign(Object.assign(Object.assign(Object.assign({}, createSiteMdxBaseComponents(baseOptions)), features.reduce((acc, feature) => (Object.assign(Object.assign({}, acc), feature)), {})), additionalComponents), components));
26
30
  };
27
31
  }
28
32
 
@@ -1,4 +1,5 @@
1
1
  import type { ComponentPropsWithoutRef } from 'react';
2
+ import type { MDXComponents } from 'mdx/types';
2
3
  type MissingFeatureBlockProps = ComponentPropsWithoutRef<'div'> & {
3
4
  feature: string;
4
5
  component: string;
@@ -14,5 +15,11 @@ export declare function createMissingMdxFeatureComponents(): {
14
15
  InlineMath: (props: ComponentPropsWithoutRef<"span">) => import("react/jsx-runtime").JSX.Element;
15
16
  Mermaid: (props: ComponentPropsWithoutRef<"div">) => import("react/jsx-runtime").JSX.Element;
16
17
  TypeTable: (props: ComponentPropsWithoutRef<"div">) => import("react/jsx-runtime").JSX.Element;
18
+ CodeBlockTab: (props: ComponentPropsWithoutRef<"div">) => import("react/jsx-runtime").JSX.Element;
19
+ CodeBlockTabs: (props: ComponentPropsWithoutRef<"div">) => import("react/jsx-runtime").JSX.Element;
20
+ CodeBlockTabsList: (props: ComponentPropsWithoutRef<"div">) => import("react/jsx-runtime").JSX.Element;
21
+ CodeBlockTabsTrigger: (props: ComponentPropsWithoutRef<"div">) => import("react/jsx-runtime").JSX.Element;
17
22
  };
23
+ export declare function createMissingMdxComponentFallback(component: string): (props: ComponentPropsWithoutRef<"div">) => import("react/jsx-runtime").JSX.Element;
24
+ export declare function withMissingMdxComponentFallback(components: MDXComponents): MDXComponents;
18
25
  export {};
@@ -41,9 +41,39 @@ function createMissingMdxFeatureComponents() {
41
41
  InlineMath: (props) => (jsxRuntime.jsx(MissingMdxFeatureInline, Object.assign({}, props, { feature: "math", component: "InlineMath" }))),
42
42
  Mermaid: (props) => (jsxRuntime.jsx(MissingMdxFeatureBlock, Object.assign({}, props, { feature: "diagram renderer", component: "Mermaid" }))),
43
43
  TypeTable: (props) => (jsxRuntime.jsx(MissingMdxFeatureBlock, Object.assign({}, props, { feature: "API table renderer", component: "TypeTable" }))),
44
+ CodeBlockTab: (props) => (jsxRuntime.jsx(MissingMdxFeatureBlock, Object.assign({}, props, { feature: "code", component: "CodeBlockTab" }))),
45
+ CodeBlockTabs: (props) => (jsxRuntime.jsx(MissingMdxFeatureBlock, Object.assign({}, props, { feature: "code", component: "CodeBlockTabs" }))),
46
+ CodeBlockTabsList: (props) => (jsxRuntime.jsx(MissingMdxFeatureBlock, Object.assign({}, props, { feature: "code", component: "CodeBlockTabsList" }))),
47
+ CodeBlockTabsTrigger: (props) => (jsxRuntime.jsx(MissingMdxFeatureBlock, Object.assign({}, props, { feature: "code", component: "CodeBlockTabsTrigger" }))),
44
48
  };
45
49
  }
50
+ function createMissingMdxComponentFallback(component) {
51
+ return function MissingMdxComponent(props) {
52
+ return (jsxRuntime.jsx(MissingMdxFeatureBlock, Object.assign({}, props, { feature: "unknown MDX component", component: component })));
53
+ };
54
+ }
55
+ function withMissingMdxComponentFallback(components) {
56
+ return new Proxy(components, {
57
+ has(target, prop) {
58
+ if (typeof prop === 'string' && /^[A-Z]/.test(prop)) {
59
+ return true;
60
+ }
61
+ return prop in target;
62
+ },
63
+ get(target, prop, receiver) {
64
+ if (typeof prop !== 'string' || prop in target) {
65
+ return Reflect.get(target, prop, receiver);
66
+ }
67
+ if (/^[A-Z]/.test(prop)) {
68
+ return createMissingMdxComponentFallback(prop);
69
+ }
70
+ return Reflect.get(target, prop, receiver);
71
+ },
72
+ });
73
+ }
46
74
 
47
75
  exports.MissingMdxFeatureBlock = MissingMdxFeatureBlock;
48
76
  exports.MissingMdxFeatureInline = MissingMdxFeatureInline;
77
+ exports.createMissingMdxComponentFallback = createMissingMdxComponentFallback;
49
78
  exports.createMissingMdxFeatureComponents = createMissingMdxFeatureComponents;
79
+ exports.withMissingMdxComponentFallback = withMissingMdxComponentFallback;
@@ -39,7 +39,35 @@ function createMissingMdxFeatureComponents() {
39
39
  InlineMath: (props) => (jsx(MissingMdxFeatureInline, Object.assign({}, props, { feature: "math", component: "InlineMath" }))),
40
40
  Mermaid: (props) => (jsx(MissingMdxFeatureBlock, Object.assign({}, props, { feature: "diagram renderer", component: "Mermaid" }))),
41
41
  TypeTable: (props) => (jsx(MissingMdxFeatureBlock, Object.assign({}, props, { feature: "API table renderer", component: "TypeTable" }))),
42
+ CodeBlockTab: (props) => (jsx(MissingMdxFeatureBlock, Object.assign({}, props, { feature: "code", component: "CodeBlockTab" }))),
43
+ CodeBlockTabs: (props) => (jsx(MissingMdxFeatureBlock, Object.assign({}, props, { feature: "code", component: "CodeBlockTabs" }))),
44
+ CodeBlockTabsList: (props) => (jsx(MissingMdxFeatureBlock, Object.assign({}, props, { feature: "code", component: "CodeBlockTabsList" }))),
45
+ CodeBlockTabsTrigger: (props) => (jsx(MissingMdxFeatureBlock, Object.assign({}, props, { feature: "code", component: "CodeBlockTabsTrigger" }))),
42
46
  };
43
47
  }
48
+ function createMissingMdxComponentFallback(component) {
49
+ return function MissingMdxComponent(props) {
50
+ return (jsx(MissingMdxFeatureBlock, Object.assign({}, props, { feature: "unknown MDX component", component: component })));
51
+ };
52
+ }
53
+ function withMissingMdxComponentFallback(components) {
54
+ return new Proxy(components, {
55
+ has(target, prop) {
56
+ if (typeof prop === 'string' && /^[A-Z]/.test(prop)) {
57
+ return true;
58
+ }
59
+ return prop in target;
60
+ },
61
+ get(target, prop, receiver) {
62
+ if (typeof prop !== 'string' || prop in target) {
63
+ return Reflect.get(target, prop, receiver);
64
+ }
65
+ if (/^[A-Z]/.test(prop)) {
66
+ return createMissingMdxComponentFallback(prop);
67
+ }
68
+ return Reflect.get(target, prop, receiver);
69
+ },
70
+ });
71
+ }
44
72
 
45
- export { MissingMdxFeatureBlock, MissingMdxFeatureInline, createMissingMdxFeatureComponents };
73
+ export { MissingMdxFeatureBlock, MissingMdxFeatureInline, createMissingMdxComponentFallback, createMissingMdxFeatureComponents, withMissingMdxComponentFallback };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windrun-huaiin/third-ui",
3
- "version": "23.0.0",
3
+ "version": "23.1.0",
4
4
  "description": "Third-party integrated UI components for windrun-huaiin projects",
5
5
  "exports": {
6
6
  "./clerk": {
@@ -48,11 +48,6 @@
48
48
  "import": "./dist/fuma/server.mjs",
49
49
  "require": "./dist/fuma/server.js"
50
50
  },
51
- "./fuma/server/site-mdx-components": {
52
- "types": "./dist/fuma/server/site-mdx-components.d.ts",
53
- "import": "./dist/fuma/server/site-mdx-components.mjs",
54
- "require": "./dist/fuma/server/site-mdx-components.js"
55
- },
56
51
  "./fuma/server/site-mdx/base": {
57
52
  "types": "./dist/fuma/server/site-mdx-base.d.ts",
58
53
  "import": "./dist/fuma/server/site-mdx-base.mjs",
@@ -93,11 +88,6 @@
93
88
  "import": "./dist/fuma/mdx/index.mjs",
94
89
  "require": "./dist/fuma/mdx/index.js"
95
90
  },
96
- "./fuma/server/optional-features": {
97
- "types": "./dist/fuma/server/optional-features.d.ts",
98
- "import": "./dist/fuma/server/optional-features.mjs",
99
- "require": "./dist/fuma/server/optional-features.js"
100
- },
101
91
  "./fuma/base": {
102
92
  "types": "./dist/fuma/base/index.d.ts",
103
93
  "import": "./dist/fuma/base/index.mjs",
@@ -147,9 +137,9 @@
147
137
  "tslib": "^2.8.1",
148
138
  "unified": "^11.0.5",
149
139
  "zod": "^4.3.6",
150
- "@windrun-huaiin/base-ui": "^23.0.0",
151
- "@windrun-huaiin/lib": "^23.0.0",
152
- "@windrun-huaiin/contracts": "^23.0.0"
140
+ "@windrun-huaiin/base-ui": "^23.1.0",
141
+ "@windrun-huaiin/contracts": "^23.1.0",
142
+ "@windrun-huaiin/lib": "^23.1.0"
153
143
  },
154
144
  "peerDependencies": {
155
145
  "clsx": "^2.1.1",
@@ -78,17 +78,13 @@ function tryToMatchIcon(
78
78
  return undefined;
79
79
  }
80
80
 
81
- export function createCodeMdxComponents(
82
- iconMap: Record<string, ReactNode> = {},
83
- ): MDXComponents {
84
- const mergedIconMap = {
85
- ...defaultCodeLanguageIconMap,
86
- ...iconMap,
87
- };
88
-
81
+ export function createCodeMdxComponents(): MDXComponents {
89
82
  return {
90
83
  pre: (props) => {
91
- const customIcon = tryToMatchIcon(props as MDXProps & { 'data-language'?: string; title?: string }, mergedIconMap);
84
+ const customIcon = tryToMatchIcon(
85
+ props as MDXProps & { 'data-language'?: string; title?: string },
86
+ defaultCodeLanguageIconMap,
87
+ );
92
88
  return (
93
89
  <CodeBlock
94
90
  {...props}
@@ -1,11 +1,13 @@
1
1
  import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
2
2
  import { Callout } from 'fumadocs-ui/components/callout';
3
+ import { Card, Cards } from 'fumadocs-ui/components/card';
3
4
  import { File, Folder, Files } from 'fumadocs-ui/components/files';
4
5
  import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
5
6
  import type { MDXComponents } from 'mdx/types';
6
7
  import { SiteX } from '../site-x';
7
8
  import { createBaseMdxComponents } from './features/base';
8
9
  import { createWidgetMdxComponents } from './features/widgets';
10
+ import { withMissingMdxComponentFallback } from './site-mdx-fallbacks';
9
11
 
10
12
  export type SiteMdxFeatureComponents = MDXComponents;
11
13
 
@@ -21,6 +23,8 @@ export interface CreateSiteMdxComponentsOptions {
21
23
  }
22
24
 
23
25
  const defaultFumaUiComponents: MDXComponents = {
26
+ Card,
27
+ Cards,
24
28
  Callout,
25
29
  File,
26
30
  Folder,
@@ -52,11 +56,11 @@ export function createSiteMdxComponents(
52
56
  } = options;
53
57
 
54
58
  return function getMDXComponents(components?: MDXComponents): MDXComponents {
55
- return {
59
+ return withMissingMdxComponentFallback({
56
60
  ...createSiteMdxBaseComponents(baseOptions),
57
61
  ...features.reduce<MDXComponents>((acc, feature) => ({ ...acc, ...feature }), {}),
58
62
  ...additionalComponents,
59
63
  ...components,
60
- };
64
+ });
61
65
  };
62
66
  }
@@ -1,4 +1,5 @@
1
1
  import type { ComponentPropsWithoutRef, ReactNode } from 'react';
2
+ import type { MDXComponents } from 'mdx/types';
2
3
 
3
4
  type MissingFeatureBlockProps = ComponentPropsWithoutRef<'div'> & {
4
5
  feature: string;
@@ -118,5 +119,54 @@ export function createMissingMdxFeatureComponents() {
118
119
  TypeTable: (props: ComponentPropsWithoutRef<'div'>) => (
119
120
  <MissingMdxFeatureBlock {...props} feature="API table renderer" component="TypeTable" />
120
121
  ),
122
+ CodeBlockTab: (props: ComponentPropsWithoutRef<'div'>) => (
123
+ <MissingMdxFeatureBlock {...props} feature="code" component="CodeBlockTab" />
124
+ ),
125
+ CodeBlockTabs: (props: ComponentPropsWithoutRef<'div'>) => (
126
+ <MissingMdxFeatureBlock {...props} feature="code" component="CodeBlockTabs" />
127
+ ),
128
+ CodeBlockTabsList: (props: ComponentPropsWithoutRef<'div'>) => (
129
+ <MissingMdxFeatureBlock {...props} feature="code" component="CodeBlockTabsList" />
130
+ ),
131
+ CodeBlockTabsTrigger: (props: ComponentPropsWithoutRef<'div'>) => (
132
+ <MissingMdxFeatureBlock {...props} feature="code" component="CodeBlockTabsTrigger" />
133
+ ),
134
+ };
135
+ }
136
+
137
+ export function createMissingMdxComponentFallback(component: string) {
138
+ return function MissingMdxComponent(props: ComponentPropsWithoutRef<'div'>) {
139
+ return (
140
+ <MissingMdxFeatureBlock
141
+ {...props}
142
+ feature="unknown MDX component"
143
+ component={component}
144
+ />
145
+ );
121
146
  };
122
147
  }
148
+
149
+ export function withMissingMdxComponentFallback(
150
+ components: MDXComponents,
151
+ ): MDXComponents {
152
+ return new Proxy(components, {
153
+ has(target, prop) {
154
+ if (typeof prop === 'string' && /^[A-Z]/.test(prop)) {
155
+ return true;
156
+ }
157
+
158
+ return prop in target;
159
+ },
160
+ get(target, prop, receiver) {
161
+ if (typeof prop !== 'string' || prop in target) {
162
+ return Reflect.get(target, prop, receiver);
163
+ }
164
+
165
+ if (/^[A-Z]/.test(prop)) {
166
+ return createMissingMdxComponentFallback(prop);
167
+ }
168
+
169
+ return Reflect.get(target, prop, receiver);
170
+ },
171
+ });
172
+ }
@@ -1,6 +0,0 @@
1
- export { createBaseMdxComponents } from './features/base';
2
- export { createCodeMdxComponents } from './features/code';
3
- export { createMathMdxComponents } from './features/math';
4
- export { createMermaidMdxComponents } from './features/mermaid';
5
- export { createTypeTableMdxComponents } from './features/type-table';
6
- export { createWidgetMdxComponents } from './features/widgets';
@@ -1,46 +0,0 @@
1
- import type { MDXComponents } from 'mdx/types';
2
- import type { ReactNode } from 'react';
3
- import {
4
- createComposedSiteMdxComponents,
5
- DEFAULT_SITE_MDX_FEATURES,
6
- type SiteMdxFeature,
7
- } from './site-mdx-presets';
8
-
9
- export interface SiteMdxComponentsOptions {
10
- imageFallbackSrc?: string;
11
- cdnBaseUrl?: string;
12
- watermarkEnabled?: boolean;
13
- watermarkText?: string;
14
- additionalComponents?: MDXComponents;
15
- iconMap?: Record<string, ReactNode>;
16
- features?: SiteMdxFeature[];
17
- }
18
-
19
- export function createSiteMdxComponents(
20
- options: SiteMdxComponentsOptions,
21
- ) {
22
- const {
23
- additionalComponents,
24
- cdnBaseUrl,
25
- features = DEFAULT_SITE_MDX_FEATURES,
26
- iconMap = {},
27
- imageFallbackSrc,
28
- watermarkEnabled,
29
- watermarkText,
30
- } = options;
31
-
32
- return function getMDXComponents(components?: MDXComponents): MDXComponents {
33
- return createComposedSiteMdxComponents(
34
- features,
35
- {
36
- cdnBaseUrl,
37
- iconMap,
38
- imageFallbackSrc,
39
- watermarkEnabled,
40
- watermarkText,
41
- },
42
- additionalComponents,
43
- components,
44
- );
45
- };
46
- }
@@ -1,131 +0,0 @@
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
-
18
- export type SiteMdxFeature = 'base' | 'code' | 'math' | 'mermaid' | 'type-table';
19
-
20
- const defaultFumaUiComponents: MDXComponents = {
21
- Callout,
22
- File,
23
- Folder,
24
- Files,
25
- Accordion,
26
- Accordions,
27
- Tab,
28
- Tabs,
29
- };
30
-
31
- export const DEFAULT_SITE_MDX_FEATURES: SiteMdxFeature[] = [
32
- 'base',
33
- 'code',
34
- 'math',
35
- 'mermaid',
36
- 'type-table',
37
- ];
38
-
39
- export function createSiteFeatureComponentMap(
40
- options: SiteMdxComponentsOptions,
41
- ) {
42
- const {
43
- cdnBaseUrl,
44
- iconMap = {},
45
- imageFallbackSrc,
46
- watermarkEnabled,
47
- watermarkText,
48
- } = options;
49
-
50
- return {
51
- base: {
52
- ...defaultFumaUiComponents,
53
- SiteX,
54
- ...createBaseMdxComponents(imageFallbackSrc),
55
- ...createWidgetMdxComponents(cdnBaseUrl, imageFallbackSrc),
56
- },
57
- code: createCodeMdxComponents(iconMap),
58
- math: createMathMdxComponents(),
59
- mermaid: createMermaidMdxComponents(watermarkEnabled, watermarkText),
60
- 'type-table': createTypeTableMdxComponents(),
61
- } satisfies Record<SiteMdxFeature, MDXComponents>;
62
- }
63
-
64
- function createSiteFeatureComponents(
65
- feature: SiteMdxFeature,
66
- options: SiteMdxComponentsOptions,
67
- ): MDXComponents {
68
- const {
69
- cdnBaseUrl,
70
- iconMap = {},
71
- imageFallbackSrc,
72
- watermarkEnabled,
73
- watermarkText,
74
- } = options;
75
-
76
- switch (feature) {
77
- case 'base':
78
- return {
79
- ...defaultFumaUiComponents,
80
- SiteX,
81
- ...createBaseMdxComponents(imageFallbackSrc),
82
- ...createWidgetMdxComponents(cdnBaseUrl, imageFallbackSrc),
83
- };
84
- case 'code':
85
- return createCodeMdxComponents(iconMap);
86
- case 'math':
87
- return createMathMdxComponents();
88
- case 'mermaid':
89
- return createMermaidMdxComponents(watermarkEnabled, watermarkText);
90
- case 'type-table':
91
- return createTypeTableMdxComponents();
92
- }
93
- }
94
-
95
- export function composeSiteMdxComponents(
96
- features: readonly SiteMdxFeature[],
97
- featureMap: Record<SiteMdxFeature, MDXComponents>,
98
- additionalComponents?: MDXComponents,
99
- components?: MDXComponents,
100
- ): MDXComponents {
101
- return {
102
- ...defaultMdxComponents,
103
- ...features.reduce<MDXComponents>((acc, feature) => {
104
- return {
105
- ...acc,
106
- ...featureMap[feature],
107
- };
108
- }, {}),
109
- ...additionalComponents,
110
- ...components,
111
- };
112
- }
113
-
114
- export function createComposedSiteMdxComponents(
115
- features: readonly SiteMdxFeature[],
116
- options: SiteMdxComponentsOptions,
117
- additionalComponents?: MDXComponents,
118
- components?: MDXComponents,
119
- ): MDXComponents {
120
- return {
121
- ...defaultMdxComponents,
122
- ...features.reduce<MDXComponents>((acc, feature) => {
123
- return {
124
- ...acc,
125
- ...createSiteFeatureComponents(feature, options),
126
- };
127
- }, {}),
128
- ...additionalComponents,
129
- ...components,
130
- };
131
- }