@frontmcp/ui 0.6.0 → 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/README.md +140 -362
- package/bridge/runtime/index.d.ts +2 -1
- package/bridge/runtime/index.d.ts.map +1 -1
- package/bundler/file-cache/component-builder.d.ts +1 -1
- package/bundler/file-cache/component-builder.d.ts.map +1 -1
- package/bundler/file-cache/hash-calculator.d.ts +1 -1
- package/bundler/file-cache/hash-calculator.d.ts.map +1 -1
- package/bundler/file-cache/storage/filesystem.d.ts +1 -1
- package/bundler/file-cache/storage/filesystem.d.ts.map +1 -1
- package/bundler/file-cache/storage/interface.d.ts +1 -1
- package/bundler/file-cache/storage/interface.d.ts.map +1 -1
- package/bundler/file-cache/storage/redis.d.ts +1 -1
- package/bundler/file-cache/storage/redis.d.ts.map +1 -1
- package/bundler/index.js +10 -1057
- package/components/alert.schema.d.ts +6 -6
- package/components/avatar.schema.d.ts +9 -9
- package/components/badge.schema.d.ts +9 -9
- package/components/button.schema.d.ts +9 -9
- package/components/card.schema.d.ts +7 -7
- package/components/form.schema.d.ts +24 -24
- package/components/index.js +128 -198
- package/components/modal.schema.d.ts +8 -8
- package/components/table.schema.d.ts +6 -6
- package/esm/bridge/runtime/index.d.ts +2 -1
- package/esm/bridge/runtime/index.d.ts.map +1 -1
- package/esm/bundler/file-cache/component-builder.d.ts +1 -1
- package/esm/bundler/file-cache/component-builder.d.ts.map +1 -1
- package/esm/bundler/file-cache/hash-calculator.d.ts +1 -1
- package/esm/bundler/file-cache/hash-calculator.d.ts.map +1 -1
- package/esm/bundler/file-cache/storage/filesystem.d.ts +1 -1
- package/esm/bundler/file-cache/storage/filesystem.d.ts.map +1 -1
- package/esm/bundler/file-cache/storage/interface.d.ts +1 -1
- package/esm/bundler/file-cache/storage/interface.d.ts.map +1 -1
- package/esm/bundler/file-cache/storage/redis.d.ts +1 -1
- package/esm/bundler/file-cache/storage/redis.d.ts.map +1 -1
- package/esm/bundler/index.js +3 -1050
- package/esm/components/alert.schema.d.ts +6 -6
- package/esm/components/avatar.schema.d.ts +9 -9
- package/esm/components/badge.schema.d.ts +9 -9
- package/esm/components/button.schema.d.ts +9 -9
- package/esm/components/card.schema.d.ts +7 -7
- package/esm/components/form.schema.d.ts +24 -24
- package/esm/components/index.js +136 -196
- package/esm/components/modal.schema.d.ts +8 -8
- package/esm/components/table.schema.d.ts +6 -6
- package/esm/index.d.ts +23 -39
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +4256 -16441
- package/esm/layouts/base.d.ts +2 -2
- package/esm/layouts/base.d.ts.map +1 -1
- package/esm/layouts/index.js +33 -516
- package/esm/package.json +8 -26
- package/esm/pages/index.js +100 -627
- package/esm/react/Alert.d.ts +1 -2
- package/esm/react/Alert.d.ts.map +1 -1
- package/esm/react/Badge.d.ts +1 -2
- package/esm/react/Badge.d.ts.map +1 -1
- package/esm/react/Button.d.ts +1 -2
- package/esm/react/Button.d.ts.map +1 -1
- package/esm/react/Card.d.ts +1 -2
- package/esm/react/Card.d.ts.map +1 -1
- package/esm/react/hooks/context.d.ts +1 -1
- package/esm/react/hooks/context.d.ts.map +1 -1
- package/esm/react/index.d.ts +5 -6
- package/esm/react/index.d.ts.map +1 -1
- package/esm/react/index.js +2074 -322
- package/esm/react/types.d.ts +1 -2
- package/esm/react/types.d.ts.map +1 -1
- package/esm/renderers/index.d.ts +10 -25
- package/esm/renderers/index.d.ts.map +1 -1
- package/esm/renderers/index.js +171 -1617
- package/esm/{runtime/adapters → renderers}/react.adapter.d.ts +2 -2
- package/esm/renderers/react.adapter.d.ts.map +1 -0
- package/esm/renderers/react.renderer.d.ts +3 -3
- package/esm/renderers/react.renderer.d.ts.map +1 -1
- package/esm/universal/index.js +1755 -0
- package/esm/web-components/index.js +232 -287
- package/esm/widgets/index.js +89 -147
- package/index.d.ts +23 -39
- package/index.d.ts.map +1 -1
- package/index.js +6123 -18539
- package/layouts/base.d.ts +2 -2
- package/layouts/base.d.ts.map +1 -1
- package/layouts/index.js +43 -536
- package/package.json +8 -26
- package/pages/index.js +111 -648
- package/react/Alert.d.ts +1 -2
- package/react/Alert.d.ts.map +1 -1
- package/react/Badge.d.ts +1 -2
- package/react/Badge.d.ts.map +1 -1
- package/react/Button.d.ts +1 -2
- package/react/Button.d.ts.map +1 -1
- package/react/Card.d.ts +1 -2
- package/react/Card.d.ts.map +1 -1
- package/react/hooks/context.d.ts +1 -1
- package/react/hooks/context.d.ts.map +1 -1
- package/react/index.d.ts +5 -6
- package/react/index.d.ts.map +1 -1
- package/react/index.js +2065 -335
- package/react/types.d.ts +1 -2
- package/react/types.d.ts.map +1 -1
- package/renderers/index.d.ts +10 -25
- package/renderers/index.d.ts.map +1 -1
- package/renderers/index.js +175 -1641
- package/{runtime/adapters → renderers}/react.adapter.d.ts +2 -2
- package/renderers/react.adapter.d.ts.map +1 -0
- package/renderers/react.renderer.d.ts +3 -3
- package/renderers/react.renderer.d.ts.map +1 -1
- package/universal/index.js +1841 -0
- package/web-components/index.js +224 -289
- package/widgets/index.js +80 -148
- package/adapters/index.d.ts +0 -13
- package/adapters/index.d.ts.map +0 -1
- package/adapters/index.js +0 -462
- package/adapters/platform-meta.d.ts +0 -166
- package/adapters/platform-meta.d.ts.map +0 -1
- package/adapters/response-builder.d.ts +0 -108
- package/adapters/response-builder.d.ts.map +0 -1
- package/adapters/serving-mode.d.ts +0 -107
- package/adapters/serving-mode.d.ts.map +0 -1
- package/base-template/bridge.d.ts +0 -90
- package/base-template/bridge.d.ts.map +0 -1
- package/base-template/default-base-template.d.ts +0 -92
- package/base-template/default-base-template.d.ts.map +0 -1
- package/base-template/index.d.ts +0 -15
- package/base-template/index.d.ts.map +0 -1
- package/base-template/index.js +0 -1398
- package/base-template/polyfills.d.ts +0 -31
- package/base-template/polyfills.d.ts.map +0 -1
- package/base-template/theme-styles.d.ts +0 -74
- package/base-template/theme-styles.d.ts.map +0 -1
- package/build/cdn-resources.d.ts +0 -243
- package/build/cdn-resources.d.ts.map +0 -1
- package/build/index.d.ts +0 -295
- package/build/index.d.ts.map +0 -1
- package/build/index.js +0 -7096
- package/build/widget-manifest.d.ts +0 -362
- package/build/widget-manifest.d.ts.map +0 -1
- package/dependency/cdn-registry.d.ts +0 -98
- package/dependency/cdn-registry.d.ts.map +0 -1
- package/dependency/import-map.d.ts +0 -186
- package/dependency/import-map.d.ts.map +0 -1
- package/dependency/import-parser.d.ts +0 -82
- package/dependency/import-parser.d.ts.map +0 -1
- package/dependency/index.d.ts +0 -17
- package/dependency/index.d.ts.map +0 -1
- package/dependency/resolver.d.ts +0 -164
- package/dependency/resolver.d.ts.map +0 -1
- package/dependency/schemas.d.ts +0 -486
- package/dependency/schemas.d.ts.map +0 -1
- package/dependency/template-loader.d.ts +0 -204
- package/dependency/template-loader.d.ts.map +0 -1
- package/dependency/template-processor.d.ts +0 -118
- package/dependency/template-processor.d.ts.map +0 -1
- package/dependency/types.d.ts +0 -739
- package/dependency/types.d.ts.map +0 -1
- package/esm/adapters/index.d.ts +0 -13
- package/esm/adapters/index.d.ts.map +0 -1
- package/esm/adapters/index.js +0 -427
- package/esm/adapters/platform-meta.d.ts +0 -166
- package/esm/adapters/platform-meta.d.ts.map +0 -1
- package/esm/adapters/response-builder.d.ts +0 -108
- package/esm/adapters/response-builder.d.ts.map +0 -1
- package/esm/adapters/serving-mode.d.ts +0 -107
- package/esm/adapters/serving-mode.d.ts.map +0 -1
- package/esm/base-template/bridge.d.ts +0 -90
- package/esm/base-template/bridge.d.ts.map +0 -1
- package/esm/base-template/default-base-template.d.ts +0 -92
- package/esm/base-template/default-base-template.d.ts.map +0 -1
- package/esm/base-template/index.d.ts +0 -15
- package/esm/base-template/index.d.ts.map +0 -1
- package/esm/base-template/index.js +0 -1364
- package/esm/base-template/polyfills.d.ts +0 -31
- package/esm/base-template/polyfills.d.ts.map +0 -1
- package/esm/base-template/theme-styles.d.ts +0 -74
- package/esm/base-template/theme-styles.d.ts.map +0 -1
- package/esm/build/cdn-resources.d.ts +0 -243
- package/esm/build/cdn-resources.d.ts.map +0 -1
- package/esm/build/index.d.ts +0 -295
- package/esm/build/index.d.ts.map +0 -1
- package/esm/build/index.js +0 -7021
- package/esm/build/widget-manifest.d.ts +0 -362
- package/esm/build/widget-manifest.d.ts.map +0 -1
- package/esm/dependency/cdn-registry.d.ts +0 -98
- package/esm/dependency/cdn-registry.d.ts.map +0 -1
- package/esm/dependency/import-map.d.ts +0 -186
- package/esm/dependency/import-map.d.ts.map +0 -1
- package/esm/dependency/import-parser.d.ts +0 -82
- package/esm/dependency/import-parser.d.ts.map +0 -1
- package/esm/dependency/index.d.ts +0 -17
- package/esm/dependency/index.d.ts.map +0 -1
- package/esm/dependency/resolver.d.ts +0 -164
- package/esm/dependency/resolver.d.ts.map +0 -1
- package/esm/dependency/schemas.d.ts +0 -486
- package/esm/dependency/schemas.d.ts.map +0 -1
- package/esm/dependency/template-loader.d.ts +0 -204
- package/esm/dependency/template-loader.d.ts.map +0 -1
- package/esm/dependency/template-processor.d.ts +0 -118
- package/esm/dependency/template-processor.d.ts.map +0 -1
- package/esm/dependency/types.d.ts +0 -739
- package/esm/dependency/types.d.ts.map +0 -1
- package/esm/handlebars/expression-extractor.d.ts +0 -147
- package/esm/handlebars/expression-extractor.d.ts.map +0 -1
- package/esm/handlebars/helpers.d.ts +0 -339
- package/esm/handlebars/helpers.d.ts.map +0 -1
- package/esm/handlebars/index.d.ts +0 -195
- package/esm/handlebars/index.d.ts.map +0 -1
- package/esm/handlebars/index.js +0 -587
- package/esm/registry/index.d.ts +0 -46
- package/esm/registry/index.d.ts.map +0 -1
- package/esm/registry/index.js +0 -6422
- package/esm/registry/render-template.d.ts +0 -91
- package/esm/registry/render-template.d.ts.map +0 -1
- package/esm/registry/tool-ui.registry.d.ts +0 -294
- package/esm/registry/tool-ui.registry.d.ts.map +0 -1
- package/esm/registry/uri-utils.d.ts +0 -56
- package/esm/registry/uri-utils.d.ts.map +0 -1
- package/esm/renderers/cache.d.ts +0 -145
- package/esm/renderers/cache.d.ts.map +0 -1
- package/esm/renderers/html.renderer.d.ts +0 -123
- package/esm/renderers/html.renderer.d.ts.map +0 -1
- package/esm/renderers/mdx.renderer.d.ts +0 -120
- package/esm/renderers/mdx.renderer.d.ts.map +0 -1
- package/esm/renderers/registry.d.ts +0 -134
- package/esm/renderers/registry.d.ts.map +0 -1
- package/esm/renderers/types.d.ts +0 -342
- package/esm/renderers/types.d.ts.map +0 -1
- package/esm/renderers/utils/detect.d.ts +0 -107
- package/esm/renderers/utils/detect.d.ts.map +0 -1
- package/esm/renderers/utils/hash.d.ts +0 -40
- package/esm/renderers/utils/hash.d.ts.map +0 -1
- package/esm/renderers/utils/index.d.ts +0 -9
- package/esm/renderers/utils/index.d.ts.map +0 -1
- package/esm/renderers/utils/transpiler.d.ts +0 -89
- package/esm/renderers/utils/transpiler.d.ts.map +0 -1
- package/esm/runtime/adapters/html.adapter.d.ts +0 -59
- package/esm/runtime/adapters/html.adapter.d.ts.map +0 -1
- package/esm/runtime/adapters/index.d.ts +0 -26
- package/esm/runtime/adapters/index.d.ts.map +0 -1
- package/esm/runtime/adapters/mdx.adapter.d.ts +0 -73
- package/esm/runtime/adapters/mdx.adapter.d.ts.map +0 -1
- package/esm/runtime/adapters/react.adapter.d.ts.map +0 -1
- package/esm/runtime/adapters/types.d.ts +0 -95
- package/esm/runtime/adapters/types.d.ts.map +0 -1
- package/esm/runtime/csp.d.ts +0 -48
- package/esm/runtime/csp.d.ts.map +0 -1
- package/esm/runtime/index.d.ts +0 -17
- package/esm/runtime/index.d.ts.map +0 -1
- package/esm/runtime/index.js +0 -5186
- package/esm/runtime/mcp-bridge.d.ts +0 -101
- package/esm/runtime/mcp-bridge.d.ts.map +0 -1
- package/esm/runtime/renderer-runtime.d.ts +0 -133
- package/esm/runtime/renderer-runtime.d.ts.map +0 -1
- package/esm/runtime/sanitizer.d.ts +0 -172
- package/esm/runtime/sanitizer.d.ts.map +0 -1
- package/esm/runtime/types.d.ts +0 -415
- package/esm/runtime/types.d.ts.map +0 -1
- package/esm/runtime/wrapper.d.ts +0 -421
- package/esm/runtime/wrapper.d.ts.map +0 -1
- package/esm/styles/index.d.ts +0 -8
- package/esm/styles/index.d.ts.map +0 -1
- package/esm/styles/index.js +0 -171
- package/esm/styles/variants.d.ts +0 -51
- package/esm/styles/variants.d.ts.map +0 -1
- package/esm/theme/cdn.d.ts +0 -195
- package/esm/theme/cdn.d.ts.map +0 -1
- package/esm/theme/index.d.ts +0 -18
- package/esm/theme/index.d.ts.map +0 -1
- package/esm/theme/index.js +0 -700
- package/esm/theme/platforms.d.ts +0 -107
- package/esm/theme/platforms.d.ts.map +0 -1
- package/esm/theme/presets/github-openai.d.ts +0 -50
- package/esm/theme/presets/github-openai.d.ts.map +0 -1
- package/esm/theme/presets/index.d.ts +0 -11
- package/esm/theme/presets/index.d.ts.map +0 -1
- package/esm/theme/theme.d.ts +0 -396
- package/esm/theme/theme.d.ts.map +0 -1
- package/esm/tool-template/builder.d.ts +0 -213
- package/esm/tool-template/builder.d.ts.map +0 -1
- package/esm/tool-template/index.d.ts +0 -16
- package/esm/tool-template/index.d.ts.map +0 -1
- package/esm/tool-template/index.js +0 -3515
- package/esm/types/index.d.ts +0 -14
- package/esm/types/index.d.ts.map +0 -1
- package/esm/types/index.js +0 -75
- package/esm/types/ui-config.d.ts +0 -639
- package/esm/types/ui-config.d.ts.map +0 -1
- package/esm/types/ui-runtime.d.ts +0 -1007
- package/esm/types/ui-runtime.d.ts.map +0 -1
- package/esm/typings/cache/cache-adapter.d.ts +0 -125
- package/esm/typings/cache/cache-adapter.d.ts.map +0 -1
- package/esm/typings/cache/index.d.ts +0 -10
- package/esm/typings/cache/index.d.ts.map +0 -1
- package/esm/typings/cache/memory-cache.d.ts +0 -92
- package/esm/typings/cache/memory-cache.d.ts.map +0 -1
- package/esm/typings/dts-parser.d.ts +0 -90
- package/esm/typings/dts-parser.d.ts.map +0 -1
- package/esm/typings/index.d.ts +0 -48
- package/esm/typings/index.d.ts.map +0 -1
- package/esm/typings/schemas.d.ts +0 -232
- package/esm/typings/schemas.d.ts.map +0 -1
- package/esm/typings/type-fetcher.d.ts +0 -89
- package/esm/typings/type-fetcher.d.ts.map +0 -1
- package/esm/typings/types.d.ts +0 -320
- package/esm/typings/types.d.ts.map +0 -1
- package/esm/utils/escape-html.d.ts +0 -58
- package/esm/utils/escape-html.d.ts.map +0 -1
- package/esm/utils/index.d.ts +0 -10
- package/esm/utils/index.d.ts.map +0 -1
- package/esm/utils/index.js +0 -40
- package/esm/utils/safe-stringify.d.ts +0 -30
- package/esm/utils/safe-stringify.d.ts.map +0 -1
- package/esm/validation/error-box.d.ts +0 -56
- package/esm/validation/error-box.d.ts.map +0 -1
- package/esm/validation/index.d.ts +0 -13
- package/esm/validation/index.d.ts.map +0 -1
- package/esm/validation/index.js +0 -562
- package/esm/validation/schema-paths.d.ts +0 -118
- package/esm/validation/schema-paths.d.ts.map +0 -1
- package/esm/validation/template-validator.d.ts +0 -143
- package/esm/validation/template-validator.d.ts.map +0 -1
- package/esm/validation/wrapper.d.ts +0 -97
- package/esm/validation/wrapper.d.ts.map +0 -1
- package/handlebars/expression-extractor.d.ts +0 -147
- package/handlebars/expression-extractor.d.ts.map +0 -1
- package/handlebars/helpers.d.ts +0 -339
- package/handlebars/helpers.d.ts.map +0 -1
- package/handlebars/index.d.ts +0 -195
- package/handlebars/index.d.ts.map +0 -1
- package/handlebars/index.js +0 -666
- package/registry/index.d.ts +0 -46
- package/registry/index.d.ts.map +0 -1
- package/registry/index.js +0 -6465
- package/registry/render-template.d.ts +0 -91
- package/registry/render-template.d.ts.map +0 -1
- package/registry/tool-ui.registry.d.ts +0 -294
- package/registry/tool-ui.registry.d.ts.map +0 -1
- package/registry/uri-utils.d.ts +0 -56
- package/registry/uri-utils.d.ts.map +0 -1
- package/renderers/cache.d.ts +0 -145
- package/renderers/cache.d.ts.map +0 -1
- package/renderers/html.renderer.d.ts +0 -123
- package/renderers/html.renderer.d.ts.map +0 -1
- package/renderers/mdx.renderer.d.ts +0 -120
- package/renderers/mdx.renderer.d.ts.map +0 -1
- package/renderers/registry.d.ts +0 -134
- package/renderers/registry.d.ts.map +0 -1
- package/renderers/types.d.ts +0 -342
- package/renderers/types.d.ts.map +0 -1
- package/renderers/utils/detect.d.ts +0 -107
- package/renderers/utils/detect.d.ts.map +0 -1
- package/renderers/utils/hash.d.ts +0 -40
- package/renderers/utils/hash.d.ts.map +0 -1
- package/renderers/utils/index.d.ts +0 -9
- package/renderers/utils/index.d.ts.map +0 -1
- package/renderers/utils/transpiler.d.ts +0 -89
- package/renderers/utils/transpiler.d.ts.map +0 -1
- package/runtime/adapters/html.adapter.d.ts +0 -59
- package/runtime/adapters/html.adapter.d.ts.map +0 -1
- package/runtime/adapters/index.d.ts +0 -26
- package/runtime/adapters/index.d.ts.map +0 -1
- package/runtime/adapters/mdx.adapter.d.ts +0 -73
- package/runtime/adapters/mdx.adapter.d.ts.map +0 -1
- package/runtime/adapters/react.adapter.d.ts.map +0 -1
- package/runtime/adapters/types.d.ts +0 -95
- package/runtime/adapters/types.d.ts.map +0 -1
- package/runtime/csp.d.ts +0 -48
- package/runtime/csp.d.ts.map +0 -1
- package/runtime/index.d.ts +0 -17
- package/runtime/index.d.ts.map +0 -1
- package/runtime/index.js +0 -5264
- package/runtime/mcp-bridge.d.ts +0 -101
- package/runtime/mcp-bridge.d.ts.map +0 -1
- package/runtime/renderer-runtime.d.ts +0 -133
- package/runtime/renderer-runtime.d.ts.map +0 -1
- package/runtime/sanitizer.d.ts +0 -172
- package/runtime/sanitizer.d.ts.map +0 -1
- package/runtime/types.d.ts +0 -415
- package/runtime/types.d.ts.map +0 -1
- package/runtime/wrapper.d.ts +0 -421
- package/runtime/wrapper.d.ts.map +0 -1
- package/styles/index.d.ts +0 -8
- package/styles/index.d.ts.map +0 -1
- package/styles/index.js +0 -222
- package/styles/variants.d.ts +0 -51
- package/styles/variants.d.ts.map +0 -1
- package/theme/cdn.d.ts +0 -195
- package/theme/cdn.d.ts.map +0 -1
- package/theme/index.d.ts +0 -18
- package/theme/index.d.ts.map +0 -1
- package/theme/index.js +0 -757
- package/theme/platforms.d.ts +0 -107
- package/theme/platforms.d.ts.map +0 -1
- package/theme/presets/github-openai.d.ts +0 -50
- package/theme/presets/github-openai.d.ts.map +0 -1
- package/theme/presets/index.d.ts +0 -11
- package/theme/presets/index.d.ts.map +0 -1
- package/theme/theme.d.ts +0 -396
- package/theme/theme.d.ts.map +0 -1
- package/tool-template/builder.d.ts +0 -213
- package/tool-template/builder.d.ts.map +0 -1
- package/tool-template/index.d.ts +0 -16
- package/tool-template/index.d.ts.map +0 -1
- package/tool-template/index.js +0 -3559
- package/types/index.d.ts +0 -14
- package/types/index.d.ts.map +0 -1
- package/types/index.js +0 -108
- package/types/ui-config.d.ts +0 -639
- package/types/ui-config.d.ts.map +0 -1
- package/types/ui-runtime.d.ts +0 -1007
- package/types/ui-runtime.d.ts.map +0 -1
- package/typings/cache/cache-adapter.d.ts +0 -125
- package/typings/cache/cache-adapter.d.ts.map +0 -1
- package/typings/cache/index.d.ts +0 -10
- package/typings/cache/index.d.ts.map +0 -1
- package/typings/cache/memory-cache.d.ts +0 -92
- package/typings/cache/memory-cache.d.ts.map +0 -1
- package/typings/dts-parser.d.ts +0 -90
- package/typings/dts-parser.d.ts.map +0 -1
- package/typings/index.d.ts +0 -48
- package/typings/index.d.ts.map +0 -1
- package/typings/schemas.d.ts +0 -232
- package/typings/schemas.d.ts.map +0 -1
- package/typings/type-fetcher.d.ts +0 -89
- package/typings/type-fetcher.d.ts.map +0 -1
- package/typings/types.d.ts +0 -320
- package/typings/types.d.ts.map +0 -1
- package/utils/escape-html.d.ts +0 -58
- package/utils/escape-html.d.ts.map +0 -1
- package/utils/index.d.ts +0 -10
- package/utils/index.d.ts.map +0 -1
- package/utils/index.js +0 -70
- package/utils/safe-stringify.d.ts +0 -30
- package/utils/safe-stringify.d.ts.map +0 -1
- package/validation/error-box.d.ts +0 -56
- package/validation/error-box.d.ts.map +0 -1
- package/validation/index.d.ts +0 -13
- package/validation/index.d.ts.map +0 -1
- package/validation/index.js +0 -603
- package/validation/schema-paths.d.ts +0 -118
- package/validation/schema-paths.d.ts.map +0 -1
- package/validation/template-validator.d.ts +0 -143
- package/validation/template-validator.d.ts.map +0 -1
- package/validation/wrapper.d.ts +0 -97
- package/validation/wrapper.d.ts.map +0 -1
package/react/index.js
CHANGED
|
@@ -62,150 +62,8 @@ __export(react_exports, {
|
|
|
62
62
|
});
|
|
63
63
|
module.exports = __toCommonJS(react_exports);
|
|
64
64
|
|
|
65
|
-
// libs/ui/src/
|
|
66
|
-
var
|
|
67
|
-
default: "bg-white border border-border rounded-xl shadow-sm",
|
|
68
|
-
outlined: "bg-transparent border-2 border-border rounded-xl",
|
|
69
|
-
elevated: "bg-white rounded-xl shadow-lg",
|
|
70
|
-
filled: "bg-gray-50 rounded-xl",
|
|
71
|
-
ghost: "bg-transparent"
|
|
72
|
-
};
|
|
73
|
-
var CARD_SIZES = {
|
|
74
|
-
sm: "p-4",
|
|
75
|
-
md: "p-6",
|
|
76
|
-
lg: "p-8"
|
|
77
|
-
};
|
|
78
|
-
function getCardVariantClasses(variant) {
|
|
79
|
-
return CARD_VARIANTS[variant];
|
|
80
|
-
}
|
|
81
|
-
function getCardSizeClasses(size) {
|
|
82
|
-
return CARD_SIZES[size];
|
|
83
|
-
}
|
|
84
|
-
var BADGE_VARIANTS = {
|
|
85
|
-
default: "bg-gray-100 text-gray-800",
|
|
86
|
-
primary: "bg-primary/10 text-primary",
|
|
87
|
-
secondary: "bg-secondary/10 text-secondary",
|
|
88
|
-
success: "bg-success/10 text-success",
|
|
89
|
-
warning: "bg-warning/10 text-warning",
|
|
90
|
-
danger: "bg-danger/10 text-danger",
|
|
91
|
-
info: "bg-blue-100 text-blue-800",
|
|
92
|
-
outline: "border border-border text-text-primary bg-transparent"
|
|
93
|
-
};
|
|
94
|
-
var BADGE_SIZES = {
|
|
95
|
-
sm: "px-2 py-0.5 text-xs",
|
|
96
|
-
md: "px-2.5 py-1 text-xs",
|
|
97
|
-
lg: "px-3 py-1.5 text-sm"
|
|
98
|
-
};
|
|
99
|
-
var BADGE_DOT_SIZES = {
|
|
100
|
-
sm: "w-2 h-2",
|
|
101
|
-
md: "w-2.5 h-2.5",
|
|
102
|
-
lg: "w-3 h-3"
|
|
103
|
-
};
|
|
104
|
-
var BADGE_DOT_VARIANTS = {
|
|
105
|
-
default: "bg-gray-400",
|
|
106
|
-
primary: "bg-primary",
|
|
107
|
-
secondary: "bg-secondary",
|
|
108
|
-
success: "bg-success",
|
|
109
|
-
warning: "bg-warning",
|
|
110
|
-
danger: "bg-danger",
|
|
111
|
-
info: "bg-blue-500",
|
|
112
|
-
outline: "border border-current"
|
|
113
|
-
};
|
|
114
|
-
function getBadgeVariantClasses(variant) {
|
|
115
|
-
return BADGE_VARIANTS[variant];
|
|
116
|
-
}
|
|
117
|
-
function getBadgeSizeClasses(size) {
|
|
118
|
-
return BADGE_SIZES[size];
|
|
119
|
-
}
|
|
120
|
-
function getBadgeDotSizeClasses(size) {
|
|
121
|
-
return BADGE_DOT_SIZES[size];
|
|
122
|
-
}
|
|
123
|
-
function getBadgeDotVariantClasses(variant) {
|
|
124
|
-
return BADGE_DOT_VARIANTS[variant];
|
|
125
|
-
}
|
|
126
|
-
var BUTTON_VARIANTS = {
|
|
127
|
-
primary: "bg-primary hover:bg-primary/90 text-white shadow-sm",
|
|
128
|
-
secondary: "bg-secondary hover:bg-secondary/90 text-white shadow-sm",
|
|
129
|
-
outline: "border-2 border-primary text-primary hover:bg-primary/10",
|
|
130
|
-
ghost: "text-text-primary hover:bg-gray-100",
|
|
131
|
-
danger: "bg-danger hover:bg-danger/90 text-white shadow-sm",
|
|
132
|
-
success: "bg-success hover:bg-success/90 text-white shadow-sm",
|
|
133
|
-
link: "text-primary hover:text-primary/80 hover:underline"
|
|
134
|
-
};
|
|
135
|
-
var BUTTON_SIZES = {
|
|
136
|
-
xs: "px-2.5 py-1.5 text-xs",
|
|
137
|
-
sm: "px-3 py-2 text-sm",
|
|
138
|
-
md: "px-4 py-2.5 text-sm",
|
|
139
|
-
lg: "px-5 py-3 text-base",
|
|
140
|
-
xl: "px-6 py-3.5 text-lg"
|
|
141
|
-
};
|
|
142
|
-
var BUTTON_ICON_SIZES = {
|
|
143
|
-
xs: "p-1.5",
|
|
144
|
-
sm: "p-2",
|
|
145
|
-
md: "p-2.5",
|
|
146
|
-
lg: "p-3",
|
|
147
|
-
xl: "p-4"
|
|
148
|
-
};
|
|
149
|
-
var BUTTON_BASE_CLASSES = "inline-flex items-center justify-center font-medium rounded-lg transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-primary/50 focus:ring-offset-2";
|
|
150
|
-
function getButtonVariantClasses(variant) {
|
|
151
|
-
return BUTTON_VARIANTS[variant];
|
|
152
|
-
}
|
|
153
|
-
function getButtonSizeClasses(size, iconOnly) {
|
|
154
|
-
return iconOnly ? BUTTON_ICON_SIZES[size] : BUTTON_SIZES[size];
|
|
155
|
-
}
|
|
156
|
-
var ALERT_VARIANTS = {
|
|
157
|
-
info: {
|
|
158
|
-
container: "bg-blue-50 border-blue-200 text-blue-800",
|
|
159
|
-
icon: "text-blue-500"
|
|
160
|
-
},
|
|
161
|
-
success: {
|
|
162
|
-
container: "bg-success/10 border-success/30 text-success",
|
|
163
|
-
icon: "text-success"
|
|
164
|
-
},
|
|
165
|
-
warning: {
|
|
166
|
-
container: "bg-warning/10 border-warning/30 text-warning",
|
|
167
|
-
icon: "text-warning"
|
|
168
|
-
},
|
|
169
|
-
danger: {
|
|
170
|
-
container: "bg-danger/10 border-danger/30 text-danger",
|
|
171
|
-
icon: "text-danger"
|
|
172
|
-
},
|
|
173
|
-
neutral: {
|
|
174
|
-
container: "bg-gray-50 border-gray-200 text-gray-800",
|
|
175
|
-
icon: "text-gray-500"
|
|
176
|
-
}
|
|
177
|
-
};
|
|
178
|
-
var ALERT_BASE_CLASSES = "rounded-lg border p-4";
|
|
179
|
-
function getAlertVariantClasses(variant) {
|
|
180
|
-
return ALERT_VARIANTS[variant];
|
|
181
|
-
}
|
|
182
|
-
var ALERT_ICONS = {
|
|
183
|
-
info: `<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
184
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
|
185
|
-
</svg>`,
|
|
186
|
-
success: `<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
187
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
|
188
|
-
</svg>`,
|
|
189
|
-
warning: `<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
190
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
|
|
191
|
-
</svg>`,
|
|
192
|
-
danger: `<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
193
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
|
194
|
-
</svg>`,
|
|
195
|
-
neutral: `<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
196
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
|
197
|
-
</svg>`
|
|
198
|
-
};
|
|
199
|
-
var CLOSE_ICON = `<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
200
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
|
201
|
-
</svg>`;
|
|
202
|
-
var LOADING_SPINNER = `<svg class="animate-spin -ml-1 mr-2 h-4 w-4" fill="none" viewBox="0 0 24 24">
|
|
203
|
-
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
|
204
|
-
<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>
|
|
205
|
-
</svg>`;
|
|
206
|
-
function cn(...classes) {
|
|
207
|
-
return classes.filter(Boolean).join(" ");
|
|
208
|
-
}
|
|
65
|
+
// libs/ui/src/react/Card.tsx
|
|
66
|
+
var import_styles = require("@frontmcp/uipack/styles");
|
|
209
67
|
|
|
210
68
|
// libs/ui/src/render/prerender.ts
|
|
211
69
|
async function renderToString(element) {
|
|
@@ -247,10 +105,10 @@ function Card({
|
|
|
247
105
|
href,
|
|
248
106
|
children
|
|
249
107
|
}) {
|
|
250
|
-
const variantClasses = getCardVariantClasses(variant);
|
|
251
|
-
const sizeClasses = getCardSizeClasses(size);
|
|
108
|
+
const variantClasses = (0, import_styles.getCardVariantClasses)(variant);
|
|
109
|
+
const sizeClasses = (0, import_styles.getCardSizeClasses)(size);
|
|
252
110
|
const clickableClasses = clickable ? "cursor-pointer hover:shadow-md transition-shadow" : "";
|
|
253
|
-
const allClasses = cn(variantClasses, sizeClasses, clickableClasses, className);
|
|
111
|
+
const allClasses = (0, import_styles.cn)(variantClasses, sizeClasses, clickableClasses, className);
|
|
254
112
|
const hasHeader = title || subtitle || headerActions;
|
|
255
113
|
const content = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
256
114
|
hasHeader && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-start justify-between mb-4", children: [
|
|
@@ -296,6 +154,7 @@ function renderCardSync(props) {
|
|
|
296
154
|
}
|
|
297
155
|
|
|
298
156
|
// libs/ui/src/react/Badge.tsx
|
|
157
|
+
var import_styles2 = require("@frontmcp/uipack/styles");
|
|
299
158
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
300
159
|
function Badge({
|
|
301
160
|
variant = "default",
|
|
@@ -309,18 +168,18 @@ function Badge({
|
|
|
309
168
|
children
|
|
310
169
|
}) {
|
|
311
170
|
if (dot) {
|
|
312
|
-
const dotClasses = cn(
|
|
171
|
+
const dotClasses = (0, import_styles2.cn)(
|
|
313
172
|
"inline-block rounded-full",
|
|
314
|
-
getBadgeDotSizeClasses(size),
|
|
315
|
-
getBadgeDotVariantClasses(variant),
|
|
173
|
+
(0, import_styles2.getBadgeDotSizeClasses)(size),
|
|
174
|
+
(0, import_styles2.getBadgeDotVariantClasses)(variant),
|
|
316
175
|
className
|
|
317
176
|
);
|
|
318
177
|
const label = typeof children === "string" ? children : void 0;
|
|
319
178
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: dotClasses, "aria-label": label, title: label });
|
|
320
179
|
}
|
|
321
|
-
const variantClasses = getBadgeVariantClasses(variant);
|
|
322
|
-
const sizeClasses = getBadgeSizeClasses(size);
|
|
323
|
-
const baseClasses = cn(
|
|
180
|
+
const variantClasses = (0, import_styles2.getBadgeVariantClasses)(variant);
|
|
181
|
+
const sizeClasses = (0, import_styles2.getBadgeSizeClasses)(size);
|
|
182
|
+
const baseClasses = (0, import_styles2.cn)(
|
|
324
183
|
"inline-flex items-center font-medium",
|
|
325
184
|
pill ? "rounded-full" : "rounded-md",
|
|
326
185
|
variantClasses,
|
|
@@ -354,6 +213,7 @@ function renderBadgeSync(props) {
|
|
|
354
213
|
}
|
|
355
214
|
|
|
356
215
|
// libs/ui/src/react/Button.tsx
|
|
216
|
+
var import_styles3 = require("@frontmcp/uipack/styles");
|
|
357
217
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
358
218
|
function Button({
|
|
359
219
|
variant = "primary",
|
|
@@ -369,14 +229,14 @@ function Button({
|
|
|
369
229
|
onClick,
|
|
370
230
|
children
|
|
371
231
|
}) {
|
|
372
|
-
const variantClasses = getButtonVariantClasses(variant);
|
|
373
|
-
const sizeClasses = getButtonSizeClasses(size, iconOnly);
|
|
232
|
+
const variantClasses = (0, import_styles3.getButtonVariantClasses)(variant);
|
|
233
|
+
const sizeClasses = (0, import_styles3.getButtonSizeClasses)(size, iconOnly);
|
|
374
234
|
const disabledClasses = disabled || loading ? "opacity-50 cursor-not-allowed" : "";
|
|
375
235
|
const widthClasses = fullWidth ? "w-full" : "";
|
|
376
|
-
const allClasses = cn(BUTTON_BASE_CLASSES, variantClasses, sizeClasses, disabledClasses, widthClasses, className);
|
|
236
|
+
const allClasses = (0, import_styles3.cn)(import_styles3.BUTTON_BASE_CLASSES, variantClasses, sizeClasses, disabledClasses, widthClasses, className);
|
|
377
237
|
const iconElement = icon && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: iconPosition === "left" ? "mr-2" : "ml-2", children: icon });
|
|
378
238
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("button", { type, className: allClasses, disabled: disabled || loading, onClick, children: [
|
|
379
|
-
loading && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "mr-2", dangerouslySetInnerHTML: { __html: LOADING_SPINNER } }),
|
|
239
|
+
loading && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "mr-2", dangerouslySetInnerHTML: { __html: import_styles3.LOADING_SPINNER } }),
|
|
380
240
|
!loading && icon && iconPosition === "left" && iconElement,
|
|
381
241
|
!iconOnly && children,
|
|
382
242
|
!loading && icon && iconPosition === "right" && iconElement
|
|
@@ -394,6 +254,7 @@ function renderButtonSync(props) {
|
|
|
394
254
|
}
|
|
395
255
|
|
|
396
256
|
// libs/ui/src/react/Alert.tsx
|
|
257
|
+
var import_styles4 = require("@frontmcp/uipack/styles");
|
|
397
258
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
398
259
|
function Alert({
|
|
399
260
|
variant = "info",
|
|
@@ -405,13 +266,13 @@ function Alert({
|
|
|
405
266
|
className,
|
|
406
267
|
children
|
|
407
268
|
}) {
|
|
408
|
-
const variantStyles = getAlertVariantClasses(variant);
|
|
409
|
-
const allClasses = cn(ALERT_BASE_CLASSES, variantStyles.container, className);
|
|
269
|
+
const variantStyles = (0, import_styles4.getAlertVariantClasses)(variant);
|
|
270
|
+
const allClasses = (0, import_styles4.cn)(import_styles4.ALERT_BASE_CLASSES, variantStyles.container, className);
|
|
410
271
|
const iconContent = icon || (showIcon ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
411
272
|
"span",
|
|
412
273
|
{
|
|
413
|
-
className: cn("flex-shrink-0", variantStyles.icon),
|
|
414
|
-
dangerouslySetInnerHTML: { __html: ALERT_ICONS[variant] }
|
|
274
|
+
className: (0, import_styles4.cn)("flex-shrink-0", variantStyles.icon),
|
|
275
|
+
dangerouslySetInnerHTML: { __html: import_styles4.ALERT_ICONS[variant] }
|
|
415
276
|
}
|
|
416
277
|
) : null);
|
|
417
278
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: allClasses, role: "alert", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex", children: [
|
|
@@ -427,7 +288,7 @@ function Alert({
|
|
|
427
288
|
className: "flex-shrink-0 ml-3 hover:opacity-70 transition-opacity",
|
|
428
289
|
"aria-label": "Dismiss",
|
|
429
290
|
onClick: onDismiss,
|
|
430
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { dangerouslySetInnerHTML: { __html: CLOSE_ICON } })
|
|
291
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { dangerouslySetInnerHTML: { __html: import_styles4.CLOSE_ICON } })
|
|
431
292
|
}
|
|
432
293
|
)
|
|
433
294
|
] }) });
|
|
@@ -980,161 +841,2058 @@ var FrontMcpBridge = class {
|
|
|
980
841
|
}
|
|
981
842
|
};
|
|
982
843
|
|
|
983
|
-
// libs/ui/src/
|
|
984
|
-
var
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
844
|
+
// libs/ui/src/bridge/adapters/base-adapter.ts
|
|
845
|
+
var DEFAULT_CAPABILITIES = {
|
|
846
|
+
canCallTools: false,
|
|
847
|
+
canSendMessages: false,
|
|
848
|
+
canOpenLinks: false,
|
|
849
|
+
canPersistState: true,
|
|
850
|
+
// localStorage fallback
|
|
851
|
+
hasNetworkAccess: true,
|
|
852
|
+
supportsDisplayModes: false,
|
|
853
|
+
supportsTheme: true
|
|
854
|
+
};
|
|
855
|
+
var DEFAULT_SAFE_AREA = {
|
|
856
|
+
top: 0,
|
|
857
|
+
bottom: 0,
|
|
858
|
+
left: 0,
|
|
859
|
+
right: 0
|
|
860
|
+
};
|
|
861
|
+
var BaseAdapter = class {
|
|
862
|
+
_capabilities = { ...DEFAULT_CAPABILITIES };
|
|
863
|
+
_hostContext;
|
|
864
|
+
_widgetState = {};
|
|
865
|
+
_toolInput = {};
|
|
866
|
+
_toolOutput = void 0;
|
|
867
|
+
_structuredContent = void 0;
|
|
868
|
+
_initialized = false;
|
|
869
|
+
_contextListeners = /* @__PURE__ */ new Set();
|
|
870
|
+
_toolResultListeners = /* @__PURE__ */ new Set();
|
|
871
|
+
constructor() {
|
|
872
|
+
this._hostContext = this._createDefaultHostContext();
|
|
873
|
+
}
|
|
874
|
+
get capabilities() {
|
|
875
|
+
return this._capabilities;
|
|
876
|
+
}
|
|
877
|
+
async initialize() {
|
|
878
|
+
if (this._initialized) return;
|
|
879
|
+
this._loadWidgetState();
|
|
880
|
+
this._readInjectedData();
|
|
881
|
+
this._initialized = true;
|
|
882
|
+
}
|
|
883
|
+
dispose() {
|
|
884
|
+
this._contextListeners.clear();
|
|
885
|
+
this._toolResultListeners.clear();
|
|
886
|
+
this._initialized = false;
|
|
887
|
+
}
|
|
888
|
+
// ============================================
|
|
889
|
+
// Data Access
|
|
890
|
+
// ============================================
|
|
891
|
+
getTheme() {
|
|
892
|
+
return this._hostContext.theme;
|
|
893
|
+
}
|
|
894
|
+
getDisplayMode() {
|
|
895
|
+
return this._hostContext.displayMode;
|
|
896
|
+
}
|
|
897
|
+
getUserAgent() {
|
|
898
|
+
return this._hostContext.userAgent;
|
|
899
|
+
}
|
|
900
|
+
getLocale() {
|
|
901
|
+
return this._hostContext.locale;
|
|
902
|
+
}
|
|
903
|
+
getToolInput() {
|
|
904
|
+
return this._toolInput;
|
|
905
|
+
}
|
|
906
|
+
getToolOutput() {
|
|
907
|
+
return this._toolOutput;
|
|
908
|
+
}
|
|
909
|
+
getStructuredContent() {
|
|
910
|
+
return this._structuredContent;
|
|
911
|
+
}
|
|
912
|
+
getWidgetState() {
|
|
913
|
+
return this._widgetState;
|
|
914
|
+
}
|
|
915
|
+
getSafeArea() {
|
|
916
|
+
return this._hostContext.safeArea;
|
|
917
|
+
}
|
|
918
|
+
getViewport() {
|
|
919
|
+
return this._hostContext.viewport;
|
|
920
|
+
}
|
|
921
|
+
getHostContext() {
|
|
922
|
+
return { ...this._hostContext };
|
|
923
|
+
}
|
|
924
|
+
// ============================================
|
|
925
|
+
// Actions (override in subclasses for real functionality)
|
|
926
|
+
// ============================================
|
|
927
|
+
async callTool(_name, _args) {
|
|
928
|
+
if (!this._capabilities.canCallTools) {
|
|
929
|
+
throw new Error(`Tool calls are not supported by ${this.name} adapter`);
|
|
930
|
+
}
|
|
931
|
+
throw new Error("callTool not implemented");
|
|
932
|
+
}
|
|
933
|
+
async sendMessage(_content) {
|
|
934
|
+
if (!this._capabilities.canSendMessages) {
|
|
935
|
+
throw new Error(`Sending messages is not supported by ${this.name} adapter`);
|
|
936
|
+
}
|
|
937
|
+
throw new Error("sendMessage not implemented");
|
|
938
|
+
}
|
|
939
|
+
async openLink(url) {
|
|
940
|
+
if (!this._capabilities.canOpenLinks) {
|
|
941
|
+
if (typeof window !== "undefined") {
|
|
942
|
+
window.open(url, "_blank", "noopener,noreferrer");
|
|
943
|
+
return;
|
|
1009
944
|
}
|
|
945
|
+
throw new Error(`Opening links is not supported by ${this.name} adapter`);
|
|
946
|
+
}
|
|
947
|
+
throw new Error("openLink not implemented");
|
|
948
|
+
}
|
|
949
|
+
async requestDisplayMode(_mode) {
|
|
950
|
+
if (!this._capabilities.supportsDisplayModes) {
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
throw new Error("requestDisplayMode not implemented");
|
|
954
|
+
}
|
|
955
|
+
async requestClose() {
|
|
956
|
+
}
|
|
957
|
+
setWidgetState(state) {
|
|
958
|
+
this._widgetState = { ...this._widgetState, ...state };
|
|
959
|
+
this._persistWidgetState();
|
|
960
|
+
}
|
|
961
|
+
// ============================================
|
|
962
|
+
// Events
|
|
963
|
+
// ============================================
|
|
964
|
+
onContextChange(callback) {
|
|
965
|
+
this._contextListeners.add(callback);
|
|
966
|
+
return () => {
|
|
967
|
+
this._contextListeners.delete(callback);
|
|
1010
968
|
};
|
|
1011
|
-
|
|
969
|
+
}
|
|
970
|
+
onToolResult(callback) {
|
|
971
|
+
this._toolResultListeners.add(callback);
|
|
1012
972
|
return () => {
|
|
1013
|
-
|
|
1014
|
-
if (bridgeInstance) {
|
|
1015
|
-
bridgeInstance.dispose();
|
|
1016
|
-
}
|
|
973
|
+
this._toolResultListeners.delete(callback);
|
|
1017
974
|
};
|
|
1018
|
-
}, [config, onReady, onError]);
|
|
1019
|
-
const contextValue = (0, import_react.useMemo)(
|
|
1020
|
-
() => ({
|
|
1021
|
-
bridge,
|
|
1022
|
-
loading,
|
|
1023
|
-
error,
|
|
1024
|
-
ready: !loading && !error && bridge !== null,
|
|
1025
|
-
adapterId: bridge?.adapterId,
|
|
1026
|
-
capabilities: bridge?.capabilities
|
|
1027
|
-
}),
|
|
1028
|
-
[bridge, loading, error]
|
|
1029
|
-
);
|
|
1030
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(McpBridgeContext.Provider, { value: contextValue, children });
|
|
1031
|
-
}
|
|
1032
|
-
var SSR_DEFAULT_CONTEXT = {
|
|
1033
|
-
bridge: null,
|
|
1034
|
-
loading: false,
|
|
1035
|
-
error: null,
|
|
1036
|
-
ready: false,
|
|
1037
|
-
adapterId: void 0,
|
|
1038
|
-
capabilities: void 0
|
|
1039
|
-
};
|
|
1040
|
-
function useMcpBridgeContext() {
|
|
1041
|
-
const context = (0, import_react.useContext)(McpBridgeContext);
|
|
1042
|
-
if (!context) {
|
|
1043
|
-
return SSR_DEFAULT_CONTEXT;
|
|
1044
975
|
}
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
const { bridge, ready } = useMcpBridgeContext();
|
|
1068
|
-
const [displayMode, setDisplayMode] = (0, import_react.useState)("inline");
|
|
1069
|
-
(0, import_react.useEffect)(() => {
|
|
1070
|
-
if (!ready || !bridge) return;
|
|
1071
|
-
setDisplayMode(bridge.getDisplayMode());
|
|
1072
|
-
const unsubscribe = bridge.onContextChange((changes) => {
|
|
1073
|
-
if (changes.displayMode) {
|
|
1074
|
-
setDisplayMode(changes.displayMode);
|
|
1075
|
-
}
|
|
1076
|
-
});
|
|
1077
|
-
return unsubscribe;
|
|
1078
|
-
}, [bridge, ready]);
|
|
1079
|
-
return displayMode;
|
|
1080
|
-
}
|
|
1081
|
-
function useHostContext() {
|
|
1082
|
-
const { bridge, ready } = useMcpBridgeContext();
|
|
1083
|
-
const [context, setContext] = (0, import_react.useState)(null);
|
|
1084
|
-
(0, import_react.useEffect)(() => {
|
|
1085
|
-
if (!ready || !bridge) return;
|
|
1086
|
-
const adapter = bridge.getAdapter?.();
|
|
1087
|
-
if (adapter) {
|
|
1088
|
-
setContext(adapter.getHostContext());
|
|
976
|
+
// ============================================
|
|
977
|
+
// Protected Helpers
|
|
978
|
+
// ============================================
|
|
979
|
+
/**
|
|
980
|
+
* Create default host context from environment detection.
|
|
981
|
+
*/
|
|
982
|
+
_createDefaultHostContext() {
|
|
983
|
+
return {
|
|
984
|
+
theme: this._detectTheme(),
|
|
985
|
+
displayMode: "inline",
|
|
986
|
+
locale: this._detectLocale(),
|
|
987
|
+
userAgent: this._detectUserAgent(),
|
|
988
|
+
safeArea: DEFAULT_SAFE_AREA,
|
|
989
|
+
viewport: this._detectViewport()
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
993
|
+
* Detect theme from CSS media query.
|
|
994
|
+
*/
|
|
995
|
+
_detectTheme() {
|
|
996
|
+
if (typeof window !== "undefined" && window.matchMedia) {
|
|
997
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
1089
998
|
}
|
|
1090
|
-
|
|
1091
|
-
setContext((prev) => prev ? { ...prev, ...changes } : null);
|
|
1092
|
-
});
|
|
1093
|
-
return unsubscribe;
|
|
1094
|
-
}, [bridge, ready]);
|
|
1095
|
-
return context;
|
|
1096
|
-
}
|
|
1097
|
-
function useCapability(cap) {
|
|
1098
|
-
const { capabilities } = useMcpBridgeContext();
|
|
1099
|
-
return capabilities?.[cap] === true;
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
// libs/ui/src/react/hooks/tools.tsx
|
|
1103
|
-
var import_react2 = require("react");
|
|
1104
|
-
function useToolInput() {
|
|
1105
|
-
const { bridge, ready } = useMcpBridgeContext();
|
|
1106
|
-
if (!ready || !bridge) {
|
|
1107
|
-
return null;
|
|
999
|
+
return "light";
|
|
1108
1000
|
}
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1001
|
+
/**
|
|
1002
|
+
* Detect locale from navigator.
|
|
1003
|
+
*/
|
|
1004
|
+
_detectLocale() {
|
|
1005
|
+
if (typeof navigator !== "undefined") {
|
|
1006
|
+
return navigator.language || "en-US";
|
|
1007
|
+
}
|
|
1008
|
+
return "en-US";
|
|
1113
1009
|
}
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1010
|
+
/**
|
|
1011
|
+
* Detect user agent capabilities.
|
|
1012
|
+
*/
|
|
1013
|
+
_detectUserAgent() {
|
|
1014
|
+
if (typeof navigator === "undefined") {
|
|
1015
|
+
return { type: "web", hover: true, touch: false };
|
|
1016
|
+
}
|
|
1017
|
+
const ua = navigator.userAgent || "";
|
|
1018
|
+
const isMobile = /iPhone|iPad|iPod|Android/i.test(ua);
|
|
1019
|
+
const hasTouch = "ontouchstart" in window || navigator.maxTouchPoints > 0;
|
|
1020
|
+
const hasHover = typeof window !== "undefined" && window.matchMedia && window.matchMedia("(hover: hover)").matches;
|
|
1021
|
+
return {
|
|
1022
|
+
type: isMobile ? "mobile" : "web",
|
|
1023
|
+
hover: hasHover !== false,
|
|
1024
|
+
touch: hasTouch
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
/**
|
|
1028
|
+
* Detect viewport dimensions.
|
|
1029
|
+
*/
|
|
1030
|
+
_detectViewport() {
|
|
1031
|
+
if (typeof window !== "undefined") {
|
|
1032
|
+
return {
|
|
1033
|
+
width: window.innerWidth,
|
|
1034
|
+
height: window.innerHeight
|
|
1035
|
+
};
|
|
1036
|
+
}
|
|
1037
|
+
return void 0;
|
|
1038
|
+
}
|
|
1039
|
+
/**
|
|
1040
|
+
* Read injected tool data from window globals.
|
|
1041
|
+
*/
|
|
1042
|
+
_readInjectedData() {
|
|
1043
|
+
if (typeof window === "undefined") return;
|
|
1044
|
+
const win = window;
|
|
1045
|
+
if (win.__mcpToolInput) {
|
|
1046
|
+
this._toolInput = win.__mcpToolInput;
|
|
1047
|
+
}
|
|
1048
|
+
if (win.__mcpToolOutput) {
|
|
1049
|
+
this._toolOutput = win.__mcpToolOutput;
|
|
1050
|
+
}
|
|
1051
|
+
if (win.__mcpStructuredContent) {
|
|
1052
|
+
this._structuredContent = win.__mcpStructuredContent;
|
|
1053
|
+
}
|
|
1054
|
+
if (win.__mcpHostContext) {
|
|
1055
|
+
this._hostContext = { ...this._hostContext, ...win.__mcpHostContext };
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* Load widget state from localStorage.
|
|
1060
|
+
*/
|
|
1061
|
+
_loadWidgetState() {
|
|
1062
|
+
if (typeof localStorage === "undefined") return;
|
|
1120
1063
|
try {
|
|
1121
|
-
const
|
|
1122
|
-
|
|
1123
|
-
|
|
1064
|
+
const key = this._getStateKey();
|
|
1065
|
+
const stored = localStorage.getItem(key);
|
|
1066
|
+
if (stored) {
|
|
1067
|
+
this._widgetState = JSON.parse(stored);
|
|
1124
1068
|
}
|
|
1125
1069
|
} catch {
|
|
1126
1070
|
}
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1071
|
+
}
|
|
1072
|
+
/**
|
|
1073
|
+
* Persist widget state to localStorage.
|
|
1074
|
+
*/
|
|
1075
|
+
_persistWidgetState() {
|
|
1076
|
+
if (typeof localStorage === "undefined") return;
|
|
1077
|
+
try {
|
|
1078
|
+
const key = this._getStateKey();
|
|
1079
|
+
localStorage.setItem(key, JSON.stringify(this._widgetState));
|
|
1080
|
+
} catch {
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
/**
|
|
1084
|
+
* Get localStorage key for widget state.
|
|
1085
|
+
*/
|
|
1086
|
+
_getStateKey() {
|
|
1087
|
+
if (typeof window !== "undefined") {
|
|
1088
|
+
const toolName = window.__mcpToolName || "unknown";
|
|
1089
|
+
return `frontmcp:widget:${toolName}`;
|
|
1090
|
+
}
|
|
1091
|
+
return "frontmcp:widget:unknown";
|
|
1092
|
+
}
|
|
1093
|
+
/**
|
|
1094
|
+
* Notify context change listeners.
|
|
1095
|
+
*/
|
|
1096
|
+
_notifyContextChange(changes) {
|
|
1097
|
+
this._hostContext = { ...this._hostContext, ...changes };
|
|
1098
|
+
this._contextListeners.forEach((cb) => {
|
|
1099
|
+
try {
|
|
1100
|
+
cb(changes);
|
|
1101
|
+
} catch (e) {
|
|
1102
|
+
console.error("[FrontMcpBridge] Context change listener error:", e);
|
|
1103
|
+
}
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
1106
|
+
/**
|
|
1107
|
+
* Notify tool result listeners.
|
|
1108
|
+
*/
|
|
1109
|
+
_notifyToolResult(result) {
|
|
1110
|
+
this._toolOutput = result;
|
|
1111
|
+
this._toolResultListeners.forEach((cb) => {
|
|
1112
|
+
try {
|
|
1113
|
+
cb(result);
|
|
1114
|
+
} catch (e) {
|
|
1115
|
+
console.error("[FrontMcpBridge] Tool result listener error:", e);
|
|
1116
|
+
}
|
|
1117
|
+
});
|
|
1118
|
+
}
|
|
1119
|
+
};
|
|
1120
|
+
|
|
1121
|
+
// libs/ui/src/bridge/adapters/openai.adapter.ts
|
|
1122
|
+
var OpenAIAdapter = class extends BaseAdapter {
|
|
1123
|
+
id = "openai";
|
|
1124
|
+
name = "OpenAI ChatGPT";
|
|
1125
|
+
priority = 100;
|
|
1126
|
+
// Highest priority
|
|
1127
|
+
_openai;
|
|
1128
|
+
_unsubscribeContext;
|
|
1129
|
+
_unsubscribeToolResult;
|
|
1130
|
+
constructor() {
|
|
1131
|
+
super();
|
|
1132
|
+
this._capabilities = {
|
|
1133
|
+
...DEFAULT_CAPABILITIES,
|
|
1134
|
+
canCallTools: true,
|
|
1135
|
+
canSendMessages: true,
|
|
1136
|
+
canOpenLinks: true,
|
|
1137
|
+
canPersistState: true,
|
|
1138
|
+
hasNetworkAccess: true,
|
|
1139
|
+
supportsDisplayModes: true,
|
|
1140
|
+
supportsTheme: true
|
|
1141
|
+
};
|
|
1142
|
+
}
|
|
1143
|
+
/**
|
|
1144
|
+
* Check if OpenAI Apps SDK is available.
|
|
1145
|
+
*/
|
|
1146
|
+
canHandle() {
|
|
1147
|
+
if (typeof window === "undefined") return false;
|
|
1148
|
+
const win = window;
|
|
1149
|
+
return Boolean(win.openai?.canvas);
|
|
1150
|
+
}
|
|
1151
|
+
/**
|
|
1152
|
+
* Initialize the OpenAI adapter.
|
|
1153
|
+
*/
|
|
1154
|
+
async initialize() {
|
|
1155
|
+
if (this._initialized) return;
|
|
1156
|
+
this._openai = window.openai;
|
|
1157
|
+
await super.initialize();
|
|
1158
|
+
this._syncContextFromSDK();
|
|
1159
|
+
if (this._openai?.canvas?.onContextChange) {
|
|
1160
|
+
this._unsubscribeContext = this._openai.canvas.onContextChange((changes) => {
|
|
1161
|
+
this._notifyContextChange(changes);
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
if (this._openai?.canvas?.onToolResult) {
|
|
1165
|
+
this._unsubscribeToolResult = this._openai.canvas.onToolResult((result) => {
|
|
1166
|
+
this._notifyToolResult(result);
|
|
1167
|
+
});
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
/**
|
|
1171
|
+
* Dispose adapter resources.
|
|
1172
|
+
*/
|
|
1173
|
+
dispose() {
|
|
1174
|
+
if (this._unsubscribeContext) {
|
|
1175
|
+
this._unsubscribeContext();
|
|
1176
|
+
this._unsubscribeContext = void 0;
|
|
1177
|
+
}
|
|
1178
|
+
if (this._unsubscribeToolResult) {
|
|
1179
|
+
this._unsubscribeToolResult();
|
|
1180
|
+
this._unsubscribeToolResult = void 0;
|
|
1181
|
+
}
|
|
1182
|
+
this._openai = void 0;
|
|
1183
|
+
super.dispose();
|
|
1184
|
+
}
|
|
1185
|
+
// ============================================
|
|
1186
|
+
// Data Access (override with SDK calls)
|
|
1187
|
+
// ============================================
|
|
1188
|
+
getTheme() {
|
|
1189
|
+
if (this._openai?.canvas?.getTheme) {
|
|
1190
|
+
const theme = this._openai.canvas.getTheme();
|
|
1191
|
+
return theme === "dark" ? "dark" : "light";
|
|
1192
|
+
}
|
|
1193
|
+
return super.getTheme();
|
|
1194
|
+
}
|
|
1195
|
+
getDisplayMode() {
|
|
1196
|
+
if (this._openai?.canvas?.getDisplayMode) {
|
|
1197
|
+
const mode = this._openai.canvas.getDisplayMode();
|
|
1198
|
+
if (mode === "fullscreen" || mode === "pip" || mode === "carousel") {
|
|
1199
|
+
return mode;
|
|
1200
|
+
}
|
|
1201
|
+
return "inline";
|
|
1202
|
+
}
|
|
1203
|
+
return super.getDisplayMode();
|
|
1204
|
+
}
|
|
1205
|
+
// ============================================
|
|
1206
|
+
// Actions (proxy to SDK)
|
|
1207
|
+
// ============================================
|
|
1208
|
+
async callTool(name, args) {
|
|
1209
|
+
if (!this._openai?.canvas?.callServerTool) {
|
|
1210
|
+
throw new Error("callServerTool not available in OpenAI SDK");
|
|
1211
|
+
}
|
|
1212
|
+
return this._openai.canvas.callServerTool(name, args);
|
|
1213
|
+
}
|
|
1214
|
+
async sendMessage(content) {
|
|
1215
|
+
if (!this._openai?.canvas?.sendMessage) {
|
|
1216
|
+
throw new Error("sendMessage not available in OpenAI SDK");
|
|
1217
|
+
}
|
|
1218
|
+
await this._openai.canvas.sendMessage(content);
|
|
1219
|
+
}
|
|
1220
|
+
async openLink(url) {
|
|
1221
|
+
if (!this._openai?.canvas?.openLink) {
|
|
1222
|
+
return super.openLink(url);
|
|
1223
|
+
}
|
|
1224
|
+
await this._openai.canvas.openLink(url);
|
|
1225
|
+
}
|
|
1226
|
+
async requestDisplayMode(mode) {
|
|
1227
|
+
if (!this._openai?.canvas?.setDisplayMode) {
|
|
1228
|
+
return super.requestDisplayMode(mode);
|
|
1229
|
+
}
|
|
1230
|
+
await this._openai.canvas.setDisplayMode(mode);
|
|
1231
|
+
this._hostContext = { ...this._hostContext, displayMode: mode };
|
|
1232
|
+
}
|
|
1233
|
+
async requestClose() {
|
|
1234
|
+
if (this._openai?.canvas?.close) {
|
|
1235
|
+
await this._openai.canvas.close();
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
// ============================================
|
|
1239
|
+
// Private Helpers
|
|
1240
|
+
// ============================================
|
|
1241
|
+
/**
|
|
1242
|
+
* Sync context from OpenAI SDK.
|
|
1243
|
+
*/
|
|
1244
|
+
_syncContextFromSDK() {
|
|
1245
|
+
if (!this._openai?.canvas) return;
|
|
1246
|
+
if (this._openai.canvas.getTheme) {
|
|
1247
|
+
const theme = this._openai.canvas.getTheme();
|
|
1248
|
+
this._hostContext.theme = theme === "dark" ? "dark" : "light";
|
|
1249
|
+
}
|
|
1250
|
+
if (this._openai.canvas.getDisplayMode) {
|
|
1251
|
+
const mode = this._openai.canvas.getDisplayMode();
|
|
1252
|
+
if (mode === "fullscreen" || mode === "pip" || mode === "carousel" || mode === "inline") {
|
|
1253
|
+
this._hostContext.displayMode = mode;
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
if (this._openai.canvas.getContext) {
|
|
1257
|
+
const ctx = this._openai.canvas.getContext();
|
|
1258
|
+
if (ctx) {
|
|
1259
|
+
this._hostContext = { ...this._hostContext, ...ctx };
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
};
|
|
1264
|
+
function createOpenAIAdapter() {
|
|
1265
|
+
return new OpenAIAdapter();
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
// libs/ui/src/bridge/adapters/ext-apps.adapter.ts
|
|
1269
|
+
var ExtAppsAdapter = class extends BaseAdapter {
|
|
1270
|
+
id = "ext-apps";
|
|
1271
|
+
name = "ext-apps (SEP-1865)";
|
|
1272
|
+
priority = 80;
|
|
1273
|
+
// High priority, but below OpenAI native
|
|
1274
|
+
_config;
|
|
1275
|
+
_messageListener;
|
|
1276
|
+
_pendingRequests = /* @__PURE__ */ new Map();
|
|
1277
|
+
_requestId = 0;
|
|
1278
|
+
_trustedOrigin;
|
|
1279
|
+
_hostCapabilities = {};
|
|
1280
|
+
constructor(config) {
|
|
1281
|
+
super();
|
|
1282
|
+
this._config = config || {};
|
|
1283
|
+
this._capabilities = {
|
|
1284
|
+
...DEFAULT_CAPABILITIES,
|
|
1285
|
+
canPersistState: true,
|
|
1286
|
+
hasNetworkAccess: true,
|
|
1287
|
+
// ext-apps usually allows network
|
|
1288
|
+
supportsTheme: true
|
|
1289
|
+
};
|
|
1290
|
+
}
|
|
1291
|
+
/**
|
|
1292
|
+
* Check if we're in an iframe (potential ext-apps context).
|
|
1293
|
+
*/
|
|
1294
|
+
canHandle() {
|
|
1295
|
+
if (typeof window === "undefined") return false;
|
|
1296
|
+
const inIframe = window.parent !== window;
|
|
1297
|
+
if (!inIframe) return false;
|
|
1298
|
+
const win = window;
|
|
1299
|
+
if (win.openai?.canvas) return false;
|
|
1300
|
+
if (win.__mcpPlatform === "ext-apps") return true;
|
|
1301
|
+
return true;
|
|
1302
|
+
}
|
|
1303
|
+
/**
|
|
1304
|
+
* Initialize the ext-apps adapter with protocol handshake.
|
|
1305
|
+
*/
|
|
1306
|
+
async initialize() {
|
|
1307
|
+
if (this._initialized) return;
|
|
1308
|
+
this._setupMessageListener();
|
|
1309
|
+
await super.initialize();
|
|
1310
|
+
await this._performHandshake();
|
|
1311
|
+
this._initialized = true;
|
|
1312
|
+
}
|
|
1313
|
+
/**
|
|
1314
|
+
* Dispose adapter resources.
|
|
1315
|
+
*/
|
|
1316
|
+
dispose() {
|
|
1317
|
+
if (this._messageListener && typeof window !== "undefined") {
|
|
1318
|
+
window.removeEventListener("message", this._messageListener);
|
|
1319
|
+
this._messageListener = void 0;
|
|
1320
|
+
}
|
|
1321
|
+
for (const [id, pending] of this._pendingRequests) {
|
|
1322
|
+
clearTimeout(pending.timeout);
|
|
1323
|
+
pending.reject(new Error("Adapter disposed"));
|
|
1324
|
+
}
|
|
1325
|
+
this._pendingRequests.clear();
|
|
1326
|
+
super.dispose();
|
|
1327
|
+
}
|
|
1328
|
+
// ============================================
|
|
1329
|
+
// Actions (via JSON-RPC)
|
|
1330
|
+
// ============================================
|
|
1331
|
+
async callTool(name, args) {
|
|
1332
|
+
if (!this._hostCapabilities.serverToolProxy) {
|
|
1333
|
+
throw new Error("Server tool proxy not supported by host");
|
|
1334
|
+
}
|
|
1335
|
+
return this._sendRequest("ui/callServerTool", {
|
|
1336
|
+
name,
|
|
1337
|
+
arguments: args
|
|
1338
|
+
});
|
|
1339
|
+
}
|
|
1340
|
+
async sendMessage(content) {
|
|
1341
|
+
await this._sendRequest("ui/message", { content });
|
|
1342
|
+
}
|
|
1343
|
+
async openLink(url) {
|
|
1344
|
+
if (!this._hostCapabilities.openLink) {
|
|
1345
|
+
return super.openLink(url);
|
|
1346
|
+
}
|
|
1347
|
+
await this._sendRequest("ui/openLink", { url });
|
|
1348
|
+
}
|
|
1349
|
+
async requestDisplayMode(mode) {
|
|
1350
|
+
await this._sendRequest("ui/setDisplayMode", { mode });
|
|
1351
|
+
this._hostContext = { ...this._hostContext, displayMode: mode };
|
|
1352
|
+
}
|
|
1353
|
+
async requestClose() {
|
|
1354
|
+
await this._sendRequest("ui/close", {});
|
|
1355
|
+
}
|
|
1356
|
+
// ============================================
|
|
1357
|
+
// Private: Message Handling
|
|
1358
|
+
// ============================================
|
|
1359
|
+
/**
|
|
1360
|
+
* Setup postMessage listener for incoming messages.
|
|
1361
|
+
*/
|
|
1362
|
+
_setupMessageListener() {
|
|
1363
|
+
if (typeof window === "undefined") return;
|
|
1364
|
+
this._messageListener = (event) => {
|
|
1365
|
+
this._handleMessage(event);
|
|
1366
|
+
};
|
|
1367
|
+
window.addEventListener("message", this._messageListener);
|
|
1368
|
+
}
|
|
1369
|
+
/**
|
|
1370
|
+
* Handle incoming postMessage events.
|
|
1371
|
+
*/
|
|
1372
|
+
_handleMessage(event) {
|
|
1373
|
+
if (!this._isOriginTrusted(event.origin)) {
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
1376
|
+
const data = event.data;
|
|
1377
|
+
if (!data || typeof data !== "object") return;
|
|
1378
|
+
if (data.jsonrpc !== "2.0") return;
|
|
1379
|
+
if ("id" in data && (data.result !== void 0 || data.error !== void 0)) {
|
|
1380
|
+
this._handleResponse(data);
|
|
1381
|
+
return;
|
|
1382
|
+
}
|
|
1383
|
+
if ("method" in data && !("id" in data)) {
|
|
1384
|
+
this._handleNotification(data);
|
|
1385
|
+
return;
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
/**
|
|
1389
|
+
* Handle JSON-RPC response.
|
|
1390
|
+
*/
|
|
1391
|
+
_handleResponse(response) {
|
|
1392
|
+
const pending = this._pendingRequests.get(response.id);
|
|
1393
|
+
if (!pending) return;
|
|
1394
|
+
clearTimeout(pending.timeout);
|
|
1395
|
+
this._pendingRequests.delete(response.id);
|
|
1396
|
+
if (response.error) {
|
|
1397
|
+
pending.reject(new Error(`${response.error.message} (code: ${response.error.code})`));
|
|
1398
|
+
} else {
|
|
1399
|
+
pending.resolve(response.result);
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
/**
|
|
1403
|
+
* Handle JSON-RPC notification from host.
|
|
1404
|
+
*/
|
|
1405
|
+
_handleNotification(notification) {
|
|
1406
|
+
switch (notification.method) {
|
|
1407
|
+
case "ui/notifications/tool-input":
|
|
1408
|
+
this._handleToolInput(notification.params);
|
|
1409
|
+
break;
|
|
1410
|
+
case "ui/notifications/tool-input-partial":
|
|
1411
|
+
this._handleToolInputPartial(notification.params);
|
|
1412
|
+
break;
|
|
1413
|
+
case "ui/notifications/tool-result":
|
|
1414
|
+
this._handleToolResult(notification.params);
|
|
1415
|
+
break;
|
|
1416
|
+
case "ui/notifications/host-context-changed":
|
|
1417
|
+
this._handleHostContextChange(notification.params);
|
|
1418
|
+
break;
|
|
1419
|
+
case "ui/notifications/initialized":
|
|
1420
|
+
break;
|
|
1421
|
+
case "ui/notifications/cancelled":
|
|
1422
|
+
this._handleCancelled(notification.params);
|
|
1423
|
+
break;
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
/**
|
|
1427
|
+
* Handle tool input notification.
|
|
1428
|
+
*/
|
|
1429
|
+
_handleToolInput(params) {
|
|
1430
|
+
this._toolInput = params.arguments || {};
|
|
1431
|
+
this._emitBridgeEvent("tool:input", { arguments: this._toolInput });
|
|
1432
|
+
}
|
|
1433
|
+
/**
|
|
1434
|
+
* Handle partial tool input (streaming).
|
|
1435
|
+
*/
|
|
1436
|
+
_handleToolInputPartial(params) {
|
|
1437
|
+
this._toolInput = { ...this._toolInput, ...params.arguments };
|
|
1438
|
+
this._emitBridgeEvent("tool:input-partial", { arguments: this._toolInput });
|
|
1439
|
+
}
|
|
1440
|
+
/**
|
|
1441
|
+
* Handle tool result notification.
|
|
1442
|
+
*/
|
|
1443
|
+
_handleToolResult(params) {
|
|
1444
|
+
this._toolOutput = params.content;
|
|
1445
|
+
this._structuredContent = params.structuredContent;
|
|
1446
|
+
this._notifyToolResult(params.content);
|
|
1447
|
+
this._emitBridgeEvent("tool:result", {
|
|
1448
|
+
content: params.content,
|
|
1449
|
+
structuredContent: params.structuredContent
|
|
1450
|
+
});
|
|
1451
|
+
}
|
|
1452
|
+
/**
|
|
1453
|
+
* Handle host context change notification.
|
|
1454
|
+
*/
|
|
1455
|
+
_handleHostContextChange(params) {
|
|
1456
|
+
const changes = {};
|
|
1457
|
+
if (params.theme !== void 0) {
|
|
1458
|
+
changes.theme = params.theme;
|
|
1459
|
+
}
|
|
1460
|
+
if (params.displayMode !== void 0) {
|
|
1461
|
+
changes.displayMode = params.displayMode;
|
|
1462
|
+
}
|
|
1463
|
+
if (params.viewport !== void 0) {
|
|
1464
|
+
changes.viewport = params.viewport;
|
|
1465
|
+
}
|
|
1466
|
+
if (params.locale !== void 0) {
|
|
1467
|
+
changes.locale = params.locale;
|
|
1468
|
+
}
|
|
1469
|
+
if (params.timezone !== void 0) {
|
|
1470
|
+
changes.timezone = params.timezone;
|
|
1471
|
+
}
|
|
1472
|
+
this._notifyContextChange(changes);
|
|
1473
|
+
}
|
|
1474
|
+
/**
|
|
1475
|
+
* Handle cancellation notification.
|
|
1476
|
+
*/
|
|
1477
|
+
_handleCancelled(params) {
|
|
1478
|
+
const reason = params?.reason;
|
|
1479
|
+
this._emitBridgeEvent("tool:cancelled", { reason });
|
|
1480
|
+
}
|
|
1481
|
+
// ============================================
|
|
1482
|
+
// Private: JSON-RPC Transport
|
|
1483
|
+
// ============================================
|
|
1484
|
+
/**
|
|
1485
|
+
* Send a JSON-RPC request to the host.
|
|
1486
|
+
*/
|
|
1487
|
+
_sendRequest(method, params) {
|
|
1488
|
+
return new Promise((resolve, reject) => {
|
|
1489
|
+
const id = ++this._requestId;
|
|
1490
|
+
const timeout = this._config.options?.initTimeout || 1e4;
|
|
1491
|
+
const request = {
|
|
1492
|
+
jsonrpc: "2.0",
|
|
1493
|
+
id,
|
|
1494
|
+
method,
|
|
1495
|
+
params
|
|
1496
|
+
};
|
|
1497
|
+
const timeoutHandle = setTimeout(() => {
|
|
1498
|
+
this._pendingRequests.delete(id);
|
|
1499
|
+
reject(new Error(`Request ${method} timed out after ${timeout}ms`));
|
|
1500
|
+
}, timeout);
|
|
1501
|
+
this._pendingRequests.set(id, {
|
|
1502
|
+
resolve,
|
|
1503
|
+
reject,
|
|
1504
|
+
timeout: timeoutHandle
|
|
1505
|
+
});
|
|
1506
|
+
this._postMessage(request);
|
|
1507
|
+
});
|
|
1508
|
+
}
|
|
1509
|
+
/**
|
|
1510
|
+
* Send a JSON-RPC notification (no response expected).
|
|
1511
|
+
*/
|
|
1512
|
+
_sendNotification(method, params) {
|
|
1513
|
+
const notification = {
|
|
1514
|
+
jsonrpc: "2.0",
|
|
1515
|
+
method,
|
|
1516
|
+
params
|
|
1517
|
+
};
|
|
1518
|
+
this._postMessage(notification);
|
|
1519
|
+
}
|
|
1520
|
+
/**
|
|
1521
|
+
* Post a message to the parent window.
|
|
1522
|
+
*/
|
|
1523
|
+
_postMessage(message) {
|
|
1524
|
+
if (typeof window === "undefined") return;
|
|
1525
|
+
const targetOrigin = this._trustedOrigin || "*";
|
|
1526
|
+
window.parent.postMessage(message, targetOrigin);
|
|
1527
|
+
}
|
|
1528
|
+
// ============================================
|
|
1529
|
+
// Private: Handshake
|
|
1530
|
+
// ============================================
|
|
1531
|
+
/**
|
|
1532
|
+
* Perform the ui/initialize handshake with the host.
|
|
1533
|
+
*/
|
|
1534
|
+
async _performHandshake() {
|
|
1535
|
+
const params = {
|
|
1536
|
+
appInfo: {
|
|
1537
|
+
name: this._config.options?.appName || "FrontMCP Widget",
|
|
1538
|
+
version: this._config.options?.appVersion || "1.0.0"
|
|
1539
|
+
},
|
|
1540
|
+
appCapabilities: {
|
|
1541
|
+
tools: {
|
|
1542
|
+
listChanged: false
|
|
1543
|
+
}
|
|
1544
|
+
},
|
|
1545
|
+
protocolVersion: this._config.options?.protocolVersion || "2024-11-05"
|
|
1546
|
+
};
|
|
1547
|
+
try {
|
|
1548
|
+
const result = await this._sendRequest("ui/initialize", params);
|
|
1549
|
+
this._hostCapabilities = result.hostCapabilities || {};
|
|
1550
|
+
this._capabilities = {
|
|
1551
|
+
...this._capabilities,
|
|
1552
|
+
canCallTools: Boolean(this._hostCapabilities.serverToolProxy),
|
|
1553
|
+
canSendMessages: true,
|
|
1554
|
+
canOpenLinks: Boolean(this._hostCapabilities.openLink),
|
|
1555
|
+
supportsDisplayModes: true
|
|
1556
|
+
};
|
|
1557
|
+
if (result.hostContext) {
|
|
1558
|
+
this._hostContext = {
|
|
1559
|
+
...this._hostContext,
|
|
1560
|
+
...result.hostContext
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
if (!this._config.options?.trustedOrigins?.length) {
|
|
1564
|
+
}
|
|
1565
|
+
} catch (error) {
|
|
1566
|
+
throw new Error(`ext-apps handshake failed: ${error}`);
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
// ============================================
|
|
1570
|
+
// Private: Origin Security
|
|
1571
|
+
// ============================================
|
|
1572
|
+
/**
|
|
1573
|
+
* Check if an origin is trusted.
|
|
1574
|
+
* Uses trust-on-first-use if no explicit origins configured.
|
|
1575
|
+
*/
|
|
1576
|
+
_isOriginTrusted(origin) {
|
|
1577
|
+
const trustedOrigins = this._config.options?.trustedOrigins;
|
|
1578
|
+
if (trustedOrigins && trustedOrigins.length > 0) {
|
|
1579
|
+
return trustedOrigins.includes(origin);
|
|
1580
|
+
}
|
|
1581
|
+
if (!this._trustedOrigin) {
|
|
1582
|
+
this._trustedOrigin = origin;
|
|
1583
|
+
return true;
|
|
1584
|
+
}
|
|
1585
|
+
return this._trustedOrigin === origin;
|
|
1586
|
+
}
|
|
1587
|
+
// ============================================
|
|
1588
|
+
// Private: Events
|
|
1589
|
+
// ============================================
|
|
1590
|
+
/**
|
|
1591
|
+
* Emit a bridge event via CustomEvent.
|
|
1592
|
+
*/
|
|
1593
|
+
_emitBridgeEvent(type, detail) {
|
|
1594
|
+
if (typeof window !== "undefined" && typeof CustomEvent !== "undefined") {
|
|
1595
|
+
try {
|
|
1596
|
+
const event = new CustomEvent(type, { detail });
|
|
1597
|
+
window.dispatchEvent(event);
|
|
1598
|
+
} catch {
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
};
|
|
1603
|
+
function createExtAppsAdapter(config) {
|
|
1604
|
+
return new ExtAppsAdapter(config);
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
// libs/ui/src/bridge/adapters/claude.adapter.ts
|
|
1608
|
+
var ClaudeAdapter = class extends BaseAdapter {
|
|
1609
|
+
id = "claude";
|
|
1610
|
+
name = "Claude (Anthropic)";
|
|
1611
|
+
priority = 60;
|
|
1612
|
+
constructor() {
|
|
1613
|
+
super();
|
|
1614
|
+
this._capabilities = {
|
|
1615
|
+
...DEFAULT_CAPABILITIES,
|
|
1616
|
+
canCallTools: false,
|
|
1617
|
+
// Claude artifacts can't call tools
|
|
1618
|
+
canSendMessages: false,
|
|
1619
|
+
// Can't send messages back to conversation
|
|
1620
|
+
canOpenLinks: true,
|
|
1621
|
+
// Can open links via window.open
|
|
1622
|
+
canPersistState: true,
|
|
1623
|
+
// localStorage works
|
|
1624
|
+
hasNetworkAccess: false,
|
|
1625
|
+
// Network is blocked
|
|
1626
|
+
supportsDisplayModes: false,
|
|
1627
|
+
// No display mode control
|
|
1628
|
+
supportsTheme: true
|
|
1629
|
+
// Can detect system theme
|
|
1630
|
+
};
|
|
1631
|
+
}
|
|
1632
|
+
/**
|
|
1633
|
+
* Check if we're running in a Claude artifact/widget context.
|
|
1634
|
+
*/
|
|
1635
|
+
canHandle() {
|
|
1636
|
+
if (typeof window === "undefined") return false;
|
|
1637
|
+
const win = window;
|
|
1638
|
+
if (win.__mcpPlatform === "claude") return true;
|
|
1639
|
+
if (win.claude) return true;
|
|
1640
|
+
if (win.__claudeArtifact) return true;
|
|
1641
|
+
if (typeof location !== "undefined") {
|
|
1642
|
+
const href = location.href;
|
|
1643
|
+
if (href.includes("claude.ai") || href.includes("anthropic.com")) {
|
|
1644
|
+
return true;
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
return false;
|
|
1648
|
+
}
|
|
1649
|
+
/**
|
|
1650
|
+
* Initialize the Claude adapter.
|
|
1651
|
+
*/
|
|
1652
|
+
async initialize() {
|
|
1653
|
+
if (this._initialized) return;
|
|
1654
|
+
await super.initialize();
|
|
1655
|
+
this._setupThemeListener();
|
|
1656
|
+
}
|
|
1657
|
+
/**
|
|
1658
|
+
* Open a link in a new tab.
|
|
1659
|
+
* This is one of the few actions available in Claude artifacts.
|
|
1660
|
+
*/
|
|
1661
|
+
async openLink(url) {
|
|
1662
|
+
if (typeof window !== "undefined") {
|
|
1663
|
+
window.open(url, "_blank", "noopener,noreferrer");
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
/**
|
|
1667
|
+
* Request display mode change (no-op for Claude).
|
|
1668
|
+
*/
|
|
1669
|
+
async requestDisplayMode(_mode) {
|
|
1670
|
+
}
|
|
1671
|
+
/**
|
|
1672
|
+
* Request close (no-op for Claude).
|
|
1673
|
+
*/
|
|
1674
|
+
async requestClose() {
|
|
1675
|
+
}
|
|
1676
|
+
// ============================================
|
|
1677
|
+
// Private Helpers
|
|
1678
|
+
// ============================================
|
|
1679
|
+
/**
|
|
1680
|
+
* Setup listener for system theme changes.
|
|
1681
|
+
*/
|
|
1682
|
+
_setupThemeListener() {
|
|
1683
|
+
if (typeof window === "undefined" || !window.matchMedia) return;
|
|
1684
|
+
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
1685
|
+
const handleChange = (e) => {
|
|
1686
|
+
const newTheme = e.matches ? "dark" : "light";
|
|
1687
|
+
if (newTheme !== this._hostContext.theme) {
|
|
1688
|
+
this._notifyContextChange({ theme: newTheme });
|
|
1689
|
+
}
|
|
1690
|
+
};
|
|
1691
|
+
if (mediaQuery.addEventListener) {
|
|
1692
|
+
mediaQuery.addEventListener("change", handleChange);
|
|
1693
|
+
} else if (mediaQuery.addListener) {
|
|
1694
|
+
mediaQuery.addListener(handleChange);
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
};
|
|
1698
|
+
function createClaudeAdapter() {
|
|
1699
|
+
return new ClaudeAdapter();
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
// libs/ui/src/bridge/adapters/gemini.adapter.ts
|
|
1703
|
+
var GeminiAdapter = class extends BaseAdapter {
|
|
1704
|
+
id = "gemini";
|
|
1705
|
+
name = "Google Gemini";
|
|
1706
|
+
priority = 40;
|
|
1707
|
+
_gemini;
|
|
1708
|
+
constructor() {
|
|
1709
|
+
super();
|
|
1710
|
+
this._capabilities = {
|
|
1711
|
+
...DEFAULT_CAPABILITIES,
|
|
1712
|
+
canCallTools: false,
|
|
1713
|
+
// May be enabled if SDK supports it
|
|
1714
|
+
canSendMessages: false,
|
|
1715
|
+
// May be enabled if SDK supports it
|
|
1716
|
+
canOpenLinks: true,
|
|
1717
|
+
canPersistState: true,
|
|
1718
|
+
hasNetworkAccess: true,
|
|
1719
|
+
supportsDisplayModes: false,
|
|
1720
|
+
supportsTheme: true
|
|
1721
|
+
};
|
|
1722
|
+
}
|
|
1723
|
+
/**
|
|
1724
|
+
* Check if we're running in a Gemini context.
|
|
1725
|
+
*/
|
|
1726
|
+
canHandle() {
|
|
1727
|
+
if (typeof window === "undefined") return false;
|
|
1728
|
+
const win = window;
|
|
1729
|
+
if (win.__mcpPlatform === "gemini") return true;
|
|
1730
|
+
if (win.gemini) return true;
|
|
1731
|
+
if (typeof location !== "undefined") {
|
|
1732
|
+
const href = location.href;
|
|
1733
|
+
if (href.includes("gemini.google.com") || href.includes("bard.google.com")) {
|
|
1734
|
+
return true;
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
return false;
|
|
1738
|
+
}
|
|
1739
|
+
/**
|
|
1740
|
+
* Initialize the Gemini adapter.
|
|
1741
|
+
*/
|
|
1742
|
+
async initialize() {
|
|
1743
|
+
if (this._initialized) return;
|
|
1744
|
+
const win = window;
|
|
1745
|
+
this._gemini = win.gemini;
|
|
1746
|
+
if (this._gemini?.ui) {
|
|
1747
|
+
if (this._gemini.ui.sendMessage) {
|
|
1748
|
+
this._capabilities = { ...this._capabilities, canSendMessages: true };
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
await super.initialize();
|
|
1752
|
+
this._setupThemeListener();
|
|
1753
|
+
}
|
|
1754
|
+
/**
|
|
1755
|
+
* Get current theme.
|
|
1756
|
+
*/
|
|
1757
|
+
getTheme() {
|
|
1758
|
+
if (this._gemini?.ui?.getTheme) {
|
|
1759
|
+
const theme = this._gemini.ui.getTheme();
|
|
1760
|
+
return theme === "dark" ? "dark" : "light";
|
|
1761
|
+
}
|
|
1762
|
+
return super.getTheme();
|
|
1763
|
+
}
|
|
1764
|
+
/**
|
|
1765
|
+
* Send a message (if supported by SDK).
|
|
1766
|
+
*/
|
|
1767
|
+
async sendMessage(content) {
|
|
1768
|
+
if (this._gemini?.ui?.sendMessage) {
|
|
1769
|
+
await this._gemini.ui.sendMessage(content);
|
|
1770
|
+
return;
|
|
1771
|
+
}
|
|
1772
|
+
throw new Error("Sending messages is not supported by Gemini adapter");
|
|
1773
|
+
}
|
|
1774
|
+
/**
|
|
1775
|
+
* Open a link.
|
|
1776
|
+
*/
|
|
1777
|
+
async openLink(url) {
|
|
1778
|
+
if (this._gemini?.ui?.openLink) {
|
|
1779
|
+
await this._gemini.ui.openLink(url);
|
|
1780
|
+
return;
|
|
1781
|
+
}
|
|
1782
|
+
return super.openLink(url);
|
|
1783
|
+
}
|
|
1784
|
+
// ============================================
|
|
1785
|
+
// Private Helpers
|
|
1786
|
+
// ============================================
|
|
1787
|
+
/**
|
|
1788
|
+
* Setup listener for system theme changes.
|
|
1789
|
+
*/
|
|
1790
|
+
_setupThemeListener() {
|
|
1791
|
+
if (typeof window === "undefined" || !window.matchMedia) return;
|
|
1792
|
+
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
1793
|
+
const handleChange = (e) => {
|
|
1794
|
+
if (!this._gemini?.ui?.getTheme) {
|
|
1795
|
+
const newTheme = e.matches ? "dark" : "light";
|
|
1796
|
+
if (newTheme !== this._hostContext.theme) {
|
|
1797
|
+
this._notifyContextChange({ theme: newTheme });
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
};
|
|
1801
|
+
if (mediaQuery.addEventListener) {
|
|
1802
|
+
mediaQuery.addEventListener("change", handleChange);
|
|
1803
|
+
} else if (mediaQuery.addListener) {
|
|
1804
|
+
mediaQuery.addListener(handleChange);
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
};
|
|
1808
|
+
function createGeminiAdapter() {
|
|
1809
|
+
return new GeminiAdapter();
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1812
|
+
// libs/ui/src/bridge/adapters/generic.adapter.ts
|
|
1813
|
+
var GenericAdapter = class extends BaseAdapter {
|
|
1814
|
+
id = "generic";
|
|
1815
|
+
name = "Generic Web";
|
|
1816
|
+
priority = 0;
|
|
1817
|
+
// Lowest priority - fallback only
|
|
1818
|
+
constructor() {
|
|
1819
|
+
super();
|
|
1820
|
+
this._capabilities = {
|
|
1821
|
+
...DEFAULT_CAPABILITIES,
|
|
1822
|
+
canCallTools: false,
|
|
1823
|
+
canSendMessages: false,
|
|
1824
|
+
canOpenLinks: true,
|
|
1825
|
+
// window.open works
|
|
1826
|
+
canPersistState: true,
|
|
1827
|
+
// localStorage works
|
|
1828
|
+
hasNetworkAccess: true,
|
|
1829
|
+
// Assume network available
|
|
1830
|
+
supportsDisplayModes: false,
|
|
1831
|
+
supportsTheme: true
|
|
1832
|
+
// System theme detection
|
|
1833
|
+
};
|
|
1834
|
+
}
|
|
1835
|
+
/**
|
|
1836
|
+
* Generic adapter can always handle the environment.
|
|
1837
|
+
* It serves as the fallback when no other adapter matches.
|
|
1838
|
+
*/
|
|
1839
|
+
canHandle() {
|
|
1840
|
+
return typeof window !== "undefined";
|
|
1841
|
+
}
|
|
1842
|
+
/**
|
|
1843
|
+
* Initialize the generic adapter.
|
|
1844
|
+
*/
|
|
1845
|
+
async initialize() {
|
|
1846
|
+
if (this._initialized) return;
|
|
1847
|
+
await super.initialize();
|
|
1848
|
+
this._setupThemeListener();
|
|
1849
|
+
}
|
|
1850
|
+
/**
|
|
1851
|
+
* Open a link using window.open.
|
|
1852
|
+
*/
|
|
1853
|
+
async openLink(url) {
|
|
1854
|
+
if (typeof window !== "undefined") {
|
|
1855
|
+
window.open(url, "_blank", "noopener,noreferrer");
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
// ============================================
|
|
1859
|
+
// Private Helpers
|
|
1860
|
+
// ============================================
|
|
1861
|
+
/**
|
|
1862
|
+
* Setup listener for system theme changes.
|
|
1863
|
+
*/
|
|
1864
|
+
_setupThemeListener() {
|
|
1865
|
+
if (typeof window === "undefined" || !window.matchMedia) return;
|
|
1866
|
+
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
1867
|
+
const handleChange = (e) => {
|
|
1868
|
+
const newTheme = e.matches ? "dark" : "light";
|
|
1869
|
+
if (newTheme !== this._hostContext.theme) {
|
|
1870
|
+
this._notifyContextChange({ theme: newTheme });
|
|
1871
|
+
}
|
|
1872
|
+
};
|
|
1873
|
+
if (mediaQuery.addEventListener) {
|
|
1874
|
+
mediaQuery.addEventListener("change", handleChange);
|
|
1875
|
+
} else if (mediaQuery.addListener) {
|
|
1876
|
+
mediaQuery.addListener(handleChange);
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
};
|
|
1880
|
+
function createGenericAdapter() {
|
|
1881
|
+
return new GenericAdapter();
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
// libs/ui/src/bridge/adapters/index.ts
|
|
1885
|
+
function registerBuiltInAdapters() {
|
|
1886
|
+
defaultRegistry.register("openai", createOpenAIAdapter);
|
|
1887
|
+
defaultRegistry.register("ext-apps", createExtAppsAdapter);
|
|
1888
|
+
defaultRegistry.register("claude", createClaudeAdapter);
|
|
1889
|
+
defaultRegistry.register("gemini", createGeminiAdapter);
|
|
1890
|
+
defaultRegistry.register("generic", createGenericAdapter);
|
|
1891
|
+
}
|
|
1892
|
+
registerBuiltInAdapters();
|
|
1893
|
+
|
|
1894
|
+
// libs/ui/src/bridge/runtime/iife-generator.ts
|
|
1895
|
+
function generateBridgeIIFE(options = {}) {
|
|
1896
|
+
const { debug = false, trustedOrigins = [], minify = false } = options;
|
|
1897
|
+
const adapters = options.adapters || ["openai", "ext-apps", "claude", "gemini", "generic"];
|
|
1898
|
+
const parts = [];
|
|
1899
|
+
parts.push("(function() {");
|
|
1900
|
+
parts.push('"use strict";');
|
|
1901
|
+
parts.push("");
|
|
1902
|
+
if (debug) {
|
|
1903
|
+
parts.push('function log(msg) { console.log("[FrontMcpBridge] " + msg); }');
|
|
1904
|
+
} else {
|
|
1905
|
+
parts.push("function log() {}");
|
|
1906
|
+
}
|
|
1907
|
+
parts.push("");
|
|
1908
|
+
parts.push("var DEFAULT_SAFE_AREA = { top: 0, bottom: 0, left: 0, right: 0 };");
|
|
1909
|
+
parts.push("");
|
|
1910
|
+
parts.push(generateContextDetection());
|
|
1911
|
+
parts.push("");
|
|
1912
|
+
parts.push(generateBaseCapabilities());
|
|
1913
|
+
parts.push("");
|
|
1914
|
+
if (adapters.includes("openai")) {
|
|
1915
|
+
parts.push(generateOpenAIAdapter());
|
|
1916
|
+
parts.push("");
|
|
1917
|
+
}
|
|
1918
|
+
if (adapters.includes("ext-apps")) {
|
|
1919
|
+
parts.push(generateExtAppsAdapter(trustedOrigins));
|
|
1920
|
+
parts.push("");
|
|
1921
|
+
}
|
|
1922
|
+
if (adapters.includes("claude")) {
|
|
1923
|
+
parts.push(generateClaudeAdapter());
|
|
1924
|
+
parts.push("");
|
|
1925
|
+
}
|
|
1926
|
+
if (adapters.includes("gemini")) {
|
|
1927
|
+
parts.push(generateGeminiAdapter());
|
|
1928
|
+
parts.push("");
|
|
1929
|
+
}
|
|
1930
|
+
if (adapters.includes("generic")) {
|
|
1931
|
+
parts.push(generateGenericAdapter());
|
|
1932
|
+
parts.push("");
|
|
1933
|
+
}
|
|
1934
|
+
parts.push(generatePlatformDetection(adapters));
|
|
1935
|
+
parts.push("");
|
|
1936
|
+
parts.push(generateBridgeClass());
|
|
1937
|
+
parts.push("");
|
|
1938
|
+
parts.push("var bridge = new FrontMcpBridge();");
|
|
1939
|
+
parts.push("bridge.initialize().then(function() {");
|
|
1940
|
+
parts.push(' log("Bridge initialized with adapter: " + bridge.adapterId);');
|
|
1941
|
+
parts.push(' window.dispatchEvent(new CustomEvent("bridge:ready", { detail: { adapter: bridge.adapterId } }));');
|
|
1942
|
+
parts.push("}).catch(function(err) {");
|
|
1943
|
+
parts.push(' console.error("[FrontMcpBridge] Init failed:", err);');
|
|
1944
|
+
parts.push(' window.dispatchEvent(new CustomEvent("bridge:error", { detail: { error: err } }));');
|
|
1945
|
+
parts.push("});");
|
|
1946
|
+
parts.push("");
|
|
1947
|
+
parts.push("window.FrontMcpBridge = bridge;");
|
|
1948
|
+
parts.push("})();");
|
|
1949
|
+
const code = parts.join("\n");
|
|
1950
|
+
if (minify) {
|
|
1951
|
+
return minifyJS(code);
|
|
1952
|
+
}
|
|
1953
|
+
return code;
|
|
1954
|
+
}
|
|
1955
|
+
function generateContextDetection() {
|
|
1956
|
+
return `
|
|
1957
|
+
function detectTheme() {
|
|
1958
|
+
if (typeof window !== 'undefined' && window.matchMedia) {
|
|
1959
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
1960
|
+
}
|
|
1961
|
+
return 'light';
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
function detectLocale() {
|
|
1965
|
+
if (typeof navigator !== 'undefined') {
|
|
1966
|
+
return navigator.language || 'en-US';
|
|
1967
|
+
}
|
|
1968
|
+
return 'en-US';
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
function detectUserAgent() {
|
|
1972
|
+
if (typeof navigator === 'undefined') {
|
|
1973
|
+
return { type: 'web', hover: true, touch: false };
|
|
1974
|
+
}
|
|
1975
|
+
var ua = navigator.userAgent || '';
|
|
1976
|
+
var isMobile = /iPhone|iPad|iPod|Android/i.test(ua);
|
|
1977
|
+
var hasTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
|
1978
|
+
var hasHover = window.matchMedia && window.matchMedia('(hover: hover)').matches;
|
|
1979
|
+
return { type: isMobile ? 'mobile' : 'web', hover: hasHover !== false, touch: hasTouch };
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
function detectViewport() {
|
|
1983
|
+
if (typeof window !== 'undefined') {
|
|
1984
|
+
return { width: window.innerWidth, height: window.innerHeight };
|
|
1985
|
+
}
|
|
1986
|
+
return undefined;
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1989
|
+
function readInjectedData() {
|
|
1990
|
+
var data = { toolInput: {}, toolOutput: undefined, structuredContent: undefined };
|
|
1991
|
+
if (typeof window !== 'undefined') {
|
|
1992
|
+
if (window.__mcpToolInput) data.toolInput = window.__mcpToolInput;
|
|
1993
|
+
if (window.__mcpToolOutput) data.toolOutput = window.__mcpToolOutput;
|
|
1994
|
+
if (window.__mcpStructuredContent) data.structuredContent = window.__mcpStructuredContent;
|
|
1995
|
+
}
|
|
1996
|
+
return data;
|
|
1997
|
+
}
|
|
1998
|
+
`.trim();
|
|
1999
|
+
}
|
|
2000
|
+
function generateBaseCapabilities() {
|
|
2001
|
+
return `
|
|
2002
|
+
var DEFAULT_CAPABILITIES = {
|
|
2003
|
+
canCallTools: false,
|
|
2004
|
+
canSendMessages: false,
|
|
2005
|
+
canOpenLinks: false,
|
|
2006
|
+
canPersistState: true,
|
|
2007
|
+
hasNetworkAccess: true,
|
|
2008
|
+
supportsDisplayModes: false,
|
|
2009
|
+
supportsTheme: true
|
|
2010
|
+
};
|
|
2011
|
+
`.trim();
|
|
2012
|
+
}
|
|
2013
|
+
function generateOpenAIAdapter() {
|
|
2014
|
+
return `
|
|
2015
|
+
var OpenAIAdapter = {
|
|
2016
|
+
id: 'openai',
|
|
2017
|
+
name: 'OpenAI ChatGPT',
|
|
2018
|
+
priority: 100,
|
|
2019
|
+
capabilities: Object.assign({}, DEFAULT_CAPABILITIES, {
|
|
2020
|
+
canCallTools: true,
|
|
2021
|
+
canSendMessages: true,
|
|
2022
|
+
canOpenLinks: true,
|
|
2023
|
+
supportsDisplayModes: true
|
|
2024
|
+
}),
|
|
2025
|
+
canHandle: function() {
|
|
2026
|
+
if (typeof window === 'undefined') return false;
|
|
2027
|
+
// Check for window.openai.callTool (the actual OpenAI SDK API)
|
|
2028
|
+
if (window.openai && typeof window.openai.callTool === 'function') return true;
|
|
2029
|
+
// Also check if we're being injected with tool metadata (OpenAI injects toolOutput)
|
|
2030
|
+
if (window.openai && (window.openai.toolOutput !== undefined || window.openai.toolInput !== undefined)) return true;
|
|
2031
|
+
return false;
|
|
2032
|
+
},
|
|
2033
|
+
initialize: function(context) {
|
|
2034
|
+
var sdk = window.openai;
|
|
2035
|
+
context.sdk = sdk;
|
|
2036
|
+
// OpenAI SDK exposes theme and displayMode directly as properties
|
|
2037
|
+
if (sdk.theme) {
|
|
2038
|
+
context.hostContext.theme = sdk.theme;
|
|
2039
|
+
}
|
|
2040
|
+
if (sdk.displayMode) {
|
|
2041
|
+
context.hostContext.displayMode = sdk.displayMode;
|
|
2042
|
+
}
|
|
2043
|
+
// Note: OpenAI SDK does not have an onContextChange equivalent
|
|
2044
|
+
return Promise.resolve();
|
|
2045
|
+
},
|
|
2046
|
+
callTool: function(context, name, args) {
|
|
2047
|
+
return context.sdk.callTool(name, args);
|
|
2048
|
+
},
|
|
2049
|
+
sendMessage: function(context, content) {
|
|
2050
|
+
if (typeof context.sdk.sendFollowUpMessage === 'function') {
|
|
2051
|
+
return context.sdk.sendFollowUpMessage(content);
|
|
2052
|
+
}
|
|
2053
|
+
return Promise.reject(new Error('Messages not supported'));
|
|
2054
|
+
},
|
|
2055
|
+
openLink: function(context, url) {
|
|
2056
|
+
window.open(url, '_blank', 'noopener,noreferrer');
|
|
2057
|
+
return Promise.resolve();
|
|
2058
|
+
},
|
|
2059
|
+
requestDisplayMode: function(context, mode) {
|
|
2060
|
+
return Promise.resolve();
|
|
2061
|
+
},
|
|
2062
|
+
requestClose: function(context) {
|
|
2063
|
+
return Promise.resolve();
|
|
2064
|
+
}
|
|
2065
|
+
};
|
|
2066
|
+
`.trim();
|
|
2067
|
+
}
|
|
2068
|
+
function generateExtAppsAdapter(trustedOrigins) {
|
|
2069
|
+
const originsArray = trustedOrigins.length > 0 ? JSON.stringify(trustedOrigins) : "[]";
|
|
2070
|
+
return `
|
|
2071
|
+
var ExtAppsAdapter = {
|
|
2072
|
+
id: 'ext-apps',
|
|
2073
|
+
name: 'ext-apps (SEP-1865)',
|
|
2074
|
+
priority: 80,
|
|
2075
|
+
capabilities: Object.assign({}, DEFAULT_CAPABILITIES, { canPersistState: true, hasNetworkAccess: true }),
|
|
2076
|
+
trustedOrigins: ${originsArray},
|
|
2077
|
+
trustedOrigin: null,
|
|
2078
|
+
pendingRequests: {},
|
|
2079
|
+
requestId: 0,
|
|
2080
|
+
hostCapabilities: {},
|
|
2081
|
+
canHandle: function() {
|
|
2082
|
+
if (typeof window === 'undefined') return false;
|
|
2083
|
+
if (window.parent === window) return false;
|
|
2084
|
+
// Check for OpenAI SDK (window.openai.callTool) - defer to OpenAIAdapter
|
|
2085
|
+
if (window.openai && typeof window.openai.callTool === 'function') return false;
|
|
2086
|
+
if (window.__mcpPlatform === 'ext-apps') return true;
|
|
2087
|
+
return true;
|
|
2088
|
+
},
|
|
2089
|
+
initialize: function(context) {
|
|
2090
|
+
var self = this;
|
|
2091
|
+
context.extApps = this;
|
|
2092
|
+
|
|
2093
|
+
window.addEventListener('message', function(event) {
|
|
2094
|
+
self.handleMessage(context, event);
|
|
2095
|
+
});
|
|
2096
|
+
|
|
2097
|
+
return self.performHandshake(context);
|
|
2098
|
+
},
|
|
2099
|
+
handleMessage: function(context, event) {
|
|
2100
|
+
if (!this.isOriginTrusted(event.origin)) return;
|
|
2101
|
+
var data = event.data;
|
|
2102
|
+
if (!data || typeof data !== 'object' || data.jsonrpc !== '2.0') return;
|
|
2103
|
+
|
|
2104
|
+
if ('id' in data && (data.result !== undefined || data.error !== undefined)) {
|
|
2105
|
+
var pending = this.pendingRequests[data.id];
|
|
2106
|
+
if (pending) {
|
|
2107
|
+
clearTimeout(pending.timeout);
|
|
2108
|
+
delete this.pendingRequests[data.id];
|
|
2109
|
+
if (data.error) {
|
|
2110
|
+
pending.reject(new Error(data.error.message + ' (code: ' + data.error.code + ')'));
|
|
2111
|
+
} else {
|
|
2112
|
+
pending.resolve(data.result);
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
return;
|
|
2116
|
+
}
|
|
2117
|
+
|
|
2118
|
+
if ('method' in data && !('id' in data)) {
|
|
2119
|
+
this.handleNotification(context, data);
|
|
2120
|
+
}
|
|
2121
|
+
},
|
|
2122
|
+
handleNotification: function(context, notification) {
|
|
2123
|
+
var params = notification.params || {};
|
|
2124
|
+
switch (notification.method) {
|
|
2125
|
+
case 'ui/notifications/tool-input':
|
|
2126
|
+
context.toolInput = params.arguments || {};
|
|
2127
|
+
window.dispatchEvent(new CustomEvent('tool:input', { detail: { arguments: context.toolInput } }));
|
|
2128
|
+
break;
|
|
2129
|
+
case 'ui/notifications/tool-result':
|
|
2130
|
+
context.toolOutput = params.content;
|
|
2131
|
+
context.structuredContent = params.structuredContent;
|
|
2132
|
+
context.notifyToolResult(params.content);
|
|
2133
|
+
window.dispatchEvent(new CustomEvent('tool:result', { detail: params }));
|
|
2134
|
+
break;
|
|
2135
|
+
case 'ui/notifications/host-context-changed':
|
|
2136
|
+
Object.assign(context.hostContext, params);
|
|
2137
|
+
context.notifyContextChange(params);
|
|
2138
|
+
break;
|
|
2139
|
+
}
|
|
2140
|
+
},
|
|
2141
|
+
isOriginTrusted: function(origin) {
|
|
2142
|
+
if (this.trustedOrigins.length > 0) {
|
|
2143
|
+
return this.trustedOrigins.indexOf(origin) !== -1;
|
|
2144
|
+
}
|
|
2145
|
+
// When no trusted origins configured, only trust first message in iframe context
|
|
2146
|
+
// This helps mitigate race conditions where a malicious iframe could establish trust
|
|
2147
|
+
if (!this.trustedOrigin) {
|
|
2148
|
+
if (window.parent !== window && origin) {
|
|
2149
|
+
this.trustedOrigin = origin;
|
|
2150
|
+
return true;
|
|
2151
|
+
}
|
|
2152
|
+
return false;
|
|
2153
|
+
}
|
|
2154
|
+
return this.trustedOrigin === origin;
|
|
2155
|
+
},
|
|
2156
|
+
sendRequest: function(method, params) {
|
|
2157
|
+
var self = this;
|
|
2158
|
+
return new Promise(function(resolve, reject) {
|
|
2159
|
+
// Security: Require trusted origin before sending requests to prevent message leaks
|
|
2160
|
+
if (!self.trustedOrigin && self.trustedOrigins.length === 0) {
|
|
2161
|
+
reject(new Error('Cannot send request: no trusted origin established'));
|
|
2162
|
+
return;
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
var id = ++self.requestId;
|
|
2166
|
+
var timeout = setTimeout(function() {
|
|
2167
|
+
delete self.pendingRequests[id];
|
|
2168
|
+
reject(new Error('Request ' + method + ' timed out'));
|
|
2169
|
+
}, 10000);
|
|
2170
|
+
|
|
2171
|
+
self.pendingRequests[id] = { resolve: resolve, reject: reject, timeout: timeout };
|
|
2172
|
+
|
|
2173
|
+
var targetOrigin = self.trustedOrigin || self.trustedOrigins[0];
|
|
2174
|
+
window.parent.postMessage({ jsonrpc: '2.0', id: id, method: method, params: params }, targetOrigin);
|
|
2175
|
+
});
|
|
2176
|
+
},
|
|
2177
|
+
performHandshake: function(context) {
|
|
2178
|
+
var self = this;
|
|
2179
|
+
var params = {
|
|
2180
|
+
appInfo: { name: 'FrontMCP Widget', version: '1.0.0' },
|
|
2181
|
+
appCapabilities: { tools: { listChanged: false } },
|
|
2182
|
+
protocolVersion: '2024-11-05'
|
|
2183
|
+
};
|
|
2184
|
+
|
|
2185
|
+
return this.sendRequest('ui/initialize', params).then(function(result) {
|
|
2186
|
+
self.hostCapabilities = result.hostCapabilities || {};
|
|
2187
|
+
self.capabilities = Object.assign({}, self.capabilities, {
|
|
2188
|
+
canCallTools: Boolean(self.hostCapabilities.serverToolProxy),
|
|
2189
|
+
canSendMessages: true,
|
|
2190
|
+
canOpenLinks: Boolean(self.hostCapabilities.openLink),
|
|
2191
|
+
supportsDisplayModes: true
|
|
2192
|
+
});
|
|
2193
|
+
if (result.hostContext) {
|
|
2194
|
+
Object.assign(context.hostContext, result.hostContext);
|
|
2195
|
+
}
|
|
2196
|
+
});
|
|
2197
|
+
},
|
|
2198
|
+
callTool: function(context, name, args) {
|
|
2199
|
+
if (!this.hostCapabilities.serverToolProxy) {
|
|
2200
|
+
return Promise.reject(new Error('Server tool proxy not supported'));
|
|
2201
|
+
}
|
|
2202
|
+
return this.sendRequest('ui/callServerTool', { name: name, arguments: args });
|
|
2203
|
+
},
|
|
2204
|
+
sendMessage: function(context, content) {
|
|
2205
|
+
return this.sendRequest('ui/message', { content: content });
|
|
2206
|
+
},
|
|
2207
|
+
openLink: function(context, url) {
|
|
2208
|
+
if (!this.hostCapabilities.openLink) {
|
|
2209
|
+
window.open(url, '_blank', 'noopener,noreferrer');
|
|
2210
|
+
return Promise.resolve();
|
|
2211
|
+
}
|
|
2212
|
+
return this.sendRequest('ui/openLink', { url: url });
|
|
2213
|
+
},
|
|
2214
|
+
requestDisplayMode: function(context, mode) {
|
|
2215
|
+
return this.sendRequest('ui/setDisplayMode', { mode: mode });
|
|
2216
|
+
},
|
|
2217
|
+
requestClose: function(context) {
|
|
2218
|
+
return this.sendRequest('ui/close', {});
|
|
2219
|
+
}
|
|
2220
|
+
};
|
|
2221
|
+
`.trim();
|
|
2222
|
+
}
|
|
2223
|
+
function generateClaudeAdapter() {
|
|
2224
|
+
return `
|
|
2225
|
+
var ClaudeAdapter = {
|
|
2226
|
+
id: 'claude',
|
|
2227
|
+
name: 'Claude (Anthropic)',
|
|
2228
|
+
priority: 60,
|
|
2229
|
+
capabilities: Object.assign({}, DEFAULT_CAPABILITIES, {
|
|
2230
|
+
canCallTools: false,
|
|
2231
|
+
canSendMessages: false,
|
|
2232
|
+
canOpenLinks: true,
|
|
2233
|
+
hasNetworkAccess: false,
|
|
2234
|
+
supportsDisplayModes: false
|
|
2235
|
+
}),
|
|
2236
|
+
canHandle: function() {
|
|
2237
|
+
if (typeof window === 'undefined') return false;
|
|
2238
|
+
if (window.__mcpPlatform === 'claude') return true;
|
|
2239
|
+
if (window.claude) return true;
|
|
2240
|
+
if (window.__claudeArtifact) return true;
|
|
2241
|
+
if (typeof location !== 'undefined') {
|
|
2242
|
+
var href = location.href;
|
|
2243
|
+
if (href.indexOf('claude.ai') !== -1 || href.indexOf('anthropic.com') !== -1) return true;
|
|
2244
|
+
}
|
|
2245
|
+
return false;
|
|
2246
|
+
},
|
|
2247
|
+
initialize: function(context) {
|
|
2248
|
+
return Promise.resolve();
|
|
2249
|
+
},
|
|
2250
|
+
callTool: function() {
|
|
2251
|
+
return Promise.reject(new Error('Tool calls not supported in Claude'));
|
|
2252
|
+
},
|
|
2253
|
+
sendMessage: function() {
|
|
2254
|
+
return Promise.reject(new Error('Messages not supported in Claude'));
|
|
2255
|
+
},
|
|
2256
|
+
openLink: function(context, url) {
|
|
2257
|
+
window.open(url, '_blank', 'noopener,noreferrer');
|
|
2258
|
+
return Promise.resolve();
|
|
2259
|
+
},
|
|
2260
|
+
requestDisplayMode: function() {
|
|
2261
|
+
return Promise.resolve();
|
|
2262
|
+
},
|
|
2263
|
+
requestClose: function() {
|
|
2264
|
+
return Promise.resolve();
|
|
2265
|
+
}
|
|
2266
|
+
};
|
|
2267
|
+
`.trim();
|
|
2268
|
+
}
|
|
2269
|
+
function generateGeminiAdapter() {
|
|
2270
|
+
return `
|
|
2271
|
+
var GeminiAdapter = {
|
|
2272
|
+
id: 'gemini',
|
|
2273
|
+
name: 'Google Gemini',
|
|
2274
|
+
priority: 40,
|
|
2275
|
+
capabilities: Object.assign({}, DEFAULT_CAPABILITIES, {
|
|
2276
|
+
canOpenLinks: true,
|
|
2277
|
+
hasNetworkAccess: true
|
|
2278
|
+
}),
|
|
2279
|
+
canHandle: function() {
|
|
2280
|
+
if (typeof window === 'undefined') return false;
|
|
2281
|
+
if (window.__mcpPlatform === 'gemini') return true;
|
|
2282
|
+
if (window.gemini) return true;
|
|
2283
|
+
if (typeof location !== 'undefined') {
|
|
2284
|
+
var href = location.href;
|
|
2285
|
+
if (href.indexOf('gemini.google.com') !== -1 || href.indexOf('bard.google.com') !== -1) return true;
|
|
2286
|
+
}
|
|
2287
|
+
return false;
|
|
2288
|
+
},
|
|
2289
|
+
initialize: function(context) {
|
|
2290
|
+
if (window.gemini && window.gemini.ui && window.gemini.ui.getTheme) {
|
|
2291
|
+
context.hostContext.theme = window.gemini.ui.getTheme() === 'dark' ? 'dark' : 'light';
|
|
2292
|
+
}
|
|
2293
|
+
return Promise.resolve();
|
|
2294
|
+
},
|
|
2295
|
+
callTool: function() {
|
|
2296
|
+
return Promise.reject(new Error('Tool calls not supported in Gemini'));
|
|
2297
|
+
},
|
|
2298
|
+
sendMessage: function(context, content) {
|
|
2299
|
+
if (window.gemini && window.gemini.ui && window.gemini.ui.sendMessage) {
|
|
2300
|
+
return window.gemini.ui.sendMessage(content);
|
|
2301
|
+
}
|
|
2302
|
+
return Promise.reject(new Error('Messages not supported in Gemini'));
|
|
2303
|
+
},
|
|
2304
|
+
openLink: function(context, url) {
|
|
2305
|
+
if (window.gemini && window.gemini.ui && window.gemini.ui.openLink) {
|
|
2306
|
+
return window.gemini.ui.openLink(url);
|
|
2307
|
+
}
|
|
2308
|
+
window.open(url, '_blank', 'noopener,noreferrer');
|
|
2309
|
+
return Promise.resolve();
|
|
2310
|
+
},
|
|
2311
|
+
requestDisplayMode: function() {
|
|
2312
|
+
return Promise.resolve();
|
|
2313
|
+
},
|
|
2314
|
+
requestClose: function() {
|
|
2315
|
+
return Promise.resolve();
|
|
2316
|
+
}
|
|
2317
|
+
};
|
|
2318
|
+
`.trim();
|
|
2319
|
+
}
|
|
2320
|
+
function generateGenericAdapter() {
|
|
2321
|
+
return `
|
|
2322
|
+
var GenericAdapter = {
|
|
2323
|
+
id: 'generic',
|
|
2324
|
+
name: 'Generic Web',
|
|
2325
|
+
priority: 0,
|
|
2326
|
+
capabilities: Object.assign({}, DEFAULT_CAPABILITIES, {
|
|
2327
|
+
canOpenLinks: true,
|
|
2328
|
+
hasNetworkAccess: true
|
|
2329
|
+
}),
|
|
2330
|
+
canHandle: function() {
|
|
2331
|
+
return typeof window !== 'undefined';
|
|
2332
|
+
},
|
|
2333
|
+
initialize: function(context) {
|
|
2334
|
+
return Promise.resolve();
|
|
2335
|
+
},
|
|
2336
|
+
callTool: function() {
|
|
2337
|
+
return Promise.reject(new Error('Tool calls not supported'));
|
|
2338
|
+
},
|
|
2339
|
+
sendMessage: function() {
|
|
2340
|
+
return Promise.reject(new Error('Messages not supported'));
|
|
2341
|
+
},
|
|
2342
|
+
openLink: function(context, url) {
|
|
2343
|
+
window.open(url, '_blank', 'noopener,noreferrer');
|
|
2344
|
+
return Promise.resolve();
|
|
2345
|
+
},
|
|
2346
|
+
requestDisplayMode: function() {
|
|
2347
|
+
return Promise.resolve();
|
|
2348
|
+
},
|
|
2349
|
+
requestClose: function() {
|
|
2350
|
+
return Promise.resolve();
|
|
2351
|
+
}
|
|
2352
|
+
};
|
|
2353
|
+
`.trim();
|
|
2354
|
+
}
|
|
2355
|
+
function generatePlatformDetection(adapters) {
|
|
2356
|
+
const adapterVars = adapters.map((a) => {
|
|
2357
|
+
switch (a) {
|
|
2358
|
+
case "openai":
|
|
2359
|
+
return "OpenAIAdapter";
|
|
2360
|
+
case "ext-apps":
|
|
2361
|
+
return "ExtAppsAdapter";
|
|
2362
|
+
case "claude":
|
|
2363
|
+
return "ClaudeAdapter";
|
|
2364
|
+
case "gemini":
|
|
2365
|
+
return "GeminiAdapter";
|
|
2366
|
+
case "generic":
|
|
2367
|
+
return "GenericAdapter";
|
|
2368
|
+
default:
|
|
2369
|
+
return "";
|
|
2370
|
+
}
|
|
2371
|
+
}).filter(Boolean);
|
|
2372
|
+
return `
|
|
2373
|
+
var ADAPTERS = [${adapterVars.join(", ")}].sort(function(a, b) { return b.priority - a.priority; });
|
|
2374
|
+
|
|
2375
|
+
function detectPlatform() {
|
|
2376
|
+
for (var i = 0; i < ADAPTERS.length; i++) {
|
|
2377
|
+
if (ADAPTERS[i].canHandle()) {
|
|
2378
|
+
log('Detected platform: ' + ADAPTERS[i].id);
|
|
2379
|
+
return ADAPTERS[i];
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2382
|
+
log('No platform detected, using generic');
|
|
2383
|
+
return GenericAdapter;
|
|
2384
|
+
}
|
|
2385
|
+
`.trim();
|
|
2386
|
+
}
|
|
2387
|
+
function generateBridgeClass() {
|
|
2388
|
+
return `
|
|
2389
|
+
function FrontMcpBridge() {
|
|
2390
|
+
this._adapter = null;
|
|
2391
|
+
this._initialized = false;
|
|
2392
|
+
this._context = {
|
|
2393
|
+
hostContext: {
|
|
2394
|
+
theme: detectTheme(),
|
|
2395
|
+
displayMode: 'inline',
|
|
2396
|
+
locale: detectLocale(),
|
|
2397
|
+
userAgent: detectUserAgent(),
|
|
2398
|
+
safeArea: DEFAULT_SAFE_AREA,
|
|
2399
|
+
viewport: detectViewport()
|
|
2400
|
+
},
|
|
2401
|
+
toolInput: {},
|
|
2402
|
+
toolOutput: undefined,
|
|
2403
|
+
structuredContent: undefined,
|
|
2404
|
+
widgetState: {},
|
|
2405
|
+
contextListeners: [],
|
|
2406
|
+
toolResultListeners: [],
|
|
2407
|
+
notifyContextChange: function(changes) {
|
|
2408
|
+
Object.assign(this.hostContext, changes);
|
|
2409
|
+
for (var i = 0; i < this.contextListeners.length; i++) {
|
|
2410
|
+
try { this.contextListeners[i](changes); } catch(e) {}
|
|
2411
|
+
}
|
|
2412
|
+
},
|
|
2413
|
+
notifyToolResult: function(result) {
|
|
2414
|
+
this.toolOutput = result;
|
|
2415
|
+
for (var i = 0; i < this.toolResultListeners.length; i++) {
|
|
2416
|
+
try { this.toolResultListeners[i](result); } catch(e) {}
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2419
|
+
};
|
|
2420
|
+
|
|
2421
|
+
var injected = readInjectedData();
|
|
2422
|
+
this._context.toolInput = injected.toolInput;
|
|
2423
|
+
this._context.toolOutput = injected.toolOutput;
|
|
2424
|
+
this._context.structuredContent = injected.structuredContent;
|
|
2425
|
+
|
|
2426
|
+
this._loadWidgetState();
|
|
2427
|
+
}
|
|
2428
|
+
|
|
2429
|
+
FrontMcpBridge.prototype._loadWidgetState = function() {
|
|
2430
|
+
try {
|
|
2431
|
+
var key = 'frontmcp:widget:' + (window.__mcpToolName || 'unknown');
|
|
2432
|
+
var stored = localStorage.getItem(key);
|
|
2433
|
+
if (stored) this._context.widgetState = JSON.parse(stored);
|
|
2434
|
+
} catch(e) {}
|
|
2435
|
+
};
|
|
2436
|
+
|
|
2437
|
+
FrontMcpBridge.prototype._saveWidgetState = function() {
|
|
2438
|
+
try {
|
|
2439
|
+
var key = 'frontmcp:widget:' + (window.__mcpToolName || 'unknown');
|
|
2440
|
+
localStorage.setItem(key, JSON.stringify(this._context.widgetState));
|
|
2441
|
+
} catch(e) {}
|
|
2442
|
+
};
|
|
2443
|
+
|
|
2444
|
+
FrontMcpBridge.prototype.initialize = function() {
|
|
2445
|
+
if (this._initialized) return Promise.resolve();
|
|
2446
|
+
var self = this;
|
|
2447
|
+
this._adapter = detectPlatform();
|
|
2448
|
+
return this._adapter.initialize(this._context).then(function() {
|
|
2449
|
+
self._initialized = true;
|
|
2450
|
+
// Set up the data-tool-call click handler after initialization
|
|
2451
|
+
self._setupDataToolCallHandler();
|
|
2452
|
+
});
|
|
2453
|
+
};
|
|
2454
|
+
|
|
2455
|
+
Object.defineProperty(FrontMcpBridge.prototype, 'initialized', { get: function() { return this._initialized; } });
|
|
2456
|
+
Object.defineProperty(FrontMcpBridge.prototype, 'adapterId', { get: function() { return this._adapter ? this._adapter.id : undefined; } });
|
|
2457
|
+
Object.defineProperty(FrontMcpBridge.prototype, 'capabilities', { get: function() { return this._adapter ? this._adapter.capabilities : DEFAULT_CAPABILITIES; } });
|
|
2458
|
+
|
|
2459
|
+
FrontMcpBridge.prototype.getTheme = function() { return this._context.hostContext.theme; };
|
|
2460
|
+
FrontMcpBridge.prototype.getDisplayMode = function() { return this._context.hostContext.displayMode; };
|
|
2461
|
+
FrontMcpBridge.prototype.getToolInput = function() { return this._context.toolInput; };
|
|
2462
|
+
FrontMcpBridge.prototype.getToolOutput = function() { return this._context.toolOutput; };
|
|
2463
|
+
FrontMcpBridge.prototype.getStructuredContent = function() { return this._context.structuredContent; };
|
|
2464
|
+
FrontMcpBridge.prototype.getWidgetState = function() { return this._context.widgetState; };
|
|
2465
|
+
FrontMcpBridge.prototype.getHostContext = function() { return Object.assign({}, this._context.hostContext); };
|
|
2466
|
+
FrontMcpBridge.prototype.hasCapability = function(cap) { return this._adapter && this._adapter.capabilities[cap] === true; };
|
|
2467
|
+
|
|
2468
|
+
// Get tool response metadata (platform-agnostic)
|
|
2469
|
+
// Used by inline mode widgets to detect when ui/html arrives
|
|
2470
|
+
FrontMcpBridge.prototype.getToolResponseMetadata = function() {
|
|
2471
|
+
// OpenAI injects toolResponseMetadata for widget-producing tools
|
|
2472
|
+
if (typeof window !== 'undefined' && window.openai && window.openai.toolResponseMetadata) {
|
|
2473
|
+
return window.openai.toolResponseMetadata;
|
|
2474
|
+
}
|
|
2475
|
+
// Claude (future support)
|
|
2476
|
+
if (typeof window !== 'undefined' && window.claude && window.claude.toolResponseMetadata) {
|
|
2477
|
+
return window.claude.toolResponseMetadata;
|
|
2478
|
+
}
|
|
2479
|
+
// FrontMCP direct injection (for testing/ext-apps)
|
|
2480
|
+
if (typeof window !== 'undefined' && window.__mcpToolResponseMetadata) {
|
|
2481
|
+
return window.__mcpToolResponseMetadata;
|
|
2482
|
+
}
|
|
2483
|
+
return null;
|
|
2484
|
+
};
|
|
2485
|
+
|
|
2486
|
+
// Subscribe to tool response metadata changes (for inline mode injection)
|
|
2487
|
+
FrontMcpBridge.prototype.onToolResponseMetadata = function(callback) {
|
|
2488
|
+
var self = this;
|
|
2489
|
+
var called = false;
|
|
2490
|
+
|
|
2491
|
+
// Check if already available
|
|
2492
|
+
var existing = self.getToolResponseMetadata();
|
|
2493
|
+
if (existing) {
|
|
2494
|
+
called = true;
|
|
2495
|
+
callback(existing);
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2498
|
+
// Set up property interceptors for OpenAI
|
|
2499
|
+
if (typeof window !== 'undefined') {
|
|
2500
|
+
// OpenAI: Intercept toolResponseMetadata assignment
|
|
2501
|
+
if (!window.__frontmcpMetadataIntercepted) {
|
|
2502
|
+
window.__frontmcpMetadataIntercepted = true;
|
|
2503
|
+
window.__frontmcpMetadataCallbacks = [];
|
|
2504
|
+
|
|
2505
|
+
// Create openai object if it doesn't exist
|
|
2506
|
+
if (!window.openai) window.openai = {};
|
|
2507
|
+
|
|
2508
|
+
var originalMetadata = window.openai.toolResponseMetadata;
|
|
2509
|
+
Object.defineProperty(window.openai, 'toolResponseMetadata', {
|
|
2510
|
+
get: function() { return originalMetadata; },
|
|
2511
|
+
set: function(val) {
|
|
2512
|
+
originalMetadata = val;
|
|
2513
|
+
log('toolResponseMetadata set, notifying ' + window.__frontmcpMetadataCallbacks.length + ' listeners');
|
|
2514
|
+
for (var i = 0; i < window.__frontmcpMetadataCallbacks.length; i++) {
|
|
2515
|
+
try { window.__frontmcpMetadataCallbacks[i](val); } catch(e) {}
|
|
2516
|
+
}
|
|
2517
|
+
},
|
|
2518
|
+
configurable: true
|
|
2519
|
+
});
|
|
2520
|
+
}
|
|
2521
|
+
|
|
2522
|
+
// Register callback wrapper (store reference for unsubscribe)
|
|
2523
|
+
var wrapper = function(metadata) {
|
|
2524
|
+
if (!called) {
|
|
2525
|
+
called = true;
|
|
2526
|
+
callback(metadata);
|
|
2527
|
+
}
|
|
2528
|
+
};
|
|
2529
|
+
window.__frontmcpMetadataCallbacks.push(wrapper);
|
|
2530
|
+
|
|
2531
|
+
// Return unsubscribe function that removes the wrapper (not the original callback)
|
|
2532
|
+
return function() {
|
|
2533
|
+
if (window.__frontmcpMetadataCallbacks) {
|
|
2534
|
+
var idx = window.__frontmcpMetadataCallbacks.indexOf(wrapper);
|
|
2535
|
+
if (idx !== -1) window.__frontmcpMetadataCallbacks.splice(idx, 1);
|
|
2536
|
+
}
|
|
2537
|
+
};
|
|
2538
|
+
}
|
|
2539
|
+
|
|
2540
|
+
// Return no-op unsubscribe for non-window environments
|
|
2541
|
+
return function() {};
|
|
2542
|
+
};
|
|
2543
|
+
|
|
2544
|
+
FrontMcpBridge.prototype.callTool = function(name, args) {
|
|
2545
|
+
// Priority 1: Direct OpenAI SDK call (most reliable in OpenAI iframe)
|
|
2546
|
+
// This bypasses adapter abstraction for maximum compatibility
|
|
2547
|
+
if (typeof window !== 'undefined' && window.openai && typeof window.openai.callTool === 'function') {
|
|
2548
|
+
log('callTool: Using OpenAI SDK directly');
|
|
2549
|
+
return window.openai.callTool(name, args);
|
|
2550
|
+
}
|
|
2551
|
+
|
|
2552
|
+
// Priority 2: Use adapter (if initialized and supports tool calls)
|
|
2553
|
+
if (this._adapter && this._adapter.capabilities && this._adapter.capabilities.canCallTools) {
|
|
2554
|
+
log('callTool: Using adapter ' + this._adapter.id);
|
|
2555
|
+
return this._adapter.callTool(this._context, name, args);
|
|
2556
|
+
}
|
|
2557
|
+
|
|
2558
|
+
// Not initialized or no tool support
|
|
2559
|
+
if (!this._adapter) {
|
|
2560
|
+
return Promise.reject(new Error('Bridge not initialized. Wait for bridge:ready event.'));
|
|
2561
|
+
}
|
|
2562
|
+
return Promise.reject(new Error('Tool calls not supported on this platform (' + this._adapter.id + ')'));
|
|
2563
|
+
};
|
|
2564
|
+
|
|
2565
|
+
FrontMcpBridge.prototype.sendMessage = function(content) {
|
|
2566
|
+
if (!this._adapter) return Promise.reject(new Error('Not initialized'));
|
|
2567
|
+
return this._adapter.sendMessage(this._context, content);
|
|
2568
|
+
};
|
|
2569
|
+
|
|
2570
|
+
FrontMcpBridge.prototype.openLink = function(url) {
|
|
2571
|
+
if (!this._adapter) return Promise.reject(new Error('Not initialized'));
|
|
2572
|
+
return this._adapter.openLink(this._context, url);
|
|
2573
|
+
};
|
|
2574
|
+
|
|
2575
|
+
FrontMcpBridge.prototype.requestDisplayMode = function(mode) {
|
|
2576
|
+
if (!this._adapter) return Promise.reject(new Error('Not initialized'));
|
|
2577
|
+
var self = this;
|
|
2578
|
+
return this._adapter.requestDisplayMode(this._context, mode).then(function() {
|
|
2579
|
+
self._context.hostContext.displayMode = mode;
|
|
2580
|
+
});
|
|
2581
|
+
};
|
|
2582
|
+
|
|
2583
|
+
FrontMcpBridge.prototype.requestClose = function() {
|
|
2584
|
+
if (!this._adapter) return Promise.reject(new Error('Not initialized'));
|
|
2585
|
+
return this._adapter.requestClose(this._context);
|
|
2586
|
+
};
|
|
2587
|
+
|
|
2588
|
+
FrontMcpBridge.prototype.setWidgetState = function(state) {
|
|
2589
|
+
Object.assign(this._context.widgetState, state);
|
|
2590
|
+
this._saveWidgetState();
|
|
2591
|
+
};
|
|
2592
|
+
|
|
2593
|
+
FrontMcpBridge.prototype.onContextChange = function(callback) {
|
|
2594
|
+
var listeners = this._context.contextListeners;
|
|
2595
|
+
listeners.push(callback);
|
|
2596
|
+
return function() {
|
|
2597
|
+
var idx = listeners.indexOf(callback);
|
|
2598
|
+
if (idx !== -1) listeners.splice(idx, 1);
|
|
2599
|
+
};
|
|
2600
|
+
};
|
|
2601
|
+
|
|
2602
|
+
FrontMcpBridge.prototype.onToolResult = function(callback) {
|
|
2603
|
+
var listeners = this._context.toolResultListeners;
|
|
2604
|
+
listeners.push(callback);
|
|
2605
|
+
return function() {
|
|
2606
|
+
var idx = listeners.indexOf(callback);
|
|
2607
|
+
if (idx !== -1) listeners.splice(idx, 1);
|
|
2608
|
+
};
|
|
2609
|
+
};
|
|
2610
|
+
|
|
2611
|
+
// ==================== data-tool-call Click Handler ====================
|
|
2612
|
+
|
|
2613
|
+
FrontMcpBridge.prototype._setupDataToolCallHandler = function() {
|
|
2614
|
+
var self = this;
|
|
2615
|
+
|
|
2616
|
+
document.addEventListener('click', function(e) {
|
|
2617
|
+
// Find the closest element with data-tool-call attribute
|
|
2618
|
+
var target = e.target;
|
|
2619
|
+
while (target && target !== document) {
|
|
2620
|
+
if (target.hasAttribute && target.hasAttribute('data-tool-call')) {
|
|
2621
|
+
var toolName = target.getAttribute('data-tool-call');
|
|
2622
|
+
var argsAttr = target.getAttribute('data-tool-args');
|
|
2623
|
+
var args = {};
|
|
2624
|
+
|
|
2625
|
+
try {
|
|
2626
|
+
if (argsAttr) {
|
|
2627
|
+
args = JSON.parse(argsAttr);
|
|
2628
|
+
}
|
|
2629
|
+
} catch (parseErr) {
|
|
2630
|
+
console.error('[frontmcp] Failed to parse data-tool-args:', parseErr);
|
|
2631
|
+
}
|
|
2632
|
+
|
|
2633
|
+
log('data-tool-call clicked: ' + toolName);
|
|
2634
|
+
|
|
2635
|
+
// Show loading state - save original content first
|
|
2636
|
+
var originalContent = target.innerHTML;
|
|
2637
|
+
var originalDisabled = target.disabled;
|
|
2638
|
+
target.disabled = true;
|
|
2639
|
+
target.classList.add('opacity-50', 'cursor-not-allowed');
|
|
2640
|
+
|
|
2641
|
+
// Add spinner for buttons
|
|
2642
|
+
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>';
|
|
2643
|
+
if (target.tagName === 'BUTTON') {
|
|
2644
|
+
target.innerHTML = spinner + 'Loading...';
|
|
2645
|
+
}
|
|
2646
|
+
|
|
2647
|
+
// Helper to reset button state
|
|
2648
|
+
function resetButton() {
|
|
2649
|
+
target.innerHTML = originalContent;
|
|
2650
|
+
target.disabled = originalDisabled;
|
|
2651
|
+
target.classList.remove('opacity-50', 'cursor-not-allowed');
|
|
2652
|
+
}
|
|
2653
|
+
|
|
2654
|
+
// Determine how to call the tool
|
|
2655
|
+
var toolCallPromise;
|
|
2656
|
+
|
|
2657
|
+
// Priority 1: Direct OpenAI SDK call (bypasses adapter abstraction)
|
|
2658
|
+
if (typeof window !== 'undefined' && window.openai && typeof window.openai.callTool === 'function') {
|
|
2659
|
+
log('Using OpenAI SDK directly for tool call');
|
|
2660
|
+
toolCallPromise = window.openai.callTool(toolName, args);
|
|
2661
|
+
}
|
|
2662
|
+
// Priority 2: Use adapter (if it supports tool calls)
|
|
2663
|
+
else if (self.hasCapability('canCallTools')) {
|
|
2664
|
+
log('Using adapter for tool call');
|
|
2665
|
+
toolCallPromise = self.callTool(toolName, args);
|
|
2666
|
+
}
|
|
2667
|
+
// No tool call capability
|
|
2668
|
+
else {
|
|
2669
|
+
console.error('[frontmcp] Tool calls not supported on this platform (' + self.adapterId + ')');
|
|
2670
|
+
resetButton();
|
|
2671
|
+
target.dispatchEvent(new CustomEvent('tool:error', {
|
|
2672
|
+
detail: { name: toolName, args: args, error: 'Tool calls not supported on this platform' },
|
|
2673
|
+
bubbles: true
|
|
2674
|
+
}));
|
|
2675
|
+
e.preventDefault();
|
|
2676
|
+
return;
|
|
2677
|
+
}
|
|
2678
|
+
|
|
2679
|
+
// Handle the tool call result
|
|
2680
|
+
toolCallPromise.then(function(result) {
|
|
2681
|
+
log('Tool call succeeded: ' + toolName);
|
|
2682
|
+
resetButton();
|
|
2683
|
+
|
|
2684
|
+
// Update bridge state to trigger widget re-render
|
|
2685
|
+
// React isn't hydrated in OpenAI iframe, so useState doesn't work
|
|
2686
|
+
// Instead, we use the bridge's reactive state system
|
|
2687
|
+
if (result && window.__frontmcp && window.__frontmcp.bridge && typeof window.__frontmcp.bridge.setWidgetState === 'function') {
|
|
2688
|
+
var newData = result.structuredContent || result;
|
|
2689
|
+
log('Updating bridge state with new data');
|
|
2690
|
+
window.__frontmcp.bridge.setWidgetState(newData);
|
|
2691
|
+
}
|
|
2692
|
+
|
|
2693
|
+
// Dispatch success event
|
|
2694
|
+
target.dispatchEvent(new CustomEvent('tool:success', {
|
|
2695
|
+
detail: { name: toolName, args: args, result: result },
|
|
2696
|
+
bubbles: true
|
|
2697
|
+
}));
|
|
2698
|
+
}).catch(function(err) {
|
|
2699
|
+
console.error('[frontmcp] Tool call failed: ' + toolName, err);
|
|
2700
|
+
resetButton();
|
|
2701
|
+
// Dispatch error event
|
|
2702
|
+
target.dispatchEvent(new CustomEvent('tool:error', {
|
|
2703
|
+
detail: { name: toolName, args: args, error: err.message || err },
|
|
2704
|
+
bubbles: true
|
|
2705
|
+
}));
|
|
2706
|
+
});
|
|
2707
|
+
|
|
2708
|
+
// Prevent default behavior (e.g., form submission)
|
|
2709
|
+
e.preventDefault();
|
|
2710
|
+
return;
|
|
2711
|
+
}
|
|
2712
|
+
target = target.parentElement;
|
|
2713
|
+
}
|
|
2714
|
+
}, true); // Use capture phase to handle before React handlers
|
|
2715
|
+
};
|
|
2716
|
+
`.trim();
|
|
2717
|
+
}
|
|
2718
|
+
function minifyJS(code) {
|
|
2719
|
+
return code.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*$/gm, "").replace(/\s+/g, " ").replace(/\s*([{};,:()[\]])\s*/g, "$1").replace(/;\}/g, "}").trim();
|
|
2720
|
+
}
|
|
2721
|
+
function generatePlatformBundle(platform, options = {}) {
|
|
2722
|
+
const platformAdapters = {
|
|
2723
|
+
chatgpt: ["openai", "generic"],
|
|
2724
|
+
claude: ["claude", "generic"],
|
|
2725
|
+
gemini: ["gemini", "generic"],
|
|
2726
|
+
universal: ["openai", "ext-apps", "claude", "gemini", "generic"]
|
|
2727
|
+
};
|
|
2728
|
+
return generateBridgeIIFE({
|
|
2729
|
+
...options,
|
|
2730
|
+
adapters: platformAdapters[platform]
|
|
2731
|
+
});
|
|
2732
|
+
}
|
|
2733
|
+
var UNIVERSAL_BRIDGE_SCRIPT = generateBridgeIIFE();
|
|
2734
|
+
var BRIDGE_SCRIPT_TAGS = {
|
|
2735
|
+
universal: `<script>${UNIVERSAL_BRIDGE_SCRIPT}</script>`,
|
|
2736
|
+
chatgpt: `<script>${generatePlatformBundle("chatgpt")}</script>`,
|
|
2737
|
+
claude: `<script>${generatePlatformBundle("claude")}</script>`,
|
|
2738
|
+
gemini: `<script>${generatePlatformBundle("gemini")}</script>`
|
|
2739
|
+
};
|
|
2740
|
+
|
|
2741
|
+
// libs/ui/src/react/hooks/context.tsx
|
|
2742
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
2743
|
+
var McpBridgeContext = (0, import_react.createContext)(null);
|
|
2744
|
+
function McpBridgeProvider({ children, config, onReady, onError }) {
|
|
2745
|
+
const [bridge, setBridge] = (0, import_react.useState)(null);
|
|
2746
|
+
const [loading, setLoading] = (0, import_react.useState)(true);
|
|
2747
|
+
const [error, setError] = (0, import_react.useState)(null);
|
|
2748
|
+
(0, import_react.useEffect)(() => {
|
|
2749
|
+
let mounted = true;
|
|
2750
|
+
let bridgeInstance = null;
|
|
2751
|
+
const initBridge = async () => {
|
|
2752
|
+
try {
|
|
2753
|
+
bridgeInstance = new FrontMcpBridge(config);
|
|
2754
|
+
await bridgeInstance.initialize();
|
|
2755
|
+
if (mounted) {
|
|
2756
|
+
setBridge(bridgeInstance);
|
|
2757
|
+
setLoading(false);
|
|
2758
|
+
onReady?.(bridgeInstance);
|
|
2759
|
+
}
|
|
2760
|
+
} catch (err) {
|
|
2761
|
+
if (mounted) {
|
|
2762
|
+
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
2763
|
+
setError(error2);
|
|
2764
|
+
setLoading(false);
|
|
2765
|
+
onError?.(error2);
|
|
2766
|
+
}
|
|
2767
|
+
}
|
|
2768
|
+
};
|
|
2769
|
+
initBridge();
|
|
2770
|
+
return () => {
|
|
2771
|
+
mounted = false;
|
|
2772
|
+
if (bridgeInstance) {
|
|
2773
|
+
bridgeInstance.dispose();
|
|
2774
|
+
}
|
|
2775
|
+
};
|
|
2776
|
+
}, [config, onReady, onError]);
|
|
2777
|
+
const contextValue = (0, import_react.useMemo)(
|
|
2778
|
+
() => ({
|
|
2779
|
+
bridge,
|
|
2780
|
+
loading,
|
|
2781
|
+
error,
|
|
2782
|
+
ready: !loading && !error && bridge !== null,
|
|
2783
|
+
adapterId: bridge?.adapterId,
|
|
2784
|
+
capabilities: bridge?.capabilities
|
|
2785
|
+
}),
|
|
2786
|
+
[bridge, loading, error]
|
|
2787
|
+
);
|
|
2788
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(McpBridgeContext.Provider, { value: contextValue, children });
|
|
2789
|
+
}
|
|
2790
|
+
var SSR_DEFAULT_CONTEXT = {
|
|
2791
|
+
bridge: null,
|
|
2792
|
+
loading: false,
|
|
2793
|
+
error: null,
|
|
2794
|
+
ready: false,
|
|
2795
|
+
adapterId: void 0,
|
|
2796
|
+
capabilities: void 0
|
|
2797
|
+
};
|
|
2798
|
+
function useMcpBridgeContext() {
|
|
2799
|
+
const context = (0, import_react.useContext)(McpBridgeContext);
|
|
2800
|
+
if (!context) {
|
|
2801
|
+
return SSR_DEFAULT_CONTEXT;
|
|
2802
|
+
}
|
|
2803
|
+
return context;
|
|
2804
|
+
}
|
|
2805
|
+
function useMcpBridge() {
|
|
2806
|
+
const { bridge } = useMcpBridgeContext();
|
|
2807
|
+
return bridge;
|
|
2808
|
+
}
|
|
2809
|
+
function useTheme() {
|
|
2810
|
+
const { bridge, ready } = useMcpBridgeContext();
|
|
2811
|
+
const [theme, setTheme] = (0, import_react.useState)("light");
|
|
2812
|
+
(0, import_react.useEffect)(() => {
|
|
2813
|
+
if (!ready || !bridge) return;
|
|
2814
|
+
setTheme(bridge.getTheme());
|
|
2815
|
+
const unsubscribe = bridge.onContextChange((changes) => {
|
|
2816
|
+
if (changes.theme) {
|
|
2817
|
+
setTheme(changes.theme);
|
|
2818
|
+
}
|
|
2819
|
+
});
|
|
2820
|
+
return unsubscribe;
|
|
2821
|
+
}, [bridge, ready]);
|
|
2822
|
+
return theme;
|
|
2823
|
+
}
|
|
2824
|
+
function useDisplayMode() {
|
|
2825
|
+
const { bridge, ready } = useMcpBridgeContext();
|
|
2826
|
+
const [displayMode, setDisplayMode] = (0, import_react.useState)("inline");
|
|
2827
|
+
(0, import_react.useEffect)(() => {
|
|
2828
|
+
if (!ready || !bridge) return;
|
|
2829
|
+
setDisplayMode(bridge.getDisplayMode());
|
|
2830
|
+
const unsubscribe = bridge.onContextChange((changes) => {
|
|
2831
|
+
if (changes.displayMode) {
|
|
2832
|
+
setDisplayMode(changes.displayMode);
|
|
2833
|
+
}
|
|
2834
|
+
});
|
|
2835
|
+
return unsubscribe;
|
|
2836
|
+
}, [bridge, ready]);
|
|
2837
|
+
return displayMode;
|
|
2838
|
+
}
|
|
2839
|
+
function useHostContext() {
|
|
2840
|
+
const { bridge, ready } = useMcpBridgeContext();
|
|
2841
|
+
const [context, setContext] = (0, import_react.useState)(null);
|
|
2842
|
+
(0, import_react.useEffect)(() => {
|
|
2843
|
+
if (!ready || !bridge) return;
|
|
2844
|
+
const adapter = bridge.getAdapter?.();
|
|
2845
|
+
if (adapter) {
|
|
2846
|
+
setContext(adapter.getHostContext());
|
|
2847
|
+
}
|
|
2848
|
+
const unsubscribe = bridge.onContextChange((changes) => {
|
|
2849
|
+
setContext((prev) => prev ? { ...prev, ...changes } : null);
|
|
2850
|
+
});
|
|
2851
|
+
return unsubscribe;
|
|
2852
|
+
}, [bridge, ready]);
|
|
2853
|
+
return context;
|
|
2854
|
+
}
|
|
2855
|
+
function useCapability(cap) {
|
|
2856
|
+
const { capabilities } = useMcpBridgeContext();
|
|
2857
|
+
return capabilities?.[cap] === true;
|
|
2858
|
+
}
|
|
2859
|
+
|
|
2860
|
+
// libs/ui/src/react/hooks/tools.tsx
|
|
2861
|
+
var import_react2 = require("react");
|
|
2862
|
+
function useToolInput() {
|
|
2863
|
+
const { bridge, ready } = useMcpBridgeContext();
|
|
2864
|
+
if (!ready || !bridge) {
|
|
2865
|
+
return null;
|
|
2866
|
+
}
|
|
2867
|
+
try {
|
|
2868
|
+
return bridge.getToolInput();
|
|
2869
|
+
} catch {
|
|
2870
|
+
return null;
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
2873
|
+
function useToolOutput() {
|
|
2874
|
+
const { bridge, ready } = useMcpBridgeContext();
|
|
2875
|
+
const [output, setOutput] = (0, import_react2.useState)(null);
|
|
2876
|
+
(0, import_react2.useEffect)(() => {
|
|
2877
|
+
if (!ready || !bridge) return;
|
|
2878
|
+
try {
|
|
2879
|
+
const initialOutput = bridge.getToolOutput();
|
|
2880
|
+
if (initialOutput !== void 0) {
|
|
2881
|
+
setOutput(initialOutput);
|
|
2882
|
+
}
|
|
2883
|
+
} catch {
|
|
2884
|
+
}
|
|
2885
|
+
const unsubscribe = bridge.onToolResult((result) => {
|
|
2886
|
+
setOutput(result);
|
|
2887
|
+
});
|
|
2888
|
+
return unsubscribe;
|
|
2889
|
+
}, [bridge, ready]);
|
|
2890
|
+
return output;
|
|
2891
|
+
}
|
|
2892
|
+
function useStructuredContent() {
|
|
2893
|
+
const { bridge, ready } = useMcpBridgeContext();
|
|
2894
|
+
if (!ready || !bridge) {
|
|
2895
|
+
return null;
|
|
1138
2896
|
}
|
|
1139
2897
|
try {
|
|
1140
2898
|
const adapter = bridge.getAdapter?.();
|
|
@@ -1326,36 +3084,8 @@ function useOpenLink() {
|
|
|
1326
3084
|
);
|
|
1327
3085
|
}
|
|
1328
3086
|
|
|
1329
|
-
// libs/ui/src/theme/platforms.ts
|
|
1330
|
-
var CLAUDE_PLATFORM = {
|
|
1331
|
-
id: "claude",
|
|
1332
|
-
name: "Claude (Artifacts)",
|
|
1333
|
-
supportsWidgets: true,
|
|
1334
|
-
supportsTailwind: true,
|
|
1335
|
-
supportsHtmx: false,
|
|
1336
|
-
// Network blocked, HTMX won't work for API calls
|
|
1337
|
-
networkMode: "blocked",
|
|
1338
|
-
scriptStrategy: "inline",
|
|
1339
|
-
maxInlineSize: 100 * 1024,
|
|
1340
|
-
// 100KB limit for artifacts
|
|
1341
|
-
cspRestrictions: ["script-src 'unsafe-inline'", "connect-src 'none'"],
|
|
1342
|
-
options: {
|
|
1343
|
-
mode: "artifacts",
|
|
1344
|
-
framework: "react"
|
|
1345
|
-
// Claude artifacts prefer React
|
|
1346
|
-
}
|
|
1347
|
-
};
|
|
1348
|
-
|
|
1349
|
-
// libs/ui/src/utils/escape-html.ts
|
|
1350
|
-
function escapeHtml(str) {
|
|
1351
|
-
if (str === null || str === void 0) {
|
|
1352
|
-
return "";
|
|
1353
|
-
}
|
|
1354
|
-
const s = String(str);
|
|
1355
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
1356
|
-
}
|
|
1357
|
-
|
|
1358
3087
|
// libs/ui/src/react/utils.ts
|
|
3088
|
+
var import_utils = require("@frontmcp/uipack/utils");
|
|
1359
3089
|
var cachedReactDOMServer = null;
|
|
1360
3090
|
function getReactDOMServer() {
|
|
1361
3091
|
if (!cachedReactDOMServer) {
|
|
@@ -1372,7 +3102,7 @@ function renderChildrenToString(children) {
|
|
|
1372
3102
|
return "";
|
|
1373
3103
|
}
|
|
1374
3104
|
if (typeof children === "string") {
|
|
1375
|
-
return escapeHtml(children);
|
|
3105
|
+
return (0, import_utils.escapeHtml)(children);
|
|
1376
3106
|
}
|
|
1377
3107
|
if (typeof children === "number") {
|
|
1378
3108
|
return String(children);
|