@blogmcp/mdx-render-react 0.9.0 → 0.9.1

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,35 +1,40 @@
1
1
  # @blogmcp/mdx-render-react
2
2
 
3
- **Status: scaffold only.** Implementation deferred until a React consumer exists (blog or md-editor React port).
3
+ React renderer for MDX components. Consumers: the Next.js managed blog (`apps/knowledge-base/blog-next`),
4
+ `@blogmcp/sdk/react` (`KbMdxContent`) and `@blogmcp/render-embed-react`.
4
5
 
5
6
  This package mirrors [`@blogmcp/mdx-render-vue`](../mdx-render-vue/) against the shared
6
7
  [`@blogmcp/mdx-schema`](../mdx-schema/) contract. Authoring stays identical across frameworks
7
8
  (same MDX syntax, same metadata, same MCP tool output) — only the renderer swaps.
8
9
 
9
- ## Contract to implement
10
-
11
- ```ts
12
- import { componentMetadata } from '@blogmcp/mdx-schema'
13
- import type { ComponentType } from 'react'
14
-
15
- export interface ReactComponentRegistration {
16
- metadata: ComponentMetadata // from @blogmcp/mdx-schema
17
- render: Record<string, () => Promise<{ default: ComponentType<any> }>>
18
- edit: () => Promise<{ default: ComponentType<any> }>
19
- }
20
-
21
- export const reactRegistry: Record<string, ReactComponentRegistration>
22
- ```
23
-
24
- Implementation mirrors [`packages/mdx-render-vue/src/registry.ts`](../mdx-render-vue/src/registry.ts)
25
- — same keys (`Callout`, `YouTube`, `Gallery`, `CodeBlock`, `AudioPlayer`), same variants, React
26
- components instead of Vue SFCs. Prefer Ark UI React primitives where Vue uses Ark UI Vue; share
27
- Tailwind classes verbatim.
28
-
29
- ## When to implement
30
-
31
- When the first React consumer appears (blog-react or editor-react). Until then, keep the package
32
- empty so the schema contract proves framework-neutral by construction.
10
+ ## Two artifacts (design D4)
11
+
12
+ - **`MdxContent`** (`src/MdxContent.tsx`) — the render path. An async Server Component: strips
13
+ frontmatter, fast-paths pure markdown through `marked`, otherwise extracts the MDX components and
14
+ renders each through `wrappedServerRenderMap` (`src/server-render-map.ts`: direct static imports,
15
+ every component's props run through the schema's `prepareProps`), recursing into children that
16
+ contain MDX. Unknown components get the yellow fallback.
17
+ - **`reactRegistry`** (`src/registry.ts`, export `./registry`) — lazy `import()` loaders with the same
18
+ keys and variants as the Vue registry, for tooling (dynamic resolution, a future React editor);
19
+ the blog does not use it. `edit` is optional.
20
+
21
+ Parity selector: every component root carries `data-mdx-component="<Name>"` in both renderers, and
22
+ the class strings come from `@blogmcp/mdx-schema/styles`, so they cannot drift (`test/style-parity.spec.tsx`).
23
+
24
+ ## Server Components and client islands
25
+
26
+ Button, Card, Alert, Badge, Callout and Icon are plain Server Components (no client JS). Tooltip
27
+ (`@ark-ui/react`), AudioPlayer, YouTube (thumbnail facade → iframe), CodeBlock (copy button),
28
+ Gallery (lightbox) and TripMap (MapLibre loaded lazily) are client islands, each with its own
29
+ `'use client'` banner. The build (`vite` with `preserveModules` + `rollup-plugin-preserve-directives`,
30
+ then `tsc --emitDeclarationOnly`) keeps those banners per file, so a Next.js consumer ships them as
31
+ islands; do not collapse the output into one bundle. Each island's initial DOM and classes match the
32
+ Vue component.
33
+
34
+ **Icon renders nothing in SSR.** `@iconify/react` resolves icon names from the API on the client, so
35
+ the server HTML holds an empty `<span>` — no `<svg>` and no `data-mdx-component` marker until
36
+ hydration (`test/ssr.spec.tsx`). A parity check must assert Icon after hydration; an app that needs
37
+ server-rendered icons must bundle the icon set. The same holds for `@iconify/vue`.
33
38
 
34
39
  ## Real-browser tests
35
40
 
@@ -2,7 +2,7 @@
2
2
  * Static render map used by MdxContent.
3
3
  *
4
4
  * Direct (non-lazy) imports of every render component, keyed by the schema's
5
- * PascalCase component name. The two client islands (Tooltip, AudioPlayer)
5
+ * PascalCase component name. The client islands (Tooltip, AudioPlayer, YouTube, CodeBlock, Gallery, TripMap)
6
6
  * are referenced here too — a Next.js consumer ships them as islands because
7
7
  * each carries its own `'use client'` banner.
8
8
  *
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@blogmcp/mdx-render-react",
3
- "version": "0.9.0",
4
- "description": "React renderer for MDX components — implements the @blogmcp/mdx-schema contract alongside @blogmcp/mdx-render-vue. Server-Component-first with two client islands (Tooltip, AudioPlayer).",
3
+ "version": "0.9.1",
4
+ "description": "React renderer for MDX components — implements the @blogmcp/mdx-schema contract alongside @blogmcp/mdx-render-vue. Server-Component-first with client islands (Tooltip, AudioPlayer).",
5
5
  "type": "module",
6
6
  "publishConfig": {
7
7
  "access": "public"
@@ -38,10 +38,10 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "@ark-ui/react": "^5.0.0",
41
- "@blogmcp/mdx-schema": "^0.9.0",
41
+ "@blogmcp/mdx-schema": "^0.9.1",
42
42
  "@blogmcp/tripmap-core": "^0.1.2",
43
- "@blogmcp/sanitize": "^0.9.0",
44
- "@blogmcp/sdk": "^0.9.0",
43
+ "@blogmcp/sanitize": "^0.9.1",
44
+ "@blogmcp/sdk": "^0.9.1",
45
45
  "@iconify/react": "^6.0.0",
46
46
  "marked": "^15.0.7"
47
47
  },
@@ -2,7 +2,7 @@
2
2
  * Static render map used by MdxContent.
3
3
  *
4
4
  * Direct (non-lazy) imports of every render component, keyed by the schema's
5
- * PascalCase component name. The two client islands (Tooltip, AudioPlayer)
5
+ * PascalCase component name. The client islands (Tooltip, AudioPlayer, YouTube, CodeBlock, Gallery, TripMap)
6
6
  * are referenced here too — a Next.js consumer ships them as islands because
7
7
  * each carries its own `'use client'` banner.
8
8
  *