@frontmcp/ui 0.5.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/LICENSE +201 -0
- package/README.md +438 -0
- package/package.json +147 -0
- package/src/adapters/index.d.ts +10 -0
- package/src/adapters/index.js +18 -0
- package/src/adapters/index.js.map +1 -0
- package/src/adapters/platform-meta.d.ts +165 -0
- package/src/adapters/platform-meta.js +310 -0
- package/src/adapters/platform-meta.js.map +1 -0
- package/src/base-template/bridge.d.ts +89 -0
- package/src/base-template/bridge.js +452 -0
- package/src/base-template/bridge.js.map +1 -0
- package/src/base-template/default-base-template.d.ts +91 -0
- package/src/base-template/default-base-template.js +435 -0
- package/src/base-template/default-base-template.js.map +1 -0
- package/src/base-template/index.d.ts +14 -0
- package/src/base-template/index.js +30 -0
- package/src/base-template/index.js.map +1 -0
- package/src/base-template/polyfills.d.ts +30 -0
- package/src/base-template/polyfills.js +190 -0
- package/src/base-template/polyfills.js.map +1 -0
- package/src/base-template/theme-styles.d.ts +73 -0
- package/src/base-template/theme-styles.js +95 -0
- package/src/base-template/theme-styles.js.map +1 -0
- package/src/bridge/adapters/base-adapter.d.ts +103 -0
- package/src/bridge/adapters/base-adapter.js +314 -0
- package/src/bridge/adapters/base-adapter.js.map +1 -0
- package/src/bridge/adapters/claude.adapter.d.ts +66 -0
- package/src/bridge/adapters/claude.adapter.js +145 -0
- package/src/bridge/adapters/claude.adapter.js.map +1 -0
- package/src/bridge/adapters/ext-apps.adapter.d.ts +142 -0
- package/src/bridge/adapters/ext-apps.adapter.js +416 -0
- package/src/bridge/adapters/ext-apps.adapter.js.map +1 -0
- package/src/bridge/adapters/gemini.adapter.d.ts +63 -0
- package/src/bridge/adapters/gemini.adapter.js +160 -0
- package/src/bridge/adapters/gemini.adapter.js.map +1 -0
- package/src/bridge/adapters/generic.adapter.d.ts +55 -0
- package/src/bridge/adapters/generic.adapter.js +108 -0
- package/src/bridge/adapters/generic.adapter.js.map +1 -0
- package/src/bridge/adapters/index.d.ts +25 -0
- package/src/bridge/adapters/index.js +65 -0
- package/src/bridge/adapters/index.js.map +1 -0
- package/src/bridge/adapters/openai.adapter.d.ts +64 -0
- package/src/bridge/adapters/openai.adapter.js +194 -0
- package/src/bridge/adapters/openai.adapter.js.map +1 -0
- package/src/bridge/core/adapter-registry.d.ts +121 -0
- package/src/bridge/core/adapter-registry.js +271 -0
- package/src/bridge/core/adapter-registry.js.map +1 -0
- package/src/bridge/core/bridge-factory.d.ts +198 -0
- package/src/bridge/core/bridge-factory.js +428 -0
- package/src/bridge/core/bridge-factory.js.map +1 -0
- package/src/bridge/core/index.d.ts +9 -0
- package/src/bridge/core/index.js +22 -0
- package/src/bridge/core/index.js.map +1 -0
- package/src/bridge/index.d.ts +61 -0
- package/src/bridge/index.js +94 -0
- package/src/bridge/index.js.map +1 -0
- package/src/bridge/runtime/iife-generator.d.ts +61 -0
- package/src/bridge/runtime/iife-generator.js +940 -0
- package/src/bridge/runtime/iife-generator.js.map +1 -0
- package/src/bridge/runtime/index.d.ts +8 -0
- package/src/bridge/runtime/index.js +16 -0
- package/src/bridge/runtime/index.js.map +1 -0
- package/src/bridge/types.d.ts +385 -0
- package/src/bridge/types.js +11 -0
- package/src/bridge/types.js.map +1 -0
- package/src/build/cdn-resources.d.ts +140 -0
- package/src/build/cdn-resources.js +314 -0
- package/src/build/cdn-resources.js.map +1 -0
- package/src/build/index.d.ts +294 -0
- package/src/build/index.js +325 -0
- package/src/build/index.js.map +1 -0
- package/src/build/widget-manifest.d.ts +212 -0
- package/src/build/widget-manifest.js +652 -0
- package/src/build/widget-manifest.js.map +1 -0
- package/src/bundler/bundler.d.ts +110 -0
- package/src/bundler/bundler.js +432 -0
- package/src/bundler/bundler.js.map +1 -0
- package/src/bundler/cache.d.ts +172 -0
- package/src/bundler/cache.js +250 -0
- package/src/bundler/cache.js.map +1 -0
- package/src/bundler/index.d.ts +41 -0
- package/src/bundler/index.js +73 -0
- package/src/bundler/index.js.map +1 -0
- package/src/bundler/sandbox/enclave-adapter.d.ts +120 -0
- package/src/bundler/sandbox/enclave-adapter.js +339 -0
- package/src/bundler/sandbox/enclave-adapter.js.map +1 -0
- package/src/bundler/sandbox/executor.d.ts +13 -0
- package/src/bundler/sandbox/executor.js +22 -0
- package/src/bundler/sandbox/executor.js.map +1 -0
- package/src/bundler/sandbox/policy.d.ts +61 -0
- package/src/bundler/sandbox/policy.js +238 -0
- package/src/bundler/sandbox/policy.js.map +1 -0
- package/src/bundler/types.d.ts +347 -0
- package/src/bundler/types.js +132 -0
- package/src/bundler/types.js.map +1 -0
- package/src/components/alert.d.ts +71 -0
- package/src/components/alert.js +189 -0
- package/src/components/alert.js.map +1 -0
- package/src/components/alert.schema.d.ts +114 -0
- package/src/components/alert.schema.js +105 -0
- package/src/components/alert.schema.js.map +1 -0
- package/src/components/avatar.d.ts +76 -0
- package/src/components/avatar.js +176 -0
- package/src/components/avatar.js.map +1 -0
- package/src/components/avatar.schema.d.ts +169 -0
- package/src/components/avatar.schema.js +103 -0
- package/src/components/avatar.schema.js.map +1 -0
- package/src/components/badge.d.ts +70 -0
- package/src/components/badge.js +149 -0
- package/src/components/badge.js.map +1 -0
- package/src/components/badge.schema.d.ts +109 -0
- package/src/components/badge.schema.js +96 -0
- package/src/components/badge.schema.js.map +1 -0
- package/src/components/button.d.ts +111 -0
- package/src/components/button.js +336 -0
- package/src/components/button.js.map +1 -0
- package/src/components/button.schema.d.ts +148 -0
- package/src/components/button.schema.js +121 -0
- package/src/components/button.schema.js.map +1 -0
- package/src/components/card.d.ts +60 -0
- package/src/components/card.js +117 -0
- package/src/components/card.js.map +1 -0
- package/src/components/card.schema.d.ts +113 -0
- package/src/components/card.schema.js +98 -0
- package/src/components/card.schema.js.map +1 -0
- package/src/components/form.d.ts +239 -0
- package/src/components/form.js +420 -0
- package/src/components/form.js.map +1 -0
- package/src/components/form.schema.d.ts +441 -0
- package/src/components/form.schema.js +406 -0
- package/src/components/form.schema.js.map +1 -0
- package/src/components/index.d.ts +29 -0
- package/src/components/index.js +98 -0
- package/src/components/index.js.map +1 -0
- package/src/components/list.d.ts +127 -0
- package/src/components/list.js +279 -0
- package/src/components/list.js.map +1 -0
- package/src/components/list.schema.d.ts +134 -0
- package/src/components/list.schema.js +168 -0
- package/src/components/list.schema.js.map +1 -0
- package/src/components/modal.d.ts +111 -0
- package/src/components/modal.js +260 -0
- package/src/components/modal.js.map +1 -0
- package/src/components/modal.schema.d.ts +186 -0
- package/src/components/modal.schema.js +167 -0
- package/src/components/modal.schema.js.map +1 -0
- package/src/components/table.d.ts +105 -0
- package/src/components/table.js +283 -0
- package/src/components/table.js.map +1 -0
- package/src/components/table.schema.d.ts +159 -0
- package/src/components/table.schema.js +173 -0
- package/src/components/table.schema.js.map +1 -0
- package/src/handlebars/helpers.d.ts +348 -0
- package/src/handlebars/helpers.js +605 -0
- package/src/handlebars/helpers.js.map +1 -0
- package/src/handlebars/index.d.ts +193 -0
- package/src/handlebars/index.js +350 -0
- package/src/handlebars/index.js.map +1 -0
- package/src/index.d.ts +50 -0
- package/src/index.js +192 -0
- package/src/index.js.map +1 -0
- package/src/layouts/base.d.ts +88 -0
- package/src/layouts/base.js +227 -0
- package/src/layouts/base.js.map +1 -0
- package/src/layouts/index.d.ts +7 -0
- package/src/layouts/index.js +25 -0
- package/src/layouts/index.js.map +1 -0
- package/src/layouts/presets.d.ts +133 -0
- package/src/layouts/presets.js +277 -0
- package/src/layouts/presets.js.map +1 -0
- package/src/pages/consent.d.ts +116 -0
- package/src/pages/consent.js +218 -0
- package/src/pages/consent.js.map +1 -0
- package/src/pages/error.d.ts +100 -0
- package/src/pages/error.js +263 -0
- package/src/pages/error.js.map +1 -0
- package/src/pages/index.d.ts +8 -0
- package/src/pages/index.js +27 -0
- package/src/pages/index.js.map +1 -0
- package/src/react/Alert.d.ts +101 -0
- package/src/react/Alert.js +51 -0
- package/src/react/Alert.js.map +1 -0
- package/src/react/Badge.d.ts +100 -0
- package/src/react/Badge.js +55 -0
- package/src/react/Badge.js.map +1 -0
- package/src/react/Button.d.ts +108 -0
- package/src/react/Button.js +52 -0
- package/src/react/Button.js.map +1 -0
- package/src/react/Card.d.ts +103 -0
- package/src/react/Card.js +55 -0
- package/src/react/Card.js.map +1 -0
- package/src/react/hooks/context.d.ts +178 -0
- package/src/react/hooks/context.js +287 -0
- package/src/react/hooks/context.js.map +1 -0
- package/src/react/hooks/index.d.ts +41 -0
- package/src/react/hooks/index.js +61 -0
- package/src/react/hooks/index.js.map +1 -0
- package/src/react/hooks/tools.d.ts +283 -0
- package/src/react/hooks/tools.js +465 -0
- package/src/react/hooks/tools.js.map +1 -0
- package/src/react/index.d.ts +80 -0
- package/src/react/index.js +113 -0
- package/src/react/index.js.map +1 -0
- package/src/react/types.d.ts +105 -0
- package/src/react/types.js +12 -0
- package/src/react/types.js.map +1 -0
- package/src/react/utils.d.ts +42 -0
- package/src/react/utils.js +99 -0
- package/src/react/utils.js.map +1 -0
- package/src/registry/index.d.ts +45 -0
- package/src/registry/index.js +67 -0
- package/src/registry/index.js.map +1 -0
- package/src/registry/render-template.d.ts +86 -0
- package/src/registry/render-template.js +239 -0
- package/src/registry/render-template.js.map +1 -0
- package/src/registry/tool-ui.registry.d.ts +260 -0
- package/src/registry/tool-ui.registry.js +438 -0
- package/src/registry/tool-ui.registry.js.map +1 -0
- package/src/registry/uri-utils.d.ts +55 -0
- package/src/registry/uri-utils.js +97 -0
- package/src/registry/uri-utils.js.map +1 -0
- package/src/render/index.d.ts +7 -0
- package/src/render/index.js +14 -0
- package/src/render/index.js.map +1 -0
- package/src/render/prerender.d.ts +56 -0
- package/src/render/prerender.js +98 -0
- package/src/render/prerender.js.map +1 -0
- package/src/renderers/cache.d.ts +144 -0
- package/src/renderers/cache.js +240 -0
- package/src/renderers/cache.js.map +1 -0
- package/src/renderers/html.renderer.d.ts +122 -0
- package/src/renderers/html.renderer.js +204 -0
- package/src/renderers/html.renderer.js.map +1 -0
- package/src/renderers/index.d.ts +35 -0
- package/src/renderers/index.js +70 -0
- package/src/renderers/index.js.map +1 -0
- package/src/renderers/mdx.renderer.d.ts +119 -0
- package/src/renderers/mdx.renderer.js +305 -0
- package/src/renderers/mdx.renderer.js.map +1 -0
- package/src/renderers/react.renderer.d.ts +95 -0
- package/src/renderers/react.renderer.js +260 -0
- package/src/renderers/react.renderer.js.map +1 -0
- package/src/renderers/registry.d.ts +133 -0
- package/src/renderers/registry.js +232 -0
- package/src/renderers/registry.js.map +1 -0
- package/src/renderers/types.d.ts +341 -0
- package/src/renderers/types.js +9 -0
- package/src/renderers/types.js.map +1 -0
- package/src/renderers/utils/detect.d.ts +106 -0
- package/src/renderers/utils/detect.js +267 -0
- package/src/renderers/utils/detect.js.map +1 -0
- package/src/renderers/utils/hash.d.ts +39 -0
- package/src/renderers/utils/hash.js +75 -0
- package/src/renderers/utils/hash.js.map +1 -0
- package/src/renderers/utils/index.d.ts +8 -0
- package/src/renderers/utils/index.js +28 -0
- package/src/renderers/utils/index.js.map +1 -0
- package/src/renderers/utils/transpiler.d.ts +88 -0
- package/src/renderers/utils/transpiler.js +215 -0
- package/src/renderers/utils/transpiler.js.map +1 -0
- package/src/runtime/adapters/html.adapter.d.ts +58 -0
- package/src/runtime/adapters/html.adapter.js +131 -0
- package/src/runtime/adapters/html.adapter.js.map +1 -0
- package/src/runtime/adapters/index.d.ts +25 -0
- package/src/runtime/adapters/index.js +54 -0
- package/src/runtime/adapters/index.js.map +1 -0
- package/src/runtime/adapters/mdx.adapter.d.ts +72 -0
- package/src/runtime/adapters/mdx.adapter.js +241 -0
- package/src/runtime/adapters/mdx.adapter.js.map +1 -0
- package/src/runtime/adapters/react.adapter.d.ts +69 -0
- package/src/runtime/adapters/react.adapter.js +245 -0
- package/src/runtime/adapters/react.adapter.js.map +1 -0
- package/src/runtime/adapters/types.d.ts +94 -0
- package/src/runtime/adapters/types.js +11 -0
- package/src/runtime/adapters/types.js.map +1 -0
- package/src/runtime/csp.d.ts +37 -0
- package/src/runtime/csp.js +140 -0
- package/src/runtime/csp.js.map +1 -0
- package/src/runtime/index.d.ts +16 -0
- package/src/runtime/index.js +72 -0
- package/src/runtime/index.js.map +1 -0
- package/src/runtime/mcp-bridge.d.ts +100 -0
- package/src/runtime/mcp-bridge.js +581 -0
- package/src/runtime/mcp-bridge.js.map +1 -0
- package/src/runtime/renderer-runtime.d.ts +132 -0
- package/src/runtime/renderer-runtime.js +389 -0
- package/src/runtime/renderer-runtime.js.map +1 -0
- package/src/runtime/sanitizer.d.ts +171 -0
- package/src/runtime/sanitizer.js +318 -0
- package/src/runtime/sanitizer.js.map +1 -0
- package/src/runtime/types.d.ts +414 -0
- package/src/runtime/types.js +12 -0
- package/src/runtime/types.js.map +1 -0
- package/src/runtime/wrapper.d.ts +375 -0
- package/src/runtime/wrapper.js +1793 -0
- package/src/runtime/wrapper.js.map +1 -0
- package/src/styles/index.d.ts +7 -0
- package/src/styles/index.js +11 -0
- package/src/styles/index.js.map +1 -0
- package/src/styles/variants.d.ts +50 -0
- package/src/styles/variants.js +175 -0
- package/src/styles/variants.js.map +1 -0
- package/src/theme/cdn.d.ts +194 -0
- package/src/theme/cdn.js +375 -0
- package/src/theme/cdn.js.map +1 -0
- package/src/theme/index.d.ts +17 -0
- package/src/theme/index.js +57 -0
- package/src/theme/index.js.map +1 -0
- package/src/theme/platforms.d.ts +106 -0
- package/src/theme/platforms.js +161 -0
- package/src/theme/platforms.js.map +1 -0
- package/src/theme/presets/github-openai.d.ts +49 -0
- package/src/theme/presets/github-openai.js +189 -0
- package/src/theme/presets/github-openai.js.map +1 -0
- package/src/theme/presets/index.d.ts +10 -0
- package/src/theme/presets/index.js +17 -0
- package/src/theme/presets/index.js.map +1 -0
- package/src/theme/theme.d.ts +395 -0
- package/src/theme/theme.js +332 -0
- package/src/theme/theme.js.map +1 -0
- package/src/tool-template/builder.d.ts +212 -0
- package/src/tool-template/builder.js +397 -0
- package/src/tool-template/builder.js.map +1 -0
- package/src/tool-template/index.d.ts +15 -0
- package/src/tool-template/index.js +38 -0
- package/src/tool-template/index.js.map +1 -0
- package/src/types/index.d.ts +13 -0
- package/src/types/index.js +26 -0
- package/src/types/index.js.map +1 -0
- package/src/types/ui-config.d.ts +357 -0
- package/src/types/ui-config.js +12 -0
- package/src/types/ui-config.js.map +1 -0
- package/src/types/ui-runtime.d.ts +965 -0
- package/src/types/ui-runtime.js +117 -0
- package/src/types/ui-runtime.js.map +1 -0
- package/src/validation/error-box.d.ts +55 -0
- package/src/validation/error-box.js +75 -0
- package/src/validation/error-box.js.map +1 -0
- package/src/validation/index.d.ts +12 -0
- package/src/validation/index.js +21 -0
- package/src/validation/index.js.map +1 -0
- package/src/validation/wrapper.d.ts +96 -0
- package/src/validation/wrapper.js +117 -0
- package/src/validation/wrapper.js.map +1 -0
- package/src/web-components/core/attribute-parser.d.ts +85 -0
- package/src/web-components/core/attribute-parser.js +189 -0
- package/src/web-components/core/attribute-parser.js.map +1 -0
- package/src/web-components/core/base-element.d.ts +197 -0
- package/src/web-components/core/base-element.js +289 -0
- package/src/web-components/core/base-element.js.map +1 -0
- package/src/web-components/core/index.d.ts +8 -0
- package/src/web-components/core/index.js +18 -0
- package/src/web-components/core/index.js.map +1 -0
- package/src/web-components/elements/fmcp-alert.d.ts +45 -0
- package/src/web-components/elements/fmcp-alert.js +93 -0
- package/src/web-components/elements/fmcp-alert.js.map +1 -0
- package/src/web-components/elements/fmcp-badge.d.ts +46 -0
- package/src/web-components/elements/fmcp-badge.js +99 -0
- package/src/web-components/elements/fmcp-badge.js.map +1 -0
- package/src/web-components/elements/fmcp-button.d.ts +124 -0
- package/src/web-components/elements/fmcp-button.js +233 -0
- package/src/web-components/elements/fmcp-button.js.map +1 -0
- package/src/web-components/elements/fmcp-card.d.ts +52 -0
- package/src/web-components/elements/fmcp-card.js +115 -0
- package/src/web-components/elements/fmcp-card.js.map +1 -0
- package/src/web-components/elements/fmcp-input.d.ts +95 -0
- package/src/web-components/elements/fmcp-input.js +248 -0
- package/src/web-components/elements/fmcp-input.js.map +1 -0
- package/src/web-components/elements/fmcp-select.d.ts +99 -0
- package/src/web-components/elements/fmcp-select.js +243 -0
- package/src/web-components/elements/fmcp-select.js.map +1 -0
- package/src/web-components/elements/index.d.ts +12 -0
- package/src/web-components/elements/index.js +34 -0
- package/src/web-components/elements/index.js.map +1 -0
- package/src/web-components/index.d.ts +49 -0
- package/src/web-components/index.js +75 -0
- package/src/web-components/index.js.map +1 -0
- package/src/web-components/register.d.ts +56 -0
- package/src/web-components/register.js +80 -0
- package/src/web-components/register.js.map +1 -0
- package/src/web-components/types.d.ts +121 -0
- package/src/web-components/types.js +25 -0
- package/src/web-components/types.js.map +1 -0
- package/src/widgets/index.d.ts +7 -0
- package/src/widgets/index.js +24 -0
- package/src/widgets/index.js.map +1 -0
- package/src/widgets/progress.d.ts +132 -0
- package/src/widgets/progress.js +303 -0
- package/src/widgets/progress.js.map +1 -0
- package/src/widgets/resource.d.ts +162 -0
- package/src/widgets/resource.js +340 -0
- package/src/widgets/resource.js.map +1 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MDX Renderer
|
|
4
|
+
*
|
|
5
|
+
* Handles MDX templates - Markdown with embedded JSX components.
|
|
6
|
+
* Uses @mdx-js/mdx for compilation and react-dom/server for SSR.
|
|
7
|
+
*
|
|
8
|
+
* MDX allows mixing Markdown with React components:
|
|
9
|
+
* - Markdown headings, lists, code blocks
|
|
10
|
+
* - JSX component tags: `<Card />`
|
|
11
|
+
* - JS expressions: `{output.items.map(...)}`
|
|
12
|
+
* - Frontmatter for metadata
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.mdxRenderer = exports.MdxRenderer = void 0;
|
|
16
|
+
exports.buildMdxHydrationScript = buildMdxHydrationScript;
|
|
17
|
+
const detect_1 = require("./utils/detect");
|
|
18
|
+
const hash_1 = require("./utils/hash");
|
|
19
|
+
const cache_1 = require("./cache");
|
|
20
|
+
/**
|
|
21
|
+
* Runtime CDN URLs (same as React since MDX compiles to React).
|
|
22
|
+
*/
|
|
23
|
+
const REACT_CDN = {
|
|
24
|
+
react: 'https://unpkg.com/react@18/umd/react.production.min.js',
|
|
25
|
+
reactDom: 'https://unpkg.com/react-dom@18/umd/react-dom.production.min.js',
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Placeholder for blocked-network platforms.
|
|
29
|
+
*/
|
|
30
|
+
const INLINE_MDX_PLACEHOLDER = `
|
|
31
|
+
// MDX runtime not available inline yet.
|
|
32
|
+
// For blocked-network platforms, use pre-rendered HTML templates.
|
|
33
|
+
console.warn('[FrontMCP] MDX hydration not available on this platform.');
|
|
34
|
+
`;
|
|
35
|
+
/**
|
|
36
|
+
* MDX Renderer Implementation.
|
|
37
|
+
*
|
|
38
|
+
* Compiles MDX (Markdown + JSX) to React components using @mdx-js/mdx,
|
|
39
|
+
* then renders to HTML using react-dom/server.
|
|
40
|
+
*
|
|
41
|
+
* @example Basic MDX template
|
|
42
|
+
* ```typescript
|
|
43
|
+
* @Tool({
|
|
44
|
+
* ui: {
|
|
45
|
+
* template: `
|
|
46
|
+
* # User Profile
|
|
47
|
+
*
|
|
48
|
+
* <UserCard name={output.name} email={output.email} />
|
|
49
|
+
*
|
|
50
|
+
* ## Recent Activity
|
|
51
|
+
* {output.items.map(item => <ActivityItem key={item.id} {...item} />)}
|
|
52
|
+
* `,
|
|
53
|
+
* mdxComponents: { UserCard, ActivityItem }
|
|
54
|
+
* }
|
|
55
|
+
* })
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* @example MDX with frontmatter
|
|
59
|
+
* ```typescript
|
|
60
|
+
* @Tool({
|
|
61
|
+
* ui: {
|
|
62
|
+
* template: `
|
|
63
|
+
* ---
|
|
64
|
+
* title: Dashboard
|
|
65
|
+
* ---
|
|
66
|
+
*
|
|
67
|
+
* # {frontmatter.title}
|
|
68
|
+
*
|
|
69
|
+
* <Dashboard data={output} />
|
|
70
|
+
* `
|
|
71
|
+
* }
|
|
72
|
+
* })
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
class MdxRenderer {
|
|
76
|
+
type = 'mdx';
|
|
77
|
+
priority = 10; // Between HTML (0) and React (20)
|
|
78
|
+
/**
|
|
79
|
+
* Lazy-loaded modules.
|
|
80
|
+
*/
|
|
81
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
82
|
+
React = null;
|
|
83
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
84
|
+
ReactDOMServer = null;
|
|
85
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
86
|
+
jsxRuntime = null;
|
|
87
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
88
|
+
mdxEvaluate = null;
|
|
89
|
+
/**
|
|
90
|
+
* Check if this renderer can handle the given template.
|
|
91
|
+
*
|
|
92
|
+
* Accepts strings containing MDX syntax (Markdown + JSX).
|
|
93
|
+
*/
|
|
94
|
+
canHandle(template) {
|
|
95
|
+
if (typeof template !== 'string') {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
return (0, detect_1.containsMdxSyntax)(template);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Transpile MDX to executable JavaScript.
|
|
102
|
+
*
|
|
103
|
+
* Uses @mdx-js/mdx to compile MDX source to a module.
|
|
104
|
+
* Note: For MDX, we use evaluate() which combines compile + run,
|
|
105
|
+
* so this method just returns the source hash for caching purposes.
|
|
106
|
+
*/
|
|
107
|
+
async transpile(template, _options) {
|
|
108
|
+
const hash = (0, hash_1.hashString)(template);
|
|
109
|
+
// Check cache - for MDX, the "code" is just the original source
|
|
110
|
+
// since we use evaluate() which handles compilation internally
|
|
111
|
+
const cached = cache_1.transpileCache.getByKey(hash);
|
|
112
|
+
if (cached) {
|
|
113
|
+
return { ...cached, cached: true };
|
|
114
|
+
}
|
|
115
|
+
const transpileResult = {
|
|
116
|
+
code: template, // Store original MDX for evaluate()
|
|
117
|
+
hash,
|
|
118
|
+
cached: false,
|
|
119
|
+
};
|
|
120
|
+
// Cache the result
|
|
121
|
+
cache_1.transpileCache.setByKey(hash, transpileResult);
|
|
122
|
+
return transpileResult;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Render MDX template to HTML string.
|
|
126
|
+
*
|
|
127
|
+
* Uses @mdx-js/mdx's evaluate() for clean compilation + execution,
|
|
128
|
+
* then renders the resulting React component to HTML via SSR.
|
|
129
|
+
*/
|
|
130
|
+
async render(template, context, options) {
|
|
131
|
+
// Ensure dependencies are loaded
|
|
132
|
+
await this.loadReact();
|
|
133
|
+
await this.loadMdx();
|
|
134
|
+
if (!this.mdxEvaluate) {
|
|
135
|
+
throw new Error('MDX compilation requires @mdx-js/mdx. Install it: npm install @mdx-js/mdx');
|
|
136
|
+
}
|
|
137
|
+
// Create a cache key based on the template hash
|
|
138
|
+
const templateHash = (0, hash_1.hashString)(template);
|
|
139
|
+
const cacheKey = `mdx-component:${templateHash}`;
|
|
140
|
+
// Check component cache
|
|
141
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
142
|
+
let Content = cache_1.componentCache.get(cacheKey);
|
|
143
|
+
if (!Content) {
|
|
144
|
+
// Evaluate MDX source to get the component
|
|
145
|
+
// evaluate() combines compile + run in one step
|
|
146
|
+
const result = await this.mdxEvaluate(template, {
|
|
147
|
+
...this.jsxRuntime,
|
|
148
|
+
Fragment: this.React.Fragment,
|
|
149
|
+
development: false,
|
|
150
|
+
});
|
|
151
|
+
Content = result.default;
|
|
152
|
+
// Cache the compiled component
|
|
153
|
+
cache_1.componentCache.set(cacheKey, Content);
|
|
154
|
+
}
|
|
155
|
+
// Build component map with custom MDX components
|
|
156
|
+
const mdxComponents = {
|
|
157
|
+
// User-provided components from tool config
|
|
158
|
+
...options?.mdxComponents,
|
|
159
|
+
// Wrapper that provides context to the content
|
|
160
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
161
|
+
wrapper: ({ children }) => {
|
|
162
|
+
return this.React.createElement('div', { className: 'mdx-content' }, children);
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
// Create props that MDX can access
|
|
166
|
+
// These become available in MDX as {props.input}, {props.output}, etc.
|
|
167
|
+
const props = {
|
|
168
|
+
input: context.input,
|
|
169
|
+
output: context.output,
|
|
170
|
+
structuredContent: context.structuredContent,
|
|
171
|
+
helpers: context.helpers,
|
|
172
|
+
};
|
|
173
|
+
// Also spread output properties at top level for convenience
|
|
174
|
+
// This allows accessing {output.name} or just {name} in MDX
|
|
175
|
+
const spreadProps = {
|
|
176
|
+
...props,
|
|
177
|
+
...(typeof context.output === 'object' && context.output !== null ? context.output : {}),
|
|
178
|
+
};
|
|
179
|
+
// Create the element with components and props
|
|
180
|
+
const element = this.React.createElement(Content, {
|
|
181
|
+
components: mdxComponents,
|
|
182
|
+
...spreadProps,
|
|
183
|
+
});
|
|
184
|
+
// Render to HTML
|
|
185
|
+
const html = this.ReactDOMServer.renderToString(element);
|
|
186
|
+
// If hydration is enabled, wrap with markers
|
|
187
|
+
if (options?.hydrate) {
|
|
188
|
+
// Full HTML attribute escaping to prevent XSS
|
|
189
|
+
const escapedProps = JSON.stringify(props)
|
|
190
|
+
.replace(/&/g, '&')
|
|
191
|
+
.replace(/'/g, ''')
|
|
192
|
+
.replace(/</g, '<')
|
|
193
|
+
.replace(/>/g, '>');
|
|
194
|
+
return `<div data-mdx-hydrate="true" data-props='${escapedProps}'>${html}</div>`;
|
|
195
|
+
}
|
|
196
|
+
return html;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Get runtime scripts for client-side functionality.
|
|
200
|
+
*/
|
|
201
|
+
getRuntimeScripts(platform) {
|
|
202
|
+
// For blocked-network platforms (Claude), scripts must be inline
|
|
203
|
+
if (platform.networkMode === 'blocked') {
|
|
204
|
+
return {
|
|
205
|
+
headScripts: '',
|
|
206
|
+
inlineScripts: INLINE_MDX_PLACEHOLDER,
|
|
207
|
+
isInline: true,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
// For platforms with network access, use CDN (React required for MDX)
|
|
211
|
+
return {
|
|
212
|
+
headScripts: `
|
|
213
|
+
<script crossorigin src="${REACT_CDN.react}"></script>
|
|
214
|
+
<script crossorigin src="${REACT_CDN.reactDom}"></script>
|
|
215
|
+
`,
|
|
216
|
+
isInline: false,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Load React and ReactDOMServer modules.
|
|
221
|
+
*/
|
|
222
|
+
async loadReact() {
|
|
223
|
+
if (this.React && this.ReactDOMServer && this.jsxRuntime) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
try {
|
|
227
|
+
const [react, reactDomServer, jsxRuntime] = await Promise.all([
|
|
228
|
+
import('react'),
|
|
229
|
+
import('react-dom/server'),
|
|
230
|
+
import('react/jsx-runtime'),
|
|
231
|
+
]);
|
|
232
|
+
this.React = react;
|
|
233
|
+
this.ReactDOMServer = reactDomServer;
|
|
234
|
+
this.jsxRuntime = jsxRuntime;
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
throw new Error('React is required for MdxRenderer. Install react and react-dom: npm install react react-dom');
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Load @mdx-js/mdx evaluate function.
|
|
242
|
+
*
|
|
243
|
+
* evaluate() is the cleanest way to run MDX - it combines
|
|
244
|
+
* compile and run in a single step, handling all the runtime
|
|
245
|
+
* injection automatically.
|
|
246
|
+
*/
|
|
247
|
+
async loadMdx() {
|
|
248
|
+
if (this.mdxEvaluate) {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
try {
|
|
252
|
+
const mdx = await import('@mdx-js/mdx');
|
|
253
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
254
|
+
this.mdxEvaluate = mdx.evaluate;
|
|
255
|
+
}
|
|
256
|
+
catch {
|
|
257
|
+
console.warn('[@frontmcp/ui] @mdx-js/mdx not available. MDX rendering disabled. ' +
|
|
258
|
+
'Install @mdx-js/mdx to enable: npm install @mdx-js/mdx');
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
exports.MdxRenderer = MdxRenderer;
|
|
263
|
+
/**
|
|
264
|
+
* Singleton instance of the MDX renderer.
|
|
265
|
+
*/
|
|
266
|
+
exports.mdxRenderer = new MdxRenderer();
|
|
267
|
+
/**
|
|
268
|
+
* Build MDX hydration script for client-side interactivity.
|
|
269
|
+
*
|
|
270
|
+
* Note: MDX hydration is more complex than React hydration
|
|
271
|
+
* because it needs the MDX runtime and component definitions.
|
|
272
|
+
*/
|
|
273
|
+
function buildMdxHydrationScript() {
|
|
274
|
+
return `
|
|
275
|
+
<script>
|
|
276
|
+
(function() {
|
|
277
|
+
// MDX hydration requires React and component definitions
|
|
278
|
+
if (typeof React === 'undefined' || typeof ReactDOM === 'undefined') {
|
|
279
|
+
console.warn('[FrontMCP] React not available for MDX hydration');
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Find all elements marked for MDX hydration
|
|
284
|
+
document.querySelectorAll('[data-mdx-hydrate]').forEach(function(root) {
|
|
285
|
+
var propsJson = root.getAttribute('data-props');
|
|
286
|
+
var props = propsJson ? JSON.parse(propsJson) : {};
|
|
287
|
+
|
|
288
|
+
// MDX content is pre-rendered, hydration mainly attaches event handlers
|
|
289
|
+
// For full interactivity, components need to be available client-side
|
|
290
|
+
if (window.__frontmcp_mdx_content) {
|
|
291
|
+
try {
|
|
292
|
+
ReactDOM.hydrateRoot(root, React.createElement(
|
|
293
|
+
window.__frontmcp_mdx_content,
|
|
294
|
+
props
|
|
295
|
+
));
|
|
296
|
+
} catch (e) {
|
|
297
|
+
console.error('[FrontMCP] MDX hydration failed', e);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
})();
|
|
302
|
+
</script>
|
|
303
|
+
`;
|
|
304
|
+
}
|
|
305
|
+
//# sourceMappingURL=mdx.renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mdx.renderer.js","sourceRoot":"","sources":["../../../src/renderers/mdx.renderer.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;;AAsTH,0DA+BC;AAzUD,2CAAmD;AACnD,uCAA0C;AAC1C,mCAAyD;AAOzD;;GAEG;AACH,MAAM,SAAS,GAAG;IAChB,KAAK,EAAE,wDAAwD;IAC/D,QAAQ,EAAE,gEAAgE;CAC3E,CAAC;AAEF;;GAEG;AACH,MAAM,sBAAsB,GAAG;;;;CAI9B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,MAAa,WAAW;IACb,IAAI,GAAG,KAAc,CAAC;IACtB,QAAQ,GAAG,EAAE,CAAC,CAAC,kCAAkC;IAE1D;;OAEG;IACH,8DAA8D;IACtD,KAAK,GAAQ,IAAI,CAAC;IAC1B,8DAA8D;IACtD,cAAc,GAAQ,IAAI,CAAC;IACnC,8DAA8D;IACtD,UAAU,GAAQ,IAAI,CAAC;IAC/B,8DAA8D;IACtD,WAAW,GAA4E,IAAI,CAAC;IAEpG;;;;OAIG;IACH,SAAS,CAAC,QAAiB;QACzB,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACjC,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO,IAAA,0BAAiB,EAAC,QAAQ,CAAC,CAAC;IACrC,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,SAAS,CAAC,QAAqB,EAAE,QAA2B;QAChE,MAAM,IAAI,GAAG,IAAA,iBAAU,EAAC,QAAQ,CAAC,CAAC;QAElC,gEAAgE;QAChE,+DAA+D;QAC/D,MAAM,MAAM,GAAG,sBAAc,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC7C,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QACrC,CAAC;QAED,MAAM,eAAe,GAAoB;YACvC,IAAI,EAAE,QAAQ,EAAE,oCAAoC;YACpD,IAAI;YACJ,MAAM,EAAE,KAAK;SACd,CAAC;QAEF,mBAAmB;QACnB,sBAAc,CAAC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;QAE/C,OAAO,eAAe,CAAC;IACzB,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,MAAM,CACV,QAAqB,EACrB,OAAiC,EACjC,OAAuB;QAEvB,iCAAiC;QACjC,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QACvB,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QAErB,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,2EAA2E,CAAC,CAAC;QAC/F,CAAC;QAED,gDAAgD;QAChD,MAAM,YAAY,GAAG,IAAA,iBAAU,EAAC,QAAQ,CAAC,CAAC;QAC1C,MAAM,QAAQ,GAAG,iBAAiB,YAAY,EAAE,CAAC;QAEjD,wBAAwB;QACxB,8DAA8D;QAC9D,IAAI,OAAO,GAAQ,sBAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAEhD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,2CAA2C;YAC3C,gDAAgD;YAChD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE;gBAC9C,GAAG,IAAI,CAAC,UAAU;gBAClB,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ;gBAC7B,WAAW,EAAE,KAAK;aACnB,CAAC,CAAC;YAEH,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;YAEzB,+BAA+B;YAC/B,sBAAc,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACxC,CAAC;QAED,iDAAiD;QACjD,MAAM,aAAa,GAAG;YACpB,4CAA4C;YAC5C,GAAG,OAAO,EAAE,aAAa;YACzB,+CAA+C;YAC/C,8DAA8D;YAC9D,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAqB,EAAE,EAAE;gBAC3C,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,aAAa,EAAE,EAAE,QAAQ,CAAC,CAAC;YACjF,CAAC;SACF,CAAC;QAEF,mCAAmC;QACnC,uEAAuE;QACvE,MAAM,KAAK,GAAyB;YAClC,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,iBAAiB,EAAE,OAAO,CAAC,iBAAiB;YAC5C,OAAO,EAAE,OAAO,CAAC,OAAO;SACzB,CAAC;QAEF,6DAA6D;QAC7D,4DAA4D;QAC5D,MAAM,WAAW,GAAG;YAClB,GAAG,KAAK;YACR,GAAG,CAAC,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;SACzF,CAAC;QAEF,+CAA+C;QAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,OAAO,EAAE;YAChD,UAAU,EAAE,aAAa;YACzB,GAAG,WAAW;SACf,CAAC,CAAC;QAEH,iBAAiB;QACjB,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;QAEzD,6CAA6C;QAC7C,IAAI,OAAO,EAAE,OAAO,EAAE,CAAC;YACrB,8CAA8C;YAC9C,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;iBACvC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;iBACtB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;iBACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;iBACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YACzB,OAAO,4CAA4C,YAAY,KAAK,IAAI,QAAQ,CAAC;QACnF,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACH,iBAAiB,CAAC,QAA8B;QAC9C,iEAAiE;QACjE,IAAI,QAAQ,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;YACvC,OAAO;gBACL,WAAW,EAAE,EAAE;gBACf,aAAa,EAAE,sBAAsB;gBACrC,QAAQ,EAAE,IAAI;aACf,CAAC;QACJ,CAAC;QAED,sEAAsE;QACtE,OAAO;YACL,WAAW,EAAE;mCACgB,SAAS,CAAC,KAAK;mCACf,SAAS,CAAC,QAAQ;OAC9C;YACD,QAAQ,EAAE,KAAK;SAChB,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,SAAS;QACrB,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACzD,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,MAAM,CAAC,KAAK,EAAE,cAAc,EAAE,UAAU,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBAC5D,MAAM,CAAC,OAAO,CAAC;gBACf,MAAM,CAAC,kBAAkB,CAAC;gBAC1B,MAAM,CAAC,mBAAmB,CAAC;aAC5B,CAAC,CAAC;YAEH,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;YACnB,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;YACrC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,6FAA6F,CAAC,CAAC;QACjH,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACK,KAAK,CAAC,OAAO;QACnB,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;YACxC,8DAA8D;YAC9D,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC,QAAe,CAAC;QACzC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,IAAI,CACV,oEAAoE;gBAClE,wDAAwD,CAC3D,CAAC;QACJ,CAAC;IACH,CAAC;CACF;AA3ND,kCA2NC;AAED;;GAEG;AACU,QAAA,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC;AAE7C;;;;;GAKG;AACH,SAAgB,uBAAuB;IACrC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BR,CAAC;AACF,CAAC","sourcesContent":["/**\n * MDX Renderer\n *\n * Handles MDX templates - Markdown with embedded JSX components.\n * Uses @mdx-js/mdx for compilation and react-dom/server for SSR.\n *\n * MDX allows mixing Markdown with React components:\n * - Markdown headings, lists, code blocks\n * - JSX component tags: `<Card />`\n * - JS expressions: `{output.items.map(...)}`\n * - Frontmatter for metadata\n */\n\nimport type { TemplateContext } from '../runtime/types';\nimport type { PlatformCapabilities } from '../theme';\nimport type {\n UIRenderer,\n TranspileResult,\n TranspileOptions,\n RenderOptions,\n RuntimeScripts,\n ToolUIProps,\n} from './types';\nimport { containsMdxSyntax } from './utils/detect';\nimport { hashString } from './utils/hash';\nimport { transpileCache, componentCache } from './cache';\n\n/**\n * Types this renderer can handle - MDX strings.\n */\ntype MdxTemplate = string;\n\n/**\n * Runtime CDN URLs (same as React since MDX compiles to React).\n */\nconst REACT_CDN = {\n react: 'https://unpkg.com/react@18/umd/react.production.min.js',\n reactDom: 'https://unpkg.com/react-dom@18/umd/react-dom.production.min.js',\n};\n\n/**\n * Placeholder for blocked-network platforms.\n */\nconst INLINE_MDX_PLACEHOLDER = `\n// MDX runtime not available inline yet.\n// For blocked-network platforms, use pre-rendered HTML templates.\nconsole.warn('[FrontMCP] MDX hydration not available on this platform.');\n`;\n\n/**\n * MDX Renderer Implementation.\n *\n * Compiles MDX (Markdown + JSX) to React components using @mdx-js/mdx,\n * then renders to HTML using react-dom/server.\n *\n * @example Basic MDX template\n * ```typescript\n * @Tool({\n * ui: {\n * template: `\n * # User Profile\n *\n * <UserCard name={output.name} email={output.email} />\n *\n * ## Recent Activity\n * {output.items.map(item => <ActivityItem key={item.id} {...item} />)}\n * `,\n * mdxComponents: { UserCard, ActivityItem }\n * }\n * })\n * ```\n *\n * @example MDX with frontmatter\n * ```typescript\n * @Tool({\n * ui: {\n * template: `\n * ---\n * title: Dashboard\n * ---\n *\n * # {frontmatter.title}\n *\n * <Dashboard data={output} />\n * `\n * }\n * })\n * ```\n */\nexport class MdxRenderer implements UIRenderer<MdxTemplate> {\n readonly type = 'mdx' as const;\n readonly priority = 10; // Between HTML (0) and React (20)\n\n /**\n * Lazy-loaded modules.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private React: any = null;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private ReactDOMServer: any = null;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private jsxRuntime: any = null;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private mdxEvaluate: ((source: string, options: object) => Promise<{ default: any }>) | null = null;\n\n /**\n * Check if this renderer can handle the given template.\n *\n * Accepts strings containing MDX syntax (Markdown + JSX).\n */\n canHandle(template: unknown): template is MdxTemplate {\n if (typeof template !== 'string') {\n return false;\n }\n\n return containsMdxSyntax(template);\n }\n\n /**\n * Transpile MDX to executable JavaScript.\n *\n * Uses @mdx-js/mdx to compile MDX source to a module.\n * Note: For MDX, we use evaluate() which combines compile + run,\n * so this method just returns the source hash for caching purposes.\n */\n async transpile(template: MdxTemplate, _options?: TranspileOptions): Promise<TranspileResult> {\n const hash = hashString(template);\n\n // Check cache - for MDX, the \"code\" is just the original source\n // since we use evaluate() which handles compilation internally\n const cached = transpileCache.getByKey(hash);\n if (cached) {\n return { ...cached, cached: true };\n }\n\n const transpileResult: TranspileResult = {\n code: template, // Store original MDX for evaluate()\n hash,\n cached: false,\n };\n\n // Cache the result\n transpileCache.setByKey(hash, transpileResult);\n\n return transpileResult;\n }\n\n /**\n * Render MDX template to HTML string.\n *\n * Uses @mdx-js/mdx's evaluate() for clean compilation + execution,\n * then renders the resulting React component to HTML via SSR.\n */\n async render<In, Out>(\n template: MdxTemplate,\n context: TemplateContext<In, Out>,\n options?: RenderOptions,\n ): Promise<string> {\n // Ensure dependencies are loaded\n await this.loadReact();\n await this.loadMdx();\n\n if (!this.mdxEvaluate) {\n throw new Error('MDX compilation requires @mdx-js/mdx. Install it: npm install @mdx-js/mdx');\n }\n\n // Create a cache key based on the template hash\n const templateHash = hashString(template);\n const cacheKey = `mdx-component:${templateHash}`;\n\n // Check component cache\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let Content: any = componentCache.get(cacheKey);\n\n if (!Content) {\n // Evaluate MDX source to get the component\n // evaluate() combines compile + run in one step\n const result = await this.mdxEvaluate(template, {\n ...this.jsxRuntime,\n Fragment: this.React.Fragment,\n development: false,\n });\n\n Content = result.default;\n\n // Cache the compiled component\n componentCache.set(cacheKey, Content);\n }\n\n // Build component map with custom MDX components\n const mdxComponents = {\n // User-provided components from tool config\n ...options?.mdxComponents,\n // Wrapper that provides context to the content\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n wrapper: ({ children }: { children: any }) => {\n return this.React.createElement('div', { className: 'mdx-content' }, children);\n },\n };\n\n // Create props that MDX can access\n // These become available in MDX as {props.input}, {props.output}, etc.\n const props: ToolUIProps<In, Out> = {\n input: context.input,\n output: context.output,\n structuredContent: context.structuredContent,\n helpers: context.helpers,\n };\n\n // Also spread output properties at top level for convenience\n // This allows accessing {output.name} or just {name} in MDX\n const spreadProps = {\n ...props,\n ...(typeof context.output === 'object' && context.output !== null ? context.output : {}),\n };\n\n // Create the element with components and props\n const element = this.React.createElement(Content, {\n components: mdxComponents,\n ...spreadProps,\n });\n\n // Render to HTML\n const html = this.ReactDOMServer.renderToString(element);\n\n // If hydration is enabled, wrap with markers\n if (options?.hydrate) {\n // Full HTML attribute escaping to prevent XSS\n const escapedProps = JSON.stringify(props)\n .replace(/&/g, '&')\n .replace(/'/g, ''')\n .replace(/</g, '<')\n .replace(/>/g, '>');\n return `<div data-mdx-hydrate=\"true\" data-props='${escapedProps}'>${html}</div>`;\n }\n\n return html;\n }\n\n /**\n * Get runtime scripts for client-side functionality.\n */\n getRuntimeScripts(platform: PlatformCapabilities): RuntimeScripts {\n // For blocked-network platforms (Claude), scripts must be inline\n if (platform.networkMode === 'blocked') {\n return {\n headScripts: '',\n inlineScripts: INLINE_MDX_PLACEHOLDER,\n isInline: true,\n };\n }\n\n // For platforms with network access, use CDN (React required for MDX)\n return {\n headScripts: `\n <script crossorigin src=\"${REACT_CDN.react}\"></script>\n <script crossorigin src=\"${REACT_CDN.reactDom}\"></script>\n `,\n isInline: false,\n };\n }\n\n /**\n * Load React and ReactDOMServer modules.\n */\n private async loadReact(): Promise<void> {\n if (this.React && this.ReactDOMServer && this.jsxRuntime) {\n return;\n }\n\n try {\n const [react, reactDomServer, jsxRuntime] = await Promise.all([\n import('react'),\n import('react-dom/server'),\n import('react/jsx-runtime'),\n ]);\n\n this.React = react;\n this.ReactDOMServer = reactDomServer;\n this.jsxRuntime = jsxRuntime;\n } catch {\n throw new Error('React is required for MdxRenderer. Install react and react-dom: npm install react react-dom');\n }\n }\n\n /**\n * Load @mdx-js/mdx evaluate function.\n *\n * evaluate() is the cleanest way to run MDX - it combines\n * compile and run in a single step, handling all the runtime\n * injection automatically.\n */\n private async loadMdx(): Promise<void> {\n if (this.mdxEvaluate) {\n return;\n }\n\n try {\n const mdx = await import('@mdx-js/mdx');\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n this.mdxEvaluate = mdx.evaluate as any;\n } catch {\n console.warn(\n '[@frontmcp/ui] @mdx-js/mdx not available. MDX rendering disabled. ' +\n 'Install @mdx-js/mdx to enable: npm install @mdx-js/mdx',\n );\n }\n }\n}\n\n/**\n * Singleton instance of the MDX renderer.\n */\nexport const mdxRenderer = new MdxRenderer();\n\n/**\n * Build MDX hydration script for client-side interactivity.\n *\n * Note: MDX hydration is more complex than React hydration\n * because it needs the MDX runtime and component definitions.\n */\nexport function buildMdxHydrationScript(): string {\n return `\n<script>\n(function() {\n // MDX hydration requires React and component definitions\n if (typeof React === 'undefined' || typeof ReactDOM === 'undefined') {\n console.warn('[FrontMCP] React not available for MDX hydration');\n return;\n }\n\n // Find all elements marked for MDX hydration\n document.querySelectorAll('[data-mdx-hydrate]').forEach(function(root) {\n var propsJson = root.getAttribute('data-props');\n var props = propsJson ? JSON.parse(propsJson) : {};\n\n // MDX content is pre-rendered, hydration mainly attaches event handlers\n // For full interactivity, components need to be available client-side\n if (window.__frontmcp_mdx_content) {\n try {\n ReactDOM.hydrateRoot(root, React.createElement(\n window.__frontmcp_mdx_content,\n props\n ));\n } catch (e) {\n console.error('[FrontMCP] MDX hydration failed', e);\n }\n }\n });\n})();\n</script>\n`;\n}\n"]}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React Renderer
|
|
3
|
+
*
|
|
4
|
+
* Handles React component templates:
|
|
5
|
+
* - Imported React components (already transpiled)
|
|
6
|
+
* - JSX string templates (transpiled at runtime with SWC)
|
|
7
|
+
*
|
|
8
|
+
* Uses react-dom/server for SSR to HTML.
|
|
9
|
+
*/
|
|
10
|
+
import type { TemplateContext } from '../runtime/types';
|
|
11
|
+
import type { PlatformCapabilities } from '../theme';
|
|
12
|
+
import type { UIRenderer, TranspileResult, TranspileOptions, RenderOptions, RuntimeScripts, ToolUIProps } from './types';
|
|
13
|
+
/**
|
|
14
|
+
* Types this renderer can handle.
|
|
15
|
+
*/
|
|
16
|
+
type ReactTemplate<In = unknown, Out = unknown> = ((props: ToolUIProps<In, Out>) => unknown) | string;
|
|
17
|
+
/**
|
|
18
|
+
* React Renderer Implementation.
|
|
19
|
+
*
|
|
20
|
+
* Handles:
|
|
21
|
+
* - Imported React components (FC or class)
|
|
22
|
+
* - JSX string templates (transpiled with SWC at runtime)
|
|
23
|
+
*
|
|
24
|
+
* Renders to HTML using react-dom/server's renderToString.
|
|
25
|
+
*
|
|
26
|
+
* @example Imported component
|
|
27
|
+
* ```typescript
|
|
28
|
+
* import { MyWidget } from './my-widget.tsx';
|
|
29
|
+
*
|
|
30
|
+
* @Tool({
|
|
31
|
+
* ui: { template: MyWidget }
|
|
32
|
+
* })
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* @example JSX string (runtime transpilation)
|
|
36
|
+
* ```typescript
|
|
37
|
+
* @Tool({
|
|
38
|
+
* ui: {
|
|
39
|
+
* template: `
|
|
40
|
+
* function Widget({ output }) {
|
|
41
|
+
* return <div>{output.name}</div>;
|
|
42
|
+
* }
|
|
43
|
+
* `
|
|
44
|
+
* }
|
|
45
|
+
* })
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare class ReactRenderer implements UIRenderer<ReactTemplate> {
|
|
49
|
+
readonly type: "react";
|
|
50
|
+
readonly priority = 20;
|
|
51
|
+
/**
|
|
52
|
+
* Lazy-loaded React modules.
|
|
53
|
+
*/
|
|
54
|
+
private React;
|
|
55
|
+
private ReactDOMServer;
|
|
56
|
+
/**
|
|
57
|
+
* Check if this renderer can handle the given template.
|
|
58
|
+
*
|
|
59
|
+
* Accepts:
|
|
60
|
+
* - React component functions (imported, already transpiled)
|
|
61
|
+
* - Strings containing JSX syntax
|
|
62
|
+
*/
|
|
63
|
+
canHandle(template: unknown): template is ReactTemplate;
|
|
64
|
+
/**
|
|
65
|
+
* Transpile the template if needed.
|
|
66
|
+
*
|
|
67
|
+
* For imported React components, no transpilation is needed.
|
|
68
|
+
* For JSX strings, SWC transpilation is performed.
|
|
69
|
+
*/
|
|
70
|
+
transpile(template: ReactTemplate, options?: TranspileOptions): Promise<TranspileResult>;
|
|
71
|
+
/**
|
|
72
|
+
* Render the template to HTML string using react-dom/server.
|
|
73
|
+
*/
|
|
74
|
+
render<In, Out>(template: ReactTemplate<In, Out>, context: TemplateContext<In, Out>, options?: RenderOptions): Promise<string>;
|
|
75
|
+
/**
|
|
76
|
+
* Get runtime scripts for client-side functionality.
|
|
77
|
+
*/
|
|
78
|
+
getRuntimeScripts(platform: PlatformCapabilities): RuntimeScripts;
|
|
79
|
+
/**
|
|
80
|
+
* Load React and ReactDOMServer modules.
|
|
81
|
+
*/
|
|
82
|
+
private loadReact;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Singleton instance of the React renderer.
|
|
86
|
+
*/
|
|
87
|
+
export declare const reactRenderer: ReactRenderer;
|
|
88
|
+
/**
|
|
89
|
+
* Build React hydration script for client-side interactivity.
|
|
90
|
+
*
|
|
91
|
+
* This script finds elements with data-hydrate attributes and
|
|
92
|
+
* hydrates them with the corresponding React component.
|
|
93
|
+
*/
|
|
94
|
+
export declare function buildHydrationScript(): string;
|
|
95
|
+
export {};
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* React Renderer
|
|
4
|
+
*
|
|
5
|
+
* Handles React component templates:
|
|
6
|
+
* - Imported React components (already transpiled)
|
|
7
|
+
* - JSX string templates (transpiled at runtime with SWC)
|
|
8
|
+
*
|
|
9
|
+
* Uses react-dom/server for SSR to HTML.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.reactRenderer = exports.ReactRenderer = void 0;
|
|
13
|
+
exports.buildHydrationScript = buildHydrationScript;
|
|
14
|
+
const detect_1 = require("./utils/detect");
|
|
15
|
+
const hash_1 = require("./utils/hash");
|
|
16
|
+
const transpiler_1 = require("./utils/transpiler");
|
|
17
|
+
const cache_1 = require("./cache");
|
|
18
|
+
/**
|
|
19
|
+
* React runtime CDN URLs.
|
|
20
|
+
* Using esm.sh for React 19 (ES modules).
|
|
21
|
+
*/
|
|
22
|
+
const REACT_CDN = {
|
|
23
|
+
react: 'https://esm.sh/react@19',
|
|
24
|
+
reactDom: 'https://esm.sh/react-dom@19/client',
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Minimal inline React runtime for blocked-network platforms (Claude).
|
|
28
|
+
* This is a placeholder - in production, we'd bundle a minified React.
|
|
29
|
+
*/
|
|
30
|
+
const INLINE_REACT_PLACEHOLDER = `
|
|
31
|
+
// React runtime not available inline yet.
|
|
32
|
+
// For blocked-network platforms, use pre-rendered HTML templates.
|
|
33
|
+
console.warn('[FrontMCP] React hydration not available on this platform.');
|
|
34
|
+
`;
|
|
35
|
+
/**
|
|
36
|
+
* React Renderer Implementation.
|
|
37
|
+
*
|
|
38
|
+
* Handles:
|
|
39
|
+
* - Imported React components (FC or class)
|
|
40
|
+
* - JSX string templates (transpiled with SWC at runtime)
|
|
41
|
+
*
|
|
42
|
+
* Renders to HTML using react-dom/server's renderToString.
|
|
43
|
+
*
|
|
44
|
+
* @example Imported component
|
|
45
|
+
* ```typescript
|
|
46
|
+
* import { MyWidget } from './my-widget.tsx';
|
|
47
|
+
*
|
|
48
|
+
* @Tool({
|
|
49
|
+
* ui: { template: MyWidget }
|
|
50
|
+
* })
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* @example JSX string (runtime transpilation)
|
|
54
|
+
* ```typescript
|
|
55
|
+
* @Tool({
|
|
56
|
+
* ui: {
|
|
57
|
+
* template: `
|
|
58
|
+
* function Widget({ output }) {
|
|
59
|
+
* return <div>{output.name}</div>;
|
|
60
|
+
* }
|
|
61
|
+
* `
|
|
62
|
+
* }
|
|
63
|
+
* })
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
class ReactRenderer {
|
|
67
|
+
type = 'react';
|
|
68
|
+
priority = 20; // Higher priority than HTML
|
|
69
|
+
/**
|
|
70
|
+
* Lazy-loaded React modules.
|
|
71
|
+
*/
|
|
72
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
73
|
+
React = null;
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
75
|
+
ReactDOMServer = null;
|
|
76
|
+
/**
|
|
77
|
+
* Check if this renderer can handle the given template.
|
|
78
|
+
*
|
|
79
|
+
* Accepts:
|
|
80
|
+
* - React component functions (imported, already transpiled)
|
|
81
|
+
* - Strings containing JSX syntax
|
|
82
|
+
*/
|
|
83
|
+
canHandle(template) {
|
|
84
|
+
// React component function
|
|
85
|
+
if (typeof template === 'function' && (0, detect_1.isReactComponent)(template)) {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
// JSX string
|
|
89
|
+
if (typeof template === 'string' && (0, detect_1.containsJsx)(template)) {
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Transpile the template if needed.
|
|
96
|
+
*
|
|
97
|
+
* For imported React components, no transpilation is needed.
|
|
98
|
+
* For JSX strings, SWC transpilation is performed.
|
|
99
|
+
*/
|
|
100
|
+
async transpile(template, options) {
|
|
101
|
+
// Imported component - no transpilation needed
|
|
102
|
+
if (typeof template === 'function') {
|
|
103
|
+
const hash = (0, hash_1.hashString)(template.toString());
|
|
104
|
+
return {
|
|
105
|
+
code: '', // No transpiled code for already-compiled components
|
|
106
|
+
hash,
|
|
107
|
+
cached: true,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
// JSX string - transpile with SWC
|
|
111
|
+
if (typeof template === 'string') {
|
|
112
|
+
return (0, transpiler_1.transpileJsx)(template, {
|
|
113
|
+
development: options?.sourceMaps ?? false,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
throw new Error('Invalid template type for ReactRenderer');
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Render the template to HTML string using react-dom/server.
|
|
120
|
+
*/
|
|
121
|
+
async render(template, context, options) {
|
|
122
|
+
// Ensure React is loaded
|
|
123
|
+
await this.loadReact();
|
|
124
|
+
// Get the component function
|
|
125
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
126
|
+
let Component;
|
|
127
|
+
if (typeof template === 'function') {
|
|
128
|
+
// Already a component function
|
|
129
|
+
Component = template;
|
|
130
|
+
}
|
|
131
|
+
else if (typeof template === 'string') {
|
|
132
|
+
// Transpile and execute the JSX string
|
|
133
|
+
const transpiled = await this.transpile(template);
|
|
134
|
+
// Check cache for the executed component
|
|
135
|
+
const cached = cache_1.transpileCache.getByKey(`exec:${transpiled.hash}`);
|
|
136
|
+
if (cached) {
|
|
137
|
+
Component = cached.code;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
// Execute the transpiled code to get the component
|
|
141
|
+
Component = await (0, transpiler_1.executeTranspiledCode)(transpiled.code, {
|
|
142
|
+
// Provide any additional MDX components if specified
|
|
143
|
+
...options?.mdxComponents,
|
|
144
|
+
});
|
|
145
|
+
// Cache the component function
|
|
146
|
+
cache_1.transpileCache.setByKey(`exec:${transpiled.hash}`, {
|
|
147
|
+
code: Component,
|
|
148
|
+
hash: transpiled.hash,
|
|
149
|
+
cached: false,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
throw new Error('Invalid template type for ReactRenderer');
|
|
155
|
+
}
|
|
156
|
+
// Build props from context
|
|
157
|
+
const props = {
|
|
158
|
+
input: context.input,
|
|
159
|
+
output: context.output,
|
|
160
|
+
structuredContent: context.structuredContent,
|
|
161
|
+
helpers: context.helpers,
|
|
162
|
+
};
|
|
163
|
+
// Create React element
|
|
164
|
+
const element = this.React.createElement(Component, props);
|
|
165
|
+
// Render to HTML string
|
|
166
|
+
const html = this.ReactDOMServer.renderToString(element);
|
|
167
|
+
// If hydration is enabled, wrap with hydration markers
|
|
168
|
+
if (options?.hydrate) {
|
|
169
|
+
const componentName = Component.name || 'Component';
|
|
170
|
+
// Full HTML attribute escaping to prevent XSS
|
|
171
|
+
const escapedProps = JSON.stringify(props)
|
|
172
|
+
.replace(/&/g, '&')
|
|
173
|
+
.replace(/'/g, ''')
|
|
174
|
+
.replace(/</g, '<')
|
|
175
|
+
.replace(/>/g, '>');
|
|
176
|
+
return `<div data-hydrate="${componentName}" data-props='${escapedProps}'>${html}</div>`;
|
|
177
|
+
}
|
|
178
|
+
return html;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Get runtime scripts for client-side functionality.
|
|
182
|
+
*/
|
|
183
|
+
getRuntimeScripts(platform) {
|
|
184
|
+
// For blocked-network platforms (Claude), scripts must be inline
|
|
185
|
+
if (platform.networkMode === 'blocked') {
|
|
186
|
+
return {
|
|
187
|
+
headScripts: '',
|
|
188
|
+
inlineScripts: INLINE_REACT_PLACEHOLDER,
|
|
189
|
+
isInline: true,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
// For platforms with network access, use CDN
|
|
193
|
+
return {
|
|
194
|
+
headScripts: `
|
|
195
|
+
<script crossorigin src="${REACT_CDN.react}"></script>
|
|
196
|
+
<script crossorigin src="${REACT_CDN.reactDom}"></script>
|
|
197
|
+
`,
|
|
198
|
+
isInline: false,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Load React and ReactDOMServer modules.
|
|
203
|
+
*/
|
|
204
|
+
async loadReact() {
|
|
205
|
+
if (this.React && this.ReactDOMServer) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
try {
|
|
209
|
+
this.React = await import('react');
|
|
210
|
+
this.ReactDOMServer = await import('react-dom/server');
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
throw new Error('React is required for ReactRenderer. Install react and react-dom: npm install react react-dom');
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
exports.ReactRenderer = ReactRenderer;
|
|
218
|
+
/**
|
|
219
|
+
* Singleton instance of the React renderer.
|
|
220
|
+
*/
|
|
221
|
+
exports.reactRenderer = new ReactRenderer();
|
|
222
|
+
/**
|
|
223
|
+
* Build React hydration script for client-side interactivity.
|
|
224
|
+
*
|
|
225
|
+
* This script finds elements with data-hydrate attributes and
|
|
226
|
+
* hydrates them with the corresponding React component.
|
|
227
|
+
*/
|
|
228
|
+
function buildHydrationScript() {
|
|
229
|
+
return `
|
|
230
|
+
<script>
|
|
231
|
+
(function() {
|
|
232
|
+
// Wait for React to be available
|
|
233
|
+
if (typeof React === 'undefined' || typeof ReactDOM === 'undefined') {
|
|
234
|
+
console.warn('[FrontMCP] React not available for hydration');
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Find all elements marked for hydration
|
|
239
|
+
document.querySelectorAll('[data-hydrate]').forEach(function(root) {
|
|
240
|
+
var componentName = root.getAttribute('data-hydrate');
|
|
241
|
+
var propsJson = root.getAttribute('data-props');
|
|
242
|
+
var props = propsJson ? JSON.parse(propsJson) : {};
|
|
243
|
+
|
|
244
|
+
// Look for the component in the global scope
|
|
245
|
+
if (window.__frontmcp_components && window.__frontmcp_components[componentName]) {
|
|
246
|
+
try {
|
|
247
|
+
ReactDOM.hydrateRoot(root, React.createElement(
|
|
248
|
+
window.__frontmcp_components[componentName],
|
|
249
|
+
props
|
|
250
|
+
));
|
|
251
|
+
} catch (e) {
|
|
252
|
+
console.error('[FrontMCP] Hydration failed for', componentName, e);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
})();
|
|
257
|
+
</script>
|
|
258
|
+
`;
|
|
259
|
+
}
|
|
260
|
+
//# sourceMappingURL=react.renderer.js.map
|