@frontmcp/uipack 0.6.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/CLAUDE.md +246 -0
- package/LICENSE +201 -0
- package/README.md +150 -0
- package/adapters/index.d.ts +13 -0
- package/adapters/index.d.ts.map +1 -0
- package/adapters/index.js +462 -0
- package/adapters/platform-meta.d.ts +166 -0
- package/adapters/platform-meta.d.ts.map +1 -0
- package/adapters/response-builder.d.ts +108 -0
- package/adapters/response-builder.d.ts.map +1 -0
- package/adapters/serving-mode.d.ts +107 -0
- package/adapters/serving-mode.d.ts.map +1 -0
- package/base-template/bridge.d.ts +90 -0
- package/base-template/bridge.d.ts.map +1 -0
- package/base-template/default-base-template.d.ts +92 -0
- package/base-template/default-base-template.d.ts.map +1 -0
- package/base-template/index.d.ts +15 -0
- package/base-template/index.d.ts.map +1 -0
- package/base-template/index.js +1398 -0
- package/base-template/polyfills.d.ts +31 -0
- package/base-template/polyfills.d.ts.map +1 -0
- package/base-template/theme-styles.d.ts +74 -0
- package/base-template/theme-styles.d.ts.map +1 -0
- package/bridge-runtime/iife-generator.d.ts +62 -0
- package/bridge-runtime/iife-generator.d.ts.map +1 -0
- package/bridge-runtime/index.d.ts +10 -0
- package/bridge-runtime/index.d.ts.map +1 -0
- package/bridge-runtime/index.js +883 -0
- package/build/cdn-resources.d.ts +243 -0
- package/build/cdn-resources.d.ts.map +1 -0
- package/build/index.d.ts +295 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +6861 -0
- package/build/widget-manifest.d.ts +362 -0
- package/build/widget-manifest.d.ts.map +1 -0
- package/bundler/cache.d.ts +173 -0
- package/bundler/cache.d.ts.map +1 -0
- package/bundler/file-cache/component-builder.d.ts +167 -0
- package/bundler/file-cache/component-builder.d.ts.map +1 -0
- package/bundler/file-cache/hash-calculator.d.ts +155 -0
- package/bundler/file-cache/hash-calculator.d.ts.map +1 -0
- package/bundler/file-cache/index.d.ts +12 -0
- package/bundler/file-cache/index.d.ts.map +1 -0
- package/bundler/file-cache/storage/filesystem.d.ts +149 -0
- package/bundler/file-cache/storage/filesystem.d.ts.map +1 -0
- package/bundler/file-cache/storage/index.d.ts +11 -0
- package/bundler/file-cache/storage/index.d.ts.map +1 -0
- package/bundler/file-cache/storage/interface.d.ts +152 -0
- package/bundler/file-cache/storage/interface.d.ts.map +1 -0
- package/bundler/file-cache/storage/redis.d.ts +139 -0
- package/bundler/file-cache/storage/redis.d.ts.map +1 -0
- package/bundler/index.d.ts +35 -0
- package/bundler/index.d.ts.map +1 -0
- package/bundler/index.js +2947 -0
- package/bundler/sandbox/enclave-adapter.d.ts +121 -0
- package/bundler/sandbox/enclave-adapter.d.ts.map +1 -0
- package/bundler/sandbox/executor.d.ts +14 -0
- package/bundler/sandbox/executor.d.ts.map +1 -0
- package/bundler/sandbox/policy.d.ts +62 -0
- package/bundler/sandbox/policy.d.ts.map +1 -0
- package/bundler/types.d.ts +702 -0
- package/bundler/types.d.ts.map +1 -0
- package/dependency/cdn-registry.d.ts +98 -0
- package/dependency/cdn-registry.d.ts.map +1 -0
- package/dependency/import-map.d.ts +186 -0
- package/dependency/import-map.d.ts.map +1 -0
- package/dependency/import-parser.d.ts +82 -0
- package/dependency/import-parser.d.ts.map +1 -0
- package/dependency/index.d.ts +17 -0
- package/dependency/index.d.ts.map +1 -0
- package/dependency/index.js +3215 -0
- package/dependency/resolver.d.ts +164 -0
- package/dependency/resolver.d.ts.map +1 -0
- package/dependency/schemas.d.ts +486 -0
- package/dependency/schemas.d.ts.map +1 -0
- package/dependency/template-loader.d.ts +204 -0
- package/dependency/template-loader.d.ts.map +1 -0
- package/dependency/template-processor.d.ts +118 -0
- package/dependency/template-processor.d.ts.map +1 -0
- package/dependency/types.d.ts +739 -0
- package/dependency/types.d.ts.map +1 -0
- package/esm/adapters/index.d.ts +13 -0
- package/esm/adapters/index.d.ts.map +1 -0
- package/esm/adapters/index.js +427 -0
- package/esm/adapters/platform-meta.d.ts +166 -0
- package/esm/adapters/platform-meta.d.ts.map +1 -0
- package/esm/adapters/response-builder.d.ts +108 -0
- package/esm/adapters/response-builder.d.ts.map +1 -0
- package/esm/adapters/serving-mode.d.ts +107 -0
- package/esm/adapters/serving-mode.d.ts.map +1 -0
- package/esm/base-template/bridge.d.ts +90 -0
- package/esm/base-template/bridge.d.ts.map +1 -0
- package/esm/base-template/default-base-template.d.ts +92 -0
- package/esm/base-template/default-base-template.d.ts.map +1 -0
- package/esm/base-template/index.d.ts +15 -0
- package/esm/base-template/index.d.ts.map +1 -0
- package/esm/base-template/index.js +1364 -0
- package/esm/base-template/polyfills.d.ts +31 -0
- package/esm/base-template/polyfills.d.ts.map +1 -0
- package/esm/base-template/theme-styles.d.ts +74 -0
- package/esm/base-template/theme-styles.d.ts.map +1 -0
- package/esm/bridge-runtime/iife-generator.d.ts +62 -0
- package/esm/bridge-runtime/iife-generator.d.ts.map +1 -0
- package/esm/bridge-runtime/index.d.ts +10 -0
- package/esm/bridge-runtime/index.d.ts.map +1 -0
- package/esm/bridge-runtime/index.js +853 -0
- package/esm/build/cdn-resources.d.ts +243 -0
- package/esm/build/cdn-resources.d.ts.map +1 -0
- package/esm/build/index.d.ts +295 -0
- package/esm/build/index.d.ts.map +1 -0
- package/esm/build/index.js +6786 -0
- package/esm/build/widget-manifest.d.ts +362 -0
- package/esm/build/widget-manifest.d.ts.map +1 -0
- package/esm/bundler/cache.d.ts +173 -0
- package/esm/bundler/cache.d.ts.map +1 -0
- package/esm/bundler/file-cache/component-builder.d.ts +167 -0
- package/esm/bundler/file-cache/component-builder.d.ts.map +1 -0
- package/esm/bundler/file-cache/hash-calculator.d.ts +155 -0
- package/esm/bundler/file-cache/hash-calculator.d.ts.map +1 -0
- package/esm/bundler/file-cache/index.d.ts +12 -0
- package/esm/bundler/file-cache/index.d.ts.map +1 -0
- package/esm/bundler/file-cache/storage/filesystem.d.ts +149 -0
- package/esm/bundler/file-cache/storage/filesystem.d.ts.map +1 -0
- package/esm/bundler/file-cache/storage/index.d.ts +11 -0
- package/esm/bundler/file-cache/storage/index.d.ts.map +1 -0
- package/esm/bundler/file-cache/storage/interface.d.ts +152 -0
- package/esm/bundler/file-cache/storage/interface.d.ts.map +1 -0
- package/esm/bundler/file-cache/storage/redis.d.ts +139 -0
- package/esm/bundler/file-cache/storage/redis.d.ts.map +1 -0
- package/esm/bundler/index.d.ts +35 -0
- package/esm/bundler/index.d.ts.map +1 -0
- package/esm/bundler/index.js +2882 -0
- package/esm/bundler/sandbox/enclave-adapter.d.ts +121 -0
- package/esm/bundler/sandbox/enclave-adapter.d.ts.map +1 -0
- package/esm/bundler/sandbox/executor.d.ts +14 -0
- package/esm/bundler/sandbox/executor.d.ts.map +1 -0
- package/esm/bundler/sandbox/policy.d.ts +62 -0
- package/esm/bundler/sandbox/policy.d.ts.map +1 -0
- package/esm/bundler/types.d.ts +702 -0
- package/esm/bundler/types.d.ts.map +1 -0
- package/esm/dependency/cdn-registry.d.ts +98 -0
- package/esm/dependency/cdn-registry.d.ts.map +1 -0
- package/esm/dependency/import-map.d.ts +186 -0
- package/esm/dependency/import-map.d.ts.map +1 -0
- package/esm/dependency/import-parser.d.ts +82 -0
- package/esm/dependency/import-parser.d.ts.map +1 -0
- package/esm/dependency/index.d.ts +17 -0
- package/esm/dependency/index.d.ts.map +1 -0
- package/esm/dependency/index.js +3096 -0
- package/esm/dependency/resolver.d.ts +164 -0
- package/esm/dependency/resolver.d.ts.map +1 -0
- package/esm/dependency/schemas.d.ts +486 -0
- package/esm/dependency/schemas.d.ts.map +1 -0
- package/esm/dependency/template-loader.d.ts +204 -0
- package/esm/dependency/template-loader.d.ts.map +1 -0
- package/esm/dependency/template-processor.d.ts +118 -0
- package/esm/dependency/template-processor.d.ts.map +1 -0
- package/esm/dependency/types.d.ts +739 -0
- package/esm/dependency/types.d.ts.map +1 -0
- package/esm/handlebars/expression-extractor.d.ts +147 -0
- package/esm/handlebars/expression-extractor.d.ts.map +1 -0
- package/esm/handlebars/helpers.d.ts +339 -0
- package/esm/handlebars/helpers.d.ts.map +1 -0
- package/esm/handlebars/index.d.ts +195 -0
- package/esm/handlebars/index.d.ts.map +1 -0
- package/esm/handlebars/index.js +587 -0
- package/esm/index.d.ts +50 -0
- package/esm/index.d.ts.map +1 -0
- package/esm/index.js +12434 -0
- package/esm/package.json +68 -0
- package/esm/registry/index.d.ts +46 -0
- package/esm/registry/index.d.ts.map +1 -0
- package/esm/registry/index.js +6237 -0
- package/esm/registry/render-template.d.ts +91 -0
- package/esm/registry/render-template.d.ts.map +1 -0
- package/esm/registry/tool-ui.registry.d.ts +294 -0
- package/esm/registry/tool-ui.registry.d.ts.map +1 -0
- package/esm/registry/uri-utils.d.ts +56 -0
- package/esm/registry/uri-utils.d.ts.map +1 -0
- package/esm/renderers/cache.d.ts +145 -0
- package/esm/renderers/cache.d.ts.map +1 -0
- package/esm/renderers/html.renderer.d.ts +123 -0
- package/esm/renderers/html.renderer.d.ts.map +1 -0
- package/esm/renderers/index.d.ts +36 -0
- package/esm/renderers/index.d.ts.map +1 -0
- package/esm/renderers/index.js +1654 -0
- package/esm/renderers/mdx.renderer.d.ts +120 -0
- package/esm/renderers/mdx.renderer.d.ts.map +1 -0
- package/esm/renderers/registry.d.ts +133 -0
- package/esm/renderers/registry.d.ts.map +1 -0
- package/esm/renderers/types.d.ts +342 -0
- package/esm/renderers/types.d.ts.map +1 -0
- package/esm/renderers/utils/detect.d.ts +107 -0
- package/esm/renderers/utils/detect.d.ts.map +1 -0
- package/esm/renderers/utils/hash.d.ts +40 -0
- package/esm/renderers/utils/hash.d.ts.map +1 -0
- package/esm/renderers/utils/index.d.ts +9 -0
- package/esm/renderers/utils/index.d.ts.map +1 -0
- package/esm/renderers/utils/transpiler.d.ts +89 -0
- package/esm/renderers/utils/transpiler.d.ts.map +1 -0
- package/esm/runtime/adapters/html.adapter.d.ts +59 -0
- package/esm/runtime/adapters/html.adapter.d.ts.map +1 -0
- package/esm/runtime/adapters/index.d.ts +26 -0
- package/esm/runtime/adapters/index.d.ts.map +1 -0
- package/esm/runtime/adapters/mdx.adapter.d.ts +73 -0
- package/esm/runtime/adapters/mdx.adapter.d.ts.map +1 -0
- package/esm/runtime/adapters/types.d.ts +95 -0
- package/esm/runtime/adapters/types.d.ts.map +1 -0
- package/esm/runtime/csp.d.ts +48 -0
- package/esm/runtime/csp.d.ts.map +1 -0
- package/esm/runtime/index.d.ts +17 -0
- package/esm/runtime/index.d.ts.map +1 -0
- package/esm/runtime/index.js +4976 -0
- package/esm/runtime/mcp-bridge.d.ts +101 -0
- package/esm/runtime/mcp-bridge.d.ts.map +1 -0
- package/esm/runtime/renderer-runtime.d.ts +133 -0
- package/esm/runtime/renderer-runtime.d.ts.map +1 -0
- package/esm/runtime/sanitizer.d.ts +172 -0
- package/esm/runtime/sanitizer.d.ts.map +1 -0
- package/esm/runtime/types.d.ts +415 -0
- package/esm/runtime/types.d.ts.map +1 -0
- package/esm/runtime/wrapper.d.ts +421 -0
- package/esm/runtime/wrapper.d.ts.map +1 -0
- package/esm/styles/index.d.ts +8 -0
- package/esm/styles/index.d.ts.map +1 -0
- package/esm/styles/index.js +171 -0
- package/esm/styles/variants.d.ts +51 -0
- package/esm/styles/variants.d.ts.map +1 -0
- package/esm/theme/cdn.d.ts +195 -0
- package/esm/theme/cdn.d.ts.map +1 -0
- package/esm/theme/index.d.ts +18 -0
- package/esm/theme/index.d.ts.map +1 -0
- package/esm/theme/index.js +700 -0
- package/esm/theme/platforms.d.ts +107 -0
- package/esm/theme/platforms.d.ts.map +1 -0
- package/esm/theme/presets/github-openai.d.ts +50 -0
- package/esm/theme/presets/github-openai.d.ts.map +1 -0
- package/esm/theme/presets/index.d.ts +11 -0
- package/esm/theme/presets/index.d.ts.map +1 -0
- package/esm/theme/theme.d.ts +396 -0
- package/esm/theme/theme.d.ts.map +1 -0
- package/esm/tool-template/builder.d.ts +213 -0
- package/esm/tool-template/builder.d.ts.map +1 -0
- package/esm/tool-template/index.d.ts +16 -0
- package/esm/tool-template/index.d.ts.map +1 -0
- package/esm/tool-template/index.js +3518 -0
- package/esm/types/index.d.ts +14 -0
- package/esm/types/index.d.ts.map +1 -0
- package/esm/types/index.js +75 -0
- package/esm/types/ui-config.d.ts +641 -0
- package/esm/types/ui-config.d.ts.map +1 -0
- package/esm/types/ui-runtime.d.ts +1008 -0
- package/esm/types/ui-runtime.d.ts.map +1 -0
- package/esm/typings/cache/cache-adapter.d.ts +125 -0
- package/esm/typings/cache/cache-adapter.d.ts.map +1 -0
- package/esm/typings/cache/index.d.ts +10 -0
- package/esm/typings/cache/index.d.ts.map +1 -0
- package/esm/typings/cache/memory-cache.d.ts +92 -0
- package/esm/typings/cache/memory-cache.d.ts.map +1 -0
- package/esm/typings/dts-parser.d.ts +90 -0
- package/esm/typings/dts-parser.d.ts.map +1 -0
- package/esm/typings/index.d.ts +48 -0
- package/esm/typings/index.d.ts.map +1 -0
- package/esm/typings/index.js +812 -0
- package/esm/typings/schemas.d.ts +232 -0
- package/esm/typings/schemas.d.ts.map +1 -0
- package/esm/typings/type-fetcher.d.ts +89 -0
- package/esm/typings/type-fetcher.d.ts.map +1 -0
- package/esm/typings/types.d.ts +320 -0
- package/esm/typings/types.d.ts.map +1 -0
- package/esm/utils/escape-html.d.ts +58 -0
- package/esm/utils/escape-html.d.ts.map +1 -0
- package/esm/utils/index.d.ts +10 -0
- package/esm/utils/index.d.ts.map +1 -0
- package/esm/utils/index.js +40 -0
- package/esm/utils/safe-stringify.d.ts +30 -0
- package/esm/utils/safe-stringify.d.ts.map +1 -0
- package/esm/validation/error-box.d.ts +56 -0
- package/esm/validation/error-box.d.ts.map +1 -0
- package/esm/validation/index.d.ts +13 -0
- package/esm/validation/index.d.ts.map +1 -0
- package/esm/validation/index.js +542 -0
- package/esm/validation/schema-paths.d.ts +118 -0
- package/esm/validation/schema-paths.d.ts.map +1 -0
- package/esm/validation/template-validator.d.ts +143 -0
- package/esm/validation/template-validator.d.ts.map +1 -0
- package/esm/validation/wrapper.d.ts +97 -0
- package/esm/validation/wrapper.d.ts.map +1 -0
- package/handlebars/expression-extractor.d.ts +147 -0
- package/handlebars/expression-extractor.d.ts.map +1 -0
- package/handlebars/helpers.d.ts +339 -0
- package/handlebars/helpers.d.ts.map +1 -0
- package/handlebars/index.d.ts +195 -0
- package/handlebars/index.d.ts.map +1 -0
- package/handlebars/index.js +666 -0
- package/index.d.ts +50 -0
- package/index.d.ts.map +1 -0
- package/index.js +12683 -0
- package/package.json +66 -0
- package/registry/index.d.ts +46 -0
- package/registry/index.d.ts.map +1 -0
- package/registry/index.js +6280 -0
- package/registry/render-template.d.ts +91 -0
- package/registry/render-template.d.ts.map +1 -0
- package/registry/tool-ui.registry.d.ts +294 -0
- package/registry/tool-ui.registry.d.ts.map +1 -0
- package/registry/uri-utils.d.ts +56 -0
- package/registry/uri-utils.d.ts.map +1 -0
- package/renderers/cache.d.ts +145 -0
- package/renderers/cache.d.ts.map +1 -0
- package/renderers/html.renderer.d.ts +123 -0
- package/renderers/html.renderer.d.ts.map +1 -0
- package/renderers/index.d.ts +36 -0
- package/renderers/index.d.ts.map +1 -0
- package/renderers/index.js +1706 -0
- package/renderers/mdx.renderer.d.ts +120 -0
- package/renderers/mdx.renderer.d.ts.map +1 -0
- package/renderers/registry.d.ts +133 -0
- package/renderers/registry.d.ts.map +1 -0
- package/renderers/types.d.ts +342 -0
- package/renderers/types.d.ts.map +1 -0
- package/renderers/utils/detect.d.ts +107 -0
- package/renderers/utils/detect.d.ts.map +1 -0
- package/renderers/utils/hash.d.ts +40 -0
- package/renderers/utils/hash.d.ts.map +1 -0
- package/renderers/utils/index.d.ts +9 -0
- package/renderers/utils/index.d.ts.map +1 -0
- package/renderers/utils/transpiler.d.ts +89 -0
- package/renderers/utils/transpiler.d.ts.map +1 -0
- package/runtime/adapters/html.adapter.d.ts +59 -0
- package/runtime/adapters/html.adapter.d.ts.map +1 -0
- package/runtime/adapters/index.d.ts +26 -0
- package/runtime/adapters/index.d.ts.map +1 -0
- package/runtime/adapters/mdx.adapter.d.ts +73 -0
- package/runtime/adapters/mdx.adapter.d.ts.map +1 -0
- package/runtime/adapters/types.d.ts +95 -0
- package/runtime/adapters/types.d.ts.map +1 -0
- package/runtime/csp.d.ts +48 -0
- package/runtime/csp.d.ts.map +1 -0
- package/runtime/index.d.ts +17 -0
- package/runtime/index.d.ts.map +1 -0
- package/runtime/index.js +5052 -0
- package/runtime/mcp-bridge.d.ts +101 -0
- package/runtime/mcp-bridge.d.ts.map +1 -0
- package/runtime/renderer-runtime.d.ts +133 -0
- package/runtime/renderer-runtime.d.ts.map +1 -0
- package/runtime/sanitizer.d.ts +172 -0
- package/runtime/sanitizer.d.ts.map +1 -0
- package/runtime/types.d.ts +415 -0
- package/runtime/types.d.ts.map +1 -0
- package/runtime/wrapper.d.ts +421 -0
- package/runtime/wrapper.d.ts.map +1 -0
- package/styles/index.d.ts +8 -0
- package/styles/index.d.ts.map +1 -0
- package/styles/index.js +222 -0
- package/styles/variants.d.ts +51 -0
- package/styles/variants.d.ts.map +1 -0
- package/theme/cdn.d.ts +195 -0
- package/theme/cdn.d.ts.map +1 -0
- package/theme/index.d.ts +18 -0
- package/theme/index.d.ts.map +1 -0
- package/theme/index.js +757 -0
- package/theme/platforms.d.ts +107 -0
- package/theme/platforms.d.ts.map +1 -0
- package/theme/presets/github-openai.d.ts +50 -0
- package/theme/presets/github-openai.d.ts.map +1 -0
- package/theme/presets/index.d.ts +11 -0
- package/theme/presets/index.d.ts.map +1 -0
- package/theme/theme.d.ts +396 -0
- package/theme/theme.d.ts.map +1 -0
- package/tool-template/builder.d.ts +213 -0
- package/tool-template/builder.d.ts.map +1 -0
- package/tool-template/index.d.ts +16 -0
- package/tool-template/index.d.ts.map +1 -0
- package/tool-template/index.js +3562 -0
- package/types/index.d.ts +14 -0
- package/types/index.d.ts.map +1 -0
- package/types/index.js +108 -0
- package/types/ui-config.d.ts +641 -0
- package/types/ui-config.d.ts.map +1 -0
- package/types/ui-runtime.d.ts +1008 -0
- package/types/ui-runtime.d.ts.map +1 -0
- package/typings/cache/cache-adapter.d.ts +125 -0
- package/typings/cache/cache-adapter.d.ts.map +1 -0
- package/typings/cache/index.d.ts +10 -0
- package/typings/cache/index.d.ts.map +1 -0
- package/typings/cache/memory-cache.d.ts +92 -0
- package/typings/cache/memory-cache.d.ts.map +1 -0
- package/typings/dts-parser.d.ts +90 -0
- package/typings/dts-parser.d.ts.map +1 -0
- package/typings/index.d.ts +48 -0
- package/typings/index.d.ts.map +1 -0
- package/typings/index.js +868 -0
- package/typings/schemas.d.ts +232 -0
- package/typings/schemas.d.ts.map +1 -0
- package/typings/type-fetcher.d.ts +89 -0
- package/typings/type-fetcher.d.ts.map +1 -0
- package/typings/types.d.ts +320 -0
- package/typings/types.d.ts.map +1 -0
- package/utils/escape-html.d.ts +58 -0
- package/utils/escape-html.d.ts.map +1 -0
- package/utils/index.d.ts +10 -0
- package/utils/index.d.ts.map +1 -0
- package/utils/index.js +70 -0
- package/utils/safe-stringify.d.ts +30 -0
- package/utils/safe-stringify.d.ts.map +1 -0
- package/validation/error-box.d.ts +56 -0
- package/validation/error-box.d.ts.map +1 -0
- package/validation/index.d.ts +13 -0
- package/validation/index.d.ts.map +1 -0
- package/validation/index.js +583 -0
- package/validation/schema-paths.d.ts +118 -0
- package/validation/schema-paths.d.ts.map +1 -0
- package/validation/template-validator.d.ts +143 -0
- package/validation/template-validator.d.ts.map +1 -0
- package/validation/wrapper.d.ts +97 -0
- package/validation/wrapper.d.ts.map +1 -0
|
@@ -0,0 +1,3562 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
11
|
+
var __export = (target, all) => {
|
|
12
|
+
for (var name in all)
|
|
13
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
14
|
+
};
|
|
15
|
+
var __copyProps = (to, from, except, desc) => {
|
|
16
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
17
|
+
for (let key of __getOwnPropNames(from))
|
|
18
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
19
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
20
|
+
}
|
|
21
|
+
return to;
|
|
22
|
+
};
|
|
23
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
24
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
25
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
26
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
27
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
28
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
29
|
+
mod
|
|
30
|
+
));
|
|
31
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
|
+
|
|
33
|
+
// libs/uipack/src/utils/safe-stringify.ts
|
|
34
|
+
var init_safe_stringify = __esm({
|
|
35
|
+
"libs/uipack/src/utils/safe-stringify.ts"() {
|
|
36
|
+
"use strict";
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// libs/uipack/src/utils/escape-html.ts
|
|
41
|
+
function escapeHtml(str) {
|
|
42
|
+
if (str === null || str === void 0) {
|
|
43
|
+
return "";
|
|
44
|
+
}
|
|
45
|
+
const s = String(str);
|
|
46
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
47
|
+
}
|
|
48
|
+
var init_escape_html = __esm({
|
|
49
|
+
"libs/uipack/src/utils/escape-html.ts"() {
|
|
50
|
+
"use strict";
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// libs/uipack/src/utils/index.ts
|
|
55
|
+
var init_utils = __esm({
|
|
56
|
+
"libs/uipack/src/utils/index.ts"() {
|
|
57
|
+
"use strict";
|
|
58
|
+
init_safe_stringify();
|
|
59
|
+
init_escape_html();
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// libs/uipack/src/handlebars/helpers.ts
|
|
64
|
+
function formatDate(date, format) {
|
|
65
|
+
if (date === null || date === void 0) {
|
|
66
|
+
return "";
|
|
67
|
+
}
|
|
68
|
+
let dateObj;
|
|
69
|
+
if (date instanceof Date) {
|
|
70
|
+
dateObj = date;
|
|
71
|
+
} else if (typeof date === "string" || typeof date === "number") {
|
|
72
|
+
dateObj = new Date(date);
|
|
73
|
+
} else {
|
|
74
|
+
return String(date);
|
|
75
|
+
}
|
|
76
|
+
if (isNaN(dateObj.getTime())) {
|
|
77
|
+
return String(date);
|
|
78
|
+
}
|
|
79
|
+
const options = {};
|
|
80
|
+
switch (format) {
|
|
81
|
+
case "short":
|
|
82
|
+
options.dateStyle = "short";
|
|
83
|
+
break;
|
|
84
|
+
case "medium":
|
|
85
|
+
options.dateStyle = "medium";
|
|
86
|
+
break;
|
|
87
|
+
case "long":
|
|
88
|
+
options.dateStyle = "long";
|
|
89
|
+
break;
|
|
90
|
+
case "full":
|
|
91
|
+
options.dateStyle = "full";
|
|
92
|
+
break;
|
|
93
|
+
case "time":
|
|
94
|
+
options.timeStyle = "short";
|
|
95
|
+
break;
|
|
96
|
+
case "datetime":
|
|
97
|
+
options.dateStyle = "medium";
|
|
98
|
+
options.timeStyle = "short";
|
|
99
|
+
break;
|
|
100
|
+
case "iso":
|
|
101
|
+
return dateObj.toISOString();
|
|
102
|
+
case "relative":
|
|
103
|
+
return formatRelativeDate(dateObj);
|
|
104
|
+
default:
|
|
105
|
+
options.dateStyle = "medium";
|
|
106
|
+
}
|
|
107
|
+
return new Intl.DateTimeFormat("en-US", options).format(dateObj);
|
|
108
|
+
}
|
|
109
|
+
function formatRelativeDate(date) {
|
|
110
|
+
const now = /* @__PURE__ */ new Date();
|
|
111
|
+
const diffMs = now.getTime() - date.getTime();
|
|
112
|
+
const diffSecs = Math.floor(diffMs / 1e3);
|
|
113
|
+
const diffMins = Math.floor(diffSecs / 60);
|
|
114
|
+
const diffHours = Math.floor(diffMins / 60);
|
|
115
|
+
const diffDays = Math.floor(diffHours / 24);
|
|
116
|
+
if (diffSecs < 60) {
|
|
117
|
+
return "just now";
|
|
118
|
+
} else if (diffMins < 60) {
|
|
119
|
+
return `${diffMins} minute${diffMins === 1 ? "" : "s"} ago`;
|
|
120
|
+
} else if (diffHours < 24) {
|
|
121
|
+
return `${diffHours} hour${diffHours === 1 ? "" : "s"} ago`;
|
|
122
|
+
} else if (diffDays < 7) {
|
|
123
|
+
return `${diffDays} day${diffDays === 1 ? "" : "s"} ago`;
|
|
124
|
+
} else {
|
|
125
|
+
return formatDate(date, "medium");
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function formatCurrency(amount, currency) {
|
|
129
|
+
if (amount === null || amount === void 0) {
|
|
130
|
+
return "";
|
|
131
|
+
}
|
|
132
|
+
const num = typeof amount === "number" ? amount : parseFloat(String(amount));
|
|
133
|
+
if (isNaN(num)) {
|
|
134
|
+
return String(amount);
|
|
135
|
+
}
|
|
136
|
+
return new Intl.NumberFormat("en-US", {
|
|
137
|
+
style: "currency",
|
|
138
|
+
currency: typeof currency === "string" ? currency : "USD"
|
|
139
|
+
}).format(num);
|
|
140
|
+
}
|
|
141
|
+
function formatNumber(value, decimals) {
|
|
142
|
+
if (value === null || value === void 0) {
|
|
143
|
+
return "";
|
|
144
|
+
}
|
|
145
|
+
const num = typeof value === "number" ? value : parseFloat(String(value));
|
|
146
|
+
if (isNaN(num)) {
|
|
147
|
+
return String(value);
|
|
148
|
+
}
|
|
149
|
+
const options = {};
|
|
150
|
+
if (typeof decimals === "number") {
|
|
151
|
+
options.minimumFractionDigits = decimals;
|
|
152
|
+
options.maximumFractionDigits = decimals;
|
|
153
|
+
}
|
|
154
|
+
return new Intl.NumberFormat("en-US", options).format(num);
|
|
155
|
+
}
|
|
156
|
+
function jsonEmbed(data) {
|
|
157
|
+
const json2 = JSON.stringify(data ?? null);
|
|
158
|
+
return json2.replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
159
|
+
}
|
|
160
|
+
function json(data, pretty) {
|
|
161
|
+
return JSON.stringify(data ?? null, null, pretty ? 2 : void 0);
|
|
162
|
+
}
|
|
163
|
+
function eq(a, b) {
|
|
164
|
+
return a === b;
|
|
165
|
+
}
|
|
166
|
+
function ne(a, b) {
|
|
167
|
+
return a !== b;
|
|
168
|
+
}
|
|
169
|
+
function gt(a, b) {
|
|
170
|
+
return Number(a) > Number(b);
|
|
171
|
+
}
|
|
172
|
+
function gte(a, b) {
|
|
173
|
+
return Number(a) >= Number(b);
|
|
174
|
+
}
|
|
175
|
+
function lt(a, b) {
|
|
176
|
+
return Number(a) < Number(b);
|
|
177
|
+
}
|
|
178
|
+
function lte(a, b) {
|
|
179
|
+
return Number(a) <= Number(b);
|
|
180
|
+
}
|
|
181
|
+
function and(a, b) {
|
|
182
|
+
return Boolean(a) && Boolean(b);
|
|
183
|
+
}
|
|
184
|
+
function or(a, b) {
|
|
185
|
+
return Boolean(a) || Boolean(b);
|
|
186
|
+
}
|
|
187
|
+
function not(value) {
|
|
188
|
+
return !value;
|
|
189
|
+
}
|
|
190
|
+
function first(arr) {
|
|
191
|
+
if (!Array.isArray(arr)) return void 0;
|
|
192
|
+
return arr[0];
|
|
193
|
+
}
|
|
194
|
+
function last(arr) {
|
|
195
|
+
if (!Array.isArray(arr)) return void 0;
|
|
196
|
+
return arr[arr.length - 1];
|
|
197
|
+
}
|
|
198
|
+
function length(value) {
|
|
199
|
+
if (Array.isArray(value)) return value.length;
|
|
200
|
+
if (typeof value === "string") return value.length;
|
|
201
|
+
return 0;
|
|
202
|
+
}
|
|
203
|
+
function includes(arr, value) {
|
|
204
|
+
if (!Array.isArray(arr)) return false;
|
|
205
|
+
return arr.includes(value);
|
|
206
|
+
}
|
|
207
|
+
function join(arr, separator) {
|
|
208
|
+
if (!Array.isArray(arr)) return "";
|
|
209
|
+
return arr.join(typeof separator === "string" ? separator : ", ");
|
|
210
|
+
}
|
|
211
|
+
function uppercase(str) {
|
|
212
|
+
return String(str ?? "").toUpperCase();
|
|
213
|
+
}
|
|
214
|
+
function lowercase(str) {
|
|
215
|
+
return String(str ?? "").toLowerCase();
|
|
216
|
+
}
|
|
217
|
+
function capitalize(str) {
|
|
218
|
+
const s = String(str ?? "");
|
|
219
|
+
return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();
|
|
220
|
+
}
|
|
221
|
+
function truncate(str, maxLength, suffix) {
|
|
222
|
+
const s = String(str ?? "");
|
|
223
|
+
const len = typeof maxLength === "number" ? maxLength : 50;
|
|
224
|
+
const suf = typeof suffix === "string" ? suffix : "...";
|
|
225
|
+
if (s.length <= len) return s;
|
|
226
|
+
return s.slice(0, len - suf.length) + suf;
|
|
227
|
+
}
|
|
228
|
+
function defaultValue(value, defaultValue2) {
|
|
229
|
+
return value || defaultValue2;
|
|
230
|
+
}
|
|
231
|
+
function uniqueId(prefix) {
|
|
232
|
+
const id = ++idCounter;
|
|
233
|
+
return prefix ? `${prefix}-${id}` : `id-${id}`;
|
|
234
|
+
}
|
|
235
|
+
function resetUniqueIdCounter() {
|
|
236
|
+
idCounter = 0;
|
|
237
|
+
}
|
|
238
|
+
function classNames(...classes) {
|
|
239
|
+
return classes.filter(Boolean).map(String).join(" ");
|
|
240
|
+
}
|
|
241
|
+
var idCounter, builtinHelpers;
|
|
242
|
+
var init_helpers = __esm({
|
|
243
|
+
"libs/uipack/src/handlebars/helpers.ts"() {
|
|
244
|
+
"use strict";
|
|
245
|
+
init_utils();
|
|
246
|
+
idCounter = 0;
|
|
247
|
+
builtinHelpers = {
|
|
248
|
+
// Escaping
|
|
249
|
+
escapeHtml,
|
|
250
|
+
// Formatting
|
|
251
|
+
formatDate,
|
|
252
|
+
formatCurrency,
|
|
253
|
+
formatNumber,
|
|
254
|
+
json,
|
|
255
|
+
jsonEmbed,
|
|
256
|
+
// Comparison
|
|
257
|
+
eq,
|
|
258
|
+
ne,
|
|
259
|
+
gt,
|
|
260
|
+
gte,
|
|
261
|
+
lt,
|
|
262
|
+
lte,
|
|
263
|
+
// Logical
|
|
264
|
+
and,
|
|
265
|
+
or,
|
|
266
|
+
not,
|
|
267
|
+
// Array
|
|
268
|
+
first,
|
|
269
|
+
last,
|
|
270
|
+
length,
|
|
271
|
+
includes,
|
|
272
|
+
join,
|
|
273
|
+
// String
|
|
274
|
+
uppercase,
|
|
275
|
+
lowercase,
|
|
276
|
+
capitalize,
|
|
277
|
+
truncate,
|
|
278
|
+
// Utility
|
|
279
|
+
default: defaultValue,
|
|
280
|
+
uniqueId,
|
|
281
|
+
classNames
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// libs/uipack/src/handlebars/expression-extractor.ts
|
|
287
|
+
function extractExpressions(template) {
|
|
288
|
+
const expressions = [];
|
|
289
|
+
const lines = template.split("\n");
|
|
290
|
+
const positionMap = buildPositionMap(template);
|
|
291
|
+
let match;
|
|
292
|
+
EXPRESSION_REGEX.lastIndex = 0;
|
|
293
|
+
while ((match = EXPRESSION_REGEX.exec(template)) !== null) {
|
|
294
|
+
const fullExpression = match[0];
|
|
295
|
+
const prefix = match[1];
|
|
296
|
+
const content = match[2].trim();
|
|
297
|
+
const position = positionMap.get(match.index) ?? { line: 1, column: 1 };
|
|
298
|
+
let type = "variable";
|
|
299
|
+
let helperName;
|
|
300
|
+
if (prefix === "/") {
|
|
301
|
+
type = "block-close";
|
|
302
|
+
helperName = content;
|
|
303
|
+
} else if (prefix === "#") {
|
|
304
|
+
type = "block";
|
|
305
|
+
const parts = content.split(/\s+/);
|
|
306
|
+
helperName = parts[0];
|
|
307
|
+
} else {
|
|
308
|
+
const parts = content.split(/\s+/);
|
|
309
|
+
if (parts.length > 1 && !content.startsWith("(")) {
|
|
310
|
+
const firstToken = parts[0];
|
|
311
|
+
if (!firstToken.includes(".") && !KEYWORDS.has(firstToken)) {
|
|
312
|
+
type = "helper";
|
|
313
|
+
helperName = firstToken;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
const paths = extractPathsFromContent(content);
|
|
318
|
+
for (const path of paths) {
|
|
319
|
+
expressions.push({
|
|
320
|
+
path,
|
|
321
|
+
fullExpression,
|
|
322
|
+
line: position.line,
|
|
323
|
+
column: position.column,
|
|
324
|
+
type,
|
|
325
|
+
helperName
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
if (paths.length === 0 && type === "variable") {
|
|
329
|
+
const cleanContent = content.trim();
|
|
330
|
+
if (!KEYWORDS.has(cleanContent) && !cleanContent.includes(" ") && !cleanContent.startsWith("(")) {
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return expressions;
|
|
335
|
+
}
|
|
336
|
+
function extractPathsFromContent(content) {
|
|
337
|
+
const paths = [];
|
|
338
|
+
let match;
|
|
339
|
+
const regex = new RegExp(PATH_REGEX.source, "g");
|
|
340
|
+
while ((match = regex.exec(content)) !== null) {
|
|
341
|
+
paths.push(match[0]);
|
|
342
|
+
}
|
|
343
|
+
return paths;
|
|
344
|
+
}
|
|
345
|
+
function buildPositionMap(template) {
|
|
346
|
+
const map = /* @__PURE__ */ new Map();
|
|
347
|
+
let line = 1;
|
|
348
|
+
let column = 1;
|
|
349
|
+
for (let i = 0; i < template.length; i++) {
|
|
350
|
+
map.set(i, { line, column });
|
|
351
|
+
if (template[i] === "\n") {
|
|
352
|
+
line++;
|
|
353
|
+
column = 1;
|
|
354
|
+
} else {
|
|
355
|
+
column++;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return map;
|
|
359
|
+
}
|
|
360
|
+
function extractVariablePaths(template) {
|
|
361
|
+
const expressions = extractExpressions(template);
|
|
362
|
+
const paths = new Set(expressions.map((e) => e.path));
|
|
363
|
+
return Array.from(paths);
|
|
364
|
+
}
|
|
365
|
+
function extractOutputPaths(template) {
|
|
366
|
+
return extractVariablePaths(template).filter((p) => p.startsWith("output."));
|
|
367
|
+
}
|
|
368
|
+
function extractInputPaths(template) {
|
|
369
|
+
return extractVariablePaths(template).filter((p) => p.startsWith("input."));
|
|
370
|
+
}
|
|
371
|
+
function extractStructuredContentPaths(template) {
|
|
372
|
+
return extractVariablePaths(template).filter((p) => p.startsWith("structuredContent."));
|
|
373
|
+
}
|
|
374
|
+
function extractAll(template) {
|
|
375
|
+
const expressions = extractExpressions(template);
|
|
376
|
+
const paths = [...new Set(expressions.map((e) => e.path))];
|
|
377
|
+
return {
|
|
378
|
+
expressions,
|
|
379
|
+
paths,
|
|
380
|
+
outputPaths: paths.filter((p) => p.startsWith("output.")),
|
|
381
|
+
inputPaths: paths.filter((p) => p.startsWith("input.")),
|
|
382
|
+
structuredContentPaths: paths.filter((p) => p.startsWith("structuredContent."))
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
function hasVariablePaths(template) {
|
|
386
|
+
return extractVariablePaths(template).length > 0;
|
|
387
|
+
}
|
|
388
|
+
function getExpressionAt(template, line, column) {
|
|
389
|
+
const expressions = extractExpressions(template);
|
|
390
|
+
return expressions.find((expr) => {
|
|
391
|
+
if (expr.line !== line) return false;
|
|
392
|
+
const exprEnd = expr.column + expr.fullExpression.length;
|
|
393
|
+
return column >= expr.column && column <= exprEnd;
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
function normalizePath(path) {
|
|
397
|
+
return path.replace(/\.\d+\./g, ".[].").replace(/\.\d+$/g, ".[]").replace(/\[\d+\]/g, ".[]");
|
|
398
|
+
}
|
|
399
|
+
var EXPRESSION_REGEX, PATH_REGEX, KEYWORDS;
|
|
400
|
+
var init_expression_extractor = __esm({
|
|
401
|
+
"libs/uipack/src/handlebars/expression-extractor.ts"() {
|
|
402
|
+
"use strict";
|
|
403
|
+
EXPRESSION_REGEX = /\{\{\{?(?!!)(#|\/)?([^}]+?)\}?\}\}/g;
|
|
404
|
+
PATH_REGEX = /\b(output|input|structuredContent)(\.[a-zA-Z_$][a-zA-Z0-9_$]*|\.\[[^\]]+\])+/g;
|
|
405
|
+
KEYWORDS = /* @__PURE__ */ new Set(["this", "else", "@index", "@key", "@first", "@last", "@root"]);
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
// libs/uipack/src/handlebars/index.ts
|
|
410
|
+
var handlebars_exports = {};
|
|
411
|
+
__export(handlebars_exports, {
|
|
412
|
+
HandlebarsRenderer: () => HandlebarsRenderer,
|
|
413
|
+
and: () => and,
|
|
414
|
+
builtinHelpers: () => builtinHelpers,
|
|
415
|
+
capitalize: () => capitalize,
|
|
416
|
+
classNames: () => classNames,
|
|
417
|
+
containsHandlebars: () => containsHandlebars,
|
|
418
|
+
createHandlebarsRenderer: () => createHandlebarsRenderer,
|
|
419
|
+
defaultValue: () => defaultValue,
|
|
420
|
+
eq: () => eq,
|
|
421
|
+
escapeHtml: () => escapeHtml,
|
|
422
|
+
extractAll: () => extractAll,
|
|
423
|
+
extractExpressions: () => extractExpressions,
|
|
424
|
+
extractInputPaths: () => extractInputPaths,
|
|
425
|
+
extractOutputPaths: () => extractOutputPaths,
|
|
426
|
+
extractStructuredContentPaths: () => extractStructuredContentPaths,
|
|
427
|
+
extractVariablePaths: () => extractVariablePaths,
|
|
428
|
+
first: () => first,
|
|
429
|
+
formatCurrency: () => formatCurrency,
|
|
430
|
+
formatDate: () => formatDate,
|
|
431
|
+
formatNumber: () => formatNumber,
|
|
432
|
+
getExpressionAt: () => getExpressionAt,
|
|
433
|
+
gt: () => gt,
|
|
434
|
+
gte: () => gte,
|
|
435
|
+
hasVariablePaths: () => hasVariablePaths,
|
|
436
|
+
includes: () => includes,
|
|
437
|
+
isHandlebarsAvailable: () => isHandlebarsAvailable,
|
|
438
|
+
join: () => join,
|
|
439
|
+
json: () => json,
|
|
440
|
+
jsonEmbed: () => jsonEmbed,
|
|
441
|
+
last: () => last,
|
|
442
|
+
length: () => length,
|
|
443
|
+
lowercase: () => lowercase,
|
|
444
|
+
lt: () => lt,
|
|
445
|
+
lte: () => lte,
|
|
446
|
+
ne: () => ne,
|
|
447
|
+
normalizePath: () => normalizePath,
|
|
448
|
+
not: () => not,
|
|
449
|
+
or: () => or,
|
|
450
|
+
renderTemplate: () => renderTemplate,
|
|
451
|
+
resetUniqueIdCounter: () => resetUniqueIdCounter,
|
|
452
|
+
truncate: () => truncate,
|
|
453
|
+
uniqueId: () => uniqueId,
|
|
454
|
+
uppercase: () => uppercase
|
|
455
|
+
});
|
|
456
|
+
async function loadHandlebars() {
|
|
457
|
+
if (Handlebars !== null) {
|
|
458
|
+
return Handlebars;
|
|
459
|
+
}
|
|
460
|
+
try {
|
|
461
|
+
Handlebars = await import("handlebars");
|
|
462
|
+
return Handlebars;
|
|
463
|
+
} catch {
|
|
464
|
+
throw new Error("Handlebars is required for template rendering. Install it: npm install handlebars");
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
async function isHandlebarsAvailable() {
|
|
468
|
+
try {
|
|
469
|
+
await loadHandlebars();
|
|
470
|
+
return true;
|
|
471
|
+
} catch {
|
|
472
|
+
return false;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
function createHandlebarsRenderer(options) {
|
|
476
|
+
return new HandlebarsRenderer(options);
|
|
477
|
+
}
|
|
478
|
+
async function renderTemplate(template, context) {
|
|
479
|
+
const renderer = createHandlebarsRenderer();
|
|
480
|
+
return renderer.render(template, context);
|
|
481
|
+
}
|
|
482
|
+
function containsHandlebars(template) {
|
|
483
|
+
return HandlebarsRenderer.containsHandlebars(template);
|
|
484
|
+
}
|
|
485
|
+
var Handlebars, HandlebarsRenderer;
|
|
486
|
+
var init_handlebars = __esm({
|
|
487
|
+
"libs/uipack/src/handlebars/index.ts"() {
|
|
488
|
+
"use strict";
|
|
489
|
+
init_helpers();
|
|
490
|
+
init_helpers();
|
|
491
|
+
init_expression_extractor();
|
|
492
|
+
Handlebars = null;
|
|
493
|
+
HandlebarsRenderer = class {
|
|
494
|
+
options;
|
|
495
|
+
compiledTemplates = /* @__PURE__ */ new Map();
|
|
496
|
+
initialized = false;
|
|
497
|
+
hbs = null;
|
|
498
|
+
constructor(options = {}) {
|
|
499
|
+
this.options = {
|
|
500
|
+
strict: false,
|
|
501
|
+
autoEscape: true,
|
|
502
|
+
...options
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Initialize the renderer with Handlebars.
|
|
507
|
+
*/
|
|
508
|
+
async init() {
|
|
509
|
+
if (this.initialized) return;
|
|
510
|
+
this.hbs = await loadHandlebars();
|
|
511
|
+
for (const [name, helper] of Object.entries(builtinHelpers)) {
|
|
512
|
+
this.hbs.registerHelper(name, helper);
|
|
513
|
+
}
|
|
514
|
+
if (this.options.helpers) {
|
|
515
|
+
for (const [name, helper] of Object.entries(this.options.helpers)) {
|
|
516
|
+
this.hbs.registerHelper(name, helper);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
if (this.options.partials) {
|
|
520
|
+
for (const [name, template] of Object.entries(this.options.partials)) {
|
|
521
|
+
this.hbs.registerPartial(name, template);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
this.initialized = true;
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Render a Handlebars template.
|
|
528
|
+
*
|
|
529
|
+
* @param template - Template string
|
|
530
|
+
* @param context - Render context with input/output
|
|
531
|
+
* @returns Rendered HTML string
|
|
532
|
+
*/
|
|
533
|
+
async render(template, context) {
|
|
534
|
+
await this.init();
|
|
535
|
+
if (!this.hbs) {
|
|
536
|
+
throw new Error("Handlebars not initialized");
|
|
537
|
+
}
|
|
538
|
+
let compiled = this.compiledTemplates.get(template);
|
|
539
|
+
if (!compiled) {
|
|
540
|
+
compiled = this.hbs.compile(template, {
|
|
541
|
+
strict: this.options.strict,
|
|
542
|
+
noEscape: !this.options.autoEscape
|
|
543
|
+
});
|
|
544
|
+
this.compiledTemplates.set(template, compiled);
|
|
545
|
+
}
|
|
546
|
+
const data = {
|
|
547
|
+
input: context.input ?? {},
|
|
548
|
+
output: context.output ?? {},
|
|
549
|
+
structuredContent: context.structuredContent,
|
|
550
|
+
// Also expose at root level for convenience
|
|
551
|
+
...context.input,
|
|
552
|
+
...typeof context.output === "object" && context.output !== null ? context.output : {}
|
|
553
|
+
};
|
|
554
|
+
try {
|
|
555
|
+
return compiled(data);
|
|
556
|
+
} catch (error) {
|
|
557
|
+
throw new Error(`Template rendering failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Render a template synchronously.
|
|
562
|
+
*
|
|
563
|
+
* Note: Requires Handlebars to be pre-loaded. Use `render()` for async loading.
|
|
564
|
+
*
|
|
565
|
+
* @param template - Template string
|
|
566
|
+
* @param context - Render context
|
|
567
|
+
* @returns Rendered HTML string
|
|
568
|
+
*/
|
|
569
|
+
renderSync(template, context) {
|
|
570
|
+
if (!this.initialized || !this.hbs) {
|
|
571
|
+
throw new Error("HandlebarsRenderer not initialized. Call render() first or use initSync().");
|
|
572
|
+
}
|
|
573
|
+
let compiled = this.compiledTemplates.get(template);
|
|
574
|
+
if (!compiled) {
|
|
575
|
+
compiled = this.hbs.compile(template, {
|
|
576
|
+
strict: this.options.strict,
|
|
577
|
+
noEscape: !this.options.autoEscape
|
|
578
|
+
});
|
|
579
|
+
this.compiledTemplates.set(template, compiled);
|
|
580
|
+
}
|
|
581
|
+
const data = {
|
|
582
|
+
input: context.input ?? {},
|
|
583
|
+
output: context.output ?? {},
|
|
584
|
+
structuredContent: context.structuredContent,
|
|
585
|
+
...context.input,
|
|
586
|
+
...typeof context.output === "object" && context.output !== null ? context.output : {}
|
|
587
|
+
};
|
|
588
|
+
return compiled(data);
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Initialize synchronously (for environments where Handlebars is already loaded).
|
|
592
|
+
*/
|
|
593
|
+
initSync(handlebars) {
|
|
594
|
+
this.hbs = handlebars;
|
|
595
|
+
for (const [name, helper] of Object.entries(builtinHelpers)) {
|
|
596
|
+
this.hbs.registerHelper(name, helper);
|
|
597
|
+
}
|
|
598
|
+
if (this.options.helpers) {
|
|
599
|
+
for (const [name, helper] of Object.entries(this.options.helpers)) {
|
|
600
|
+
this.hbs.registerHelper(name, helper);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
if (this.options.partials) {
|
|
604
|
+
for (const [name, template] of Object.entries(this.options.partials)) {
|
|
605
|
+
this.hbs.registerPartial(name, template);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
this.initialized = true;
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Register a custom helper.
|
|
612
|
+
*
|
|
613
|
+
* @param name - Helper name
|
|
614
|
+
* @param fn - Helper function
|
|
615
|
+
*/
|
|
616
|
+
registerHelper(name, fn) {
|
|
617
|
+
if (this.hbs) {
|
|
618
|
+
this.hbs.registerHelper(name, fn);
|
|
619
|
+
}
|
|
620
|
+
if (!this.options.helpers) {
|
|
621
|
+
this.options.helpers = {};
|
|
622
|
+
}
|
|
623
|
+
this.options.helpers[name] = fn;
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Register a partial template.
|
|
627
|
+
*
|
|
628
|
+
* @param name - Partial name
|
|
629
|
+
* @param template - Partial template string
|
|
630
|
+
*/
|
|
631
|
+
registerPartial(name, template) {
|
|
632
|
+
if (this.hbs) {
|
|
633
|
+
this.hbs.registerPartial(name, template);
|
|
634
|
+
}
|
|
635
|
+
if (!this.options.partials) {
|
|
636
|
+
this.options.partials = {};
|
|
637
|
+
}
|
|
638
|
+
this.options.partials[name] = template;
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Clear compiled template cache.
|
|
642
|
+
*/
|
|
643
|
+
clearCache() {
|
|
644
|
+
this.compiledTemplates.clear();
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Check if a template string contains Handlebars syntax.
|
|
648
|
+
*
|
|
649
|
+
* @param template - Template string to check
|
|
650
|
+
* @returns true if contains {{...}} syntax
|
|
651
|
+
*/
|
|
652
|
+
static containsHandlebars(template) {
|
|
653
|
+
return /\{\{(?!!)[\s\S]*?\}\}/.test(template);
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Check if the renderer is initialized.
|
|
657
|
+
*/
|
|
658
|
+
get isInitialized() {
|
|
659
|
+
return this.initialized;
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
// libs/uipack/src/tool-template/index.ts
|
|
666
|
+
var tool_template_exports = {};
|
|
667
|
+
__export(tool_template_exports, {
|
|
668
|
+
buildTemplateContext: () => buildTemplateContext,
|
|
669
|
+
container: () => container,
|
|
670
|
+
createTemplate: () => createTemplate,
|
|
671
|
+
createToolUI: () => createToolUI,
|
|
672
|
+
dataList: () => dataList,
|
|
673
|
+
errorDisplay: () => errorDisplay,
|
|
674
|
+
executeTemplate: () => executeTemplate,
|
|
675
|
+
heading: () => heading,
|
|
676
|
+
keyValue: () => keyValue,
|
|
677
|
+
loadingDisplay: () => loadingDisplay,
|
|
678
|
+
paragraph: () => paragraph,
|
|
679
|
+
renderTemplate: () => renderTemplate2,
|
|
680
|
+
renderToolTemplate: () => renderToolTemplate,
|
|
681
|
+
renderToolTemplateAsync: () => renderToolTemplateAsync,
|
|
682
|
+
successDisplay: () => successDisplay
|
|
683
|
+
});
|
|
684
|
+
module.exports = __toCommonJS(tool_template_exports);
|
|
685
|
+
|
|
686
|
+
// libs/uipack/src/bridge-runtime/iife-generator.ts
|
|
687
|
+
function generateBridgeIIFE(options = {}) {
|
|
688
|
+
const { debug = false, trustedOrigins = [], minify = false } = options;
|
|
689
|
+
const adapters = options.adapters || ["openai", "ext-apps", "claude", "gemini", "generic"];
|
|
690
|
+
const parts = [];
|
|
691
|
+
parts.push("(function() {");
|
|
692
|
+
parts.push('"use strict";');
|
|
693
|
+
parts.push("");
|
|
694
|
+
if (debug) {
|
|
695
|
+
parts.push('function log(msg) { console.log("[FrontMcpBridge] " + msg); }');
|
|
696
|
+
} else {
|
|
697
|
+
parts.push("function log() {}");
|
|
698
|
+
}
|
|
699
|
+
parts.push("");
|
|
700
|
+
parts.push("var DEFAULT_SAFE_AREA = { top: 0, bottom: 0, left: 0, right: 0 };");
|
|
701
|
+
parts.push("");
|
|
702
|
+
parts.push(generateContextDetection());
|
|
703
|
+
parts.push("");
|
|
704
|
+
parts.push(generateBaseCapabilities());
|
|
705
|
+
parts.push("");
|
|
706
|
+
if (adapters.includes("openai")) {
|
|
707
|
+
parts.push(generateOpenAIAdapter());
|
|
708
|
+
parts.push("");
|
|
709
|
+
}
|
|
710
|
+
if (adapters.includes("ext-apps")) {
|
|
711
|
+
parts.push(generateExtAppsAdapter(trustedOrigins));
|
|
712
|
+
parts.push("");
|
|
713
|
+
}
|
|
714
|
+
if (adapters.includes("claude")) {
|
|
715
|
+
parts.push(generateClaudeAdapter());
|
|
716
|
+
parts.push("");
|
|
717
|
+
}
|
|
718
|
+
if (adapters.includes("gemini")) {
|
|
719
|
+
parts.push(generateGeminiAdapter());
|
|
720
|
+
parts.push("");
|
|
721
|
+
}
|
|
722
|
+
if (adapters.includes("generic")) {
|
|
723
|
+
parts.push(generateGenericAdapter());
|
|
724
|
+
parts.push("");
|
|
725
|
+
}
|
|
726
|
+
parts.push(generatePlatformDetection(adapters));
|
|
727
|
+
parts.push("");
|
|
728
|
+
parts.push(generateBridgeClass());
|
|
729
|
+
parts.push("");
|
|
730
|
+
parts.push("var bridge = new FrontMcpBridge();");
|
|
731
|
+
parts.push("bridge.initialize().then(function() {");
|
|
732
|
+
parts.push(' log("Bridge initialized with adapter: " + bridge.adapterId);');
|
|
733
|
+
parts.push(' window.dispatchEvent(new CustomEvent("bridge:ready", { detail: { adapter: bridge.adapterId } }));');
|
|
734
|
+
parts.push("}).catch(function(err) {");
|
|
735
|
+
parts.push(' console.error("[FrontMcpBridge] Init failed:", err);');
|
|
736
|
+
parts.push(' window.dispatchEvent(new CustomEvent("bridge:error", { detail: { error: err } }));');
|
|
737
|
+
parts.push("});");
|
|
738
|
+
parts.push("");
|
|
739
|
+
parts.push("window.FrontMcpBridge = bridge;");
|
|
740
|
+
parts.push("})();");
|
|
741
|
+
const code = parts.join("\n");
|
|
742
|
+
if (minify) {
|
|
743
|
+
return minifyJS(code);
|
|
744
|
+
}
|
|
745
|
+
return code;
|
|
746
|
+
}
|
|
747
|
+
function generateContextDetection() {
|
|
748
|
+
return `
|
|
749
|
+
function detectTheme() {
|
|
750
|
+
if (typeof window !== 'undefined' && window.matchMedia) {
|
|
751
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
752
|
+
}
|
|
753
|
+
return 'light';
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
function detectLocale() {
|
|
757
|
+
if (typeof navigator !== 'undefined') {
|
|
758
|
+
return navigator.language || 'en-US';
|
|
759
|
+
}
|
|
760
|
+
return 'en-US';
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
function detectUserAgent() {
|
|
764
|
+
if (typeof navigator === 'undefined') {
|
|
765
|
+
return { type: 'web', hover: true, touch: false };
|
|
766
|
+
}
|
|
767
|
+
var ua = navigator.userAgent || '';
|
|
768
|
+
var isMobile = /iPhone|iPad|iPod|Android/i.test(ua);
|
|
769
|
+
var hasTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
|
770
|
+
var hasHover = window.matchMedia && window.matchMedia('(hover: hover)').matches;
|
|
771
|
+
return { type: isMobile ? 'mobile' : 'web', hover: hasHover !== false, touch: hasTouch };
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
function detectViewport() {
|
|
775
|
+
if (typeof window !== 'undefined') {
|
|
776
|
+
return { width: window.innerWidth, height: window.innerHeight };
|
|
777
|
+
}
|
|
778
|
+
return undefined;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
function readInjectedData() {
|
|
782
|
+
var data = { toolInput: {}, toolOutput: undefined, structuredContent: undefined };
|
|
783
|
+
if (typeof window !== 'undefined') {
|
|
784
|
+
if (window.__mcpToolInput) data.toolInput = window.__mcpToolInput;
|
|
785
|
+
if (window.__mcpToolOutput) data.toolOutput = window.__mcpToolOutput;
|
|
786
|
+
if (window.__mcpStructuredContent) data.structuredContent = window.__mcpStructuredContent;
|
|
787
|
+
}
|
|
788
|
+
return data;
|
|
789
|
+
}
|
|
790
|
+
`.trim();
|
|
791
|
+
}
|
|
792
|
+
function generateBaseCapabilities() {
|
|
793
|
+
return `
|
|
794
|
+
var DEFAULT_CAPABILITIES = {
|
|
795
|
+
canCallTools: false,
|
|
796
|
+
canSendMessages: false,
|
|
797
|
+
canOpenLinks: false,
|
|
798
|
+
canPersistState: true,
|
|
799
|
+
hasNetworkAccess: true,
|
|
800
|
+
supportsDisplayModes: false,
|
|
801
|
+
supportsTheme: true
|
|
802
|
+
};
|
|
803
|
+
`.trim();
|
|
804
|
+
}
|
|
805
|
+
function generateOpenAIAdapter() {
|
|
806
|
+
return `
|
|
807
|
+
var OpenAIAdapter = {
|
|
808
|
+
id: 'openai',
|
|
809
|
+
name: 'OpenAI ChatGPT',
|
|
810
|
+
priority: 100,
|
|
811
|
+
capabilities: Object.assign({}, DEFAULT_CAPABILITIES, {
|
|
812
|
+
canCallTools: true,
|
|
813
|
+
canSendMessages: true,
|
|
814
|
+
canOpenLinks: true,
|
|
815
|
+
supportsDisplayModes: true
|
|
816
|
+
}),
|
|
817
|
+
canHandle: function() {
|
|
818
|
+
if (typeof window === 'undefined') return false;
|
|
819
|
+
// Check for window.openai.callTool (the actual OpenAI SDK API)
|
|
820
|
+
if (window.openai && typeof window.openai.callTool === 'function') return true;
|
|
821
|
+
// Also check if we're being injected with tool metadata (OpenAI injects toolOutput)
|
|
822
|
+
if (window.openai && (window.openai.toolOutput !== undefined || window.openai.toolInput !== undefined)) return true;
|
|
823
|
+
return false;
|
|
824
|
+
},
|
|
825
|
+
initialize: function(context) {
|
|
826
|
+
var sdk = window.openai;
|
|
827
|
+
context.sdk = sdk;
|
|
828
|
+
// OpenAI SDK exposes theme and displayMode directly as properties
|
|
829
|
+
if (sdk.theme) {
|
|
830
|
+
context.hostContext.theme = sdk.theme;
|
|
831
|
+
}
|
|
832
|
+
if (sdk.displayMode) {
|
|
833
|
+
context.hostContext.displayMode = sdk.displayMode;
|
|
834
|
+
}
|
|
835
|
+
// Note: OpenAI SDK does not have an onContextChange equivalent
|
|
836
|
+
return Promise.resolve();
|
|
837
|
+
},
|
|
838
|
+
callTool: function(context, name, args) {
|
|
839
|
+
return context.sdk.callTool(name, args);
|
|
840
|
+
},
|
|
841
|
+
sendMessage: function(context, content) {
|
|
842
|
+
if (typeof context.sdk.sendFollowUpMessage === 'function') {
|
|
843
|
+
return context.sdk.sendFollowUpMessage(content);
|
|
844
|
+
}
|
|
845
|
+
return Promise.reject(new Error('Messages not supported'));
|
|
846
|
+
},
|
|
847
|
+
openLink: function(context, url) {
|
|
848
|
+
window.open(url, '_blank', 'noopener,noreferrer');
|
|
849
|
+
return Promise.resolve();
|
|
850
|
+
},
|
|
851
|
+
requestDisplayMode: function(context, mode) {
|
|
852
|
+
return Promise.resolve();
|
|
853
|
+
},
|
|
854
|
+
requestClose: function(context) {
|
|
855
|
+
return Promise.resolve();
|
|
856
|
+
}
|
|
857
|
+
};
|
|
858
|
+
`.trim();
|
|
859
|
+
}
|
|
860
|
+
function generateExtAppsAdapter(trustedOrigins) {
|
|
861
|
+
const originsArray = trustedOrigins.length > 0 ? JSON.stringify(trustedOrigins) : "[]";
|
|
862
|
+
return `
|
|
863
|
+
var ExtAppsAdapter = {
|
|
864
|
+
id: 'ext-apps',
|
|
865
|
+
name: 'ext-apps (SEP-1865)',
|
|
866
|
+
priority: 80,
|
|
867
|
+
capabilities: Object.assign({}, DEFAULT_CAPABILITIES, { canPersistState: true, hasNetworkAccess: true }),
|
|
868
|
+
trustedOrigins: ${originsArray},
|
|
869
|
+
trustedOrigin: null,
|
|
870
|
+
pendingRequests: {},
|
|
871
|
+
requestId: 0,
|
|
872
|
+
hostCapabilities: {},
|
|
873
|
+
canHandle: function() {
|
|
874
|
+
if (typeof window === 'undefined') return false;
|
|
875
|
+
if (window.parent === window) return false;
|
|
876
|
+
// Check for OpenAI SDK (window.openai.callTool) - defer to OpenAIAdapter
|
|
877
|
+
if (window.openai && typeof window.openai.callTool === 'function') return false;
|
|
878
|
+
if (window.__mcpPlatform === 'ext-apps') return true;
|
|
879
|
+
return true;
|
|
880
|
+
},
|
|
881
|
+
initialize: function(context) {
|
|
882
|
+
var self = this;
|
|
883
|
+
context.extApps = this;
|
|
884
|
+
|
|
885
|
+
window.addEventListener('message', function(event) {
|
|
886
|
+
self.handleMessage(context, event);
|
|
887
|
+
});
|
|
888
|
+
|
|
889
|
+
return self.performHandshake(context);
|
|
890
|
+
},
|
|
891
|
+
handleMessage: function(context, event) {
|
|
892
|
+
if (!this.isOriginTrusted(event.origin)) return;
|
|
893
|
+
var data = event.data;
|
|
894
|
+
if (!data || typeof data !== 'object' || data.jsonrpc !== '2.0') return;
|
|
895
|
+
|
|
896
|
+
if ('id' in data && (data.result !== undefined || data.error !== undefined)) {
|
|
897
|
+
var pending = this.pendingRequests[data.id];
|
|
898
|
+
if (pending) {
|
|
899
|
+
clearTimeout(pending.timeout);
|
|
900
|
+
delete this.pendingRequests[data.id];
|
|
901
|
+
if (data.error) {
|
|
902
|
+
pending.reject(new Error(data.error.message + ' (code: ' + data.error.code + ')'));
|
|
903
|
+
} else {
|
|
904
|
+
pending.resolve(data.result);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
if ('method' in data && !('id' in data)) {
|
|
911
|
+
this.handleNotification(context, data);
|
|
912
|
+
}
|
|
913
|
+
},
|
|
914
|
+
handleNotification: function(context, notification) {
|
|
915
|
+
var params = notification.params || {};
|
|
916
|
+
switch (notification.method) {
|
|
917
|
+
case 'ui/notifications/tool-input':
|
|
918
|
+
context.toolInput = params.arguments || {};
|
|
919
|
+
window.dispatchEvent(new CustomEvent('tool:input', { detail: { arguments: context.toolInput } }));
|
|
920
|
+
break;
|
|
921
|
+
case 'ui/notifications/tool-result':
|
|
922
|
+
context.toolOutput = params.content;
|
|
923
|
+
context.structuredContent = params.structuredContent;
|
|
924
|
+
context.notifyToolResult(params.content);
|
|
925
|
+
window.dispatchEvent(new CustomEvent('tool:result', { detail: params }));
|
|
926
|
+
break;
|
|
927
|
+
case 'ui/notifications/host-context-changed':
|
|
928
|
+
Object.assign(context.hostContext, params);
|
|
929
|
+
context.notifyContextChange(params);
|
|
930
|
+
break;
|
|
931
|
+
}
|
|
932
|
+
},
|
|
933
|
+
isOriginTrusted: function(origin) {
|
|
934
|
+
if (this.trustedOrigins.length > 0) {
|
|
935
|
+
return this.trustedOrigins.indexOf(origin) !== -1;
|
|
936
|
+
}
|
|
937
|
+
// When no trusted origins configured, trust first message origin (trust-on-first-use).
|
|
938
|
+
// SECURITY WARNING: This creates a race condition - whichever iframe sends the first
|
|
939
|
+
// message establishes permanent trust. For production, always configure trustedOrigins.
|
|
940
|
+
if (!this.trustedOrigin) {
|
|
941
|
+
if (window.parent !== window && origin) {
|
|
942
|
+
this.trustedOrigin = origin;
|
|
943
|
+
return true;
|
|
944
|
+
}
|
|
945
|
+
return false;
|
|
946
|
+
}
|
|
947
|
+
return this.trustedOrigin === origin;
|
|
948
|
+
},
|
|
949
|
+
sendRequest: function(method, params) {
|
|
950
|
+
var self = this;
|
|
951
|
+
return new Promise(function(resolve, reject) {
|
|
952
|
+
// Security: Require trusted origin before sending requests to prevent message leaks
|
|
953
|
+
if (!self.trustedOrigin && self.trustedOrigins.length === 0) {
|
|
954
|
+
reject(new Error('Cannot send request: no trusted origin established'));
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
var id = ++self.requestId;
|
|
959
|
+
var timeout = setTimeout(function() {
|
|
960
|
+
delete self.pendingRequests[id];
|
|
961
|
+
reject(new Error('Request ' + method + ' timed out'));
|
|
962
|
+
}, 10000);
|
|
963
|
+
|
|
964
|
+
self.pendingRequests[id] = { resolve: resolve, reject: reject, timeout: timeout };
|
|
965
|
+
|
|
966
|
+
var targetOrigin = self.trustedOrigin || self.trustedOrigins[0];
|
|
967
|
+
window.parent.postMessage({ jsonrpc: '2.0', id: id, method: method, params: params }, targetOrigin);
|
|
968
|
+
});
|
|
969
|
+
},
|
|
970
|
+
performHandshake: function(context) {
|
|
971
|
+
var self = this;
|
|
972
|
+
var params = {
|
|
973
|
+
appInfo: { name: 'FrontMCP Widget', version: '1.0.0' },
|
|
974
|
+
appCapabilities: { tools: { listChanged: false } },
|
|
975
|
+
protocolVersion: '2024-11-05'
|
|
976
|
+
};
|
|
977
|
+
|
|
978
|
+
return this.sendRequest('ui/initialize', params).then(function(result) {
|
|
979
|
+
self.hostCapabilities = result.hostCapabilities || {};
|
|
980
|
+
self.capabilities = Object.assign({}, self.capabilities, {
|
|
981
|
+
canCallTools: Boolean(self.hostCapabilities.serverToolProxy),
|
|
982
|
+
canSendMessages: true,
|
|
983
|
+
canOpenLinks: Boolean(self.hostCapabilities.openLink),
|
|
984
|
+
supportsDisplayModes: true
|
|
985
|
+
});
|
|
986
|
+
if (result.hostContext) {
|
|
987
|
+
Object.assign(context.hostContext, result.hostContext);
|
|
988
|
+
}
|
|
989
|
+
});
|
|
990
|
+
},
|
|
991
|
+
callTool: function(context, name, args) {
|
|
992
|
+
if (!this.hostCapabilities.serverToolProxy) {
|
|
993
|
+
return Promise.reject(new Error('Server tool proxy not supported'));
|
|
994
|
+
}
|
|
995
|
+
return this.sendRequest('ui/callServerTool', { name: name, arguments: args });
|
|
996
|
+
},
|
|
997
|
+
sendMessage: function(context, content) {
|
|
998
|
+
return this.sendRequest('ui/message', { content: content });
|
|
999
|
+
},
|
|
1000
|
+
openLink: function(context, url) {
|
|
1001
|
+
if (!this.hostCapabilities.openLink) {
|
|
1002
|
+
window.open(url, '_blank', 'noopener,noreferrer');
|
|
1003
|
+
return Promise.resolve();
|
|
1004
|
+
}
|
|
1005
|
+
return this.sendRequest('ui/openLink', { url: url });
|
|
1006
|
+
},
|
|
1007
|
+
requestDisplayMode: function(context, mode) {
|
|
1008
|
+
return this.sendRequest('ui/setDisplayMode', { mode: mode });
|
|
1009
|
+
},
|
|
1010
|
+
requestClose: function(context) {
|
|
1011
|
+
return this.sendRequest('ui/close', {});
|
|
1012
|
+
}
|
|
1013
|
+
};
|
|
1014
|
+
`.trim();
|
|
1015
|
+
}
|
|
1016
|
+
function generateClaudeAdapter() {
|
|
1017
|
+
return `
|
|
1018
|
+
var ClaudeAdapter = {
|
|
1019
|
+
id: 'claude',
|
|
1020
|
+
name: 'Claude (Anthropic)',
|
|
1021
|
+
priority: 60,
|
|
1022
|
+
capabilities: Object.assign({}, DEFAULT_CAPABILITIES, {
|
|
1023
|
+
canCallTools: false,
|
|
1024
|
+
canSendMessages: false,
|
|
1025
|
+
canOpenLinks: true,
|
|
1026
|
+
hasNetworkAccess: false,
|
|
1027
|
+
supportsDisplayModes: false
|
|
1028
|
+
}),
|
|
1029
|
+
canHandle: function() {
|
|
1030
|
+
if (typeof window === 'undefined') return false;
|
|
1031
|
+
if (window.__mcpPlatform === 'claude') return true;
|
|
1032
|
+
if (window.claude) return true;
|
|
1033
|
+
if (window.__claudeArtifact) return true;
|
|
1034
|
+
if (typeof location !== 'undefined') {
|
|
1035
|
+
var href = location.href;
|
|
1036
|
+
if (href.indexOf('claude.ai') !== -1 || href.indexOf('anthropic.com') !== -1) return true;
|
|
1037
|
+
}
|
|
1038
|
+
return false;
|
|
1039
|
+
},
|
|
1040
|
+
initialize: function(context) {
|
|
1041
|
+
return Promise.resolve();
|
|
1042
|
+
},
|
|
1043
|
+
callTool: function() {
|
|
1044
|
+
return Promise.reject(new Error('Tool calls not supported in Claude'));
|
|
1045
|
+
},
|
|
1046
|
+
sendMessage: function() {
|
|
1047
|
+
return Promise.reject(new Error('Messages not supported in Claude'));
|
|
1048
|
+
},
|
|
1049
|
+
openLink: function(context, url) {
|
|
1050
|
+
window.open(url, '_blank', 'noopener,noreferrer');
|
|
1051
|
+
return Promise.resolve();
|
|
1052
|
+
},
|
|
1053
|
+
requestDisplayMode: function() {
|
|
1054
|
+
return Promise.resolve();
|
|
1055
|
+
},
|
|
1056
|
+
requestClose: function() {
|
|
1057
|
+
return Promise.resolve();
|
|
1058
|
+
}
|
|
1059
|
+
};
|
|
1060
|
+
`.trim();
|
|
1061
|
+
}
|
|
1062
|
+
function generateGeminiAdapter() {
|
|
1063
|
+
return `
|
|
1064
|
+
var GeminiAdapter = {
|
|
1065
|
+
id: 'gemini',
|
|
1066
|
+
name: 'Google Gemini',
|
|
1067
|
+
priority: 40,
|
|
1068
|
+
capabilities: Object.assign({}, DEFAULT_CAPABILITIES, {
|
|
1069
|
+
canOpenLinks: true,
|
|
1070
|
+
hasNetworkAccess: true
|
|
1071
|
+
}),
|
|
1072
|
+
canHandle: function() {
|
|
1073
|
+
if (typeof window === 'undefined') return false;
|
|
1074
|
+
if (window.__mcpPlatform === 'gemini') return true;
|
|
1075
|
+
if (window.gemini) return true;
|
|
1076
|
+
if (typeof location !== 'undefined') {
|
|
1077
|
+
var href = location.href;
|
|
1078
|
+
if (href.indexOf('gemini.google.com') !== -1 || href.indexOf('bard.google.com') !== -1) return true;
|
|
1079
|
+
}
|
|
1080
|
+
return false;
|
|
1081
|
+
},
|
|
1082
|
+
initialize: function(context) {
|
|
1083
|
+
if (window.gemini && window.gemini.ui && window.gemini.ui.getTheme) {
|
|
1084
|
+
context.hostContext.theme = window.gemini.ui.getTheme() === 'dark' ? 'dark' : 'light';
|
|
1085
|
+
}
|
|
1086
|
+
return Promise.resolve();
|
|
1087
|
+
},
|
|
1088
|
+
callTool: function() {
|
|
1089
|
+
return Promise.reject(new Error('Tool calls not supported in Gemini'));
|
|
1090
|
+
},
|
|
1091
|
+
sendMessage: function(context, content) {
|
|
1092
|
+
if (window.gemini && window.gemini.ui && window.gemini.ui.sendMessage) {
|
|
1093
|
+
return window.gemini.ui.sendMessage(content);
|
|
1094
|
+
}
|
|
1095
|
+
return Promise.reject(new Error('Messages not supported in Gemini'));
|
|
1096
|
+
},
|
|
1097
|
+
openLink: function(context, url) {
|
|
1098
|
+
if (window.gemini && window.gemini.ui && window.gemini.ui.openLink) {
|
|
1099
|
+
return window.gemini.ui.openLink(url);
|
|
1100
|
+
}
|
|
1101
|
+
window.open(url, '_blank', 'noopener,noreferrer');
|
|
1102
|
+
return Promise.resolve();
|
|
1103
|
+
},
|
|
1104
|
+
requestDisplayMode: function() {
|
|
1105
|
+
return Promise.resolve();
|
|
1106
|
+
},
|
|
1107
|
+
requestClose: function() {
|
|
1108
|
+
return Promise.resolve();
|
|
1109
|
+
}
|
|
1110
|
+
};
|
|
1111
|
+
`.trim();
|
|
1112
|
+
}
|
|
1113
|
+
function generateGenericAdapter() {
|
|
1114
|
+
return `
|
|
1115
|
+
var GenericAdapter = {
|
|
1116
|
+
id: 'generic',
|
|
1117
|
+
name: 'Generic Web',
|
|
1118
|
+
priority: 0,
|
|
1119
|
+
capabilities: Object.assign({}, DEFAULT_CAPABILITIES, {
|
|
1120
|
+
canOpenLinks: true,
|
|
1121
|
+
hasNetworkAccess: true
|
|
1122
|
+
}),
|
|
1123
|
+
canHandle: function() {
|
|
1124
|
+
return typeof window !== 'undefined';
|
|
1125
|
+
},
|
|
1126
|
+
initialize: function(context) {
|
|
1127
|
+
return Promise.resolve();
|
|
1128
|
+
},
|
|
1129
|
+
callTool: function() {
|
|
1130
|
+
return Promise.reject(new Error('Tool calls not supported'));
|
|
1131
|
+
},
|
|
1132
|
+
sendMessage: function() {
|
|
1133
|
+
return Promise.reject(new Error('Messages not supported'));
|
|
1134
|
+
},
|
|
1135
|
+
openLink: function(context, url) {
|
|
1136
|
+
window.open(url, '_blank', 'noopener,noreferrer');
|
|
1137
|
+
return Promise.resolve();
|
|
1138
|
+
},
|
|
1139
|
+
requestDisplayMode: function() {
|
|
1140
|
+
return Promise.resolve();
|
|
1141
|
+
},
|
|
1142
|
+
requestClose: function() {
|
|
1143
|
+
return Promise.resolve();
|
|
1144
|
+
}
|
|
1145
|
+
};
|
|
1146
|
+
`.trim();
|
|
1147
|
+
}
|
|
1148
|
+
function generatePlatformDetection(adapters) {
|
|
1149
|
+
const adapterVars = adapters.map((a) => {
|
|
1150
|
+
switch (a) {
|
|
1151
|
+
case "openai":
|
|
1152
|
+
return "OpenAIAdapter";
|
|
1153
|
+
case "ext-apps":
|
|
1154
|
+
return "ExtAppsAdapter";
|
|
1155
|
+
case "claude":
|
|
1156
|
+
return "ClaudeAdapter";
|
|
1157
|
+
case "gemini":
|
|
1158
|
+
return "GeminiAdapter";
|
|
1159
|
+
case "generic":
|
|
1160
|
+
return "GenericAdapter";
|
|
1161
|
+
default:
|
|
1162
|
+
return "";
|
|
1163
|
+
}
|
|
1164
|
+
}).filter(Boolean);
|
|
1165
|
+
return `
|
|
1166
|
+
var ADAPTERS = [${adapterVars.join(", ")}].sort(function(a, b) { return b.priority - a.priority; });
|
|
1167
|
+
|
|
1168
|
+
function detectPlatform() {
|
|
1169
|
+
for (var i = 0; i < ADAPTERS.length; i++) {
|
|
1170
|
+
if (ADAPTERS[i].canHandle()) {
|
|
1171
|
+
log('Detected platform: ' + ADAPTERS[i].id);
|
|
1172
|
+
return ADAPTERS[i];
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
log('No platform detected, using generic');
|
|
1176
|
+
return GenericAdapter;
|
|
1177
|
+
}
|
|
1178
|
+
`.trim();
|
|
1179
|
+
}
|
|
1180
|
+
function generateBridgeClass() {
|
|
1181
|
+
return `
|
|
1182
|
+
function FrontMcpBridge() {
|
|
1183
|
+
this._adapter = null;
|
|
1184
|
+
this._initialized = false;
|
|
1185
|
+
this._context = {
|
|
1186
|
+
hostContext: {
|
|
1187
|
+
theme: detectTheme(),
|
|
1188
|
+
displayMode: 'inline',
|
|
1189
|
+
locale: detectLocale(),
|
|
1190
|
+
userAgent: detectUserAgent(),
|
|
1191
|
+
safeArea: DEFAULT_SAFE_AREA,
|
|
1192
|
+
viewport: detectViewport()
|
|
1193
|
+
},
|
|
1194
|
+
toolInput: {},
|
|
1195
|
+
toolOutput: undefined,
|
|
1196
|
+
structuredContent: undefined,
|
|
1197
|
+
widgetState: {},
|
|
1198
|
+
contextListeners: [],
|
|
1199
|
+
toolResultListeners: [],
|
|
1200
|
+
notifyContextChange: function(changes) {
|
|
1201
|
+
Object.assign(this.hostContext, changes);
|
|
1202
|
+
for (var i = 0; i < this.contextListeners.length; i++) {
|
|
1203
|
+
try { this.contextListeners[i](changes); } catch(e) {}
|
|
1204
|
+
}
|
|
1205
|
+
},
|
|
1206
|
+
notifyToolResult: function(result) {
|
|
1207
|
+
this.toolOutput = result;
|
|
1208
|
+
for (var i = 0; i < this.toolResultListeners.length; i++) {
|
|
1209
|
+
try { this.toolResultListeners[i](result); } catch(e) {}
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
};
|
|
1213
|
+
|
|
1214
|
+
var injected = readInjectedData();
|
|
1215
|
+
this._context.toolInput = injected.toolInput;
|
|
1216
|
+
this._context.toolOutput = injected.toolOutput;
|
|
1217
|
+
this._context.structuredContent = injected.structuredContent;
|
|
1218
|
+
|
|
1219
|
+
this._loadWidgetState();
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
FrontMcpBridge.prototype._loadWidgetState = function() {
|
|
1223
|
+
try {
|
|
1224
|
+
var key = 'frontmcp:widget:' + (window.__mcpToolName || 'unknown');
|
|
1225
|
+
var stored = localStorage.getItem(key);
|
|
1226
|
+
if (stored) this._context.widgetState = JSON.parse(stored);
|
|
1227
|
+
} catch(e) {}
|
|
1228
|
+
};
|
|
1229
|
+
|
|
1230
|
+
FrontMcpBridge.prototype._saveWidgetState = function() {
|
|
1231
|
+
try {
|
|
1232
|
+
var key = 'frontmcp:widget:' + (window.__mcpToolName || 'unknown');
|
|
1233
|
+
localStorage.setItem(key, JSON.stringify(this._context.widgetState));
|
|
1234
|
+
} catch(e) {}
|
|
1235
|
+
};
|
|
1236
|
+
|
|
1237
|
+
FrontMcpBridge.prototype.initialize = function() {
|
|
1238
|
+
if (this._initialized) return Promise.resolve();
|
|
1239
|
+
var self = this;
|
|
1240
|
+
this._adapter = detectPlatform();
|
|
1241
|
+
return this._adapter.initialize(this._context).then(function() {
|
|
1242
|
+
self._initialized = true;
|
|
1243
|
+
// Set up the data-tool-call click handler after initialization
|
|
1244
|
+
self._setupDataToolCallHandler();
|
|
1245
|
+
});
|
|
1246
|
+
};
|
|
1247
|
+
|
|
1248
|
+
Object.defineProperty(FrontMcpBridge.prototype, 'initialized', { get: function() { return this._initialized; } });
|
|
1249
|
+
Object.defineProperty(FrontMcpBridge.prototype, 'adapterId', { get: function() { return this._adapter ? this._adapter.id : undefined; } });
|
|
1250
|
+
Object.defineProperty(FrontMcpBridge.prototype, 'capabilities', { get: function() { return this._adapter ? this._adapter.capabilities : DEFAULT_CAPABILITIES; } });
|
|
1251
|
+
|
|
1252
|
+
FrontMcpBridge.prototype.getTheme = function() { return this._context.hostContext.theme; };
|
|
1253
|
+
FrontMcpBridge.prototype.getDisplayMode = function() { return this._context.hostContext.displayMode; };
|
|
1254
|
+
FrontMcpBridge.prototype.getToolInput = function() { return this._context.toolInput; };
|
|
1255
|
+
FrontMcpBridge.prototype.getToolOutput = function() { return this._context.toolOutput; };
|
|
1256
|
+
FrontMcpBridge.prototype.getStructuredContent = function() { return this._context.structuredContent; };
|
|
1257
|
+
FrontMcpBridge.prototype.getWidgetState = function() { return this._context.widgetState; };
|
|
1258
|
+
FrontMcpBridge.prototype.getHostContext = function() { return Object.assign({}, this._context.hostContext); };
|
|
1259
|
+
FrontMcpBridge.prototype.hasCapability = function(cap) { return this._adapter && this._adapter.capabilities[cap] === true; };
|
|
1260
|
+
|
|
1261
|
+
// Get tool response metadata (platform-agnostic)
|
|
1262
|
+
// Used by inline mode widgets to detect when ui/html arrives
|
|
1263
|
+
FrontMcpBridge.prototype.getToolResponseMetadata = function() {
|
|
1264
|
+
// OpenAI injects toolResponseMetadata for widget-producing tools
|
|
1265
|
+
if (typeof window !== 'undefined' && window.openai && window.openai.toolResponseMetadata) {
|
|
1266
|
+
return window.openai.toolResponseMetadata;
|
|
1267
|
+
}
|
|
1268
|
+
// Claude (future support)
|
|
1269
|
+
if (typeof window !== 'undefined' && window.claude && window.claude.toolResponseMetadata) {
|
|
1270
|
+
return window.claude.toolResponseMetadata;
|
|
1271
|
+
}
|
|
1272
|
+
// FrontMCP direct injection (for testing/ext-apps)
|
|
1273
|
+
if (typeof window !== 'undefined' && window.__mcpToolResponseMetadata) {
|
|
1274
|
+
return window.__mcpToolResponseMetadata;
|
|
1275
|
+
}
|
|
1276
|
+
return null;
|
|
1277
|
+
};
|
|
1278
|
+
|
|
1279
|
+
// Subscribe to tool response metadata changes (for inline mode injection)
|
|
1280
|
+
FrontMcpBridge.prototype.onToolResponseMetadata = function(callback) {
|
|
1281
|
+
var self = this;
|
|
1282
|
+
var called = false;
|
|
1283
|
+
|
|
1284
|
+
// Check if already available
|
|
1285
|
+
var existing = self.getToolResponseMetadata();
|
|
1286
|
+
if (existing) {
|
|
1287
|
+
called = true;
|
|
1288
|
+
callback(existing);
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
// Set up property interceptors for OpenAI
|
|
1292
|
+
if (typeof window !== 'undefined') {
|
|
1293
|
+
// OpenAI: Intercept toolResponseMetadata assignment
|
|
1294
|
+
if (!window.__frontmcpMetadataIntercepted) {
|
|
1295
|
+
window.__frontmcpMetadataIntercepted = true;
|
|
1296
|
+
window.__frontmcpMetadataCallbacks = [];
|
|
1297
|
+
|
|
1298
|
+
// Create openai object if it doesn't exist
|
|
1299
|
+
if (!window.openai) window.openai = {};
|
|
1300
|
+
|
|
1301
|
+
var originalMetadata = window.openai.toolResponseMetadata;
|
|
1302
|
+
Object.defineProperty(window.openai, 'toolResponseMetadata', {
|
|
1303
|
+
get: function() { return originalMetadata; },
|
|
1304
|
+
set: function(val) {
|
|
1305
|
+
originalMetadata = val;
|
|
1306
|
+
log('toolResponseMetadata set, notifying ' + window.__frontmcpMetadataCallbacks.length + ' listeners');
|
|
1307
|
+
for (var i = 0; i < window.__frontmcpMetadataCallbacks.length; i++) {
|
|
1308
|
+
try { window.__frontmcpMetadataCallbacks[i](val); } catch(e) {}
|
|
1309
|
+
}
|
|
1310
|
+
},
|
|
1311
|
+
configurable: true
|
|
1312
|
+
});
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
// Register callback wrapper (store reference for unsubscribe)
|
|
1316
|
+
var wrapper = function(metadata) {
|
|
1317
|
+
if (!called) {
|
|
1318
|
+
called = true;
|
|
1319
|
+
callback(metadata);
|
|
1320
|
+
}
|
|
1321
|
+
};
|
|
1322
|
+
window.__frontmcpMetadataCallbacks.push(wrapper);
|
|
1323
|
+
|
|
1324
|
+
// Return unsubscribe function that removes the wrapper (not the original callback)
|
|
1325
|
+
return function() {
|
|
1326
|
+
if (window.__frontmcpMetadataCallbacks) {
|
|
1327
|
+
var idx = window.__frontmcpMetadataCallbacks.indexOf(wrapper);
|
|
1328
|
+
if (idx !== -1) window.__frontmcpMetadataCallbacks.splice(idx, 1);
|
|
1329
|
+
}
|
|
1330
|
+
};
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
// Return no-op unsubscribe for non-window environments
|
|
1334
|
+
return function() {};
|
|
1335
|
+
};
|
|
1336
|
+
|
|
1337
|
+
FrontMcpBridge.prototype.callTool = function(name, args) {
|
|
1338
|
+
// Priority 1: Direct OpenAI SDK call (most reliable in OpenAI iframe)
|
|
1339
|
+
// This bypasses adapter abstraction for maximum compatibility
|
|
1340
|
+
if (typeof window !== 'undefined' && window.openai && typeof window.openai.callTool === 'function') {
|
|
1341
|
+
log('callTool: Using OpenAI SDK directly');
|
|
1342
|
+
return window.openai.callTool(name, args);
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
// Priority 2: Use adapter (if initialized and supports tool calls)
|
|
1346
|
+
if (this._adapter && this._adapter.capabilities && this._adapter.capabilities.canCallTools) {
|
|
1347
|
+
log('callTool: Using adapter ' + this._adapter.id);
|
|
1348
|
+
return this._adapter.callTool(this._context, name, args);
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
// Not initialized or no tool support
|
|
1352
|
+
if (!this._adapter) {
|
|
1353
|
+
return Promise.reject(new Error('Bridge not initialized. Wait for bridge:ready event.'));
|
|
1354
|
+
}
|
|
1355
|
+
return Promise.reject(new Error('Tool calls not supported on this platform (' + this._adapter.id + ')'));
|
|
1356
|
+
};
|
|
1357
|
+
|
|
1358
|
+
FrontMcpBridge.prototype.sendMessage = function(content) {
|
|
1359
|
+
if (!this._adapter) return Promise.reject(new Error('Not initialized'));
|
|
1360
|
+
return this._adapter.sendMessage(this._context, content);
|
|
1361
|
+
};
|
|
1362
|
+
|
|
1363
|
+
FrontMcpBridge.prototype.openLink = function(url) {
|
|
1364
|
+
if (!this._adapter) return Promise.reject(new Error('Not initialized'));
|
|
1365
|
+
return this._adapter.openLink(this._context, url);
|
|
1366
|
+
};
|
|
1367
|
+
|
|
1368
|
+
FrontMcpBridge.prototype.requestDisplayMode = function(mode) {
|
|
1369
|
+
if (!this._adapter) return Promise.reject(new Error('Not initialized'));
|
|
1370
|
+
var self = this;
|
|
1371
|
+
return this._adapter.requestDisplayMode(this._context, mode).then(function() {
|
|
1372
|
+
self._context.hostContext.displayMode = mode;
|
|
1373
|
+
});
|
|
1374
|
+
};
|
|
1375
|
+
|
|
1376
|
+
FrontMcpBridge.prototype.requestClose = function() {
|
|
1377
|
+
if (!this._adapter) return Promise.reject(new Error('Not initialized'));
|
|
1378
|
+
return this._adapter.requestClose(this._context);
|
|
1379
|
+
};
|
|
1380
|
+
|
|
1381
|
+
FrontMcpBridge.prototype.setWidgetState = function(state) {
|
|
1382
|
+
Object.assign(this._context.widgetState, state);
|
|
1383
|
+
this._saveWidgetState();
|
|
1384
|
+
};
|
|
1385
|
+
|
|
1386
|
+
FrontMcpBridge.prototype.onContextChange = function(callback) {
|
|
1387
|
+
var listeners = this._context.contextListeners;
|
|
1388
|
+
listeners.push(callback);
|
|
1389
|
+
return function() {
|
|
1390
|
+
var idx = listeners.indexOf(callback);
|
|
1391
|
+
if (idx !== -1) listeners.splice(idx, 1);
|
|
1392
|
+
};
|
|
1393
|
+
};
|
|
1394
|
+
|
|
1395
|
+
FrontMcpBridge.prototype.onToolResult = function(callback) {
|
|
1396
|
+
var listeners = this._context.toolResultListeners;
|
|
1397
|
+
listeners.push(callback);
|
|
1398
|
+
return function() {
|
|
1399
|
+
var idx = listeners.indexOf(callback);
|
|
1400
|
+
if (idx !== -1) listeners.splice(idx, 1);
|
|
1401
|
+
};
|
|
1402
|
+
};
|
|
1403
|
+
|
|
1404
|
+
// ==================== data-tool-call Click Handler ====================
|
|
1405
|
+
|
|
1406
|
+
FrontMcpBridge.prototype._setupDataToolCallHandler = function() {
|
|
1407
|
+
var self = this;
|
|
1408
|
+
|
|
1409
|
+
document.addEventListener('click', function(e) {
|
|
1410
|
+
// Find the closest element with data-tool-call attribute
|
|
1411
|
+
var target = e.target;
|
|
1412
|
+
while (target && target !== document) {
|
|
1413
|
+
if (target.hasAttribute && target.hasAttribute('data-tool-call')) {
|
|
1414
|
+
var toolName = target.getAttribute('data-tool-call');
|
|
1415
|
+
var argsAttr = target.getAttribute('data-tool-args');
|
|
1416
|
+
var args = {};
|
|
1417
|
+
|
|
1418
|
+
try {
|
|
1419
|
+
if (argsAttr) {
|
|
1420
|
+
args = JSON.parse(argsAttr);
|
|
1421
|
+
}
|
|
1422
|
+
} catch (parseErr) {
|
|
1423
|
+
console.error('[frontmcp] Failed to parse data-tool-args:', parseErr);
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
log('data-tool-call clicked: ' + toolName);
|
|
1427
|
+
|
|
1428
|
+
// Show loading state - save original content first
|
|
1429
|
+
var originalContent = target.innerHTML;
|
|
1430
|
+
var originalDisabled = target.disabled;
|
|
1431
|
+
target.disabled = true;
|
|
1432
|
+
target.classList.add('opacity-50', 'cursor-not-allowed');
|
|
1433
|
+
|
|
1434
|
+
// Add spinner for buttons
|
|
1435
|
+
var spinner = '<svg class="animate-spin -ml-1 mr-2 h-4 w-4 inline" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>';
|
|
1436
|
+
if (target.tagName === 'BUTTON') {
|
|
1437
|
+
target.innerHTML = spinner + 'Loading...';
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
// Helper to reset button state
|
|
1441
|
+
function resetButton() {
|
|
1442
|
+
target.innerHTML = originalContent;
|
|
1443
|
+
target.disabled = originalDisabled;
|
|
1444
|
+
target.classList.remove('opacity-50', 'cursor-not-allowed');
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
// Determine how to call the tool
|
|
1448
|
+
var toolCallPromise;
|
|
1449
|
+
|
|
1450
|
+
// Priority 1: Direct OpenAI SDK call (bypasses adapter abstraction)
|
|
1451
|
+
if (typeof window !== 'undefined' && window.openai && typeof window.openai.callTool === 'function') {
|
|
1452
|
+
log('Using OpenAI SDK directly for tool call');
|
|
1453
|
+
toolCallPromise = window.openai.callTool(toolName, args);
|
|
1454
|
+
}
|
|
1455
|
+
// Priority 2: Use adapter (if it supports tool calls)
|
|
1456
|
+
else if (self.hasCapability('canCallTools')) {
|
|
1457
|
+
log('Using adapter for tool call');
|
|
1458
|
+
toolCallPromise = self.callTool(toolName, args);
|
|
1459
|
+
}
|
|
1460
|
+
// No tool call capability
|
|
1461
|
+
else {
|
|
1462
|
+
console.error('[frontmcp] Tool calls not supported on this platform (' + self.adapterId + ')');
|
|
1463
|
+
resetButton();
|
|
1464
|
+
target.dispatchEvent(new CustomEvent('tool:error', {
|
|
1465
|
+
detail: { name: toolName, args: args, error: 'Tool calls not supported on this platform' },
|
|
1466
|
+
bubbles: true
|
|
1467
|
+
}));
|
|
1468
|
+
e.preventDefault();
|
|
1469
|
+
return;
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
// Handle the tool call result
|
|
1473
|
+
toolCallPromise.then(function(result) {
|
|
1474
|
+
log('Tool call succeeded: ' + toolName);
|
|
1475
|
+
resetButton();
|
|
1476
|
+
|
|
1477
|
+
// Update bridge state to trigger widget re-render
|
|
1478
|
+
// React isn't hydrated in OpenAI iframe, so useState doesn't work
|
|
1479
|
+
// Instead, we use the bridge's reactive state system
|
|
1480
|
+
if (result && window.__frontmcp && window.__frontmcp.bridge && typeof window.__frontmcp.bridge.setWidgetState === 'function') {
|
|
1481
|
+
var newData = result.structuredContent || result;
|
|
1482
|
+
log('Updating bridge state with new data');
|
|
1483
|
+
window.__frontmcp.bridge.setWidgetState(newData);
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
// Dispatch success event
|
|
1487
|
+
target.dispatchEvent(new CustomEvent('tool:success', {
|
|
1488
|
+
detail: { name: toolName, args: args, result: result },
|
|
1489
|
+
bubbles: true
|
|
1490
|
+
}));
|
|
1491
|
+
}).catch(function(err) {
|
|
1492
|
+
console.error('[frontmcp] Tool call failed: ' + toolName, err);
|
|
1493
|
+
resetButton();
|
|
1494
|
+
// Dispatch error event
|
|
1495
|
+
target.dispatchEvent(new CustomEvent('tool:error', {
|
|
1496
|
+
detail: { name: toolName, args: args, error: err.message || err },
|
|
1497
|
+
bubbles: true
|
|
1498
|
+
}));
|
|
1499
|
+
});
|
|
1500
|
+
|
|
1501
|
+
// Prevent default behavior (e.g., form submission)
|
|
1502
|
+
e.preventDefault();
|
|
1503
|
+
return;
|
|
1504
|
+
}
|
|
1505
|
+
target = target.parentElement;
|
|
1506
|
+
}
|
|
1507
|
+
}, true); // Use capture phase to handle before React handlers
|
|
1508
|
+
};
|
|
1509
|
+
`.trim();
|
|
1510
|
+
}
|
|
1511
|
+
function minifyJS(code) {
|
|
1512
|
+
return code.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*$/gm, "").replace(/\s+/g, " ").replace(/\s*([{};,:()[\]])\s*/g, "$1").replace(/;\}/g, "}").trim();
|
|
1513
|
+
}
|
|
1514
|
+
function generatePlatformBundle(platform, options = {}) {
|
|
1515
|
+
const platformAdapters = {
|
|
1516
|
+
chatgpt: ["openai", "generic"],
|
|
1517
|
+
claude: ["claude", "generic"],
|
|
1518
|
+
gemini: ["gemini", "generic"],
|
|
1519
|
+
universal: ["openai", "ext-apps", "claude", "gemini", "generic"]
|
|
1520
|
+
};
|
|
1521
|
+
return generateBridgeIIFE({
|
|
1522
|
+
...options,
|
|
1523
|
+
adapters: platformAdapters[platform]
|
|
1524
|
+
});
|
|
1525
|
+
}
|
|
1526
|
+
var UNIVERSAL_BRIDGE_SCRIPT = generateBridgeIIFE();
|
|
1527
|
+
var BRIDGE_SCRIPT_TAGS = {
|
|
1528
|
+
universal: `<script>${UNIVERSAL_BRIDGE_SCRIPT}</script>`,
|
|
1529
|
+
chatgpt: `<script>${generatePlatformBundle("chatgpt")}</script>`,
|
|
1530
|
+
claude: `<script>${generatePlatformBundle("claude")}</script>`,
|
|
1531
|
+
gemini: `<script>${generatePlatformBundle("gemini")}</script>`
|
|
1532
|
+
};
|
|
1533
|
+
|
|
1534
|
+
// libs/uipack/src/runtime/mcp-bridge.ts
|
|
1535
|
+
var MCP_BRIDGE_RUNTIME = `
|
|
1536
|
+
<script>
|
|
1537
|
+
(function() {
|
|
1538
|
+
'use strict';
|
|
1539
|
+
|
|
1540
|
+
// ==================== Environment Detection ====================
|
|
1541
|
+
|
|
1542
|
+
var isOpenAI = typeof window.openai !== 'undefined';
|
|
1543
|
+
var isExtApps = window.parent !== window && !isOpenAI;
|
|
1544
|
+
var isClaude = typeof window.claude !== 'undefined' ||
|
|
1545
|
+
(window.__mcpPlatform === 'claude');
|
|
1546
|
+
var isGemini = window.__mcpPlatform === 'gemini';
|
|
1547
|
+
|
|
1548
|
+
// ==================== Internal State ====================
|
|
1549
|
+
|
|
1550
|
+
var messageId = 0;
|
|
1551
|
+
var pendingRequests = new Map();
|
|
1552
|
+
var contextChangeListeners = [];
|
|
1553
|
+
var toolResultListeners = [];
|
|
1554
|
+
|
|
1555
|
+
// Default values for polyfilled properties
|
|
1556
|
+
var defaultSafeArea = { top: 0, bottom: 0, left: 0, right: 0 };
|
|
1557
|
+
var defaultUserAgent = { type: 'web', hover: true, touch: false };
|
|
1558
|
+
|
|
1559
|
+
// Host context (for ext-apps and polyfill mode)
|
|
1560
|
+
var hostContext = window.__mcpHostContext || {
|
|
1561
|
+
theme: 'light',
|
|
1562
|
+
displayMode: 'inline'
|
|
1563
|
+
};
|
|
1564
|
+
|
|
1565
|
+
// Trusted origin for postMessage validation (set during ext-apps initialization)
|
|
1566
|
+
var trustedOrigin = null;
|
|
1567
|
+
|
|
1568
|
+
// Detect device capabilities
|
|
1569
|
+
var detectUserAgent = function() {
|
|
1570
|
+
var ua = navigator.userAgent || '';
|
|
1571
|
+
var isMobile = /iPhone|iPad|iPod|Android/i.test(ua);
|
|
1572
|
+
var hasTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
|
1573
|
+
var hasHover = window.matchMedia && window.matchMedia('(hover: hover)').matches;
|
|
1574
|
+
return {
|
|
1575
|
+
type: isMobile ? 'mobile' : 'web',
|
|
1576
|
+
hover: hasHover !== false,
|
|
1577
|
+
touch: hasTouch
|
|
1578
|
+
};
|
|
1579
|
+
};
|
|
1580
|
+
|
|
1581
|
+
// Detect theme from system preference
|
|
1582
|
+
var detectTheme = function() {
|
|
1583
|
+
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
|
1584
|
+
return 'dark';
|
|
1585
|
+
}
|
|
1586
|
+
return 'light';
|
|
1587
|
+
};
|
|
1588
|
+
|
|
1589
|
+
// ==================== Bridge Implementation ====================
|
|
1590
|
+
|
|
1591
|
+
/**
|
|
1592
|
+
* MCP Bridge provides full OpenAI API compatibility.
|
|
1593
|
+
* On OpenAI, it proxies directly to window.openai.
|
|
1594
|
+
* On other platforms, it provides polyfills.
|
|
1595
|
+
*/
|
|
1596
|
+
var bridge = {
|
|
1597
|
+
// ==================== Environment Info ====================
|
|
1598
|
+
|
|
1599
|
+
get provider() {
|
|
1600
|
+
if (isOpenAI) return 'openai';
|
|
1601
|
+
if (isClaude) return 'claude';
|
|
1602
|
+
if (isGemini) return 'gemini';
|
|
1603
|
+
if (isExtApps) return 'ext-apps';
|
|
1604
|
+
return 'unknown';
|
|
1605
|
+
},
|
|
1606
|
+
|
|
1607
|
+
// ==================== OpenAI-Compatible Properties ====================
|
|
1608
|
+
|
|
1609
|
+
get theme() {
|
|
1610
|
+
if (isOpenAI && window.openai) {
|
|
1611
|
+
return window.openai.theme || 'light';
|
|
1612
|
+
}
|
|
1613
|
+
return hostContext.theme || detectTheme();
|
|
1614
|
+
},
|
|
1615
|
+
|
|
1616
|
+
get userAgent() {
|
|
1617
|
+
if (isOpenAI && window.openai && window.openai.userAgent) {
|
|
1618
|
+
return window.openai.userAgent;
|
|
1619
|
+
}
|
|
1620
|
+
return hostContext.userAgent || detectUserAgent();
|
|
1621
|
+
},
|
|
1622
|
+
|
|
1623
|
+
get locale() {
|
|
1624
|
+
if (isOpenAI && window.openai && window.openai.locale) {
|
|
1625
|
+
return window.openai.locale;
|
|
1626
|
+
}
|
|
1627
|
+
return navigator.language || 'en-US';
|
|
1628
|
+
},
|
|
1629
|
+
|
|
1630
|
+
get maxHeight() {
|
|
1631
|
+
if (isOpenAI && window.openai) {
|
|
1632
|
+
return window.openai.maxHeight;
|
|
1633
|
+
}
|
|
1634
|
+
return hostContext.viewport ? hostContext.viewport.maxHeight : undefined;
|
|
1635
|
+
},
|
|
1636
|
+
|
|
1637
|
+
get displayMode() {
|
|
1638
|
+
if (isOpenAI && window.openai) {
|
|
1639
|
+
return window.openai.displayMode || 'inline';
|
|
1640
|
+
}
|
|
1641
|
+
return hostContext.displayMode || 'inline';
|
|
1642
|
+
},
|
|
1643
|
+
|
|
1644
|
+
get safeArea() {
|
|
1645
|
+
if (isOpenAI && window.openai && window.openai.safeArea) {
|
|
1646
|
+
return window.openai.safeArea;
|
|
1647
|
+
}
|
|
1648
|
+
return hostContext.safeArea || defaultSafeArea;
|
|
1649
|
+
},
|
|
1650
|
+
|
|
1651
|
+
get toolInput() {
|
|
1652
|
+
if (isOpenAI && window.openai && window.openai.toolInput) {
|
|
1653
|
+
return window.openai.toolInput;
|
|
1654
|
+
}
|
|
1655
|
+
return window.__mcpToolInput || {};
|
|
1656
|
+
},
|
|
1657
|
+
|
|
1658
|
+
get toolOutput() {
|
|
1659
|
+
if (isOpenAI && window.openai) {
|
|
1660
|
+
return window.openai.toolOutput;
|
|
1661
|
+
}
|
|
1662
|
+
return window.__mcpToolOutput;
|
|
1663
|
+
},
|
|
1664
|
+
|
|
1665
|
+
get structuredContent() {
|
|
1666
|
+
// Alias for toolOutput to maintain compatibility
|
|
1667
|
+
if (isOpenAI && window.openai) {
|
|
1668
|
+
return window.openai.toolOutput;
|
|
1669
|
+
}
|
|
1670
|
+
return window.__mcpStructuredContent;
|
|
1671
|
+
},
|
|
1672
|
+
|
|
1673
|
+
get toolResponseMetadata() {
|
|
1674
|
+
if (isOpenAI && window.openai && window.openai.toolResponseMetadata) {
|
|
1675
|
+
return window.openai.toolResponseMetadata;
|
|
1676
|
+
}
|
|
1677
|
+
return window.__mcpToolResponseMetadata || {};
|
|
1678
|
+
},
|
|
1679
|
+
|
|
1680
|
+
get widgetState() {
|
|
1681
|
+
if (isOpenAI && window.openai) {
|
|
1682
|
+
return window.openai.widgetState || {};
|
|
1683
|
+
}
|
|
1684
|
+
// Polyfill: use localStorage
|
|
1685
|
+
try {
|
|
1686
|
+
var stored = localStorage.getItem('__mcpWidgetState');
|
|
1687
|
+
return stored ? JSON.parse(stored) : {};
|
|
1688
|
+
} catch (e) {
|
|
1689
|
+
return {};
|
|
1690
|
+
}
|
|
1691
|
+
},
|
|
1692
|
+
|
|
1693
|
+
// ==================== OpenAI-Compatible Methods ====================
|
|
1694
|
+
|
|
1695
|
+
callTool: function(name, params) {
|
|
1696
|
+
if (isOpenAI && window.openai && window.openai.callTool) {
|
|
1697
|
+
return window.openai.callTool(name, params);
|
|
1698
|
+
}
|
|
1699
|
+
if (isClaude) {
|
|
1700
|
+
return Promise.reject(new Error(
|
|
1701
|
+
'Tool calls are not supported in Claude widgets (network-blocked sandbox)'
|
|
1702
|
+
));
|
|
1703
|
+
}
|
|
1704
|
+
if (isGemini) {
|
|
1705
|
+
return Promise.reject(new Error(
|
|
1706
|
+
'Tool calls are not supported in Gemini widgets'
|
|
1707
|
+
));
|
|
1708
|
+
}
|
|
1709
|
+
if (isExtApps) {
|
|
1710
|
+
return this._sendRequest('tools/call', { name: name, arguments: params });
|
|
1711
|
+
}
|
|
1712
|
+
return Promise.reject(new Error('Tool calls not supported in this environment'));
|
|
1713
|
+
},
|
|
1714
|
+
|
|
1715
|
+
requestDisplayMode: function(options) {
|
|
1716
|
+
if (isOpenAI && window.openai && window.openai.requestDisplayMode) {
|
|
1717
|
+
return window.openai.requestDisplayMode(options);
|
|
1718
|
+
}
|
|
1719
|
+
if (isExtApps) {
|
|
1720
|
+
return this._sendRequest('ui/request-display-mode', options);
|
|
1721
|
+
}
|
|
1722
|
+
// Polyfill: just update local state
|
|
1723
|
+
if (options && options.mode) {
|
|
1724
|
+
hostContext.displayMode = options.mode;
|
|
1725
|
+
contextChangeListeners.forEach(function(cb) {
|
|
1726
|
+
try { cb({ displayMode: options.mode }); } catch (e) {}
|
|
1727
|
+
});
|
|
1728
|
+
}
|
|
1729
|
+
return Promise.resolve();
|
|
1730
|
+
},
|
|
1731
|
+
|
|
1732
|
+
requestClose: function() {
|
|
1733
|
+
if (isOpenAI && window.openai && window.openai.requestClose) {
|
|
1734
|
+
return window.openai.requestClose();
|
|
1735
|
+
}
|
|
1736
|
+
if (isExtApps) {
|
|
1737
|
+
return this._sendRequest('ui/request-close', {});
|
|
1738
|
+
}
|
|
1739
|
+
// Polyfill: dispatch event for parent to handle
|
|
1740
|
+
window.dispatchEvent(new CustomEvent('mcp:request-close'));
|
|
1741
|
+
return Promise.resolve();
|
|
1742
|
+
},
|
|
1743
|
+
|
|
1744
|
+
openExternal: function(options) {
|
|
1745
|
+
var href = typeof options === 'string' ? options : (options && options.href);
|
|
1746
|
+
if (!href) {
|
|
1747
|
+
return Promise.reject(new Error('URL required'));
|
|
1748
|
+
}
|
|
1749
|
+
if (isOpenAI && window.openai && window.openai.openExternal) {
|
|
1750
|
+
return window.openai.openExternal({ href: href });
|
|
1751
|
+
}
|
|
1752
|
+
if (isExtApps) {
|
|
1753
|
+
return this._sendRequest('ui/open-link', { url: href });
|
|
1754
|
+
}
|
|
1755
|
+
// Fallback: open in new window
|
|
1756
|
+
window.open(href, '_blank', 'noopener,noreferrer');
|
|
1757
|
+
return Promise.resolve();
|
|
1758
|
+
},
|
|
1759
|
+
|
|
1760
|
+
// Alias for openExternal (backwards compatibility)
|
|
1761
|
+
openLink: function(url) {
|
|
1762
|
+
return this.openExternal({ href: url });
|
|
1763
|
+
},
|
|
1764
|
+
|
|
1765
|
+
sendFollowUpMessage: function(options) {
|
|
1766
|
+
var prompt = typeof options === 'string' ? options : (options && options.prompt);
|
|
1767
|
+
if (!prompt) {
|
|
1768
|
+
return Promise.reject(new Error('Prompt required'));
|
|
1769
|
+
}
|
|
1770
|
+
if (isOpenAI && window.openai && window.openai.sendFollowUpMessage) {
|
|
1771
|
+
return window.openai.sendFollowUpMessage({ prompt: prompt });
|
|
1772
|
+
}
|
|
1773
|
+
if (isClaude) {
|
|
1774
|
+
return Promise.reject(new Error(
|
|
1775
|
+
'Follow-up messages are not supported in Claude widgets (network-blocked sandbox)'
|
|
1776
|
+
));
|
|
1777
|
+
}
|
|
1778
|
+
if (isGemini) {
|
|
1779
|
+
return Promise.reject(new Error(
|
|
1780
|
+
'Follow-up messages are not supported in Gemini widgets'
|
|
1781
|
+
));
|
|
1782
|
+
}
|
|
1783
|
+
if (isExtApps) {
|
|
1784
|
+
return this._sendRequest('ui/message', {
|
|
1785
|
+
role: 'user',
|
|
1786
|
+
content: { type: 'text', text: prompt }
|
|
1787
|
+
});
|
|
1788
|
+
}
|
|
1789
|
+
return Promise.reject(new Error('Messages not supported in this environment'));
|
|
1790
|
+
},
|
|
1791
|
+
|
|
1792
|
+
// Alias for sendFollowUpMessage (backwards compatibility)
|
|
1793
|
+
sendMessage: function(content) {
|
|
1794
|
+
return this.sendFollowUpMessage({ prompt: content });
|
|
1795
|
+
},
|
|
1796
|
+
|
|
1797
|
+
setWidgetState: function(state) {
|
|
1798
|
+
if (isOpenAI && window.openai && window.openai.setWidgetState) {
|
|
1799
|
+
window.openai.setWidgetState(state);
|
|
1800
|
+
return;
|
|
1801
|
+
}
|
|
1802
|
+
// Polyfill: persist to localStorage
|
|
1803
|
+
try {
|
|
1804
|
+
localStorage.setItem('__mcpWidgetState', JSON.stringify(state));
|
|
1805
|
+
} catch (e) {
|
|
1806
|
+
console.warn('Failed to persist widget state:', e);
|
|
1807
|
+
}
|
|
1808
|
+
},
|
|
1809
|
+
|
|
1810
|
+
// ==================== Context API (MCP-specific) ====================
|
|
1811
|
+
|
|
1812
|
+
get context() {
|
|
1813
|
+
return {
|
|
1814
|
+
theme: this.theme,
|
|
1815
|
+
displayMode: this.displayMode,
|
|
1816
|
+
viewport: this.maxHeight ? { maxHeight: this.maxHeight } : undefined,
|
|
1817
|
+
userAgent: this.userAgent,
|
|
1818
|
+
locale: this.locale,
|
|
1819
|
+
safeArea: this.safeArea
|
|
1820
|
+
};
|
|
1821
|
+
},
|
|
1822
|
+
|
|
1823
|
+
onContextChange: function(callback) {
|
|
1824
|
+
contextChangeListeners.push(callback);
|
|
1825
|
+
return function() {
|
|
1826
|
+
var index = contextChangeListeners.indexOf(callback);
|
|
1827
|
+
if (index > -1) contextChangeListeners.splice(index, 1);
|
|
1828
|
+
};
|
|
1829
|
+
},
|
|
1830
|
+
|
|
1831
|
+
onToolResult: function(callback) {
|
|
1832
|
+
toolResultListeners.push(callback);
|
|
1833
|
+
return function() {
|
|
1834
|
+
var index = toolResultListeners.indexOf(callback);
|
|
1835
|
+
if (index > -1) toolResultListeners.splice(index, 1);
|
|
1836
|
+
};
|
|
1837
|
+
},
|
|
1838
|
+
|
|
1839
|
+
// ==================== Internal Methods ====================
|
|
1840
|
+
|
|
1841
|
+
_sendRequest: function(method, params) {
|
|
1842
|
+
return new Promise(function(resolve, reject) {
|
|
1843
|
+
var id = ++messageId;
|
|
1844
|
+
pendingRequests.set(id, { resolve: resolve, reject: reject });
|
|
1845
|
+
|
|
1846
|
+
window.parent.postMessage({
|
|
1847
|
+
jsonrpc: '2.0',
|
|
1848
|
+
id: id,
|
|
1849
|
+
method: method,
|
|
1850
|
+
params: params
|
|
1851
|
+
}, '*');
|
|
1852
|
+
|
|
1853
|
+
// Timeout after 30s
|
|
1854
|
+
setTimeout(function() {
|
|
1855
|
+
if (pendingRequests.has(id)) {
|
|
1856
|
+
pendingRequests.delete(id);
|
|
1857
|
+
reject(new Error('Request timeout'));
|
|
1858
|
+
}
|
|
1859
|
+
}, 30000);
|
|
1860
|
+
});
|
|
1861
|
+
},
|
|
1862
|
+
|
|
1863
|
+
_initExtApps: function() {
|
|
1864
|
+
var self = this;
|
|
1865
|
+
return this._sendRequest('ui/initialize', {}).then(function(result) {
|
|
1866
|
+
if (result && result.hostContext) {
|
|
1867
|
+
hostContext = Object.assign(hostContext, result.hostContext);
|
|
1868
|
+
}
|
|
1869
|
+
// Note: trustedOrigin is now set from first message event origin (trust-on-first-use)
|
|
1870
|
+
// Send initialized notification
|
|
1871
|
+
window.parent.postMessage({
|
|
1872
|
+
jsonrpc: '2.0',
|
|
1873
|
+
method: 'ui/notifications/initialized',
|
|
1874
|
+
params: {}
|
|
1875
|
+
}, '*');
|
|
1876
|
+
return result;
|
|
1877
|
+
});
|
|
1878
|
+
}
|
|
1879
|
+
};
|
|
1880
|
+
|
|
1881
|
+
// ==================== Event Handling ====================
|
|
1882
|
+
|
|
1883
|
+
window.addEventListener('message', function(event) {
|
|
1884
|
+
var data = event.data;
|
|
1885
|
+
if (!data || data.jsonrpc !== '2.0') return;
|
|
1886
|
+
|
|
1887
|
+
// Trust-on-first-use: Establish trusted origin from the first valid message
|
|
1888
|
+
if (isExtApps && !trustedOrigin && event.origin) {
|
|
1889
|
+
trustedOrigin = event.origin;
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
// Validate origin for ext-apps environment to prevent spoofed messages
|
|
1893
|
+
if (isExtApps && trustedOrigin && event.origin !== trustedOrigin) {
|
|
1894
|
+
console.warn('MCP Bridge: Ignoring message from untrusted origin:', event.origin);
|
|
1895
|
+
return;
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
// Handle responses
|
|
1899
|
+
if (data.id && pendingRequests.has(data.id)) {
|
|
1900
|
+
var pending = pendingRequests.get(data.id);
|
|
1901
|
+
pendingRequests.delete(data.id);
|
|
1902
|
+
|
|
1903
|
+
if (data.error) {
|
|
1904
|
+
var err = new Error(data.error.message || 'Unknown error');
|
|
1905
|
+
err.code = data.error.code;
|
|
1906
|
+
err.data = data.error.data;
|
|
1907
|
+
pending.reject(err);
|
|
1908
|
+
} else {
|
|
1909
|
+
pending.resolve(data.result);
|
|
1910
|
+
}
|
|
1911
|
+
return;
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1914
|
+
// Handle notifications
|
|
1915
|
+
switch (data.method) {
|
|
1916
|
+
case 'ui/notifications/tool-input':
|
|
1917
|
+
window.__mcpToolInput = data.params && data.params.arguments;
|
|
1918
|
+
window.dispatchEvent(new CustomEvent('mcp:tool-input', { detail: data.params }));
|
|
1919
|
+
break;
|
|
1920
|
+
|
|
1921
|
+
case 'ui/notifications/tool-input-partial':
|
|
1922
|
+
if (data.params && data.params.arguments) {
|
|
1923
|
+
window.__mcpToolInput = Object.assign(window.__mcpToolInput || {}, data.params.arguments);
|
|
1924
|
+
}
|
|
1925
|
+
break;
|
|
1926
|
+
|
|
1927
|
+
case 'ui/notifications/tool-result':
|
|
1928
|
+
window.__mcpToolOutput = data.params && data.params.content;
|
|
1929
|
+
window.__mcpStructuredContent = data.params && data.params.structuredContent;
|
|
1930
|
+
toolResultListeners.forEach(function(cb) {
|
|
1931
|
+
try { cb(data.params); } catch (e) { console.error('Tool result listener error:', e); }
|
|
1932
|
+
});
|
|
1933
|
+
window.dispatchEvent(new CustomEvent('mcp:tool-result', { detail: data.params }));
|
|
1934
|
+
break;
|
|
1935
|
+
|
|
1936
|
+
case 'ui/notifications/tool-cancelled':
|
|
1937
|
+
window.dispatchEvent(new CustomEvent('mcp:tool-cancelled', { detail: data.params }));
|
|
1938
|
+
break;
|
|
1939
|
+
|
|
1940
|
+
case 'ui/host-context-change':
|
|
1941
|
+
hostContext = Object.assign(hostContext, data.params);
|
|
1942
|
+
contextChangeListeners.forEach(function(cb) {
|
|
1943
|
+
try { cb(data.params); } catch (e) { console.error('Context change listener error:', e); }
|
|
1944
|
+
});
|
|
1945
|
+
window.dispatchEvent(new CustomEvent('mcp:context-change', { detail: data.params }));
|
|
1946
|
+
break;
|
|
1947
|
+
|
|
1948
|
+
case 'ui/size-change':
|
|
1949
|
+
if (hostContext.viewport) {
|
|
1950
|
+
hostContext.viewport = Object.assign(hostContext.viewport, data.params);
|
|
1951
|
+
} else {
|
|
1952
|
+
hostContext.viewport = data.params;
|
|
1953
|
+
}
|
|
1954
|
+
break;
|
|
1955
|
+
}
|
|
1956
|
+
});
|
|
1957
|
+
|
|
1958
|
+
// ==================== Initialize ====================
|
|
1959
|
+
|
|
1960
|
+
// Export bridge
|
|
1961
|
+
window.mcpBridge = bridge;
|
|
1962
|
+
|
|
1963
|
+
// Also create window.openai polyfill for non-OpenAI platforms
|
|
1964
|
+
// This allows code written for OpenAI to work on other platforms
|
|
1965
|
+
if (!isOpenAI) {
|
|
1966
|
+
window.openai = {
|
|
1967
|
+
get theme() { return bridge.theme; },
|
|
1968
|
+
get userAgent() { return bridge.userAgent; },
|
|
1969
|
+
get locale() { return bridge.locale; },
|
|
1970
|
+
get maxHeight() { return bridge.maxHeight; },
|
|
1971
|
+
get displayMode() { return bridge.displayMode; },
|
|
1972
|
+
get safeArea() { return bridge.safeArea; },
|
|
1973
|
+
get toolInput() { return bridge.toolInput; },
|
|
1974
|
+
get toolOutput() { return bridge.toolOutput; },
|
|
1975
|
+
get toolResponseMetadata() { return bridge.toolResponseMetadata; },
|
|
1976
|
+
get widgetState() { return bridge.widgetState; },
|
|
1977
|
+
callTool: function(n, a) { return bridge.callTool(n, a); },
|
|
1978
|
+
requestDisplayMode: function(o) { return bridge.requestDisplayMode(o); },
|
|
1979
|
+
requestClose: function() { return bridge.requestClose(); },
|
|
1980
|
+
openExternal: function(o) { return bridge.openExternal(o); },
|
|
1981
|
+
sendFollowUpMessage: function(o) { return bridge.sendFollowUpMessage(o); },
|
|
1982
|
+
setWidgetState: function(s) { return bridge.setWidgetState(s); }
|
|
1983
|
+
};
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
// Auto-initialize for ext-apps
|
|
1987
|
+
if (isExtApps) {
|
|
1988
|
+
bridge._initExtApps().catch(function(err) {
|
|
1989
|
+
console.warn('Failed to initialize MCP bridge:', err);
|
|
1990
|
+
});
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
// Dispatch ready event
|
|
1994
|
+
window.dispatchEvent(new CustomEvent('mcp:bridge-ready'));
|
|
1995
|
+
})();
|
|
1996
|
+
</script>
|
|
1997
|
+
`;
|
|
1998
|
+
var FRONTMCP_BRIDGE_RUNTIME = BRIDGE_SCRIPT_TAGS.universal;
|
|
1999
|
+
|
|
2000
|
+
// libs/uipack/src/runtime/csp.ts
|
|
2001
|
+
var DEFAULT_CDN_DOMAINS = [
|
|
2002
|
+
"https://cdn.jsdelivr.net",
|
|
2003
|
+
// Tailwind, Alpine, React, icons
|
|
2004
|
+
"https://cdnjs.cloudflare.com",
|
|
2005
|
+
// HTMX, other libraries
|
|
2006
|
+
"https://fonts.googleapis.com",
|
|
2007
|
+
// Google Fonts stylesheets
|
|
2008
|
+
"https://fonts.gstatic.com"
|
|
2009
|
+
// Google Fonts files
|
|
2010
|
+
];
|
|
2011
|
+
var DEFAULT_CSP_DIRECTIVES = [
|
|
2012
|
+
"default-src 'none'",
|
|
2013
|
+
`script-src 'self' 'unsafe-inline' ${DEFAULT_CDN_DOMAINS.join(" ")}`,
|
|
2014
|
+
`style-src 'self' 'unsafe-inline' ${DEFAULT_CDN_DOMAINS.join(" ")}`,
|
|
2015
|
+
`img-src 'self' data: ${DEFAULT_CDN_DOMAINS.join(" ")}`,
|
|
2016
|
+
`font-src 'self' data: ${DEFAULT_CDN_DOMAINS.join(" ")}`,
|
|
2017
|
+
"connect-src 'none'"
|
|
2018
|
+
];
|
|
2019
|
+
function buildCSPDirectives(csp) {
|
|
2020
|
+
if (!csp) {
|
|
2021
|
+
return [...DEFAULT_CSP_DIRECTIVES];
|
|
2022
|
+
}
|
|
2023
|
+
const validResourceDomains = sanitizeCSPDomains(csp.resourceDomains);
|
|
2024
|
+
const validConnectDomains = sanitizeCSPDomains(csp.connectDomains);
|
|
2025
|
+
const allResourceDomains = [.../* @__PURE__ */ new Set([...DEFAULT_CDN_DOMAINS, ...validResourceDomains])];
|
|
2026
|
+
const directives = [
|
|
2027
|
+
"default-src 'none'",
|
|
2028
|
+
`script-src 'self' 'unsafe-inline' ${allResourceDomains.join(" ")}`,
|
|
2029
|
+
`style-src 'self' 'unsafe-inline' ${allResourceDomains.join(" ")}`
|
|
2030
|
+
];
|
|
2031
|
+
const imgSources = ["'self'", "data:", ...allResourceDomains];
|
|
2032
|
+
directives.push(`img-src ${imgSources.join(" ")}`);
|
|
2033
|
+
const fontSources = ["'self'", "data:", ...allResourceDomains];
|
|
2034
|
+
directives.push(`font-src ${fontSources.join(" ")}`);
|
|
2035
|
+
if (validConnectDomains.length) {
|
|
2036
|
+
directives.push(`connect-src ${validConnectDomains.join(" ")}`);
|
|
2037
|
+
} else {
|
|
2038
|
+
directives.push("connect-src 'none'");
|
|
2039
|
+
}
|
|
2040
|
+
return directives;
|
|
2041
|
+
}
|
|
2042
|
+
function buildCSPMetaTag(csp) {
|
|
2043
|
+
const directives = buildCSPDirectives(csp);
|
|
2044
|
+
const content = directives.join("; ");
|
|
2045
|
+
return `<meta http-equiv="Content-Security-Policy" content="${escapeAttribute(content)}">`;
|
|
2046
|
+
}
|
|
2047
|
+
function escapeAttribute(str) {
|
|
2048
|
+
return str.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
|
|
2049
|
+
}
|
|
2050
|
+
function validateCSPDomain(domain) {
|
|
2051
|
+
if (domain.startsWith("https://*.")) {
|
|
2052
|
+
const rest = domain.slice(10);
|
|
2053
|
+
return /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?\.[a-zA-Z]{2,}$/.test(rest);
|
|
2054
|
+
}
|
|
2055
|
+
try {
|
|
2056
|
+
const url = new URL(domain);
|
|
2057
|
+
return url.protocol === "https:";
|
|
2058
|
+
} catch {
|
|
2059
|
+
return false;
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
function sanitizeCSPDomains(domains) {
|
|
2063
|
+
if (!domains) return [];
|
|
2064
|
+
const valid = [];
|
|
2065
|
+
for (const domain of domains) {
|
|
2066
|
+
if (validateCSPDomain(domain)) {
|
|
2067
|
+
valid.push(domain);
|
|
2068
|
+
} else {
|
|
2069
|
+
console.warn(`Invalid CSP domain ignored: ${domain}`);
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
return valid;
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2075
|
+
// libs/uipack/src/theme/cdn.ts
|
|
2076
|
+
var CDN = {
|
|
2077
|
+
/**
|
|
2078
|
+
* Tailwind CSS v4 Browser CDN
|
|
2079
|
+
* Generates styles on-the-fly with @theme support
|
|
2080
|
+
* @see https://tailwindcss.com/docs/installation/play-cdn
|
|
2081
|
+
*/
|
|
2082
|
+
tailwind: "https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4",
|
|
2083
|
+
/**
|
|
2084
|
+
* HTMX 2.x - High power tools for HTML
|
|
2085
|
+
* Enables AJAX, WebSockets, Server Sent Events directly in HTML
|
|
2086
|
+
* @see https://htmx.org
|
|
2087
|
+
*/
|
|
2088
|
+
htmx: {
|
|
2089
|
+
url: "https://cdnjs.cloudflare.com/ajax/libs/htmx/2.0.7/htmx.min.js",
|
|
2090
|
+
integrity: "sha512-T6VLg/MJYMbLTmQ8VLvonbWg8VOvmDhXcOvHzCwo6ShdGuUU5SEcp1IAPXL4k9lVoMi8gRXl5K/S/zh43Y9rJA=="
|
|
2091
|
+
},
|
|
2092
|
+
/**
|
|
2093
|
+
* Alpine.js - Lightweight reactive framework
|
|
2094
|
+
* Used for more complex client-side interactions
|
|
2095
|
+
* @see https://alpinejs.dev
|
|
2096
|
+
*/
|
|
2097
|
+
alpine: {
|
|
2098
|
+
url: "https://cdn.jsdelivr.net/npm/alpinejs@3.14.3/dist/cdn.min.js",
|
|
2099
|
+
integrity: "sha384-6zY8MFQJ/EqS1r4RJl+7j8rvZPuBWpT0RzWf+IFcKhxqUzQNmJzA1X1VEVZhYaEz"
|
|
2100
|
+
},
|
|
2101
|
+
/**
|
|
2102
|
+
* Google Fonts - Inter for modern UI typography
|
|
2103
|
+
*/
|
|
2104
|
+
fonts: {
|
|
2105
|
+
preconnect: ["https://fonts.googleapis.com", "https://fonts.gstatic.com"],
|
|
2106
|
+
inter: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap",
|
|
2107
|
+
mono: "https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap"
|
|
2108
|
+
},
|
|
2109
|
+
/**
|
|
2110
|
+
* Lucide Icons - Beautiful & consistent icons
|
|
2111
|
+
* @see https://lucide.dev
|
|
2112
|
+
*/
|
|
2113
|
+
icons: {
|
|
2114
|
+
url: "https://cdn.jsdelivr.net/npm/lucide@0.294.0/dist/umd/lucide.min.js",
|
|
2115
|
+
integrity: "sha384-wpLmHb7v7V1LsEuTmPQ9tXqWZvTtRWWVqJuE+Yz6X0I6O2T6bHJVeXH1lVWqF4qE"
|
|
2116
|
+
}
|
|
2117
|
+
};
|
|
2118
|
+
var scriptCache = /* @__PURE__ */ new Map();
|
|
2119
|
+
function getCachedScript(url) {
|
|
2120
|
+
return scriptCache.get(url);
|
|
2121
|
+
}
|
|
2122
|
+
function isScriptCached(url) {
|
|
2123
|
+
return scriptCache.has(url);
|
|
2124
|
+
}
|
|
2125
|
+
function buildFontPreconnect() {
|
|
2126
|
+
return CDN.fonts.preconnect.map((url, i) => `<link rel="preconnect" href="${url}"${i > 0 ? " crossorigin" : ""}>`).join("\n ");
|
|
2127
|
+
}
|
|
2128
|
+
function buildFontStylesheets(options = {}) {
|
|
2129
|
+
const { inter = true, mono = false } = options;
|
|
2130
|
+
const links = [];
|
|
2131
|
+
if (inter) {
|
|
2132
|
+
links.push(`<link href="${CDN.fonts.inter}" rel="stylesheet">`);
|
|
2133
|
+
}
|
|
2134
|
+
if (mono) {
|
|
2135
|
+
links.push(`<link href="${CDN.fonts.mono}" rel="stylesheet">`);
|
|
2136
|
+
}
|
|
2137
|
+
return links.join("\n ");
|
|
2138
|
+
}
|
|
2139
|
+
function buildScriptTag(url, integrity, options = {}) {
|
|
2140
|
+
const attrs = [`src="${url}"`];
|
|
2141
|
+
if (integrity) {
|
|
2142
|
+
attrs.push(`integrity="${integrity}"`);
|
|
2143
|
+
attrs.push('crossorigin="anonymous"');
|
|
2144
|
+
}
|
|
2145
|
+
if (options.defer) attrs.push("defer");
|
|
2146
|
+
if (options.async) attrs.push("async");
|
|
2147
|
+
return `<script ${attrs.join(" ")}></script>`;
|
|
2148
|
+
}
|
|
2149
|
+
function buildInlineScriptTag(content) {
|
|
2150
|
+
return `<script>${content}</script>`;
|
|
2151
|
+
}
|
|
2152
|
+
function buildCdnScripts(options = {}) {
|
|
2153
|
+
const { tailwind = true, htmx = true, alpine = false, icons = false, inline = false } = options;
|
|
2154
|
+
const scripts = [];
|
|
2155
|
+
if (inline) {
|
|
2156
|
+
if (tailwind) {
|
|
2157
|
+
if (isScriptCached(CDN.tailwind)) {
|
|
2158
|
+
scripts.push(buildInlineScriptTag(getCachedScript(CDN.tailwind)));
|
|
2159
|
+
} else {
|
|
2160
|
+
console.warn(
|
|
2161
|
+
"[frontmcp/ui] Inline mode requested but Tailwind script not cached. Call fetchAndCacheScripts() first."
|
|
2162
|
+
);
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
if (htmx) {
|
|
2166
|
+
if (isScriptCached(CDN.htmx.url)) {
|
|
2167
|
+
scripts.push(buildInlineScriptTag(getCachedScript(CDN.htmx.url)));
|
|
2168
|
+
} else {
|
|
2169
|
+
console.warn(
|
|
2170
|
+
"[frontmcp/ui] Inline mode requested but HTMX script not cached. Call fetchAndCacheScripts() first."
|
|
2171
|
+
);
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
2174
|
+
if (alpine) {
|
|
2175
|
+
if (isScriptCached(CDN.alpine.url)) {
|
|
2176
|
+
scripts.push(buildInlineScriptTag(getCachedScript(CDN.alpine.url)));
|
|
2177
|
+
} else {
|
|
2178
|
+
console.warn(
|
|
2179
|
+
"[frontmcp/ui] Inline mode requested but Alpine.js script not cached. Call fetchAndCacheScripts() first."
|
|
2180
|
+
);
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
if (icons) {
|
|
2184
|
+
if (isScriptCached(CDN.icons.url)) {
|
|
2185
|
+
scripts.push(buildInlineScriptTag(getCachedScript(CDN.icons.url)));
|
|
2186
|
+
} else {
|
|
2187
|
+
console.warn(
|
|
2188
|
+
"[frontmcp/ui] Inline mode requested but Lucide icons script not cached. Call fetchAndCacheScripts() first."
|
|
2189
|
+
);
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
} else {
|
|
2193
|
+
if (tailwind) {
|
|
2194
|
+
scripts.push(buildScriptTag(CDN.tailwind));
|
|
2195
|
+
}
|
|
2196
|
+
if (htmx) {
|
|
2197
|
+
scripts.push(buildScriptTag(CDN.htmx.url, CDN.htmx.integrity));
|
|
2198
|
+
}
|
|
2199
|
+
if (alpine) {
|
|
2200
|
+
scripts.push(buildScriptTag(CDN.alpine.url, CDN.alpine.integrity, { defer: true }));
|
|
2201
|
+
}
|
|
2202
|
+
if (icons) {
|
|
2203
|
+
scripts.push(buildScriptTag(CDN.icons.url, CDN.icons.integrity));
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
return scripts.join("\n ");
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2209
|
+
// libs/uipack/src/theme/platforms.ts
|
|
2210
|
+
var OPENAI_PLATFORM = {
|
|
2211
|
+
id: "openai",
|
|
2212
|
+
name: "OpenAI",
|
|
2213
|
+
supportsWidgets: true,
|
|
2214
|
+
supportsTailwind: true,
|
|
2215
|
+
supportsHtmx: true,
|
|
2216
|
+
networkMode: "full",
|
|
2217
|
+
scriptStrategy: "cdn",
|
|
2218
|
+
options: {
|
|
2219
|
+
sdk: "apps-sdk",
|
|
2220
|
+
version: "1.0"
|
|
2221
|
+
}
|
|
2222
|
+
};
|
|
2223
|
+
var CLAUDE_PLATFORM = {
|
|
2224
|
+
id: "claude",
|
|
2225
|
+
name: "Claude (Artifacts)",
|
|
2226
|
+
supportsWidgets: true,
|
|
2227
|
+
supportsTailwind: true,
|
|
2228
|
+
supportsHtmx: false,
|
|
2229
|
+
// Network blocked, HTMX won't work for API calls
|
|
2230
|
+
networkMode: "blocked",
|
|
2231
|
+
scriptStrategy: "inline",
|
|
2232
|
+
maxInlineSize: 100 * 1024,
|
|
2233
|
+
// 100KB limit for artifacts
|
|
2234
|
+
cspRestrictions: ["script-src 'unsafe-inline'", "connect-src 'none'"],
|
|
2235
|
+
options: {
|
|
2236
|
+
mode: "artifacts",
|
|
2237
|
+
framework: "react"
|
|
2238
|
+
// Claude artifacts prefer React
|
|
2239
|
+
}
|
|
2240
|
+
};
|
|
2241
|
+
function canUseCdn(platform) {
|
|
2242
|
+
return platform.networkMode === "full" && platform.scriptStrategy === "cdn";
|
|
2243
|
+
}
|
|
2244
|
+
function needsInlineScripts(platform) {
|
|
2245
|
+
return platform.scriptStrategy === "inline" || platform.networkMode === "blocked";
|
|
2246
|
+
}
|
|
2247
|
+
|
|
2248
|
+
// libs/uipack/src/theme/presets/github-openai.ts
|
|
2249
|
+
var GITHUB_OPENAI_THEME = {
|
|
2250
|
+
name: "github-openai",
|
|
2251
|
+
colors: {
|
|
2252
|
+
semantic: {
|
|
2253
|
+
// Primary: Near-black for main actions and branding
|
|
2254
|
+
primary: "#24292f",
|
|
2255
|
+
// Secondary: Medium gray for secondary elements
|
|
2256
|
+
secondary: "#57606a",
|
|
2257
|
+
// Accent: Blue for links, focus states, and highlights
|
|
2258
|
+
accent: "#0969da",
|
|
2259
|
+
// Status colors
|
|
2260
|
+
success: "#1a7f37",
|
|
2261
|
+
// GitHub green
|
|
2262
|
+
warning: "#9a6700",
|
|
2263
|
+
// Amber warning
|
|
2264
|
+
danger: "#cf222e",
|
|
2265
|
+
// GitHub red
|
|
2266
|
+
info: "#0969da"
|
|
2267
|
+
// Blue info
|
|
2268
|
+
},
|
|
2269
|
+
surface: {
|
|
2270
|
+
// Pure white background
|
|
2271
|
+
background: "#ffffff",
|
|
2272
|
+
// Light gray surface (GitHub code background style)
|
|
2273
|
+
surface: "#f6f8fa",
|
|
2274
|
+
// White elevated surfaces (modals, cards)
|
|
2275
|
+
elevated: "#ffffff",
|
|
2276
|
+
// Dark semi-transparent overlay
|
|
2277
|
+
overlay: "rgba(27, 31, 36, 0.5)"
|
|
2278
|
+
},
|
|
2279
|
+
text: {
|
|
2280
|
+
// Near-black for primary text
|
|
2281
|
+
primary: "#24292f",
|
|
2282
|
+
// Gray for secondary/muted text
|
|
2283
|
+
secondary: "#57606a",
|
|
2284
|
+
// Light gray for disabled text
|
|
2285
|
+
disabled: "#8c959f",
|
|
2286
|
+
// White for text on dark backgrounds
|
|
2287
|
+
inverse: "#ffffff",
|
|
2288
|
+
// Blue for links
|
|
2289
|
+
link: "#0969da"
|
|
2290
|
+
},
|
|
2291
|
+
border: {
|
|
2292
|
+
// Light gray border (GitHub style)
|
|
2293
|
+
default: "#d0d7de",
|
|
2294
|
+
// Medium gray on hover
|
|
2295
|
+
hover: "#8c959f",
|
|
2296
|
+
// Blue focus ring
|
|
2297
|
+
focus: "#0969da",
|
|
2298
|
+
// Subtle divider
|
|
2299
|
+
divider: "#d8dee4"
|
|
2300
|
+
}
|
|
2301
|
+
},
|
|
2302
|
+
typography: {
|
|
2303
|
+
families: {
|
|
2304
|
+
// System UI font stack (GitHub/Apple style)
|
|
2305
|
+
sans: '-apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"',
|
|
2306
|
+
// Monospace stack
|
|
2307
|
+
mono: 'ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, "Liberation Mono", monospace'
|
|
2308
|
+
},
|
|
2309
|
+
sizes: {
|
|
2310
|
+
xs: "0.75rem",
|
|
2311
|
+
// 12px
|
|
2312
|
+
sm: "0.875rem",
|
|
2313
|
+
// 14px
|
|
2314
|
+
base: "1rem",
|
|
2315
|
+
// 16px
|
|
2316
|
+
lg: "1.125rem",
|
|
2317
|
+
// 18px
|
|
2318
|
+
xl: "1.25rem",
|
|
2319
|
+
// 20px
|
|
2320
|
+
"2xl": "1.5rem",
|
|
2321
|
+
// 24px
|
|
2322
|
+
"3xl": "1.875rem",
|
|
2323
|
+
// 30px
|
|
2324
|
+
"4xl": "2.25rem"
|
|
2325
|
+
// 36px
|
|
2326
|
+
},
|
|
2327
|
+
weights: {
|
|
2328
|
+
normal: "400",
|
|
2329
|
+
medium: "500",
|
|
2330
|
+
semibold: "600",
|
|
2331
|
+
bold: "700"
|
|
2332
|
+
}
|
|
2333
|
+
},
|
|
2334
|
+
radius: {
|
|
2335
|
+
none: "0",
|
|
2336
|
+
sm: "3px",
|
|
2337
|
+
// GitHub uses smaller radii
|
|
2338
|
+
md: "6px",
|
|
2339
|
+
lg: "8px",
|
|
2340
|
+
xl: "12px",
|
|
2341
|
+
"2xl": "16px",
|
|
2342
|
+
full: "9999px"
|
|
2343
|
+
},
|
|
2344
|
+
shadows: {
|
|
2345
|
+
// Subtle shadows with gray tones
|
|
2346
|
+
sm: "0 1px 0 rgba(27, 31, 36, 0.04)",
|
|
2347
|
+
md: "0 3px 6px rgba(140, 149, 159, 0.15)",
|
|
2348
|
+
lg: "0 8px 24px rgba(140, 149, 159, 0.2)",
|
|
2349
|
+
xl: "0 12px 28px rgba(140, 149, 159, 0.3)"
|
|
2350
|
+
},
|
|
2351
|
+
components: {
|
|
2352
|
+
button: {
|
|
2353
|
+
radius: "6px",
|
|
2354
|
+
paddingX: "16px",
|
|
2355
|
+
paddingY: "5px",
|
|
2356
|
+
fontSize: "14px",
|
|
2357
|
+
fontWeight: "500"
|
|
2358
|
+
},
|
|
2359
|
+
card: {
|
|
2360
|
+
radius: "6px",
|
|
2361
|
+
padding: "16px",
|
|
2362
|
+
shadow: "0 1px 0 rgba(27, 31, 36, 0.04)",
|
|
2363
|
+
borderWidth: "1px"
|
|
2364
|
+
},
|
|
2365
|
+
input: {
|
|
2366
|
+
radius: "6px",
|
|
2367
|
+
paddingX: "12px",
|
|
2368
|
+
paddingY: "5px",
|
|
2369
|
+
borderWidth: "1px",
|
|
2370
|
+
focusRingWidth: "3px"
|
|
2371
|
+
}
|
|
2372
|
+
},
|
|
2373
|
+
cdn: {
|
|
2374
|
+
fonts: {
|
|
2375
|
+
preconnect: ["https://fonts.googleapis.com", "https://fonts.gstatic.com"],
|
|
2376
|
+
stylesheets: [
|
|
2377
|
+
// System UI fonts don't need external stylesheets, but we include
|
|
2378
|
+
// Inter as an optional enhancement for consistent cross-platform rendering
|
|
2379
|
+
"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
|
|
2380
|
+
]
|
|
2381
|
+
},
|
|
2382
|
+
icons: {
|
|
2383
|
+
script: {
|
|
2384
|
+
url: "https://cdn.jsdelivr.net/npm/lucide@0.294.0/dist/umd/lucide.min.js"
|
|
2385
|
+
}
|
|
2386
|
+
},
|
|
2387
|
+
scripts: {
|
|
2388
|
+
tailwind: "https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4",
|
|
2389
|
+
htmx: {
|
|
2390
|
+
url: "https://cdnjs.cloudflare.com/ajax/libs/htmx/2.0.7/htmx.min.js",
|
|
2391
|
+
integrity: "sha512-T6VLg/MJYMbLTmQ8VLvonbWg8VOvmDhXcOvHzCwo6ShdGuUU5SEcp1IAPXL4k9lVoMi8gRXl5K/S/zh43Y9rJA=="
|
|
2392
|
+
},
|
|
2393
|
+
alpine: {
|
|
2394
|
+
url: "https://cdn.jsdelivr.net/npm/alpinejs@3.14.3/dist/cdn.min.js",
|
|
2395
|
+
integrity: "sha384-6zY8MFQJ/EqS1r4RJl+7j8rvZPuBWpT0RzWf+IFcKhxqUzQNmJzA1X1VEVZhYaEz"
|
|
2396
|
+
}
|
|
2397
|
+
}
|
|
2398
|
+
}
|
|
2399
|
+
};
|
|
2400
|
+
var DEFAULT_THEME = GITHUB_OPENAI_THEME;
|
|
2401
|
+
|
|
2402
|
+
// libs/uipack/src/theme/theme.ts
|
|
2403
|
+
function mergeThemesCore(base, override) {
|
|
2404
|
+
const baseColors = base.colors ?? { semantic: { primary: "#24292f" } };
|
|
2405
|
+
const filterStrings = (arr) => arr.filter((s) => s !== void 0);
|
|
2406
|
+
return {
|
|
2407
|
+
...base,
|
|
2408
|
+
...override,
|
|
2409
|
+
colors: {
|
|
2410
|
+
...baseColors,
|
|
2411
|
+
...override.colors,
|
|
2412
|
+
semantic: { ...baseColors.semantic, ...override.colors?.semantic },
|
|
2413
|
+
surface: { ...baseColors.surface, ...override.colors?.surface },
|
|
2414
|
+
text: { ...baseColors.text, ...override.colors?.text },
|
|
2415
|
+
border: { ...baseColors.border, ...override.colors?.border },
|
|
2416
|
+
custom: { ...baseColors.custom, ...override.colors?.custom }
|
|
2417
|
+
},
|
|
2418
|
+
typography: {
|
|
2419
|
+
...base.typography,
|
|
2420
|
+
...override.typography,
|
|
2421
|
+
families: { ...base.typography?.families, ...override.typography?.families },
|
|
2422
|
+
sizes: { ...base.typography?.sizes, ...override.typography?.sizes },
|
|
2423
|
+
weights: { ...base.typography?.weights, ...override.typography?.weights },
|
|
2424
|
+
lineHeight: { ...base.typography?.lineHeight, ...override.typography?.lineHeight }
|
|
2425
|
+
},
|
|
2426
|
+
spacing: { ...base.spacing, ...override.spacing },
|
|
2427
|
+
radius: { ...base.radius, ...override.radius },
|
|
2428
|
+
shadows: { ...base.shadows, ...override.shadows },
|
|
2429
|
+
components: {
|
|
2430
|
+
...base.components,
|
|
2431
|
+
...override.components,
|
|
2432
|
+
button: { ...base.components?.button, ...override.components?.button },
|
|
2433
|
+
card: { ...base.components?.card, ...override.components?.card },
|
|
2434
|
+
input: { ...base.components?.input, ...override.components?.input }
|
|
2435
|
+
},
|
|
2436
|
+
cdn: {
|
|
2437
|
+
...base.cdn,
|
|
2438
|
+
...override.cdn,
|
|
2439
|
+
fonts: {
|
|
2440
|
+
// Concatenate then dedupe so base entries are preserved without duplicates
|
|
2441
|
+
preconnect: filterStrings(
|
|
2442
|
+
Array.from(/* @__PURE__ */ new Set([...base.cdn?.fonts?.preconnect ?? [], ...override.cdn?.fonts?.preconnect ?? []]))
|
|
2443
|
+
),
|
|
2444
|
+
stylesheets: filterStrings(
|
|
2445
|
+
Array.from(/* @__PURE__ */ new Set([...base.cdn?.fonts?.stylesheets ?? [], ...override.cdn?.fonts?.stylesheets ?? []]))
|
|
2446
|
+
)
|
|
2447
|
+
},
|
|
2448
|
+
icons: {
|
|
2449
|
+
...base.cdn?.icons,
|
|
2450
|
+
...override.cdn?.icons,
|
|
2451
|
+
// Deep merge script to preserve integrity when only url is overridden
|
|
2452
|
+
script: override.cdn?.icons?.script ? { ...base.cdn?.icons?.script, ...override.cdn?.icons?.script } : base.cdn?.icons?.script
|
|
2453
|
+
},
|
|
2454
|
+
scripts: {
|
|
2455
|
+
// tailwind is a simple string, just use override or base
|
|
2456
|
+
tailwind: override.cdn?.scripts?.tailwind ?? base.cdn?.scripts?.tailwind,
|
|
2457
|
+
// Deep merge htmx/alpine to preserve integrity when only url is overridden
|
|
2458
|
+
htmx: override.cdn?.scripts?.htmx ? { ...base.cdn?.scripts?.htmx, ...override.cdn?.scripts?.htmx } : base.cdn?.scripts?.htmx,
|
|
2459
|
+
alpine: override.cdn?.scripts?.alpine ? { ...base.cdn?.scripts?.alpine, ...override.cdn?.scripts?.alpine } : base.cdn?.scripts?.alpine
|
|
2460
|
+
}
|
|
2461
|
+
},
|
|
2462
|
+
customVars: { ...base.customVars, ...override.customVars },
|
|
2463
|
+
customCss: [base.customCss, override.customCss].filter(Boolean).join("\n")
|
|
2464
|
+
};
|
|
2465
|
+
}
|
|
2466
|
+
function mergeThemes(base, override) {
|
|
2467
|
+
const merged = mergeThemesCore(base, override);
|
|
2468
|
+
let darkVariant;
|
|
2469
|
+
if (override.dark !== void 0) {
|
|
2470
|
+
const darkBase = base.dark ?? base;
|
|
2471
|
+
const { dark: _nestedDark, ...overrideDarkWithoutNested } = override.dark;
|
|
2472
|
+
darkVariant = mergeThemesCore(darkBase, overrideDarkWithoutNested);
|
|
2473
|
+
} else if (base.dark !== void 0) {
|
|
2474
|
+
const { dark: _nestedDark, ...baseDarkWithoutNested } = base.dark;
|
|
2475
|
+
darkVariant = baseDarkWithoutNested;
|
|
2476
|
+
}
|
|
2477
|
+
return {
|
|
2478
|
+
...merged,
|
|
2479
|
+
dark: darkVariant
|
|
2480
|
+
};
|
|
2481
|
+
}
|
|
2482
|
+
function emitColorScale(lines, name, scale) {
|
|
2483
|
+
for (const [shade, value] of Object.entries(scale)) {
|
|
2484
|
+
if (value) lines.push(`--color-${name}-${shade}: ${value};`);
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
function buildThemeCss(theme) {
|
|
2488
|
+
const lines = [];
|
|
2489
|
+
const semantic = theme.colors.semantic;
|
|
2490
|
+
if (typeof semantic.primary === "string") {
|
|
2491
|
+
lines.push(`--color-primary: ${semantic.primary};`);
|
|
2492
|
+
} else if (semantic.primary) {
|
|
2493
|
+
emitColorScale(lines, "primary", semantic.primary);
|
|
2494
|
+
}
|
|
2495
|
+
if (semantic.secondary) {
|
|
2496
|
+
if (typeof semantic.secondary === "string") {
|
|
2497
|
+
lines.push(`--color-secondary: ${semantic.secondary};`);
|
|
2498
|
+
} else {
|
|
2499
|
+
emitColorScale(lines, "secondary", semantic.secondary);
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2502
|
+
if (semantic.accent) {
|
|
2503
|
+
if (typeof semantic.accent === "string") {
|
|
2504
|
+
lines.push(`--color-accent: ${semantic.accent};`);
|
|
2505
|
+
} else {
|
|
2506
|
+
emitColorScale(lines, "accent", semantic.accent);
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
if (semantic.neutral) {
|
|
2510
|
+
if (typeof semantic.neutral === "string") {
|
|
2511
|
+
lines.push(`--color-neutral: ${semantic.neutral};`);
|
|
2512
|
+
} else {
|
|
2513
|
+
emitColorScale(lines, "neutral", semantic.neutral);
|
|
2514
|
+
}
|
|
2515
|
+
}
|
|
2516
|
+
if (semantic.success) lines.push(`--color-success: ${semantic.success};`);
|
|
2517
|
+
if (semantic.warning) lines.push(`--color-warning: ${semantic.warning};`);
|
|
2518
|
+
if (semantic.danger) lines.push(`--color-danger: ${semantic.danger};`);
|
|
2519
|
+
if (semantic.info) lines.push(`--color-info: ${semantic.info};`);
|
|
2520
|
+
const surface = theme.colors.surface;
|
|
2521
|
+
if (surface?.background) lines.push(`--color-background: ${surface.background};`);
|
|
2522
|
+
if (surface?.surface) lines.push(`--color-surface: ${surface.surface};`);
|
|
2523
|
+
if (surface?.elevated) lines.push(`--color-elevated: ${surface.elevated};`);
|
|
2524
|
+
if (surface?.overlay) lines.push(`--color-overlay: ${surface.overlay};`);
|
|
2525
|
+
const text = theme.colors.text;
|
|
2526
|
+
if (text?.primary) lines.push(`--color-text-primary: ${text.primary};`);
|
|
2527
|
+
if (text?.secondary) lines.push(`--color-text-secondary: ${text.secondary};`);
|
|
2528
|
+
if (text?.disabled) lines.push(`--color-text-disabled: ${text.disabled};`);
|
|
2529
|
+
if (text?.inverse) lines.push(`--color-text-inverse: ${text.inverse};`);
|
|
2530
|
+
if (text?.link) lines.push(`--color-text-link: ${text.link};`);
|
|
2531
|
+
const border = theme.colors.border;
|
|
2532
|
+
if (border?.default) lines.push(`--color-border: ${border.default};`);
|
|
2533
|
+
if (border?.hover) lines.push(`--color-border-hover: ${border.hover};`);
|
|
2534
|
+
if (border?.focus) lines.push(`--color-border-focus: ${border.focus};`);
|
|
2535
|
+
if (border?.divider) lines.push(`--color-divider: ${border.divider};`);
|
|
2536
|
+
if (theme.colors.custom) {
|
|
2537
|
+
for (const [key, value] of Object.entries(theme.colors.custom)) {
|
|
2538
|
+
lines.push(`--color-${key}: ${value};`);
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
const typography = theme.typography;
|
|
2542
|
+
if (typography?.families?.sans) lines.push(`--font-sans: ${typography.families.sans};`);
|
|
2543
|
+
if (typography?.families?.serif) lines.push(`--font-serif: ${typography.families.serif};`);
|
|
2544
|
+
if (typography?.families?.mono) lines.push(`--font-mono: ${typography.families.mono};`);
|
|
2545
|
+
if (typography?.families?.display) lines.push(`--font-display: ${typography.families.display};`);
|
|
2546
|
+
const radius = theme.radius;
|
|
2547
|
+
if (radius?.none) lines.push(`--radius-none: ${radius.none};`);
|
|
2548
|
+
if (radius?.sm) lines.push(`--radius-sm: ${radius.sm};`);
|
|
2549
|
+
if (radius?.md) lines.push(`--radius-md: ${radius.md};`);
|
|
2550
|
+
if (radius?.lg) lines.push(`--radius-lg: ${radius.lg};`);
|
|
2551
|
+
if (radius?.xl) lines.push(`--radius-xl: ${radius.xl};`);
|
|
2552
|
+
if (radius?.["2xl"]) lines.push(`--radius-2xl: ${radius["2xl"]};`);
|
|
2553
|
+
if (radius?.full) lines.push(`--radius-full: ${radius.full};`);
|
|
2554
|
+
if (theme.customVars) {
|
|
2555
|
+
for (const [key, value] of Object.entries(theme.customVars)) {
|
|
2556
|
+
lines.push(`${key}: ${value};`);
|
|
2557
|
+
}
|
|
2558
|
+
}
|
|
2559
|
+
return lines.join("\n ");
|
|
2560
|
+
}
|
|
2561
|
+
|
|
2562
|
+
// libs/uipack/src/runtime/wrapper.ts
|
|
2563
|
+
init_utils();
|
|
2564
|
+
|
|
2565
|
+
// libs/uipack/src/runtime/sanitizer.ts
|
|
2566
|
+
var REDACTION_TOKENS = {
|
|
2567
|
+
EMAIL: "[EMAIL]",
|
|
2568
|
+
PHONE: "[PHONE]",
|
|
2569
|
+
CARD: "[CARD]",
|
|
2570
|
+
ID: "[ID]",
|
|
2571
|
+
IP: "[IP]",
|
|
2572
|
+
REDACTED: "[REDACTED]"
|
|
2573
|
+
};
|
|
2574
|
+
var PII_PATTERNS = {
|
|
2575
|
+
// Email: user@domain.tld
|
|
2576
|
+
email: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
|
|
2577
|
+
// Email embedded in text
|
|
2578
|
+
emailInText: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
|
|
2579
|
+
// Phone: Various formats (US-centric but flexible)
|
|
2580
|
+
phone: /^[+]?[(]?[0-9]{1,4}[)]?[-\s.]?[0-9]{1,4}[-\s.]?[0-9]{1,9}$/,
|
|
2581
|
+
// Phone embedded in text
|
|
2582
|
+
phoneInText: /(?:\+?1[-.\s]?)?\(?[0-9]{3}\)?[-.\s]?[0-9]{3}[-.\s]?[0-9]{4}/g,
|
|
2583
|
+
// Credit card: 13-19 digits (with optional separators)
|
|
2584
|
+
creditCard: /^(?:[0-9]{4}[-\s]?){3,4}[0-9]{1,4}$/,
|
|
2585
|
+
// Credit card embedded in text
|
|
2586
|
+
creditCardInText: /\b(?:[0-9]{4}[-\s]?){3,4}[0-9]{1,4}\b/g,
|
|
2587
|
+
// SSN: XXX-XX-XXXX
|
|
2588
|
+
ssn: /^[0-9]{3}[-]?[0-9]{2}[-]?[0-9]{4}$/,
|
|
2589
|
+
// SSN embedded in text
|
|
2590
|
+
ssnInText: /\b[0-9]{3}[-]?[0-9]{2}[-]?[0-9]{4}\b/g,
|
|
2591
|
+
// IPv4 address
|
|
2592
|
+
ipv4: /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/,
|
|
2593
|
+
// IPv4 embedded in text
|
|
2594
|
+
ipv4InText: /\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/g
|
|
2595
|
+
};
|
|
2596
|
+
function isEmail(value) {
|
|
2597
|
+
return PII_PATTERNS.email.test(value);
|
|
2598
|
+
}
|
|
2599
|
+
function isPhone(value) {
|
|
2600
|
+
return PII_PATTERNS.phone.test(value);
|
|
2601
|
+
}
|
|
2602
|
+
function isCreditCard(value) {
|
|
2603
|
+
const digits = value.replace(/[-\s]/g, "");
|
|
2604
|
+
return digits.length >= 13 && digits.length <= 19 && PII_PATTERNS.creditCard.test(value);
|
|
2605
|
+
}
|
|
2606
|
+
function isSSN(value) {
|
|
2607
|
+
return PII_PATTERNS.ssn.test(value);
|
|
2608
|
+
}
|
|
2609
|
+
function isIPv4(value) {
|
|
2610
|
+
return PII_PATTERNS.ipv4.test(value);
|
|
2611
|
+
}
|
|
2612
|
+
function detectPIIType(value) {
|
|
2613
|
+
if (isEmail(value)) return "EMAIL";
|
|
2614
|
+
if (isCreditCard(value)) return "CARD";
|
|
2615
|
+
if (isSSN(value)) return "ID";
|
|
2616
|
+
if (isPhone(value)) return "PHONE";
|
|
2617
|
+
if (isIPv4(value)) return "IP";
|
|
2618
|
+
return null;
|
|
2619
|
+
}
|
|
2620
|
+
function redactPIIFromText(text) {
|
|
2621
|
+
let result = text;
|
|
2622
|
+
result = result.replace(PII_PATTERNS.creditCardInText, REDACTION_TOKENS.CARD);
|
|
2623
|
+
result = result.replace(PII_PATTERNS.ssnInText, REDACTION_TOKENS.ID);
|
|
2624
|
+
result = result.replace(PII_PATTERNS.emailInText, REDACTION_TOKENS.EMAIL);
|
|
2625
|
+
result = result.replace(PII_PATTERNS.phoneInText, REDACTION_TOKENS.PHONE);
|
|
2626
|
+
result = result.replace(PII_PATTERNS.ipv4InText, REDACTION_TOKENS.IP);
|
|
2627
|
+
return result;
|
|
2628
|
+
}
|
|
2629
|
+
function sanitizeValue(key, value, path, options, depth, visited) {
|
|
2630
|
+
const maxDepth = options.maxDepth ?? 10;
|
|
2631
|
+
if (depth > maxDepth) {
|
|
2632
|
+
return value;
|
|
2633
|
+
}
|
|
2634
|
+
if (value === null || value === void 0) {
|
|
2635
|
+
return value;
|
|
2636
|
+
}
|
|
2637
|
+
if (typeof options.mode === "function") {
|
|
2638
|
+
return options.mode(key, value, path);
|
|
2639
|
+
}
|
|
2640
|
+
if (Array.isArray(options.mode)) {
|
|
2641
|
+
const lowerKey = key.toLowerCase();
|
|
2642
|
+
if (options.mode.some((f) => f.toLowerCase() === lowerKey)) {
|
|
2643
|
+
return REDACTION_TOKENS.REDACTED;
|
|
2644
|
+
}
|
|
2645
|
+
}
|
|
2646
|
+
if (typeof value === "string") {
|
|
2647
|
+
if (options.mode === true) {
|
|
2648
|
+
const piiType = detectPIIType(value);
|
|
2649
|
+
if (piiType) {
|
|
2650
|
+
return REDACTION_TOKENS[piiType];
|
|
2651
|
+
}
|
|
2652
|
+
if (options.sanitizeInText !== false) {
|
|
2653
|
+
const redacted = redactPIIFromText(value);
|
|
2654
|
+
if (redacted !== value) {
|
|
2655
|
+
return redacted;
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2659
|
+
return value;
|
|
2660
|
+
}
|
|
2661
|
+
if (Array.isArray(value)) {
|
|
2662
|
+
if (visited.has(value)) {
|
|
2663
|
+
return REDACTION_TOKENS.REDACTED;
|
|
2664
|
+
}
|
|
2665
|
+
visited.add(value);
|
|
2666
|
+
return value.map(
|
|
2667
|
+
(item, index) => sanitizeValue(String(index), item, [...path, String(index)], options, depth + 1, visited)
|
|
2668
|
+
);
|
|
2669
|
+
}
|
|
2670
|
+
if (typeof value === "object") {
|
|
2671
|
+
if (visited.has(value)) {
|
|
2672
|
+
return REDACTION_TOKENS.REDACTED;
|
|
2673
|
+
}
|
|
2674
|
+
visited.add(value);
|
|
2675
|
+
const result = {};
|
|
2676
|
+
for (const [k, v] of Object.entries(value)) {
|
|
2677
|
+
result[k] = sanitizeValue(k, v, [...path, k], options, depth + 1, visited);
|
|
2678
|
+
}
|
|
2679
|
+
return result;
|
|
2680
|
+
}
|
|
2681
|
+
return value;
|
|
2682
|
+
}
|
|
2683
|
+
function sanitizeInput(input, mode = true) {
|
|
2684
|
+
if (!input || typeof input !== "object") {
|
|
2685
|
+
return {};
|
|
2686
|
+
}
|
|
2687
|
+
const options = {
|
|
2688
|
+
mode,
|
|
2689
|
+
maxDepth: 10,
|
|
2690
|
+
sanitizeInText: true
|
|
2691
|
+
};
|
|
2692
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
2693
|
+
return sanitizeValue("", input, [], options, 0, visited);
|
|
2694
|
+
}
|
|
2695
|
+
|
|
2696
|
+
// libs/uipack/src/runtime/wrapper.ts
|
|
2697
|
+
function createTemplateHelpers() {
|
|
2698
|
+
let idCounter2 = 0;
|
|
2699
|
+
return {
|
|
2700
|
+
/**
|
|
2701
|
+
* Escape HTML special characters to prevent XSS
|
|
2702
|
+
*/
|
|
2703
|
+
escapeHtml,
|
|
2704
|
+
/**
|
|
2705
|
+
* Format a date for display
|
|
2706
|
+
*/
|
|
2707
|
+
formatDate: (date, format) => {
|
|
2708
|
+
const d = typeof date === "string" ? new Date(date) : date;
|
|
2709
|
+
if (isNaN(d.getTime())) return String(date);
|
|
2710
|
+
if (format === "iso") {
|
|
2711
|
+
return d.toISOString();
|
|
2712
|
+
}
|
|
2713
|
+
if (format === "time") {
|
|
2714
|
+
return d.toLocaleTimeString();
|
|
2715
|
+
}
|
|
2716
|
+
if (format === "datetime") {
|
|
2717
|
+
return d.toLocaleString();
|
|
2718
|
+
}
|
|
2719
|
+
return d.toLocaleDateString();
|
|
2720
|
+
},
|
|
2721
|
+
/**
|
|
2722
|
+
* Format a number as currency
|
|
2723
|
+
*/
|
|
2724
|
+
formatCurrency: (amount, currency = "USD") => {
|
|
2725
|
+
return new Intl.NumberFormat("en-US", {
|
|
2726
|
+
style: "currency",
|
|
2727
|
+
currency
|
|
2728
|
+
}).format(amount);
|
|
2729
|
+
},
|
|
2730
|
+
/**
|
|
2731
|
+
* Generate a unique ID for DOM elements
|
|
2732
|
+
*/
|
|
2733
|
+
uniqueId: (prefix = "mcp") => {
|
|
2734
|
+
return `${prefix}-${++idCounter2}-${Date.now().toString(36)}`;
|
|
2735
|
+
},
|
|
2736
|
+
/**
|
|
2737
|
+
* Safely embed JSON data in HTML
|
|
2738
|
+
* Escapes characters that could break out of script tags or HTML
|
|
2739
|
+
*/
|
|
2740
|
+
jsonEmbed: (data) => {
|
|
2741
|
+
const json2 = JSON.stringify(data);
|
|
2742
|
+
if (json2 === void 0) {
|
|
2743
|
+
return "undefined";
|
|
2744
|
+
}
|
|
2745
|
+
return json2.replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/'/g, "\\u0027");
|
|
2746
|
+
}
|
|
2747
|
+
};
|
|
2748
|
+
}
|
|
2749
|
+
function wrapToolUI(options) {
|
|
2750
|
+
const {
|
|
2751
|
+
content,
|
|
2752
|
+
toolName,
|
|
2753
|
+
input = {},
|
|
2754
|
+
output,
|
|
2755
|
+
structuredContent,
|
|
2756
|
+
csp,
|
|
2757
|
+
widgetAccessible = false,
|
|
2758
|
+
title,
|
|
2759
|
+
theme: themeOverrides,
|
|
2760
|
+
platform = OPENAI_PLATFORM,
|
|
2761
|
+
hostContext,
|
|
2762
|
+
sanitizeInput: sanitizeOption,
|
|
2763
|
+
rendererType,
|
|
2764
|
+
hydrate = false,
|
|
2765
|
+
// Disabled by default to prevent React hydration Error #418 in MCP clients
|
|
2766
|
+
skipCspMeta = false
|
|
2767
|
+
} = options;
|
|
2768
|
+
let sanitizedInput = input;
|
|
2769
|
+
if (sanitizeOption) {
|
|
2770
|
+
const sanitizeMode = sanitizeOption === true ? true : sanitizeOption;
|
|
2771
|
+
sanitizedInput = sanitizeInput(input, sanitizeMode);
|
|
2772
|
+
}
|
|
2773
|
+
const theme = themeOverrides ? mergeThemes(DEFAULT_THEME, themeOverrides) : DEFAULT_THEME;
|
|
2774
|
+
const useCdn = canUseCdn(platform);
|
|
2775
|
+
const useInline = needsInlineScripts(platform);
|
|
2776
|
+
const fontPreconnect = useCdn ? buildFontPreconnect() : "";
|
|
2777
|
+
const fontStylesheets = useCdn ? buildFontStylesheets({ inter: true }) : "";
|
|
2778
|
+
const scripts = buildCdnScripts({
|
|
2779
|
+
tailwind: platform.supportsTailwind,
|
|
2780
|
+
htmx: false,
|
|
2781
|
+
alpine: false,
|
|
2782
|
+
icons: false,
|
|
2783
|
+
inline: useInline
|
|
2784
|
+
});
|
|
2785
|
+
const frameworkScripts = buildFrameworkRuntimeScripts({
|
|
2786
|
+
rendererType,
|
|
2787
|
+
hydrate,
|
|
2788
|
+
platform
|
|
2789
|
+
});
|
|
2790
|
+
const themeCss = buildThemeCss(theme);
|
|
2791
|
+
const customCss = theme.customCss || "";
|
|
2792
|
+
const styleBlock = platform.supportsTailwind ? `<style type="text/tailwindcss">
|
|
2793
|
+
@theme {
|
|
2794
|
+
${themeCss}
|
|
2795
|
+
}
|
|
2796
|
+
${customCss}
|
|
2797
|
+
</style>` : "";
|
|
2798
|
+
const cspMetaTag = skipCspMeta ? "" : buildCSPMetaTag(csp);
|
|
2799
|
+
const dataScript = buildDataInjectionScript({
|
|
2800
|
+
toolName,
|
|
2801
|
+
input: sanitizedInput,
|
|
2802
|
+
output,
|
|
2803
|
+
structuredContent,
|
|
2804
|
+
widgetAccessible,
|
|
2805
|
+
hostContext
|
|
2806
|
+
});
|
|
2807
|
+
const pageTitle = title || `${escapeHtml(toolName)} - Tool Result`;
|
|
2808
|
+
return `<!DOCTYPE html>
|
|
2809
|
+
<html lang="en">
|
|
2810
|
+
<head>
|
|
2811
|
+
<meta charset="UTF-8">
|
|
2812
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
2813
|
+
<title>${pageTitle}</title>
|
|
2814
|
+
${cspMetaTag}
|
|
2815
|
+
|
|
2816
|
+
<!-- Fonts -->
|
|
2817
|
+
${fontPreconnect}
|
|
2818
|
+
${fontStylesheets}
|
|
2819
|
+
|
|
2820
|
+
<!-- Tailwind CSS -->
|
|
2821
|
+
${scripts}
|
|
2822
|
+
${styleBlock}
|
|
2823
|
+
|
|
2824
|
+
<!-- Framework Runtime -->
|
|
2825
|
+
${frameworkScripts}
|
|
2826
|
+
|
|
2827
|
+
<!-- Tool Data -->
|
|
2828
|
+
${dataScript}
|
|
2829
|
+
|
|
2830
|
+
<!-- MCP Bridge Runtime -->
|
|
2831
|
+
${MCP_BRIDGE_RUNTIME}
|
|
2832
|
+
</head>
|
|
2833
|
+
<body class="bg-background text-text-primary font-sans antialiased">
|
|
2834
|
+
${content}
|
|
2835
|
+
</body>
|
|
2836
|
+
</html>`;
|
|
2837
|
+
}
|
|
2838
|
+
function buildFrameworkRuntimeScripts(options) {
|
|
2839
|
+
const { rendererType, hydrate, platform } = options;
|
|
2840
|
+
if (!rendererType || rendererType === "html" || rendererType === "html-fallback") {
|
|
2841
|
+
return "";
|
|
2842
|
+
}
|
|
2843
|
+
if (!hydrate) {
|
|
2844
|
+
return "";
|
|
2845
|
+
}
|
|
2846
|
+
if (rendererType === "react" || rendererType === "mdx") {
|
|
2847
|
+
const useCdn = canUseCdn(platform);
|
|
2848
|
+
if (useCdn) {
|
|
2849
|
+
return `
|
|
2850
|
+
<!-- React Runtime (for hydration) -->
|
|
2851
|
+
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
|
|
2852
|
+
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
|
|
2853
|
+
<script>
|
|
2854
|
+
// Hydration script for React/MDX components
|
|
2855
|
+
(function() {
|
|
2856
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
2857
|
+
var hydratables = document.querySelectorAll('[data-hydrate], [data-mdx-hydrate]');
|
|
2858
|
+
if (hydratables.length > 0 && window.__frontmcp_components) {
|
|
2859
|
+
hydratables.forEach(function(el) {
|
|
2860
|
+
var componentName = el.getAttribute('data-hydrate');
|
|
2861
|
+
var propsJson = el.getAttribute('data-props');
|
|
2862
|
+
var props = propsJson ? JSON.parse(propsJson) : {};
|
|
2863
|
+
|
|
2864
|
+
if (window.__frontmcp_components[componentName]) {
|
|
2865
|
+
try {
|
|
2866
|
+
ReactDOM.hydrateRoot(el, React.createElement(
|
|
2867
|
+
window.__frontmcp_components[componentName],
|
|
2868
|
+
props
|
|
2869
|
+
));
|
|
2870
|
+
} catch (e) {
|
|
2871
|
+
console.error('[FrontMCP] Hydration failed:', e);
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
});
|
|
2875
|
+
}
|
|
2876
|
+
});
|
|
2877
|
+
})();
|
|
2878
|
+
</script>`;
|
|
2879
|
+
} else {
|
|
2880
|
+
return `
|
|
2881
|
+
<!-- React hydration not available on blocked-network platforms -->
|
|
2882
|
+
<script>
|
|
2883
|
+
console.warn('[FrontMCP] React hydration disabled - platform does not support external scripts');
|
|
2884
|
+
</script>`;
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
return "";
|
|
2888
|
+
}
|
|
2889
|
+
function buildDataInjectionScript(options) {
|
|
2890
|
+
const { toolName, input, output, structuredContent, widgetAccessible, hostContext } = options;
|
|
2891
|
+
const helpers = createTemplateHelpers();
|
|
2892
|
+
const contextData = {
|
|
2893
|
+
theme: hostContext?.theme || "light",
|
|
2894
|
+
displayMode: hostContext?.displayMode || "inline",
|
|
2895
|
+
...hostContext
|
|
2896
|
+
};
|
|
2897
|
+
return `<script>
|
|
2898
|
+
// Tool metadata
|
|
2899
|
+
window.__mcpToolName = ${helpers.jsonEmbed(toolName)};
|
|
2900
|
+
window.__mcpToolInput = ${helpers.jsonEmbed(input)};
|
|
2901
|
+
window.__mcpToolOutput = ${helpers.jsonEmbed(output)};
|
|
2902
|
+
window.__mcpStructuredContent = ${helpers.jsonEmbed(structuredContent)};
|
|
2903
|
+
window.__mcpWidgetAccessible = ${helpers.jsonEmbed(widgetAccessible)};
|
|
2904
|
+
window.__mcpHostContext = ${helpers.jsonEmbed(contextData)};
|
|
2905
|
+
</script>`;
|
|
2906
|
+
}
|
|
2907
|
+
|
|
2908
|
+
// libs/uipack/src/tool-template/builder.ts
|
|
2909
|
+
init_utils();
|
|
2910
|
+
|
|
2911
|
+
// libs/uipack/src/renderers/utils/detect.ts
|
|
2912
|
+
function isReactComponent(value) {
|
|
2913
|
+
if (typeof value !== "function") {
|
|
2914
|
+
return false;
|
|
2915
|
+
}
|
|
2916
|
+
const fn = value;
|
|
2917
|
+
const typeofSymbol = fn.$$typeof;
|
|
2918
|
+
if (typeofSymbol) {
|
|
2919
|
+
const symbolString = typeofSymbol.toString();
|
|
2920
|
+
return symbolString.includes("react.memo") || symbolString.includes("react.forward_ref") || symbolString.includes("react.lazy");
|
|
2921
|
+
}
|
|
2922
|
+
if (fn.prototype?.isReactComponent) {
|
|
2923
|
+
return true;
|
|
2924
|
+
}
|
|
2925
|
+
if (fn.name && /^[A-Z]/.test(fn.name)) {
|
|
2926
|
+
return true;
|
|
2927
|
+
}
|
|
2928
|
+
return false;
|
|
2929
|
+
}
|
|
2930
|
+
function isTemplateBuilderFunction(fn) {
|
|
2931
|
+
if (isReactComponent(fn)) {
|
|
2932
|
+
return false;
|
|
2933
|
+
}
|
|
2934
|
+
if (fn.name && /^[A-Z]/.test(fn.name)) {
|
|
2935
|
+
return false;
|
|
2936
|
+
}
|
|
2937
|
+
return true;
|
|
2938
|
+
}
|
|
2939
|
+
function containsJsx(source) {
|
|
2940
|
+
if (/<[A-Z][a-zA-Z0-9]*(\s|>|\/)/.test(source)) {
|
|
2941
|
+
return true;
|
|
2942
|
+
}
|
|
2943
|
+
if (/<[A-Z][a-zA-Z0-9]*[^>]*\/>/.test(source)) {
|
|
2944
|
+
return true;
|
|
2945
|
+
}
|
|
2946
|
+
if (/<[a-z]+[^>]*\{[^}]+\}/.test(source)) {
|
|
2947
|
+
return true;
|
|
2948
|
+
}
|
|
2949
|
+
if (/\s(className|onClick|onChange|onSubmit|htmlFor)=/.test(source)) {
|
|
2950
|
+
return true;
|
|
2951
|
+
}
|
|
2952
|
+
if (/<>|<\/>|<React\.Fragment>/.test(source)) {
|
|
2953
|
+
return true;
|
|
2954
|
+
}
|
|
2955
|
+
if (/=>\s*\(?\s*</.test(source)) {
|
|
2956
|
+
return true;
|
|
2957
|
+
}
|
|
2958
|
+
if (/return\s*\(?\s*</.test(source)) {
|
|
2959
|
+
return true;
|
|
2960
|
+
}
|
|
2961
|
+
return false;
|
|
2962
|
+
}
|
|
2963
|
+
function containsMdxSyntax(source) {
|
|
2964
|
+
if (/<[A-Z][a-zA-Z0-9]*/.test(source)) {
|
|
2965
|
+
return true;
|
|
2966
|
+
}
|
|
2967
|
+
if (/^(import|export)\s/m.test(source)) {
|
|
2968
|
+
return true;
|
|
2969
|
+
}
|
|
2970
|
+
if (/\s(className|onClick|onChange|onSubmit|htmlFor|dangerouslySetInnerHTML)=/.test(source)) {
|
|
2971
|
+
return true;
|
|
2972
|
+
}
|
|
2973
|
+
if (/\{[^}"'\n]*\}/.test(source) && !/=\s*["'][^"']*\{/.test(source)) {
|
|
2974
|
+
return true;
|
|
2975
|
+
}
|
|
2976
|
+
if (/^---[\s\S]*?---/m.test(source)) {
|
|
2977
|
+
return true;
|
|
2978
|
+
}
|
|
2979
|
+
if (/<>|<\/>/.test(source)) {
|
|
2980
|
+
return true;
|
|
2981
|
+
}
|
|
2982
|
+
return false;
|
|
2983
|
+
}
|
|
2984
|
+
function detectTemplateType(template) {
|
|
2985
|
+
if (typeof template === "function") {
|
|
2986
|
+
if (isReactComponent(template)) {
|
|
2987
|
+
return {
|
|
2988
|
+
type: "react",
|
|
2989
|
+
confidence: 0.9,
|
|
2990
|
+
reason: "Function detected as React component (PascalCase name or React symbols)"
|
|
2991
|
+
};
|
|
2992
|
+
}
|
|
2993
|
+
return {
|
|
2994
|
+
type: "html-function",
|
|
2995
|
+
confidence: 0.8,
|
|
2996
|
+
reason: "Function assumed to be HTML template builder"
|
|
2997
|
+
};
|
|
2998
|
+
}
|
|
2999
|
+
if (typeof template === "string") {
|
|
3000
|
+
if (containsMdxSyntax(template)) {
|
|
3001
|
+
const hasMarkdown = /^#{1,6}\s|^\*\s|^\d+\.\s|^-\s/m.test(template);
|
|
3002
|
+
if (hasMarkdown) {
|
|
3003
|
+
return {
|
|
3004
|
+
type: "mdx",
|
|
3005
|
+
confidence: 0.9,
|
|
3006
|
+
reason: "String contains Markdown with JSX components"
|
|
3007
|
+
};
|
|
3008
|
+
}
|
|
3009
|
+
return {
|
|
3010
|
+
type: "jsx-string",
|
|
3011
|
+
confidence: 0.8,
|
|
3012
|
+
reason: "String contains JSX syntax"
|
|
3013
|
+
};
|
|
3014
|
+
}
|
|
3015
|
+
if (containsJsx(template)) {
|
|
3016
|
+
return {
|
|
3017
|
+
type: "jsx-string",
|
|
3018
|
+
confidence: 0.85,
|
|
3019
|
+
reason: "String contains JSX component tags or expressions"
|
|
3020
|
+
};
|
|
3021
|
+
}
|
|
3022
|
+
return {
|
|
3023
|
+
type: "html-string",
|
|
3024
|
+
confidence: 1,
|
|
3025
|
+
reason: "Plain HTML string"
|
|
3026
|
+
};
|
|
3027
|
+
}
|
|
3028
|
+
return {
|
|
3029
|
+
type: "html-string",
|
|
3030
|
+
confidence: 0.5,
|
|
3031
|
+
reason: "Unknown template type, defaulting to HTML"
|
|
3032
|
+
};
|
|
3033
|
+
}
|
|
3034
|
+
|
|
3035
|
+
// libs/uipack/src/renderers/utils/hash.ts
|
|
3036
|
+
function hashString(source) {
|
|
3037
|
+
let hash = 2166136261;
|
|
3038
|
+
for (let i = 0; i < source.length; i++) {
|
|
3039
|
+
hash ^= source.charCodeAt(i);
|
|
3040
|
+
hash = hash * 16777619 >>> 0;
|
|
3041
|
+
}
|
|
3042
|
+
return hash.toString(36);
|
|
3043
|
+
}
|
|
3044
|
+
|
|
3045
|
+
// libs/uipack/src/renderers/html.renderer.ts
|
|
3046
|
+
var handlebarsRenderer = null;
|
|
3047
|
+
async function loadHandlebarsRenderer() {
|
|
3048
|
+
if (handlebarsRenderer !== null) {
|
|
3049
|
+
return handlebarsRenderer;
|
|
3050
|
+
}
|
|
3051
|
+
try {
|
|
3052
|
+
const handlebarsModule = await Promise.resolve().then(() => (init_handlebars(), handlebars_exports));
|
|
3053
|
+
const { HandlebarsRenderer: HandlebarsRenderer2 } = handlebarsModule;
|
|
3054
|
+
const renderer = new HandlebarsRenderer2();
|
|
3055
|
+
handlebarsRenderer = {
|
|
3056
|
+
render: (template, context) => renderer.render(template, {
|
|
3057
|
+
input: context.input ?? {},
|
|
3058
|
+
output: context.output,
|
|
3059
|
+
structuredContent: context.structuredContent
|
|
3060
|
+
}),
|
|
3061
|
+
containsHandlebars: (template) => HandlebarsRenderer2.containsHandlebars(template)
|
|
3062
|
+
};
|
|
3063
|
+
return handlebarsRenderer;
|
|
3064
|
+
} catch {
|
|
3065
|
+
return null;
|
|
3066
|
+
}
|
|
3067
|
+
}
|
|
3068
|
+
function containsHandlebars2(template) {
|
|
3069
|
+
return /\{\{(?!!)[\s\S]*?\}\}/.test(template);
|
|
3070
|
+
}
|
|
3071
|
+
var HtmlRenderer = class {
|
|
3072
|
+
type = "html";
|
|
3073
|
+
priority = 0;
|
|
3074
|
+
// Lowest priority - fallback renderer
|
|
3075
|
+
/**
|
|
3076
|
+
* Check if this renderer can handle the given template.
|
|
3077
|
+
*
|
|
3078
|
+
* Accepts:
|
|
3079
|
+
* - Any string (assumed to be HTML, with or without Handlebars)
|
|
3080
|
+
* - Functions that are template builders (not React components)
|
|
3081
|
+
*/
|
|
3082
|
+
canHandle(template) {
|
|
3083
|
+
if (typeof template === "string") {
|
|
3084
|
+
return true;
|
|
3085
|
+
}
|
|
3086
|
+
if (typeof template === "function") {
|
|
3087
|
+
return isTemplateBuilderFunction(template);
|
|
3088
|
+
}
|
|
3089
|
+
return false;
|
|
3090
|
+
}
|
|
3091
|
+
/**
|
|
3092
|
+
* Check if a template uses Handlebars syntax.
|
|
3093
|
+
*
|
|
3094
|
+
* @param template - Template string to check
|
|
3095
|
+
* @returns true if template contains {{...}} syntax
|
|
3096
|
+
*/
|
|
3097
|
+
usesHandlebars(template) {
|
|
3098
|
+
return containsHandlebars2(template);
|
|
3099
|
+
}
|
|
3100
|
+
/**
|
|
3101
|
+
* Transpile the template.
|
|
3102
|
+
*
|
|
3103
|
+
* For HTML templates, no transpilation is needed.
|
|
3104
|
+
* This method returns a dummy result for consistency.
|
|
3105
|
+
*/
|
|
3106
|
+
async transpile(template, _options) {
|
|
3107
|
+
const source = typeof template === "string" ? template : template.toString();
|
|
3108
|
+
const hash = hashString(source);
|
|
3109
|
+
return {
|
|
3110
|
+
code: "",
|
|
3111
|
+
// No transpiled code needed
|
|
3112
|
+
hash,
|
|
3113
|
+
cached: true
|
|
3114
|
+
// Always "cached" since no work is done
|
|
3115
|
+
};
|
|
3116
|
+
}
|
|
3117
|
+
/**
|
|
3118
|
+
* Render the template to HTML string.
|
|
3119
|
+
*
|
|
3120
|
+
* For static strings without Handlebars, returns the string directly.
|
|
3121
|
+
* For strings with Handlebars syntax, processes with HandlebarsRenderer.
|
|
3122
|
+
* For functions, calls the function with the context.
|
|
3123
|
+
*/
|
|
3124
|
+
async render(template, context, _options) {
|
|
3125
|
+
if (typeof template === "string") {
|
|
3126
|
+
if (containsHandlebars2(template)) {
|
|
3127
|
+
return this.renderHandlebars(template, context);
|
|
3128
|
+
}
|
|
3129
|
+
return template;
|
|
3130
|
+
}
|
|
3131
|
+
if (typeof template === "function") {
|
|
3132
|
+
const result = template(context);
|
|
3133
|
+
if (typeof result === "string" && containsHandlebars2(result)) {
|
|
3134
|
+
return this.renderHandlebars(result, context);
|
|
3135
|
+
}
|
|
3136
|
+
return result;
|
|
3137
|
+
}
|
|
3138
|
+
return String(template);
|
|
3139
|
+
}
|
|
3140
|
+
/**
|
|
3141
|
+
* Render Handlebars template with context.
|
|
3142
|
+
*/
|
|
3143
|
+
async renderHandlebars(template, context) {
|
|
3144
|
+
const renderer = await loadHandlebarsRenderer();
|
|
3145
|
+
if (!renderer) {
|
|
3146
|
+
console.warn(
|
|
3147
|
+
"[@frontmcp/ui] Template contains Handlebars syntax but handlebars is not installed. Install it for template interpolation: npm install handlebars"
|
|
3148
|
+
);
|
|
3149
|
+
return template;
|
|
3150
|
+
}
|
|
3151
|
+
return renderer.render(template, {
|
|
3152
|
+
input: context.input,
|
|
3153
|
+
output: context.output,
|
|
3154
|
+
structuredContent: context.structuredContent
|
|
3155
|
+
});
|
|
3156
|
+
}
|
|
3157
|
+
/**
|
|
3158
|
+
* Get runtime scripts for client-side functionality.
|
|
3159
|
+
*
|
|
3160
|
+
* HTML templates don't need additional runtime scripts.
|
|
3161
|
+
*/
|
|
3162
|
+
getRuntimeScripts(_platform) {
|
|
3163
|
+
return {
|
|
3164
|
+
headScripts: "",
|
|
3165
|
+
isInline: false
|
|
3166
|
+
};
|
|
3167
|
+
}
|
|
3168
|
+
};
|
|
3169
|
+
var htmlRenderer = new HtmlRenderer();
|
|
3170
|
+
|
|
3171
|
+
// libs/uipack/src/renderers/registry.ts
|
|
3172
|
+
var RendererRegistry = class {
|
|
3173
|
+
renderers = /* @__PURE__ */ new Map();
|
|
3174
|
+
sortedRenderers = [];
|
|
3175
|
+
defaultRenderer = "html";
|
|
3176
|
+
debug;
|
|
3177
|
+
constructor(options = {}) {
|
|
3178
|
+
this.debug = options.debug ?? false;
|
|
3179
|
+
this.register(htmlRenderer);
|
|
3180
|
+
}
|
|
3181
|
+
/**
|
|
3182
|
+
* Register a renderer.
|
|
3183
|
+
*
|
|
3184
|
+
* Renderers are sorted by priority (highest first) for detection.
|
|
3185
|
+
*
|
|
3186
|
+
* @param renderer - Renderer to register
|
|
3187
|
+
*/
|
|
3188
|
+
register(renderer) {
|
|
3189
|
+
this.renderers.set(renderer.type, renderer);
|
|
3190
|
+
this.updateSortedList();
|
|
3191
|
+
if (this.debug) {
|
|
3192
|
+
console.log(`[RendererRegistry] Registered renderer: ${renderer.type} (priority: ${renderer.priority})`);
|
|
3193
|
+
}
|
|
3194
|
+
}
|
|
3195
|
+
/**
|
|
3196
|
+
* Unregister a renderer.
|
|
3197
|
+
*
|
|
3198
|
+
* @param type - Type of renderer to remove
|
|
3199
|
+
* @returns True if renderer was removed
|
|
3200
|
+
*/
|
|
3201
|
+
unregister(type) {
|
|
3202
|
+
const removed = this.renderers.delete(type);
|
|
3203
|
+
if (removed) {
|
|
3204
|
+
this.updateSortedList();
|
|
3205
|
+
}
|
|
3206
|
+
return removed;
|
|
3207
|
+
}
|
|
3208
|
+
/**
|
|
3209
|
+
* Get a renderer by type.
|
|
3210
|
+
*
|
|
3211
|
+
* @param type - Renderer type
|
|
3212
|
+
* @returns Renderer or undefined if not found
|
|
3213
|
+
*/
|
|
3214
|
+
get(type) {
|
|
3215
|
+
return this.renderers.get(type);
|
|
3216
|
+
}
|
|
3217
|
+
/**
|
|
3218
|
+
* Check if a renderer type is registered.
|
|
3219
|
+
*
|
|
3220
|
+
* @param type - Renderer type
|
|
3221
|
+
* @returns True if registered
|
|
3222
|
+
*/
|
|
3223
|
+
has(type) {
|
|
3224
|
+
return this.renderers.has(type);
|
|
3225
|
+
}
|
|
3226
|
+
/**
|
|
3227
|
+
* Get all registered renderer types.
|
|
3228
|
+
*
|
|
3229
|
+
* @returns Array of renderer types
|
|
3230
|
+
*/
|
|
3231
|
+
getTypes() {
|
|
3232
|
+
return Array.from(this.renderers.keys());
|
|
3233
|
+
}
|
|
3234
|
+
/**
|
|
3235
|
+
* Auto-detect the renderer for a template.
|
|
3236
|
+
*
|
|
3237
|
+
* Checks renderers in priority order (highest first).
|
|
3238
|
+
* Returns HTML renderer as fallback.
|
|
3239
|
+
*
|
|
3240
|
+
* @param template - Template to detect
|
|
3241
|
+
* @returns Detection result with renderer and confidence
|
|
3242
|
+
*/
|
|
3243
|
+
detect(template) {
|
|
3244
|
+
for (const renderer of this.sortedRenderers) {
|
|
3245
|
+
if (renderer.canHandle(template)) {
|
|
3246
|
+
const result = {
|
|
3247
|
+
renderer,
|
|
3248
|
+
confidence: renderer.priority / 100,
|
|
3249
|
+
// Normalize to 0-1
|
|
3250
|
+
reason: `Matched by ${renderer.type} renderer`
|
|
3251
|
+
};
|
|
3252
|
+
if (this.debug) {
|
|
3253
|
+
console.log(`[RendererRegistry] Detected template as ${renderer.type} (confidence: ${result.confidence})`);
|
|
3254
|
+
}
|
|
3255
|
+
return result;
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3258
|
+
const fallback = this.renderers.get(this.defaultRenderer);
|
|
3259
|
+
if (!fallback) {
|
|
3260
|
+
throw new Error(`Default renderer '${this.defaultRenderer}' not found`);
|
|
3261
|
+
}
|
|
3262
|
+
return {
|
|
3263
|
+
renderer: fallback,
|
|
3264
|
+
confidence: 0.5,
|
|
3265
|
+
reason: "Fallback to default HTML renderer"
|
|
3266
|
+
};
|
|
3267
|
+
}
|
|
3268
|
+
/**
|
|
3269
|
+
* Render a template with auto-detection.
|
|
3270
|
+
*
|
|
3271
|
+
* @param template - Template to render (React, MDX, or HTML)
|
|
3272
|
+
* @param context - Template context with input/output
|
|
3273
|
+
* @param options - Render options
|
|
3274
|
+
* @returns Rendered result with HTML and metadata
|
|
3275
|
+
*/
|
|
3276
|
+
async render(template, context, options = {}) {
|
|
3277
|
+
const platform = options.platform ?? OPENAI_PLATFORM;
|
|
3278
|
+
const detection = this.detect(template);
|
|
3279
|
+
const renderer = detection.renderer;
|
|
3280
|
+
if (this.debug) {
|
|
3281
|
+
console.log(`[RendererRegistry] Rendering with ${renderer.type} renderer`);
|
|
3282
|
+
}
|
|
3283
|
+
const transpileResult = await renderer.transpile(template);
|
|
3284
|
+
const html = await renderer.render(template, context, options);
|
|
3285
|
+
const runtimeScripts = renderer.getRuntimeScripts(platform);
|
|
3286
|
+
return {
|
|
3287
|
+
html,
|
|
3288
|
+
rendererType: renderer.type,
|
|
3289
|
+
transpileCached: transpileResult.cached,
|
|
3290
|
+
runtimeScripts
|
|
3291
|
+
};
|
|
3292
|
+
}
|
|
3293
|
+
/**
|
|
3294
|
+
* Render with a specific renderer type.
|
|
3295
|
+
*
|
|
3296
|
+
* @param type - Renderer type to use
|
|
3297
|
+
* @param template - Template to render
|
|
3298
|
+
* @param context - Template context
|
|
3299
|
+
* @param options - Render options
|
|
3300
|
+
* @returns Rendered result
|
|
3301
|
+
*/
|
|
3302
|
+
async renderWith(type, template, context, options = {}) {
|
|
3303
|
+
const renderer = this.renderers.get(type);
|
|
3304
|
+
if (!renderer) {
|
|
3305
|
+
throw new Error(`Renderer '${type}' not registered`);
|
|
3306
|
+
}
|
|
3307
|
+
const platform = options.platform ?? OPENAI_PLATFORM;
|
|
3308
|
+
const transpileResult = await renderer.transpile(template);
|
|
3309
|
+
const html = await renderer.render(template, context, options);
|
|
3310
|
+
const runtimeScripts = renderer.getRuntimeScripts(platform);
|
|
3311
|
+
return {
|
|
3312
|
+
html,
|
|
3313
|
+
rendererType: type,
|
|
3314
|
+
transpileCached: transpileResult.cached,
|
|
3315
|
+
runtimeScripts
|
|
3316
|
+
};
|
|
3317
|
+
}
|
|
3318
|
+
/**
|
|
3319
|
+
* Update the sorted renderer list by priority.
|
|
3320
|
+
*/
|
|
3321
|
+
updateSortedList() {
|
|
3322
|
+
this.sortedRenderers = Array.from(this.renderers.values()).sort((a, b) => b.priority - a.priority);
|
|
3323
|
+
}
|
|
3324
|
+
/**
|
|
3325
|
+
* Set the default renderer type.
|
|
3326
|
+
*
|
|
3327
|
+
* @param type - Renderer type to use as default
|
|
3328
|
+
*/
|
|
3329
|
+
setDefault(type) {
|
|
3330
|
+
if (!this.renderers.has(type)) {
|
|
3331
|
+
throw new Error(`Cannot set default to unregistered renderer '${type}'`);
|
|
3332
|
+
}
|
|
3333
|
+
this.defaultRenderer = type;
|
|
3334
|
+
}
|
|
3335
|
+
/**
|
|
3336
|
+
* Get registry statistics.
|
|
3337
|
+
*/
|
|
3338
|
+
getStats() {
|
|
3339
|
+
return {
|
|
3340
|
+
registeredRenderers: this.getTypes(),
|
|
3341
|
+
defaultRenderer: this.defaultRenderer,
|
|
3342
|
+
priorityOrder: this.sortedRenderers.map((r) => ({
|
|
3343
|
+
type: r.type,
|
|
3344
|
+
priority: r.priority
|
|
3345
|
+
}))
|
|
3346
|
+
};
|
|
3347
|
+
}
|
|
3348
|
+
};
|
|
3349
|
+
var rendererRegistry = new RendererRegistry();
|
|
3350
|
+
|
|
3351
|
+
// libs/uipack/src/tool-template/builder.ts
|
|
3352
|
+
function buildTemplateContext(input, output, structuredContent) {
|
|
3353
|
+
return {
|
|
3354
|
+
input,
|
|
3355
|
+
output,
|
|
3356
|
+
structuredContent,
|
|
3357
|
+
helpers: createTemplateHelpers()
|
|
3358
|
+
};
|
|
3359
|
+
}
|
|
3360
|
+
function executeTemplate(template, ctx) {
|
|
3361
|
+
if (typeof template === "string") {
|
|
3362
|
+
return template;
|
|
3363
|
+
}
|
|
3364
|
+
return template(ctx);
|
|
3365
|
+
}
|
|
3366
|
+
async function renderTemplate2(template, ctx, options) {
|
|
3367
|
+
const detection = detectTemplateType(template);
|
|
3368
|
+
if (detection.type === "html-function" || detection.type === "html-string") {
|
|
3369
|
+
const html = typeof template === "function" ? template(ctx) : template;
|
|
3370
|
+
return { html, rendererType: "html" };
|
|
3371
|
+
}
|
|
3372
|
+
try {
|
|
3373
|
+
const result = await rendererRegistry.render(template, ctx, {
|
|
3374
|
+
hydrate: options?.hydrate,
|
|
3375
|
+
mdxComponents: options?.mdxComponents
|
|
3376
|
+
});
|
|
3377
|
+
return { html: result.html, rendererType: result.rendererType };
|
|
3378
|
+
} catch (error) {
|
|
3379
|
+
console.warn(
|
|
3380
|
+
`[@frontmcp/ui] Renderer failed for ${detection.type}, falling back to HTML:`,
|
|
3381
|
+
error instanceof Error ? error.message : error
|
|
3382
|
+
);
|
|
3383
|
+
if (typeof template === "function") {
|
|
3384
|
+
try {
|
|
3385
|
+
const html = template(ctx);
|
|
3386
|
+
return { html, rendererType: "html-fallback" };
|
|
3387
|
+
} catch {
|
|
3388
|
+
return {
|
|
3389
|
+
html: `<div class="error">Template rendering failed: ${error instanceof Error ? error.message : "Unknown error"}</div>`,
|
|
3390
|
+
rendererType: "error"
|
|
3391
|
+
};
|
|
3392
|
+
}
|
|
3393
|
+
}
|
|
3394
|
+
return { html: String(template), rendererType: "html-fallback" };
|
|
3395
|
+
}
|
|
3396
|
+
}
|
|
3397
|
+
function renderToolTemplate(options) {
|
|
3398
|
+
const { ui, toolName, input, output, structuredContent, theme, platform } = options;
|
|
3399
|
+
const ctx = buildTemplateContext(input, output, structuredContent);
|
|
3400
|
+
const content = executeTemplate(ui.template, ctx);
|
|
3401
|
+
const wrapperOptions = {
|
|
3402
|
+
content,
|
|
3403
|
+
toolName,
|
|
3404
|
+
input,
|
|
3405
|
+
output,
|
|
3406
|
+
structuredContent,
|
|
3407
|
+
csp: ui.csp,
|
|
3408
|
+
widgetAccessible: ui.widgetAccessible,
|
|
3409
|
+
title: `${toolName} Result`,
|
|
3410
|
+
theme,
|
|
3411
|
+
platform
|
|
3412
|
+
};
|
|
3413
|
+
const html = wrapToolUI(wrapperOptions);
|
|
3414
|
+
const openaiMeta = buildOpenAIMetaFromUI(ui);
|
|
3415
|
+
return {
|
|
3416
|
+
html,
|
|
3417
|
+
mimeType: "text/html",
|
|
3418
|
+
openaiMeta: Object.keys(openaiMeta).length > 0 ? openaiMeta : void 0
|
|
3419
|
+
};
|
|
3420
|
+
}
|
|
3421
|
+
async function renderToolTemplateAsync(options) {
|
|
3422
|
+
const { ui, toolName, input, output, structuredContent, theme, platform } = options;
|
|
3423
|
+
const ctx = buildTemplateContext(input, output, structuredContent);
|
|
3424
|
+
const { html: content, rendererType } = await renderTemplate2(ui.template, ctx, {
|
|
3425
|
+
hydrate: ui.hydrate,
|
|
3426
|
+
mdxComponents: ui.mdxComponents
|
|
3427
|
+
});
|
|
3428
|
+
const wrappedContent = ui.wrapper ? ui.wrapper(content, ctx) : content;
|
|
3429
|
+
const wrapperOptions = {
|
|
3430
|
+
content: wrappedContent,
|
|
3431
|
+
toolName,
|
|
3432
|
+
input,
|
|
3433
|
+
output,
|
|
3434
|
+
structuredContent,
|
|
3435
|
+
csp: ui.csp,
|
|
3436
|
+
widgetAccessible: ui.widgetAccessible,
|
|
3437
|
+
title: `${toolName} Result`,
|
|
3438
|
+
theme,
|
|
3439
|
+
platform,
|
|
3440
|
+
// Pass renderer type for framework runtime injection
|
|
3441
|
+
rendererType,
|
|
3442
|
+
hydrate: ui.hydrate
|
|
3443
|
+
};
|
|
3444
|
+
const html = wrapToolUI(wrapperOptions);
|
|
3445
|
+
const openaiMeta = buildOpenAIMetaFromUI(ui);
|
|
3446
|
+
return {
|
|
3447
|
+
html,
|
|
3448
|
+
mimeType: "text/html",
|
|
3449
|
+
rendererType,
|
|
3450
|
+
openaiMeta: Object.keys(openaiMeta).length > 0 ? openaiMeta : void 0
|
|
3451
|
+
};
|
|
3452
|
+
}
|
|
3453
|
+
function buildOpenAIMetaFromUI(ui) {
|
|
3454
|
+
const meta = {};
|
|
3455
|
+
if (ui.widgetAccessible) {
|
|
3456
|
+
meta["openai/widgetAccessible"] = true;
|
|
3457
|
+
}
|
|
3458
|
+
if (ui.widgetDescription) {
|
|
3459
|
+
meta["openai/widgetDescription"] = ui.widgetDescription;
|
|
3460
|
+
}
|
|
3461
|
+
if (ui.csp) {
|
|
3462
|
+
const cspConfig = {};
|
|
3463
|
+
if (ui.csp.connectDomains?.length) {
|
|
3464
|
+
cspConfig["connect_domains"] = ui.csp.connectDomains;
|
|
3465
|
+
}
|
|
3466
|
+
if (ui.csp.resourceDomains?.length) {
|
|
3467
|
+
cspConfig["resource_domains"] = ui.csp.resourceDomains;
|
|
3468
|
+
}
|
|
3469
|
+
if (Object.keys(cspConfig).length > 0) {
|
|
3470
|
+
meta["openai/widgetCSP"] = cspConfig;
|
|
3471
|
+
}
|
|
3472
|
+
}
|
|
3473
|
+
if (ui.displayMode && ui.displayMode !== "inline") {
|
|
3474
|
+
meta["openai/displayMode"] = ui.displayMode;
|
|
3475
|
+
}
|
|
3476
|
+
return meta;
|
|
3477
|
+
}
|
|
3478
|
+
function createTemplate(builder) {
|
|
3479
|
+
return builder;
|
|
3480
|
+
}
|
|
3481
|
+
function createToolUI(config) {
|
|
3482
|
+
return config;
|
|
3483
|
+
}
|
|
3484
|
+
function container(content, className = "") {
|
|
3485
|
+
const baseClasses = "p-4 rounded-lg bg-surface";
|
|
3486
|
+
return `<div class="${baseClasses} ${className}">${content}</div>`;
|
|
3487
|
+
}
|
|
3488
|
+
function heading(text, level = 2) {
|
|
3489
|
+
const tag = `h${level}`;
|
|
3490
|
+
const classes = {
|
|
3491
|
+
1: "text-2xl font-bold text-text-primary mb-4",
|
|
3492
|
+
2: "text-xl font-semibold text-text-primary mb-3",
|
|
3493
|
+
3: "text-lg font-medium text-text-primary mb-2",
|
|
3494
|
+
4: "text-base font-medium text-text-secondary mb-2"
|
|
3495
|
+
}[level] || "";
|
|
3496
|
+
return `<${tag} class="${classes}">${escapeHtml(text)}</${tag}>`;
|
|
3497
|
+
}
|
|
3498
|
+
function paragraph(text, className = "") {
|
|
3499
|
+
return `<p class="text-text-secondary ${className}">${escapeHtml(text)}</p>`;
|
|
3500
|
+
}
|
|
3501
|
+
function keyValue(key, value, className = "") {
|
|
3502
|
+
const displayValue = typeof value === "boolean" ? value ? "Yes" : "No" : String(value);
|
|
3503
|
+
return `<div class="flex justify-between items-center py-2 border-b border-border ${className}">
|
|
3504
|
+
<span class="text-text-secondary">${escapeHtml(key)}</span>
|
|
3505
|
+
<span class="text-text-primary font-medium">${escapeHtml(displayValue)}</span>
|
|
3506
|
+
</div>`;
|
|
3507
|
+
}
|
|
3508
|
+
function dataList(items) {
|
|
3509
|
+
const listItems = items.map((item) => keyValue(item.key, item.value)).join("");
|
|
3510
|
+
return `<div class="space-y-1">${listItems}</div>`;
|
|
3511
|
+
}
|
|
3512
|
+
function errorDisplay(message, details) {
|
|
3513
|
+
return `<div class="p-4 bg-danger/10 border border-danger rounded-lg">
|
|
3514
|
+
<div class="flex items-center gap-2 text-danger">
|
|
3515
|
+
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
|
3516
|
+
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
|
|
3517
|
+
</svg>
|
|
3518
|
+
<span class="font-medium">${escapeHtml(message)}</span>
|
|
3519
|
+
</div>
|
|
3520
|
+
${details ? `<p class="mt-2 text-sm text-text-secondary">${escapeHtml(details)}</p>` : ""}
|
|
3521
|
+
</div>`;
|
|
3522
|
+
}
|
|
3523
|
+
function successDisplay(message, details) {
|
|
3524
|
+
return `<div class="p-4 bg-success/10 border border-success rounded-lg">
|
|
3525
|
+
<div class="flex items-center gap-2 text-success">
|
|
3526
|
+
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
|
3527
|
+
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
|
|
3528
|
+
</svg>
|
|
3529
|
+
<span class="font-medium">${escapeHtml(message)}</span>
|
|
3530
|
+
</div>
|
|
3531
|
+
${details ? `<p class="mt-2 text-sm text-text-secondary">${escapeHtml(details)}</p>` : ""}
|
|
3532
|
+
</div>`;
|
|
3533
|
+
}
|
|
3534
|
+
function loadingDisplay(message = "Loading...") {
|
|
3535
|
+
return `<div class="flex items-center justify-center p-8">
|
|
3536
|
+
<div class="flex items-center gap-3 text-text-secondary">
|
|
3537
|
+
<svg class="animate-spin w-5 h-5" fill="none" viewBox="0 0 24 24">
|
|
3538
|
+
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
|
3539
|
+
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
3540
|
+
</svg>
|
|
3541
|
+
<span>${escapeHtml(message)}</span>
|
|
3542
|
+
</div>
|
|
3543
|
+
</div>`;
|
|
3544
|
+
}
|
|
3545
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
3546
|
+
0 && (module.exports = {
|
|
3547
|
+
buildTemplateContext,
|
|
3548
|
+
container,
|
|
3549
|
+
createTemplate,
|
|
3550
|
+
createToolUI,
|
|
3551
|
+
dataList,
|
|
3552
|
+
errorDisplay,
|
|
3553
|
+
executeTemplate,
|
|
3554
|
+
heading,
|
|
3555
|
+
keyValue,
|
|
3556
|
+
loadingDisplay,
|
|
3557
|
+
paragraph,
|
|
3558
|
+
renderTemplate,
|
|
3559
|
+
renderToolTemplate,
|
|
3560
|
+
renderToolTemplateAsync,
|
|
3561
|
+
successDisplay
|
|
3562
|
+
});
|