@frontmcp/ui 0.6.1 → 0.6.2
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/bridge/core/bridge-factory.d.ts +1 -0
- package/bridge/core/bridge-factory.d.ts.map +1 -1
- package/bridge/index.d.ts +1 -1
- package/bridge/index.d.ts.map +1 -1
- package/bridge/index.js +39 -881
- package/bundler/browser-components.d.ts +42 -0
- package/bundler/browser-components.d.ts.map +1 -0
- package/bundler/bundler.d.ts +78 -4
- package/bundler/bundler.d.ts.map +1 -1
- package/bundler/index.d.ts +8 -8
- package/bundler/index.d.ts.map +1 -1
- package/bundler/index.js +1315 -1854
- package/bundler/types.d.ts +188 -7
- package/bundler/types.d.ts.map +1 -1
- package/esm/bridge/{index.js → index.mjs} +40 -877
- package/esm/bundler/{index.js → index.mjs} +1391 -1895
- package/esm/{index.js → index.mjs} +215 -3091
- package/esm/layouts/{index.js → index.mjs} +3 -3
- package/esm/package.json +9 -8
- package/esm/react/index.mjs +1183 -0
- package/esm/renderers/index.mjs +611 -0
- package/esm/universal/{index.js → index.mjs} +266 -70
- package/index.d.ts +1 -4
- package/index.d.ts.map +1 -1
- package/index.js +208 -3113
- package/layouts/base.d.ts.map +1 -1
- package/layouts/index.js +3 -3
- package/layouts/presets.d.ts.map +1 -1
- package/package.json +9 -8
- package/react/Badge.d.ts.map +1 -1
- package/react/hooks/context.d.ts.map +1 -1
- package/react/index.d.ts +0 -1
- package/react/index.d.ts.map +1 -1
- package/react/index.js +57 -2001
- package/react/types.d.ts.map +1 -1
- package/renderers/index.d.ts +9 -4
- package/renderers/index.d.ts.map +1 -1
- package/renderers/index.js +328 -88
- package/renderers/mdx.renderer.d.ts +99 -0
- package/renderers/mdx.renderer.d.ts.map +1 -0
- package/renderers/react.renderer.d.ts +22 -13
- package/renderers/react.renderer.d.ts.map +1 -1
- package/renderers/transpiler.d.ts +49 -0
- package/renderers/transpiler.d.ts.map +1 -0
- package/universal/cached-runtime.d.ts +25 -1
- package/universal/cached-runtime.d.ts.map +1 -1
- package/universal/index.js +266 -70
- package/universal/runtime-builder.d.ts.map +1 -1
- package/universal/types.d.ts.map +1 -1
- package/web-components/elements/fmcp-input.d.ts.map +1 -1
- package/web-components/elements/fmcp-select.d.ts.map +1 -1
- package/web-components/index.d.ts +0 -1
- package/web-components/index.d.ts.map +1 -1
- package/bundler/cache.d.ts +0 -173
- package/bundler/cache.d.ts.map +0 -1
- package/bundler/file-cache/component-builder.d.ts +0 -167
- package/bundler/file-cache/component-builder.d.ts.map +0 -1
- package/bundler/file-cache/hash-calculator.d.ts +0 -155
- package/bundler/file-cache/hash-calculator.d.ts.map +0 -1
- package/bundler/file-cache/index.d.ts +0 -12
- package/bundler/file-cache/index.d.ts.map +0 -1
- package/bundler/file-cache/storage/filesystem.d.ts +0 -149
- package/bundler/file-cache/storage/filesystem.d.ts.map +0 -1
- package/bundler/file-cache/storage/index.d.ts +0 -11
- package/bundler/file-cache/storage/index.d.ts.map +0 -1
- package/bundler/file-cache/storage/interface.d.ts +0 -152
- package/bundler/file-cache/storage/interface.d.ts.map +0 -1
- package/bundler/file-cache/storage/redis.d.ts +0 -139
- package/bundler/file-cache/storage/redis.d.ts.map +0 -1
- package/bundler/sandbox/enclave-adapter.d.ts +0 -121
- package/bundler/sandbox/enclave-adapter.d.ts.map +0 -1
- package/bundler/sandbox/executor.d.ts +0 -14
- package/bundler/sandbox/executor.d.ts.map +0 -1
- package/bundler/sandbox/policy.d.ts +0 -62
- package/bundler/sandbox/policy.d.ts.map +0 -1
- package/esm/bridge/adapters/base-adapter.d.ts +0 -104
- package/esm/bridge/adapters/base-adapter.d.ts.map +0 -1
- package/esm/bridge/adapters/claude.adapter.d.ts +0 -67
- package/esm/bridge/adapters/claude.adapter.d.ts.map +0 -1
- package/esm/bridge/adapters/ext-apps.adapter.d.ts +0 -143
- package/esm/bridge/adapters/ext-apps.adapter.d.ts.map +0 -1
- package/esm/bridge/adapters/gemini.adapter.d.ts +0 -64
- package/esm/bridge/adapters/gemini.adapter.d.ts.map +0 -1
- package/esm/bridge/adapters/generic.adapter.d.ts +0 -56
- package/esm/bridge/adapters/generic.adapter.d.ts.map +0 -1
- package/esm/bridge/adapters/index.d.ts +0 -26
- package/esm/bridge/adapters/index.d.ts.map +0 -1
- package/esm/bridge/adapters/openai.adapter.d.ts +0 -65
- package/esm/bridge/adapters/openai.adapter.d.ts.map +0 -1
- package/esm/bridge/core/adapter-registry.d.ts +0 -122
- package/esm/bridge/core/adapter-registry.d.ts.map +0 -1
- package/esm/bridge/core/bridge-factory.d.ts +0 -199
- package/esm/bridge/core/bridge-factory.d.ts.map +0 -1
- package/esm/bridge/core/index.d.ts +0 -10
- package/esm/bridge/core/index.d.ts.map +0 -1
- package/esm/bridge/index.d.ts +0 -62
- package/esm/bridge/index.d.ts.map +0 -1
- package/esm/bridge/runtime/iife-generator.d.ts +0 -62
- package/esm/bridge/runtime/iife-generator.d.ts.map +0 -1
- package/esm/bridge/runtime/index.d.ts +0 -10
- package/esm/bridge/runtime/index.d.ts.map +0 -1
- package/esm/bridge/types.d.ts +0 -386
- package/esm/bridge/types.d.ts.map +0 -1
- package/esm/bundler/bundler.d.ts +0 -208
- package/esm/bundler/bundler.d.ts.map +0 -1
- package/esm/bundler/cache.d.ts +0 -173
- package/esm/bundler/cache.d.ts.map +0 -1
- package/esm/bundler/file-cache/component-builder.d.ts +0 -167
- package/esm/bundler/file-cache/component-builder.d.ts.map +0 -1
- package/esm/bundler/file-cache/hash-calculator.d.ts +0 -155
- package/esm/bundler/file-cache/hash-calculator.d.ts.map +0 -1
- package/esm/bundler/file-cache/index.d.ts +0 -12
- package/esm/bundler/file-cache/index.d.ts.map +0 -1
- package/esm/bundler/file-cache/storage/filesystem.d.ts +0 -149
- package/esm/bundler/file-cache/storage/filesystem.d.ts.map +0 -1
- package/esm/bundler/file-cache/storage/index.d.ts +0 -11
- package/esm/bundler/file-cache/storage/index.d.ts.map +0 -1
- package/esm/bundler/file-cache/storage/interface.d.ts +0 -152
- package/esm/bundler/file-cache/storage/interface.d.ts.map +0 -1
- package/esm/bundler/file-cache/storage/redis.d.ts +0 -139
- package/esm/bundler/file-cache/storage/redis.d.ts.map +0 -1
- package/esm/bundler/index.d.ts +0 -43
- package/esm/bundler/index.d.ts.map +0 -1
- package/esm/bundler/sandbox/enclave-adapter.d.ts +0 -121
- package/esm/bundler/sandbox/enclave-adapter.d.ts.map +0 -1
- package/esm/bundler/sandbox/executor.d.ts +0 -14
- package/esm/bundler/sandbox/executor.d.ts.map +0 -1
- package/esm/bundler/sandbox/policy.d.ts +0 -62
- package/esm/bundler/sandbox/policy.d.ts.map +0 -1
- package/esm/bundler/types.d.ts +0 -702
- package/esm/bundler/types.d.ts.map +0 -1
- package/esm/components/alert.d.ts +0 -66
- package/esm/components/alert.d.ts.map +0 -1
- package/esm/components/alert.schema.d.ts +0 -98
- package/esm/components/alert.schema.d.ts.map +0 -1
- package/esm/components/avatar.d.ts +0 -77
- package/esm/components/avatar.d.ts.map +0 -1
- package/esm/components/avatar.schema.d.ts +0 -170
- package/esm/components/avatar.schema.d.ts.map +0 -1
- package/esm/components/badge.d.ts +0 -64
- package/esm/components/badge.d.ts.map +0 -1
- package/esm/components/badge.schema.d.ts +0 -91
- package/esm/components/badge.schema.d.ts.map +0 -1
- package/esm/components/button.d.ts +0 -100
- package/esm/components/button.d.ts.map +0 -1
- package/esm/components/button.schema.d.ts +0 -120
- package/esm/components/button.schema.d.ts.map +0 -1
- package/esm/components/card.d.ts +0 -53
- package/esm/components/card.d.ts.map +0 -1
- package/esm/components/card.schema.d.ts +0 -93
- package/esm/components/card.schema.d.ts.map +0 -1
- package/esm/components/form.d.ts +0 -212
- package/esm/components/form.d.ts.map +0 -1
- package/esm/components/form.schema.d.ts +0 -365
- package/esm/components/form.schema.d.ts.map +0 -1
- package/esm/components/index.d.ts +0 -29
- package/esm/components/index.d.ts.map +0 -1
- package/esm/components/list.d.ts +0 -121
- package/esm/components/list.d.ts.map +0 -1
- package/esm/components/list.schema.d.ts +0 -129
- package/esm/components/list.schema.d.ts.map +0 -1
- package/esm/components/modal.d.ts +0 -100
- package/esm/components/modal.d.ts.map +0 -1
- package/esm/components/modal.schema.d.ts +0 -151
- package/esm/components/modal.schema.d.ts.map +0 -1
- package/esm/components/table.d.ts +0 -91
- package/esm/components/table.d.ts.map +0 -1
- package/esm/components/table.schema.d.ts +0 -123
- package/esm/components/table.schema.d.ts.map +0 -1
- package/esm/index.d.ts +0 -40
- package/esm/index.d.ts.map +0 -1
- package/esm/layouts/base.d.ts +0 -86
- package/esm/layouts/base.d.ts.map +0 -1
- package/esm/layouts/index.d.ts +0 -8
- package/esm/layouts/index.d.ts.map +0 -1
- package/esm/layouts/presets.d.ts +0 -134
- package/esm/layouts/presets.d.ts.map +0 -1
- package/esm/pages/consent.d.ts +0 -117
- package/esm/pages/consent.d.ts.map +0 -1
- package/esm/pages/error.d.ts +0 -101
- package/esm/pages/error.d.ts.map +0 -1
- package/esm/pages/index.d.ts +0 -9
- package/esm/pages/index.d.ts.map +0 -1
- package/esm/pages/index.js +0 -1036
- package/esm/react/Alert.d.ts +0 -101
- package/esm/react/Alert.d.ts.map +0 -1
- package/esm/react/Badge.d.ts +0 -100
- package/esm/react/Badge.d.ts.map +0 -1
- package/esm/react/Button.d.ts +0 -108
- package/esm/react/Button.d.ts.map +0 -1
- package/esm/react/Card.d.ts +0 -103
- package/esm/react/Card.d.ts.map +0 -1
- package/esm/react/hooks/context.d.ts +0 -179
- package/esm/react/hooks/context.d.ts.map +0 -1
- package/esm/react/hooks/index.d.ts +0 -42
- package/esm/react/hooks/index.d.ts.map +0 -1
- package/esm/react/hooks/tools.d.ts +0 -284
- package/esm/react/hooks/tools.d.ts.map +0 -1
- package/esm/react/index.d.ts +0 -80
- package/esm/react/index.d.ts.map +0 -1
- package/esm/react/index.js +0 -3124
- package/esm/react/types.d.ts +0 -105
- package/esm/react/types.d.ts.map +0 -1
- package/esm/react/utils.d.ts +0 -43
- package/esm/react/utils.d.ts.map +0 -1
- package/esm/render/index.d.ts +0 -8
- package/esm/render/index.d.ts.map +0 -1
- package/esm/render/prerender.d.ts +0 -57
- package/esm/render/prerender.d.ts.map +0 -1
- package/esm/renderers/index.d.ts +0 -21
- package/esm/renderers/index.d.ts.map +0 -1
- package/esm/renderers/index.js +0 -381
- package/esm/renderers/react.adapter.d.ts +0 -70
- package/esm/renderers/react.adapter.d.ts.map +0 -1
- package/esm/renderers/react.renderer.d.ts +0 -96
- package/esm/renderers/react.renderer.d.ts.map +0 -1
- package/esm/universal/UniversalApp.d.ts +0 -108
- package/esm/universal/UniversalApp.d.ts.map +0 -1
- package/esm/universal/cached-runtime.d.ts +0 -115
- package/esm/universal/cached-runtime.d.ts.map +0 -1
- package/esm/universal/context.d.ts +0 -122
- package/esm/universal/context.d.ts.map +0 -1
- package/esm/universal/index.d.ts +0 -57
- package/esm/universal/index.d.ts.map +0 -1
- package/esm/universal/renderers/html.renderer.d.ts +0 -37
- package/esm/universal/renderers/html.renderer.d.ts.map +0 -1
- package/esm/universal/renderers/index.d.ts +0 -112
- package/esm/universal/renderers/index.d.ts.map +0 -1
- package/esm/universal/renderers/markdown.renderer.d.ts +0 -33
- package/esm/universal/renderers/markdown.renderer.d.ts.map +0 -1
- package/esm/universal/renderers/mdx.renderer.d.ts +0 -38
- package/esm/universal/renderers/mdx.renderer.d.ts.map +0 -1
- package/esm/universal/renderers/react.renderer.d.ts +0 -46
- package/esm/universal/renderers/react.renderer.d.ts.map +0 -1
- package/esm/universal/runtime-builder.d.ts +0 -33
- package/esm/universal/runtime-builder.d.ts.map +0 -1
- package/esm/universal/store.d.ts +0 -135
- package/esm/universal/store.d.ts.map +0 -1
- package/esm/universal/types.d.ts +0 -199
- package/esm/universal/types.d.ts.map +0 -1
- package/esm/web-components/core/attribute-parser.d.ts +0 -82
- package/esm/web-components/core/attribute-parser.d.ts.map +0 -1
- package/esm/web-components/core/base-element.d.ts +0 -197
- package/esm/web-components/core/base-element.d.ts.map +0 -1
- package/esm/web-components/core/index.d.ts +0 -9
- package/esm/web-components/core/index.d.ts.map +0 -1
- package/esm/web-components/elements/fmcp-alert.d.ts +0 -46
- package/esm/web-components/elements/fmcp-alert.d.ts.map +0 -1
- package/esm/web-components/elements/fmcp-badge.d.ts +0 -47
- package/esm/web-components/elements/fmcp-badge.d.ts.map +0 -1
- package/esm/web-components/elements/fmcp-button.d.ts +0 -117
- package/esm/web-components/elements/fmcp-button.d.ts.map +0 -1
- package/esm/web-components/elements/fmcp-card.d.ts +0 -53
- package/esm/web-components/elements/fmcp-card.d.ts.map +0 -1
- package/esm/web-components/elements/fmcp-input.d.ts +0 -96
- package/esm/web-components/elements/fmcp-input.d.ts.map +0 -1
- package/esm/web-components/elements/fmcp-select.d.ts +0 -100
- package/esm/web-components/elements/fmcp-select.d.ts.map +0 -1
- package/esm/web-components/elements/index.d.ts +0 -13
- package/esm/web-components/elements/index.d.ts.map +0 -1
- package/esm/web-components/index.d.ts +0 -50
- package/esm/web-components/index.d.ts.map +0 -1
- package/esm/web-components/register.d.ts +0 -57
- package/esm/web-components/register.d.ts.map +0 -1
- package/esm/web-components/types.d.ts +0 -122
- package/esm/web-components/types.d.ts.map +0 -1
- package/esm/widgets/index.d.ts +0 -8
- package/esm/widgets/index.d.ts.map +0 -1
- package/esm/widgets/index.js +0 -883
- package/esm/widgets/progress.d.ts +0 -133
- package/esm/widgets/progress.d.ts.map +0 -1
- package/esm/widgets/resource.d.ts +0 -163
- package/esm/widgets/resource.d.ts.map +0 -1
- package/pages/consent.d.ts +0 -117
- package/pages/consent.d.ts.map +0 -1
- package/pages/error.d.ts +0 -101
- package/pages/error.d.ts.map +0 -1
- package/pages/index.d.ts +0 -9
- package/pages/index.d.ts.map +0 -1
- package/pages/index.js +0 -1065
- package/react/utils.d.ts +0 -43
- package/react/utils.d.ts.map +0 -1
- package/widgets/index.d.ts +0 -8
- package/widgets/index.d.ts.map +0 -1
- package/widgets/index.js +0 -910
- package/widgets/progress.d.ts +0 -133
- package/widgets/progress.d.ts.map +0 -1
- package/widgets/resource.d.ts +0 -163
- package/widgets/resource.d.ts.map +0 -1
- /package/esm/components/{index.js → index.mjs} +0 -0
- /package/esm/render/{index.js → index.mjs} +0 -0
- /package/esm/web-components/{index.js → index.mjs} +0 -0
package/index.js
CHANGED
|
@@ -43,7 +43,7 @@ __export(index_exports, {
|
|
|
43
43
|
AvatarSpacingSchema: () => AvatarSpacingSchema,
|
|
44
44
|
AvatarStatusSchema: () => AvatarStatusSchema,
|
|
45
45
|
AvatarWithTextOptionsSchema: () => AvatarWithTextOptionsSchema,
|
|
46
|
-
BRIDGE_SCRIPT_TAGS: () => BRIDGE_SCRIPT_TAGS,
|
|
46
|
+
BRIDGE_SCRIPT_TAGS: () => import_uipack.BRIDGE_SCRIPT_TAGS,
|
|
47
47
|
Badge: () => Badge,
|
|
48
48
|
BadgeGroupOptionsSchema: () => BadgeGroupOptionsSchema,
|
|
49
49
|
BadgeOptionsSchema: () => BadgeOptionsSchema,
|
|
@@ -107,7 +107,7 @@ __export(index_exports, {
|
|
|
107
107
|
TextareaResizeSchema: () => TextareaResizeSchema,
|
|
108
108
|
ToastOptionsSchema: () => ToastOptionsSchema,
|
|
109
109
|
ToastPositionSchema: () => ToastPositionSchema,
|
|
110
|
-
UNIVERSAL_BRIDGE_SCRIPT: () => UNIVERSAL_BRIDGE_SCRIPT,
|
|
110
|
+
UNIVERSAL_BRIDGE_SCRIPT: () => import_uipack.UNIVERSAL_BRIDGE_SCRIPT,
|
|
111
111
|
actionList: () => actionList,
|
|
112
112
|
activeBadge: () => activeBadge,
|
|
113
113
|
alert: () => alert,
|
|
@@ -121,22 +121,15 @@ __export(index_exports, {
|
|
|
121
121
|
badgeGroup: () => badgeGroup,
|
|
122
122
|
baseLayout: () => baseLayout,
|
|
123
123
|
betaBadge: () => betaBadge,
|
|
124
|
-
buildHydrationScript: () => buildHydrationScript,
|
|
125
124
|
busyDot: () => busyDot,
|
|
126
125
|
button: () => button,
|
|
127
126
|
buttonGroup: () => buttonGroup,
|
|
128
127
|
card: () => card,
|
|
129
128
|
cardGroup: () => cardGroup,
|
|
130
129
|
checkbox: () => checkbox,
|
|
131
|
-
circularProgress: () => circularProgress,
|
|
132
|
-
codePreview: () => codePreview,
|
|
133
130
|
confirmModal: () => confirmModal,
|
|
134
|
-
consentDeniedPage: () => consentDeniedPage,
|
|
135
131
|
consentLayout: () => consentLayout,
|
|
136
132
|
consentLayoutBuilder: () => consentLayoutBuilder,
|
|
137
|
-
consentPage: () => consentPage,
|
|
138
|
-
consentSuccessPage: () => consentSuccessPage,
|
|
139
|
-
contentSkeleton: () => contentSkeleton,
|
|
140
133
|
createBridge: () => createBridge,
|
|
141
134
|
createLayoutBuilder: () => createLayoutBuilder,
|
|
142
135
|
createReactAdapter: () => createReactAdapter,
|
|
@@ -149,45 +142,33 @@ __export(index_exports, {
|
|
|
149
142
|
errorBadge: () => errorBadge,
|
|
150
143
|
errorLayout: () => errorLayout,
|
|
151
144
|
errorLayoutBuilder: () => errorLayoutBuilder,
|
|
152
|
-
errorPage: () => errorPage,
|
|
153
145
|
escapeHtml: () => import_utils2.escapeHtml,
|
|
154
146
|
featureList: () => featureList,
|
|
155
|
-
forbiddenPage: () => forbiddenPage,
|
|
156
147
|
form: () => form,
|
|
157
148
|
formActions: () => formActions,
|
|
158
149
|
formRow: () => formRow,
|
|
159
150
|
formSection: () => formSection,
|
|
160
|
-
generateBridgeIIFE: () => generateBridgeIIFE,
|
|
161
|
-
generatePlatformBundle: () => generatePlatformBundle,
|
|
151
|
+
generateBridgeIIFE: () => import_uipack.generateBridgeIIFE,
|
|
152
|
+
generatePlatformBundle: () => import_uipack.generatePlatformBundle,
|
|
162
153
|
ghostButton: () => ghostButton,
|
|
163
154
|
hiddenInput: () => hiddenInput,
|
|
164
|
-
imagePreview: () => imagePreview,
|
|
165
155
|
inactiveBadge: () => inactiveBadge,
|
|
166
156
|
infoAlert: () => infoAlert,
|
|
167
157
|
input: () => input,
|
|
168
|
-
isBrowser: () => isBrowser,
|
|
169
|
-
isReactAvailable: () => isReactAvailable,
|
|
170
|
-
isServer: () => isServer,
|
|
171
158
|
linkButton: () => linkButton,
|
|
172
159
|
loadReactAdapter: () => loadReactAdapter,
|
|
173
160
|
loadingLayout: () => loadingLayout,
|
|
174
|
-
maintenancePage: () => maintenancePage,
|
|
175
161
|
modal: () => modal,
|
|
176
162
|
modalTrigger: () => modalTrigger,
|
|
177
163
|
newBadge: () => newBadge,
|
|
178
|
-
notFoundPage: () => notFoundPage,
|
|
179
|
-
oauthErrorPage: () => oauthErrorPage,
|
|
180
164
|
offlineDot: () => offlineDot,
|
|
181
|
-
offlinePage: () => offlinePage,
|
|
182
165
|
onlineDot: () => onlineDot,
|
|
183
166
|
outlineButton: () => outlineButton,
|
|
184
167
|
pagination: () => pagination,
|
|
185
168
|
pendingBadge: () => pendingBadge,
|
|
186
169
|
permissionList: () => permissionList,
|
|
187
170
|
primaryButton: () => primaryButton,
|
|
188
|
-
progressBar: () => progressBar,
|
|
189
171
|
radioGroup: () => radioGroup,
|
|
190
|
-
rateLimitPage: () => rateLimitPage,
|
|
191
172
|
reactRenderer: () => reactRenderer,
|
|
192
173
|
registerAdapter: () => registerAdapter,
|
|
193
174
|
registerAllComponents: () => registerAllComponents,
|
|
@@ -205,26 +186,14 @@ __export(index_exports, {
|
|
|
205
186
|
renderButtonSync: () => renderButtonSync,
|
|
206
187
|
renderCard: () => renderCard,
|
|
207
188
|
renderCardSync: () => renderCardSync,
|
|
208
|
-
renderChildrenToString: () => renderChildrenToString,
|
|
209
|
-
renderToString: () => renderToString,
|
|
210
|
-
renderToStringSync: () => renderToStringSync,
|
|
211
|
-
resourceItem: () => resourceItem,
|
|
212
|
-
resourceList: () => resourceList,
|
|
213
|
-
resourceWidget: () => resourceWidget,
|
|
214
189
|
secondaryButton: () => secondaryButton,
|
|
215
190
|
select: () => select,
|
|
216
|
-
serverErrorPage: () => serverErrorPage,
|
|
217
|
-
sessionExpiredPage: () => sessionExpiredPage,
|
|
218
|
-
skeleton: () => skeleton,
|
|
219
|
-
statusIndicator: () => statusIndicator,
|
|
220
|
-
stepProgress: () => stepProgress,
|
|
221
191
|
successAlert: () => successAlert,
|
|
222
192
|
successLayout: () => successLayout,
|
|
223
193
|
table: () => table,
|
|
224
194
|
textarea: () => textarea,
|
|
225
195
|
toast: () => toast,
|
|
226
196
|
toastContainer: () => toastContainer,
|
|
227
|
-
unauthorizedPage: () => unauthorizedPage,
|
|
228
197
|
useCallTool: () => useCallTool,
|
|
229
198
|
useCapability: () => useCapability,
|
|
230
199
|
useDisplayMode: () => useDisplayMode,
|
|
@@ -947,7 +916,7 @@ function getAlignmentClasses(alignment) {
|
|
|
947
916
|
};
|
|
948
917
|
return alignMap[alignment];
|
|
949
918
|
}
|
|
950
|
-
function getBackgroundClasses(background
|
|
919
|
+
function getBackgroundClasses(background) {
|
|
951
920
|
switch (background) {
|
|
952
921
|
case "gradient":
|
|
953
922
|
return "bg-gradient-to-br from-primary to-secondary";
|
|
@@ -992,7 +961,7 @@ function buildBodyAttrs(attrs) {
|
|
|
992
961
|
function baseLayout(content, options) {
|
|
993
962
|
const {
|
|
994
963
|
title,
|
|
995
|
-
pageType = "custom",
|
|
964
|
+
pageType: _pageType = "custom",
|
|
996
965
|
size = "md",
|
|
997
966
|
alignment = "center",
|
|
998
967
|
background = "solid",
|
|
@@ -1029,7 +998,7 @@ function baseLayout(content, options) {
|
|
|
1029
998
|
</style>` : "";
|
|
1030
999
|
const sizeClass = getSizeClass(size);
|
|
1031
1000
|
const alignmentClasses = getAlignmentClasses(alignment);
|
|
1032
|
-
const backgroundClasses = getBackgroundClasses(background
|
|
1001
|
+
const backgroundClasses = getBackgroundClasses(background);
|
|
1033
1002
|
const allBodyClasses = [backgroundClasses, "font-sans antialiased", bodyClass].filter(Boolean).join(" ");
|
|
1034
1003
|
const metaTags = buildMetaTags(options);
|
|
1035
1004
|
const bodyAttrStr = buildBodyAttrs(bodyAttrs);
|
|
@@ -2896,926 +2865,6 @@ var errorLayoutBuilder = createLayoutBuilder({
|
|
|
2896
2865
|
background: "solid"
|
|
2897
2866
|
});
|
|
2898
2867
|
|
|
2899
|
-
// libs/ui/src/pages/consent.ts
|
|
2900
|
-
function consentPage(options) {
|
|
2901
|
-
const {
|
|
2902
|
-
client,
|
|
2903
|
-
user,
|
|
2904
|
-
permissions,
|
|
2905
|
-
approveUrl,
|
|
2906
|
-
denyUrl,
|
|
2907
|
-
csrfToken,
|
|
2908
|
-
state,
|
|
2909
|
-
redirectUri,
|
|
2910
|
-
responseType,
|
|
2911
|
-
nonce,
|
|
2912
|
-
codeChallenge,
|
|
2913
|
-
codeChallengeMethod,
|
|
2914
|
-
error,
|
|
2915
|
-
layout = {},
|
|
2916
|
-
warningMessage,
|
|
2917
|
-
allowScopeSelection = false,
|
|
2918
|
-
approveText = "Allow",
|
|
2919
|
-
denyText = "Deny"
|
|
2920
|
-
} = options;
|
|
2921
|
-
const errorAlert = error ? alert(error, { variant: "danger", dismissible: true }) : "";
|
|
2922
|
-
const unverifiedWarning = !client.verified ? alert(warningMessage || "This application has not been verified. Only authorize applications you trust.", {
|
|
2923
|
-
variant: "warning",
|
|
2924
|
-
title: "Unverified Application"
|
|
2925
|
-
}) : "";
|
|
2926
|
-
const clientHeader = `
|
|
2927
|
-
<div class="text-center mb-6">
|
|
2928
|
-
${client.icon ? `<img src="${(0, import_utils2.escapeHtml)(client.icon)}" alt="${(0, import_utils2.escapeHtml)(
|
|
2929
|
-
client.name
|
|
2930
|
-
)}" class="w-16 h-16 rounded-xl mx-auto mb-4 shadow-md">` : `<div class="inline-flex items-center justify-center w-16 h-16 rounded-xl bg-gradient-to-br from-primary to-secondary text-white font-bold text-2xl mx-auto mb-4 shadow-md">
|
|
2931
|
-
${(0, import_utils2.escapeHtml)(client.name.charAt(0).toUpperCase())}
|
|
2932
|
-
</div>`}
|
|
2933
|
-
<h1 class="text-xl font-bold text-text-primary">
|
|
2934
|
-
${client.verified ? `<span class="inline-flex items-center gap-1">
|
|
2935
|
-
${(0, import_utils2.escapeHtml)(client.name)}
|
|
2936
|
-
<svg class="w-5 h-5 text-primary" fill="currentColor" viewBox="0 0 20 20">
|
|
2937
|
-
<path fill-rule="evenodd" d="M6.267 3.455a3.066 3.066 0 001.745-.723 3.066 3.066 0 013.976 0 3.066 3.066 0 001.745.723 3.066 3.066 0 012.812 2.812c.051.643.304 1.254.723 1.745a3.066 3.066 0 010 3.976 3.066 3.066 0 00-.723 1.745 3.066 3.066 0 01-2.812 2.812 3.066 3.066 0 00-1.745.723 3.066 3.066 0 01-3.976 0 3.066 3.066 0 00-1.745-.723 3.066 3.066 0 01-2.812-2.812 3.066 3.066 0 00-.723-1.745 3.066 3.066 0 010-3.976 3.066 3.066 0 00.723-1.745 3.066 3.066 0 012.812-2.812zm7.44 5.252a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
|
|
2938
|
-
</svg>
|
|
2939
|
-
</span>` : (0, import_utils2.escapeHtml)(client.name)}
|
|
2940
|
-
</h1>
|
|
2941
|
-
<p class="text-text-secondary mt-1">wants to access your account</p>
|
|
2942
|
-
</div>
|
|
2943
|
-
`;
|
|
2944
|
-
const userSection = user ? `
|
|
2945
|
-
<div class="flex items-center gap-3 p-4 bg-gray-50 rounded-lg mb-6">
|
|
2946
|
-
${user.avatar ? `<img src="${(0, import_utils2.escapeHtml)(user.avatar)}" class="w-12 h-12 rounded-full">` : `<div class="w-12 h-12 rounded-full bg-primary text-white flex items-center justify-center font-semibold text-lg">
|
|
2947
|
-
${(0, import_utils2.escapeHtml)((user.name || user.email || "U").charAt(0).toUpperCase())}
|
|
2948
|
-
</div>`}
|
|
2949
|
-
<div class="flex-1 min-w-0">
|
|
2950
|
-
${user.name ? `<div class="font-medium text-text-primary truncate">${(0, import_utils2.escapeHtml)(user.name)}</div>` : ""}
|
|
2951
|
-
${user.email ? `<div class="text-sm text-text-secondary truncate">${(0, import_utils2.escapeHtml)(user.email)}</div>` : ""}
|
|
2952
|
-
</div>
|
|
2953
|
-
<a href="/login?prompt=select_account" class="text-sm text-primary hover:text-primary/80">
|
|
2954
|
-
Switch account
|
|
2955
|
-
</a>
|
|
2956
|
-
</div>
|
|
2957
|
-
` : "";
|
|
2958
|
-
const permissionsSection = `
|
|
2959
|
-
<div class="mb-6">
|
|
2960
|
-
<h3 class="font-medium text-text-primary mb-3">This will allow ${(0, import_utils2.escapeHtml)(client.name)} to:</h3>
|
|
2961
|
-
${permissionList(permissions, {
|
|
2962
|
-
checkable: allowScopeSelection,
|
|
2963
|
-
inputName: "scope"
|
|
2964
|
-
})}
|
|
2965
|
-
</div>
|
|
2966
|
-
`;
|
|
2967
|
-
const hiddenFields = [
|
|
2968
|
-
csrfToken ? csrfInput(csrfToken) : "",
|
|
2969
|
-
state ? hiddenInput("state", state) : "",
|
|
2970
|
-
redirectUri ? hiddenInput("redirect_uri", redirectUri) : "",
|
|
2971
|
-
responseType ? hiddenInput("response_type", responseType) : "",
|
|
2972
|
-
nonce ? hiddenInput("nonce", nonce) : "",
|
|
2973
|
-
codeChallenge ? hiddenInput("code_challenge", codeChallenge) : "",
|
|
2974
|
-
codeChallengeMethod ? hiddenInput("code_challenge_method", codeChallengeMethod) : "",
|
|
2975
|
-
hiddenInput("client_id", client.clientId),
|
|
2976
|
-
// Include all scopes if not selectable
|
|
2977
|
-
!allowScopeSelection ? permissions.map((p) => hiddenInput("scope[]", p.scope)).join("\n") : ""
|
|
2978
|
-
].filter(Boolean).join("\n");
|
|
2979
|
-
const actionsHtml = `
|
|
2980
|
-
<div class="flex gap-3 pt-4">
|
|
2981
|
-
<form action="${(0, import_utils2.escapeHtml)(denyUrl || approveUrl)}" method="post" class="flex-1">
|
|
2982
|
-
${hiddenFields}
|
|
2983
|
-
<input type="hidden" name="action" value="deny">
|
|
2984
|
-
${outlineButton(denyText, { type: "submit", fullWidth: true })}
|
|
2985
|
-
</form>
|
|
2986
|
-
<form action="${(0, import_utils2.escapeHtml)(approveUrl)}" method="post" class="flex-1">
|
|
2987
|
-
${hiddenFields}
|
|
2988
|
-
<input type="hidden" name="action" value="approve">
|
|
2989
|
-
${primaryButton(approveText, { type: "submit", fullWidth: true })}
|
|
2990
|
-
</form>
|
|
2991
|
-
</div>
|
|
2992
|
-
`;
|
|
2993
|
-
const linksHtml = client.privacyUrl || client.termsUrl || client.websiteUrl ? `
|
|
2994
|
-
<div class="text-center text-xs text-text-secondary mt-6 space-x-3">
|
|
2995
|
-
${client.websiteUrl ? `<a href="${(0, import_utils2.escapeHtml)(
|
|
2996
|
-
client.websiteUrl
|
|
2997
|
-
)}" target="_blank" rel="noopener" class="hover:text-primary">Website</a>` : ""}
|
|
2998
|
-
${client.privacyUrl ? `<a href="${(0, import_utils2.escapeHtml)(
|
|
2999
|
-
client.privacyUrl
|
|
3000
|
-
)}" target="_blank" rel="noopener" class="hover:text-primary">Privacy Policy</a>` : ""}
|
|
3001
|
-
${client.termsUrl ? `<a href="${(0, import_utils2.escapeHtml)(
|
|
3002
|
-
client.termsUrl
|
|
3003
|
-
)}" target="_blank" rel="noopener" class="hover:text-primary">Terms of Service</a>` : ""}
|
|
3004
|
-
</div>
|
|
3005
|
-
` : "";
|
|
3006
|
-
const content = `
|
|
3007
|
-
${errorAlert}
|
|
3008
|
-
${unverifiedWarning}
|
|
3009
|
-
${clientHeader}
|
|
3010
|
-
${userSection}
|
|
3011
|
-
${permissionsSection}
|
|
3012
|
-
${actionsHtml}
|
|
3013
|
-
${linksHtml}
|
|
3014
|
-
`;
|
|
3015
|
-
return consentLayout(content, {
|
|
3016
|
-
title: `Authorize ${client.name}`,
|
|
3017
|
-
clientName: client.name,
|
|
3018
|
-
clientIcon: client.icon,
|
|
3019
|
-
userInfo: user,
|
|
3020
|
-
...layout
|
|
3021
|
-
});
|
|
3022
|
-
}
|
|
3023
|
-
function consentSuccessPage(options) {
|
|
3024
|
-
const { client, redirectUrl, autoRedirectDelay = 3e3, layout = {} } = options;
|
|
3025
|
-
const redirectScript = redirectUrl && autoRedirectDelay > 0 ? `
|
|
3026
|
-
<script>
|
|
3027
|
-
setTimeout(() => {
|
|
3028
|
-
window.location.href = '${(0, import_utils2.escapeHtml)(redirectUrl)}';
|
|
3029
|
-
}, ${autoRedirectDelay});
|
|
3030
|
-
</script>
|
|
3031
|
-
` : "";
|
|
3032
|
-
const content = `
|
|
3033
|
-
<div class="text-center">
|
|
3034
|
-
<div class="inline-flex items-center justify-center w-16 h-16 rounded-full bg-success/10 mb-6">
|
|
3035
|
-
<svg class="w-8 h-8 text-success" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
3036
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
|
|
3037
|
-
</svg>
|
|
3038
|
-
</div>
|
|
3039
|
-
<h1 class="text-2xl font-bold text-text-primary mb-2">Authorization Successful</h1>
|
|
3040
|
-
<p class="text-text-secondary mb-4">
|
|
3041
|
-
You have authorized <strong>${(0, import_utils2.escapeHtml)(client.name)}</strong> to access your account.
|
|
3042
|
-
</p>
|
|
3043
|
-
${redirectUrl ? `<p class="text-sm text-text-secondary">Redirecting you back to ${(0, import_utils2.escapeHtml)(client.name)}...</p>` : ""}
|
|
3044
|
-
</div>
|
|
3045
|
-
${redirectScript}
|
|
3046
|
-
`;
|
|
3047
|
-
return consentLayout(content, {
|
|
3048
|
-
title: "Authorization Successful",
|
|
3049
|
-
clientName: client.name,
|
|
3050
|
-
clientIcon: client.icon,
|
|
3051
|
-
...layout
|
|
3052
|
-
});
|
|
3053
|
-
}
|
|
3054
|
-
function consentDeniedPage(options) {
|
|
3055
|
-
const { client, redirectUrl, layout = {} } = options;
|
|
3056
|
-
const content = `
|
|
3057
|
-
<div class="text-center">
|
|
3058
|
-
<div class="inline-flex items-center justify-center w-16 h-16 rounded-full bg-gray-100 mb-6">
|
|
3059
|
-
<svg class="w-8 h-8 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
3060
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
|
3061
|
-
</svg>
|
|
3062
|
-
</div>
|
|
3063
|
-
<h1 class="text-2xl font-bold text-text-primary mb-2">Authorization Denied</h1>
|
|
3064
|
-
<p class="text-text-secondary mb-6">
|
|
3065
|
-
You denied <strong>${(0, import_utils2.escapeHtml)(client.name)}</strong> access to your account.
|
|
3066
|
-
</p>
|
|
3067
|
-
${redirectUrl ? `
|
|
3068
|
-
<a href="${(0, import_utils2.escapeHtml)(
|
|
3069
|
-
redirectUrl
|
|
3070
|
-
)}" class="inline-block px-6 py-3 bg-primary hover:bg-primary/90 text-white font-medium rounded-lg transition-colors">
|
|
3071
|
-
Return to ${(0, import_utils2.escapeHtml)(client.name)}
|
|
3072
|
-
</a>
|
|
3073
|
-
` : ""}
|
|
3074
|
-
</div>
|
|
3075
|
-
`;
|
|
3076
|
-
return consentLayout(content, {
|
|
3077
|
-
title: "Authorization Denied",
|
|
3078
|
-
clientName: client.name,
|
|
3079
|
-
clientIcon: client.icon,
|
|
3080
|
-
...layout
|
|
3081
|
-
});
|
|
3082
|
-
}
|
|
3083
|
-
|
|
3084
|
-
// libs/ui/src/pages/error.ts
|
|
3085
|
-
function errorPage(options) {
|
|
3086
|
-
const {
|
|
3087
|
-
code,
|
|
3088
|
-
title = "Something went wrong",
|
|
3089
|
-
message,
|
|
3090
|
-
details,
|
|
3091
|
-
showStack = false,
|
|
3092
|
-
stack,
|
|
3093
|
-
showRetry = true,
|
|
3094
|
-
retryUrl,
|
|
3095
|
-
showHome = true,
|
|
3096
|
-
homeUrl = "/",
|
|
3097
|
-
showBack = false,
|
|
3098
|
-
actions,
|
|
3099
|
-
layout = {},
|
|
3100
|
-
requestId
|
|
3101
|
-
} = options;
|
|
3102
|
-
const detailsHtml = details || showStack && stack ? `
|
|
3103
|
-
<div class="mt-8 text-left">
|
|
3104
|
-
${details ? `
|
|
3105
|
-
<div class="p-4 bg-gray-50 rounded-lg text-sm text-text-secondary mb-4">
|
|
3106
|
-
<strong class="text-text-primary">Details:</strong>
|
|
3107
|
-
<p class="mt-1">${(0, import_utils2.escapeHtml)(details)}</p>
|
|
3108
|
-
</div>
|
|
3109
|
-
` : ""}
|
|
3110
|
-
${showStack && stack ? `
|
|
3111
|
-
<details class="p-4 bg-gray-900 rounded-lg text-sm">
|
|
3112
|
-
<summary class="text-gray-300 cursor-pointer hover:text-white">Stack Trace</summary>
|
|
3113
|
-
<pre class="mt-2 text-xs text-gray-400 overflow-x-auto whitespace-pre-wrap">${(0, import_utils2.escapeHtml)(stack)}</pre>
|
|
3114
|
-
</details>
|
|
3115
|
-
` : ""}
|
|
3116
|
-
</div>
|
|
3117
|
-
` : "";
|
|
3118
|
-
const requestIdHtml = requestId ? `
|
|
3119
|
-
<p class="text-xs text-text-secondary mt-6">
|
|
3120
|
-
Request ID: <code class="px-1.5 py-0.5 bg-gray-100 rounded text-xs">${(0, import_utils2.escapeHtml)(requestId)}</code>
|
|
3121
|
-
</p>
|
|
3122
|
-
` : "";
|
|
3123
|
-
const content = `
|
|
3124
|
-
${detailsHtml}
|
|
3125
|
-
${actions || ""}
|
|
3126
|
-
${requestIdHtml}
|
|
3127
|
-
`;
|
|
3128
|
-
return errorLayout(content, {
|
|
3129
|
-
title: `${code ? `Error ${code} - ` : ""}${title}`,
|
|
3130
|
-
errorCode: code?.toString(),
|
|
3131
|
-
errorTitle: title,
|
|
3132
|
-
errorMessage: message,
|
|
3133
|
-
showRetry,
|
|
3134
|
-
retryUrl,
|
|
3135
|
-
showHome,
|
|
3136
|
-
homeUrl,
|
|
3137
|
-
...layout
|
|
3138
|
-
});
|
|
3139
|
-
}
|
|
3140
|
-
function notFoundPage(options = {}) {
|
|
3141
|
-
return errorPage({
|
|
3142
|
-
code: 404,
|
|
3143
|
-
title: "Page Not Found",
|
|
3144
|
-
message: "The page you're looking for doesn't exist or has been moved.",
|
|
3145
|
-
showRetry: false,
|
|
3146
|
-
...options
|
|
3147
|
-
});
|
|
3148
|
-
}
|
|
3149
|
-
function forbiddenPage(options = {}) {
|
|
3150
|
-
return errorPage({
|
|
3151
|
-
code: 403,
|
|
3152
|
-
title: "Access Denied",
|
|
3153
|
-
message: "You don't have permission to access this resource.",
|
|
3154
|
-
showRetry: false,
|
|
3155
|
-
...options
|
|
3156
|
-
});
|
|
3157
|
-
}
|
|
3158
|
-
function unauthorizedPage(options = {}) {
|
|
3159
|
-
const { loginUrl = "/login", ...rest } = options;
|
|
3160
|
-
return errorPage({
|
|
3161
|
-
code: 401,
|
|
3162
|
-
title: "Authentication Required",
|
|
3163
|
-
message: "Please sign in to access this resource.",
|
|
3164
|
-
showRetry: false,
|
|
3165
|
-
showHome: false,
|
|
3166
|
-
actions: `
|
|
3167
|
-
<div class="flex justify-center mt-8">
|
|
3168
|
-
<a href="${(0, import_utils2.escapeHtml)(
|
|
3169
|
-
loginUrl
|
|
3170
|
-
)}" class="px-6 py-3 bg-primary hover:bg-primary/90 text-white font-medium rounded-lg transition-colors">
|
|
3171
|
-
Sign In
|
|
3172
|
-
</a>
|
|
3173
|
-
</div>
|
|
3174
|
-
`,
|
|
3175
|
-
...rest
|
|
3176
|
-
});
|
|
3177
|
-
}
|
|
3178
|
-
function serverErrorPage(options = {}) {
|
|
3179
|
-
return errorPage({
|
|
3180
|
-
code: 500,
|
|
3181
|
-
title: "Server Error",
|
|
3182
|
-
message: "We're having trouble processing your request. Please try again later.",
|
|
3183
|
-
showRetry: true,
|
|
3184
|
-
...options
|
|
3185
|
-
});
|
|
3186
|
-
}
|
|
3187
|
-
function maintenancePage(options = {}) {
|
|
3188
|
-
const { estimatedTime, ...rest } = options;
|
|
3189
|
-
const timeMessage = estimatedTime ? `We expect to be back by ${(0, import_utils2.escapeHtml)(estimatedTime)}.` : "We'll be back shortly.";
|
|
3190
|
-
return errorPage({
|
|
3191
|
-
code: 503,
|
|
3192
|
-
title: "Under Maintenance",
|
|
3193
|
-
message: `We're currently performing scheduled maintenance. ${timeMessage}`,
|
|
3194
|
-
showRetry: true,
|
|
3195
|
-
showHome: false,
|
|
3196
|
-
...rest
|
|
3197
|
-
});
|
|
3198
|
-
}
|
|
3199
|
-
function rateLimitPage(options = {}) {
|
|
3200
|
-
const { retryAfter, ...rest } = options;
|
|
3201
|
-
const retryMessage = retryAfter ? `Please wait ${retryAfter} seconds before trying again.` : "Please wait a moment before trying again.";
|
|
3202
|
-
return errorPage({
|
|
3203
|
-
code: 429,
|
|
3204
|
-
title: "Too Many Requests",
|
|
3205
|
-
message: `You've made too many requests. ${retryMessage}`,
|
|
3206
|
-
showRetry: true,
|
|
3207
|
-
showHome: true,
|
|
3208
|
-
...rest
|
|
3209
|
-
});
|
|
3210
|
-
}
|
|
3211
|
-
function offlinePage(options = {}) {
|
|
3212
|
-
return errorPage({
|
|
3213
|
-
title: "You're Offline",
|
|
3214
|
-
message: "Please check your internet connection and try again.",
|
|
3215
|
-
showRetry: true,
|
|
3216
|
-
showHome: false,
|
|
3217
|
-
...options,
|
|
3218
|
-
layout: {
|
|
3219
|
-
...options.layout
|
|
3220
|
-
}
|
|
3221
|
-
});
|
|
3222
|
-
}
|
|
3223
|
-
function sessionExpiredPage(options = {}) {
|
|
3224
|
-
const { loginUrl = "/login", ...rest } = options;
|
|
3225
|
-
return errorPage({
|
|
3226
|
-
title: "Session Expired",
|
|
3227
|
-
message: "Your session has expired. Please sign in again to continue.",
|
|
3228
|
-
showRetry: false,
|
|
3229
|
-
showHome: false,
|
|
3230
|
-
actions: `
|
|
3231
|
-
<div class="flex justify-center mt-8">
|
|
3232
|
-
<a href="${(0, import_utils2.escapeHtml)(
|
|
3233
|
-
loginUrl
|
|
3234
|
-
)}" class="px-6 py-3 bg-primary hover:bg-primary/90 text-white font-medium rounded-lg transition-colors">
|
|
3235
|
-
Sign In Again
|
|
3236
|
-
</a>
|
|
3237
|
-
</div>
|
|
3238
|
-
`,
|
|
3239
|
-
...rest
|
|
3240
|
-
});
|
|
3241
|
-
}
|
|
3242
|
-
function oauthErrorPage(options) {
|
|
3243
|
-
const { errorCode, errorDescription, redirectUri, clientName, ...rest } = options;
|
|
3244
|
-
const errorMessages = {
|
|
3245
|
-
invalid_request: {
|
|
3246
|
-
title: "Invalid Request",
|
|
3247
|
-
message: "The authorization request is missing required parameters or is malformed."
|
|
3248
|
-
},
|
|
3249
|
-
unauthorized_client: {
|
|
3250
|
-
title: "Unauthorized Client",
|
|
3251
|
-
message: "The client is not authorized to request an authorization code."
|
|
3252
|
-
},
|
|
3253
|
-
access_denied: {
|
|
3254
|
-
title: "Access Denied",
|
|
3255
|
-
message: "The resource owner denied the authorization request."
|
|
3256
|
-
},
|
|
3257
|
-
unsupported_response_type: {
|
|
3258
|
-
title: "Unsupported Response Type",
|
|
3259
|
-
message: "The authorization server does not support the requested response type."
|
|
3260
|
-
},
|
|
3261
|
-
invalid_scope: {
|
|
3262
|
-
title: "Invalid Scope",
|
|
3263
|
-
message: "The requested scope is invalid, unknown, or malformed."
|
|
3264
|
-
},
|
|
3265
|
-
server_error: {
|
|
3266
|
-
title: "Server Error",
|
|
3267
|
-
message: "The authorization server encountered an unexpected error."
|
|
3268
|
-
},
|
|
3269
|
-
temporarily_unavailable: {
|
|
3270
|
-
title: "Temporarily Unavailable",
|
|
3271
|
-
message: "The authorization server is temporarily unavailable. Please try again later."
|
|
3272
|
-
}
|
|
3273
|
-
};
|
|
3274
|
-
const errorInfo = errorCode && errorMessages[errorCode] ? errorMessages[errorCode] : { title: "Authorization Error", message: errorDescription || "An error occurred during authorization." };
|
|
3275
|
-
const clientMessage = clientName ? ` while connecting to ${(0, import_utils2.escapeHtml)(clientName)}` : "";
|
|
3276
|
-
const redirectAction = redirectUri ? `
|
|
3277
|
-
<a href="${(0, import_utils2.escapeHtml)(
|
|
3278
|
-
redirectUri
|
|
3279
|
-
)}" class="px-6 py-3 bg-gray-100 hover:bg-gray-200 text-text-primary font-medium rounded-lg transition-colors">
|
|
3280
|
-
Return to ${clientName ? (0, import_utils2.escapeHtml)(clientName) : "Application"}
|
|
3281
|
-
</a>
|
|
3282
|
-
` : "";
|
|
3283
|
-
return errorPage({
|
|
3284
|
-
title: errorInfo.title,
|
|
3285
|
-
message: `${errorInfo.message}${clientMessage}`,
|
|
3286
|
-
details: errorCode && errorDescription ? `Error: ${errorCode}
|
|
3287
|
-
${errorDescription}` : void 0,
|
|
3288
|
-
showRetry: errorCode === "server_error" || errorCode === "temporarily_unavailable",
|
|
3289
|
-
showHome: true,
|
|
3290
|
-
actions: redirectAction ? `<div class="flex justify-center gap-4 mt-8">${redirectAction}</div>` : void 0,
|
|
3291
|
-
...rest
|
|
3292
|
-
});
|
|
3293
|
-
}
|
|
3294
|
-
|
|
3295
|
-
// libs/ui/src/widgets/resource.ts
|
|
3296
|
-
var resourceIcons = {
|
|
3297
|
-
document: `<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
3298
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
|
|
3299
|
-
</svg>`,
|
|
3300
|
-
image: `<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
3301
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"/>
|
|
3302
|
-
</svg>`,
|
|
3303
|
-
code: `<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
3304
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"/>
|
|
3305
|
-
</svg>`,
|
|
3306
|
-
data: `<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
3307
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4"/>
|
|
3308
|
-
</svg>`,
|
|
3309
|
-
file: `<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
3310
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z"/>
|
|
3311
|
-
</svg>`,
|
|
3312
|
-
link: `<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
3313
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"/>
|
|
3314
|
-
</svg>`,
|
|
3315
|
-
user: `<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
3316
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/>
|
|
3317
|
-
</svg>`,
|
|
3318
|
-
event: `<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
3319
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
|
|
3320
|
-
</svg>`,
|
|
3321
|
-
message: `<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
3322
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z"/>
|
|
3323
|
-
</svg>`,
|
|
3324
|
-
task: `<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
3325
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"/>
|
|
3326
|
-
</svg>`,
|
|
3327
|
-
custom: `<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
3328
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"/>
|
|
3329
|
-
</svg>`
|
|
3330
|
-
};
|
|
3331
|
-
function formatFileSize(bytes) {
|
|
3332
|
-
if (bytes === 0) return "0 B";
|
|
3333
|
-
const k = 1024;
|
|
3334
|
-
const sizes = ["B", "KB", "MB", "GB", "TB"];
|
|
3335
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
3336
|
-
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;
|
|
3337
|
-
}
|
|
3338
|
-
function formatDate(date) {
|
|
3339
|
-
const d = typeof date === "string" ? new Date(date) : date;
|
|
3340
|
-
return d.toLocaleDateString("en-US", {
|
|
3341
|
-
year: "numeric",
|
|
3342
|
-
month: "short",
|
|
3343
|
-
day: "numeric"
|
|
3344
|
-
});
|
|
3345
|
-
}
|
|
3346
|
-
function resourceWidget(options) {
|
|
3347
|
-
const {
|
|
3348
|
-
type,
|
|
3349
|
-
title,
|
|
3350
|
-
description,
|
|
3351
|
-
icon,
|
|
3352
|
-
thumbnail,
|
|
3353
|
-
url,
|
|
3354
|
-
meta,
|
|
3355
|
-
status,
|
|
3356
|
-
actions = [],
|
|
3357
|
-
className = "",
|
|
3358
|
-
cardOptions = {}
|
|
3359
|
-
} = options;
|
|
3360
|
-
const iconHtml = thumbnail ? `<div class="w-16 h-16 rounded-lg overflow-hidden bg-gray-100 flex-shrink-0">
|
|
3361
|
-
<img src="${(0, import_utils2.escapeHtml)(thumbnail)}" alt="${(0, import_utils2.escapeHtml)(title)}" class="w-full h-full object-cover">
|
|
3362
|
-
</div>` : `<div class="w-16 h-16 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0 text-gray-400">
|
|
3363
|
-
${icon || resourceIcons[type]}
|
|
3364
|
-
</div>`;
|
|
3365
|
-
const statusHtml = status ? badge(status.label, { variant: status.variant, size: "sm" }) : "";
|
|
3366
|
-
const metaItems = [];
|
|
3367
|
-
if (meta?.size) {
|
|
3368
|
-
metaItems.push(formatFileSize(meta.size));
|
|
3369
|
-
}
|
|
3370
|
-
if (meta?.mimeType) {
|
|
3371
|
-
metaItems.push(meta.mimeType);
|
|
3372
|
-
}
|
|
3373
|
-
if (meta?.updatedAt) {
|
|
3374
|
-
metaItems.push(`Updated ${formatDate(meta.updatedAt)}`);
|
|
3375
|
-
} else if (meta?.createdAt) {
|
|
3376
|
-
metaItems.push(`Created ${formatDate(meta.createdAt)}`);
|
|
3377
|
-
}
|
|
3378
|
-
if (meta?.author) {
|
|
3379
|
-
metaItems.push(`by ${meta.author}`);
|
|
3380
|
-
}
|
|
3381
|
-
const metaHtml = metaItems.length > 0 ? `<div class="text-xs text-text-secondary mt-1">${metaItems.join(" \u2022 ")}</div>` : "";
|
|
3382
|
-
const tagsHtml = meta?.tags && meta.tags.length > 0 ? `<div class="flex flex-wrap gap-1 mt-2">
|
|
3383
|
-
${meta.tags.map((tag) => badge(tag, { variant: "default", size: "sm" })).join("")}
|
|
3384
|
-
</div>` : "";
|
|
3385
|
-
const actionsHtml = actions.length > 0 ? `<div class="flex gap-2 mt-4">
|
|
3386
|
-
${actions.map((action) => {
|
|
3387
|
-
const variantMap = {
|
|
3388
|
-
primary: "primary",
|
|
3389
|
-
secondary: "secondary",
|
|
3390
|
-
danger: "danger",
|
|
3391
|
-
ghost: "ghost"
|
|
3392
|
-
};
|
|
3393
|
-
const variant = action.variant ? variantMap[action.variant] : "ghost";
|
|
3394
|
-
const htmxAttrs = [];
|
|
3395
|
-
if (action.htmx) {
|
|
3396
|
-
if (action.htmx.get) htmxAttrs.push(`hx-get="${(0, import_utils2.escapeHtml)(action.htmx.get)}"`);
|
|
3397
|
-
if (action.htmx.post) htmxAttrs.push(`hx-post="${(0, import_utils2.escapeHtml)(action.htmx.post)}"`);
|
|
3398
|
-
if (action.htmx.delete) htmxAttrs.push(`hx-delete="${(0, import_utils2.escapeHtml)(action.htmx.delete)}"`);
|
|
3399
|
-
if (action.htmx.target) htmxAttrs.push(`hx-target="${(0, import_utils2.escapeHtml)(action.htmx.target)}"`);
|
|
3400
|
-
if (action.htmx.swap) htmxAttrs.push(`hx-swap="${(0, import_utils2.escapeHtml)(action.htmx.swap)}"`);
|
|
3401
|
-
if (action.htmx.confirm) htmxAttrs.push(`hx-confirm="${(0, import_utils2.escapeHtml)(action.htmx.confirm)}"`);
|
|
3402
|
-
}
|
|
3403
|
-
return button(action.label, {
|
|
3404
|
-
variant,
|
|
3405
|
-
size: "sm",
|
|
3406
|
-
href: action.href,
|
|
3407
|
-
disabled: action.disabled,
|
|
3408
|
-
iconBefore: action.icon
|
|
3409
|
-
});
|
|
3410
|
-
}).join("")}
|
|
3411
|
-
</div>` : "";
|
|
3412
|
-
const content = `
|
|
3413
|
-
<div class="flex gap-4">
|
|
3414
|
-
${iconHtml}
|
|
3415
|
-
<div class="flex-1 min-w-0">
|
|
3416
|
-
<div class="flex items-start justify-between gap-2">
|
|
3417
|
-
<div class="min-w-0">
|
|
3418
|
-
${url ? `<a href="${(0, import_utils2.escapeHtml)(
|
|
3419
|
-
url
|
|
3420
|
-
)}" class="font-semibold text-text-primary hover:text-primary truncate block">${(0, import_utils2.escapeHtml)(
|
|
3421
|
-
title
|
|
3422
|
-
)}</a>` : `<h3 class="font-semibold text-text-primary truncate">${(0, import_utils2.escapeHtml)(title)}</h3>`}
|
|
3423
|
-
${description ? `<p class="text-sm text-text-secondary mt-0.5 line-clamp-2">${(0, import_utils2.escapeHtml)(description)}</p>` : ""}
|
|
3424
|
-
${metaHtml}
|
|
3425
|
-
${tagsHtml}
|
|
3426
|
-
</div>
|
|
3427
|
-
${statusHtml}
|
|
3428
|
-
</div>
|
|
3429
|
-
${actionsHtml}
|
|
3430
|
-
</div>
|
|
3431
|
-
</div>
|
|
3432
|
-
`;
|
|
3433
|
-
return card(content, {
|
|
3434
|
-
variant: "default",
|
|
3435
|
-
size: "md",
|
|
3436
|
-
className: `resource-widget resource-${type} ${className}`,
|
|
3437
|
-
...cardOptions
|
|
3438
|
-
});
|
|
3439
|
-
}
|
|
3440
|
-
function resourceList(options) {
|
|
3441
|
-
const {
|
|
3442
|
-
resources,
|
|
3443
|
-
title,
|
|
3444
|
-
emptyMessage = "No resources found",
|
|
3445
|
-
layout = "list",
|
|
3446
|
-
columns = 2,
|
|
3447
|
-
className = "",
|
|
3448
|
-
showLoadMore = false,
|
|
3449
|
-
loadMoreUrl
|
|
3450
|
-
} = options;
|
|
3451
|
-
const titleHtml = title ? `<h2 class="text-lg font-semibold text-text-primary mb-4">${(0, import_utils2.escapeHtml)(title)}</h2>` : "";
|
|
3452
|
-
if (resources.length === 0) {
|
|
3453
|
-
return `<div class="${className}">
|
|
3454
|
-
${titleHtml}
|
|
3455
|
-
<div class="text-center py-12 text-text-secondary">
|
|
3456
|
-
<svg class="w-12 h-12 mx-auto mb-4 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
3457
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4"/>
|
|
3458
|
-
</svg>
|
|
3459
|
-
<p>${(0, import_utils2.escapeHtml)(emptyMessage)}</p>
|
|
3460
|
-
</div>
|
|
3461
|
-
</div>`;
|
|
3462
|
-
}
|
|
3463
|
-
const layoutClasses = layout === "grid" ? `grid grid-cols-1 md:grid-cols-${columns} gap-4` : "space-y-4";
|
|
3464
|
-
const resourcesHtml = resources.map((r) => resourceWidget(r)).join("\n");
|
|
3465
|
-
const loadMoreHtml = showLoadMore && loadMoreUrl ? `<div class="text-center mt-6">
|
|
3466
|
-
${button("Load More", {
|
|
3467
|
-
variant: "outline",
|
|
3468
|
-
href: loadMoreUrl
|
|
3469
|
-
})}
|
|
3470
|
-
</div>` : "";
|
|
3471
|
-
return `<div class="resource-list ${className}">
|
|
3472
|
-
${titleHtml}
|
|
3473
|
-
<div class="${layoutClasses}">
|
|
3474
|
-
${resourcesHtml}
|
|
3475
|
-
</div>
|
|
3476
|
-
${loadMoreHtml}
|
|
3477
|
-
</div>`;
|
|
3478
|
-
}
|
|
3479
|
-
function resourceItem(options) {
|
|
3480
|
-
const { type, title, description, icon, url, meta, status } = options;
|
|
3481
|
-
const iconHtml = `<div class="w-10 h-10 rounded-lg bg-gray-100 flex items-center justify-center flex-shrink-0 text-gray-400">
|
|
3482
|
-
${icon || resourceIcons[type]}
|
|
3483
|
-
</div>`;
|
|
3484
|
-
const statusHtml = status ? badge(status.label, { variant: status.variant, size: "sm" }) : "";
|
|
3485
|
-
const metaText = meta?.size ? formatFileSize(meta.size) : "";
|
|
3486
|
-
const titleElement = url ? `<a href="${(0, import_utils2.escapeHtml)(url)}" class="font-medium text-text-primary hover:text-primary">${(0, import_utils2.escapeHtml)(title)}</a>` : `<span class="font-medium text-text-primary">${(0, import_utils2.escapeHtml)(title)}</span>`;
|
|
3487
|
-
return `<div class="flex items-center gap-3 p-3 rounded-lg hover:bg-gray-50 transition-colors">
|
|
3488
|
-
${iconHtml}
|
|
3489
|
-
<div class="flex-1 min-w-0">
|
|
3490
|
-
<div class="flex items-center gap-2">
|
|
3491
|
-
${titleElement}
|
|
3492
|
-
${statusHtml}
|
|
3493
|
-
</div>
|
|
3494
|
-
${description || metaText ? `<p class="text-sm text-text-secondary truncate">${(0, import_utils2.escapeHtml)(description || metaText)}</p>` : ""}
|
|
3495
|
-
</div>
|
|
3496
|
-
</div>`;
|
|
3497
|
-
}
|
|
3498
|
-
function codePreview(options) {
|
|
3499
|
-
const {
|
|
3500
|
-
code,
|
|
3501
|
-
language = "text",
|
|
3502
|
-
filename,
|
|
3503
|
-
lineNumbers = true,
|
|
3504
|
-
maxHeight = "400px",
|
|
3505
|
-
showCopy = true,
|
|
3506
|
-
className = ""
|
|
3507
|
-
} = options;
|
|
3508
|
-
const lines = code.split("\n");
|
|
3509
|
-
const lineNumbersHtml = lineNumbers ? `<div class="text-right select-none pr-4 text-gray-500">
|
|
3510
|
-
${lines.map((_, i) => `<div>${i + 1}</div>`).join("")}
|
|
3511
|
-
</div>` : "";
|
|
3512
|
-
const copyScript = showCopy ? `<script>
|
|
3513
|
-
function copyCode(btn, code) {
|
|
3514
|
-
navigator.clipboard.writeText(code).then(() => {
|
|
3515
|
-
const original = btn.innerHTML;
|
|
3516
|
-
btn.innerHTML = '<svg class="w-4 h-4 text-success" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>';
|
|
3517
|
-
setTimeout(() => btn.innerHTML = original, 2000);
|
|
3518
|
-
});
|
|
3519
|
-
}
|
|
3520
|
-
</script>` : "";
|
|
3521
|
-
const copyButton = showCopy ? `<button
|
|
3522
|
-
type="button"
|
|
3523
|
-
onclick="copyCode(this, ${(0, import_utils2.escapeHtml)(JSON.stringify(code))})"
|
|
3524
|
-
class="p-1.5 rounded hover:bg-gray-700 transition-colors"
|
|
3525
|
-
title="Copy code"
|
|
3526
|
-
>
|
|
3527
|
-
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
3528
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"/>
|
|
3529
|
-
</svg>
|
|
3530
|
-
</button>` : "";
|
|
3531
|
-
return `<div class="code-preview rounded-lg overflow-hidden ${className}">
|
|
3532
|
-
${filename || showCopy ? `
|
|
3533
|
-
<div class="flex items-center justify-between px-4 py-2 bg-gray-800 border-b border-gray-700">
|
|
3534
|
-
${filename ? `<span class="text-sm text-gray-300">${(0, import_utils2.escapeHtml)(filename)}</span>` : "<span></span>"}
|
|
3535
|
-
<div class="flex items-center gap-2">
|
|
3536
|
-
${language ? `<span class="text-xs text-gray-500">${(0, import_utils2.escapeHtml)(language)}</span>` : ""}
|
|
3537
|
-
${copyButton}
|
|
3538
|
-
</div>
|
|
3539
|
-
</div>
|
|
3540
|
-
` : ""}
|
|
3541
|
-
<div class="bg-gray-900 p-4 overflow-auto" style="max-height: ${maxHeight}">
|
|
3542
|
-
<div class="flex text-sm font-mono">
|
|
3543
|
-
${lineNumbersHtml}
|
|
3544
|
-
<pre class="flex-1 text-gray-100"><code>${(0, import_utils2.escapeHtml)(code)}</code></pre>
|
|
3545
|
-
</div>
|
|
3546
|
-
</div>
|
|
3547
|
-
${copyScript}
|
|
3548
|
-
</div>`;
|
|
3549
|
-
}
|
|
3550
|
-
function imagePreview(options) {
|
|
3551
|
-
const { src, alt, caption, maxHeight = "400px", clickable = true, className = "" } = options;
|
|
3552
|
-
const imageHtml = `<img
|
|
3553
|
-
src="${(0, import_utils2.escapeHtml)(src)}"
|
|
3554
|
-
alt="${(0, import_utils2.escapeHtml)(alt)}"
|
|
3555
|
-
class="max-w-full h-auto rounded-lg"
|
|
3556
|
-
style="max-height: ${maxHeight}"
|
|
3557
|
-
>`;
|
|
3558
|
-
const captionHtml = caption ? `<p class="text-sm text-text-secondary mt-2 text-center">${(0, import_utils2.escapeHtml)(caption)}</p>` : "";
|
|
3559
|
-
const content = clickable ? `<a href="${(0, import_utils2.escapeHtml)(src)}" target="_blank" rel="noopener" class="block">${imageHtml}</a>` : imageHtml;
|
|
3560
|
-
return `<div class="image-preview ${className}">
|
|
3561
|
-
${content}
|
|
3562
|
-
${captionHtml}
|
|
3563
|
-
</div>`;
|
|
3564
|
-
}
|
|
3565
|
-
|
|
3566
|
-
// libs/ui/src/widgets/progress.ts
|
|
3567
|
-
function progressBar(options) {
|
|
3568
|
-
const {
|
|
3569
|
-
value,
|
|
3570
|
-
showLabel = true,
|
|
3571
|
-
labelPosition = "outside",
|
|
3572
|
-
size = "md",
|
|
3573
|
-
variant = "primary",
|
|
3574
|
-
animated = false,
|
|
3575
|
-
className = "",
|
|
3576
|
-
label
|
|
3577
|
-
} = options;
|
|
3578
|
-
const clampedValue = Math.min(100, Math.max(0, value));
|
|
3579
|
-
const sizeClasses = {
|
|
3580
|
-
sm: "h-1.5",
|
|
3581
|
-
md: "h-2.5",
|
|
3582
|
-
lg: "h-4"
|
|
3583
|
-
};
|
|
3584
|
-
const variantClasses = {
|
|
3585
|
-
primary: "bg-primary",
|
|
3586
|
-
success: "bg-success",
|
|
3587
|
-
warning: "bg-warning",
|
|
3588
|
-
danger: "bg-danger",
|
|
3589
|
-
info: "bg-blue-500"
|
|
3590
|
-
};
|
|
3591
|
-
const animatedClass = animated ? "bg-stripes animate-stripes" : "";
|
|
3592
|
-
const displayLabel = label || `${Math.round(clampedValue)}%`;
|
|
3593
|
-
const insideLabel = labelPosition === "inside" && size === "lg" && clampedValue > 10 ? `<span class="absolute inset-0 flex items-center justify-center text-xs font-medium text-white">${(0, import_utils2.escapeHtml)(
|
|
3594
|
-
displayLabel
|
|
3595
|
-
)}</span>` : "";
|
|
3596
|
-
const outsideLabel = showLabel && labelPosition === "outside" ? `<div class="flex justify-between mb-1">
|
|
3597
|
-
<span class="text-sm font-medium text-text-primary">${label ? (0, import_utils2.escapeHtml)(label) : "Progress"}</span>
|
|
3598
|
-
<span class="text-sm text-text-secondary">${Math.round(clampedValue)}%</span>
|
|
3599
|
-
</div>` : "";
|
|
3600
|
-
return `<div class="progress-bar ${className}">
|
|
3601
|
-
${outsideLabel}
|
|
3602
|
-
<div class="relative w-full ${sizeClasses[size]} bg-gray-200 rounded-full overflow-hidden">
|
|
3603
|
-
<div
|
|
3604
|
-
class="${variantClasses[variant]} ${sizeClasses[size]} ${animatedClass} rounded-full transition-all duration-300"
|
|
3605
|
-
style="width: ${clampedValue}%"
|
|
3606
|
-
role="progressbar"
|
|
3607
|
-
aria-valuenow="${clampedValue}"
|
|
3608
|
-
aria-valuemin="0"
|
|
3609
|
-
aria-valuemax="100"
|
|
3610
|
-
></div>
|
|
3611
|
-
${insideLabel}
|
|
3612
|
-
</div>
|
|
3613
|
-
</div>
|
|
3614
|
-
${animated ? `<style>
|
|
3615
|
-
.bg-stripes {
|
|
3616
|
-
background-image: linear-gradient(
|
|
3617
|
-
45deg,
|
|
3618
|
-
rgba(255,255,255,0.15) 25%,
|
|
3619
|
-
transparent 25%,
|
|
3620
|
-
transparent 50%,
|
|
3621
|
-
rgba(255,255,255,0.15) 50%,
|
|
3622
|
-
rgba(255,255,255,0.15) 75%,
|
|
3623
|
-
transparent 75%,
|
|
3624
|
-
transparent
|
|
3625
|
-
);
|
|
3626
|
-
background-size: 1rem 1rem;
|
|
3627
|
-
}
|
|
3628
|
-
@keyframes stripes {
|
|
3629
|
-
from { background-position: 1rem 0; }
|
|
3630
|
-
to { background-position: 0 0; }
|
|
3631
|
-
}
|
|
3632
|
-
.animate-stripes {
|
|
3633
|
-
animation: stripes 1s linear infinite;
|
|
3634
|
-
}
|
|
3635
|
-
</style>` : ""}`;
|
|
3636
|
-
}
|
|
3637
|
-
function stepProgress(options) {
|
|
3638
|
-
const { steps, orientation = "horizontal", connector = "line", className = "" } = options;
|
|
3639
|
-
const getStepIcon = (step, index) => {
|
|
3640
|
-
if (step.icon) return step.icon;
|
|
3641
|
-
if (step.status === "completed") {
|
|
3642
|
-
return `<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
3643
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
|
|
3644
|
-
</svg>`;
|
|
3645
|
-
}
|
|
3646
|
-
return `<span class="font-medium">${index + 1}</span>`;
|
|
3647
|
-
};
|
|
3648
|
-
const getStepClasses = (status) => {
|
|
3649
|
-
switch (status) {
|
|
3650
|
-
case "completed":
|
|
3651
|
-
return {
|
|
3652
|
-
circle: "bg-success text-white",
|
|
3653
|
-
text: "text-text-primary"
|
|
3654
|
-
};
|
|
3655
|
-
case "current":
|
|
3656
|
-
return {
|
|
3657
|
-
circle: "bg-primary text-white ring-4 ring-primary/20",
|
|
3658
|
-
text: "text-primary font-medium"
|
|
3659
|
-
};
|
|
3660
|
-
case "upcoming":
|
|
3661
|
-
default:
|
|
3662
|
-
return {
|
|
3663
|
-
circle: "bg-gray-200 text-gray-500",
|
|
3664
|
-
text: "text-text-secondary"
|
|
3665
|
-
};
|
|
3666
|
-
}
|
|
3667
|
-
};
|
|
3668
|
-
if (orientation === "vertical") {
|
|
3669
|
-
const stepsHtml2 = steps.map((step, index) => {
|
|
3670
|
-
const classes = getStepClasses(step.status);
|
|
3671
|
-
const isLast = index === steps.length - 1;
|
|
3672
|
-
const connectorHtml = !isLast && connector !== "none" ? `<div class="ml-5 w-0.5 h-8 ${connector === "dashed" ? "border-l-2 border-dashed border-gray-300" : "bg-gray-200"} ${step.status === "completed" ? "bg-success" : ""}"></div>` : "";
|
|
3673
|
-
const stepContent = `
|
|
3674
|
-
<div class="flex items-start gap-4">
|
|
3675
|
-
<div class="w-10 h-10 rounded-full ${classes.circle} flex items-center justify-center flex-shrink-0">
|
|
3676
|
-
${getStepIcon(step, index)}
|
|
3677
|
-
</div>
|
|
3678
|
-
<div class="pt-2">
|
|
3679
|
-
<div class="${classes.text}">${(0, import_utils2.escapeHtml)(step.label)}</div>
|
|
3680
|
-
${step.description ? `<p class="text-sm text-text-secondary mt-0.5">${(0, import_utils2.escapeHtml)(step.description)}</p>` : ""}
|
|
3681
|
-
</div>
|
|
3682
|
-
</div>
|
|
3683
|
-
${connectorHtml}
|
|
3684
|
-
`;
|
|
3685
|
-
return step.href && step.status === "completed" ? `<a href="${(0, import_utils2.escapeHtml)(step.href)}" class="block hover:opacity-80">${stepContent}</a>` : `<div>${stepContent}</div>`;
|
|
3686
|
-
}).join("\n");
|
|
3687
|
-
return `<div class="step-progress ${className}">${stepsHtml2}</div>`;
|
|
3688
|
-
}
|
|
3689
|
-
const stepsHtml = steps.map((step, index) => {
|
|
3690
|
-
const classes = getStepClasses(step.status);
|
|
3691
|
-
const isLast = index === steps.length - 1;
|
|
3692
|
-
const connectorHtml = !isLast && connector !== "none" ? `<div class="flex-1 h-0.5 mx-2 ${connector === "dashed" ? "border-t-2 border-dashed border-gray-300" : "bg-gray-200"} ${step.status === "completed" ? "bg-success" : ""}"></div>` : "";
|
|
3693
|
-
const stepHtml = `
|
|
3694
|
-
<div class="flex flex-col items-center">
|
|
3695
|
-
<div class="w-10 h-10 rounded-full ${classes.circle} flex items-center justify-center">
|
|
3696
|
-
${getStepIcon(step, index)}
|
|
3697
|
-
</div>
|
|
3698
|
-
<div class="mt-2 text-center">
|
|
3699
|
-
<div class="${classes.text} text-sm">${(0, import_utils2.escapeHtml)(step.label)}</div>
|
|
3700
|
-
${step.description ? `<p class="text-xs text-text-secondary mt-0.5 max-w-[120px]">${(0, import_utils2.escapeHtml)(step.description)}</p>` : ""}
|
|
3701
|
-
</div>
|
|
3702
|
-
</div>
|
|
3703
|
-
`;
|
|
3704
|
-
const clickableStep = step.href && step.status === "completed" ? `<a href="${(0, import_utils2.escapeHtml)(step.href)}" class="hover:opacity-80">${stepHtml}</a>` : stepHtml;
|
|
3705
|
-
return `${clickableStep}${connectorHtml}`;
|
|
3706
|
-
}).join("\n");
|
|
3707
|
-
return `<div class="step-progress flex items-start ${className}">${stepsHtml}</div>`;
|
|
3708
|
-
}
|
|
3709
|
-
function circularProgress(options) {
|
|
3710
|
-
const { value, size = 80, strokeWidth = 8, variant = "primary", showLabel = true, label, className = "" } = options;
|
|
3711
|
-
const clampedValue = Math.min(100, Math.max(0, value));
|
|
3712
|
-
const radius = (size - strokeWidth) / 2;
|
|
3713
|
-
const circumference = radius * 2 * Math.PI;
|
|
3714
|
-
const offset = circumference - clampedValue / 100 * circumference;
|
|
3715
|
-
const variantColors = {
|
|
3716
|
-
primary: "text-primary",
|
|
3717
|
-
success: "text-success",
|
|
3718
|
-
warning: "text-warning",
|
|
3719
|
-
danger: "text-danger"
|
|
3720
|
-
};
|
|
3721
|
-
const displayLabel = label || `${Math.round(clampedValue)}%`;
|
|
3722
|
-
return `<div class="circular-progress inline-flex items-center justify-center ${className}" style="width: ${size}px; height: ${size}px;">
|
|
3723
|
-
<svg class="transform -rotate-90" width="${size}" height="${size}">
|
|
3724
|
-
<!-- Background circle -->
|
|
3725
|
-
<circle
|
|
3726
|
-
cx="${size / 2}"
|
|
3727
|
-
cy="${size / 2}"
|
|
3728
|
-
r="${radius}"
|
|
3729
|
-
fill="none"
|
|
3730
|
-
stroke="currentColor"
|
|
3731
|
-
stroke-width="${strokeWidth}"
|
|
3732
|
-
class="text-gray-200"
|
|
3733
|
-
/>
|
|
3734
|
-
<!-- Progress circle -->
|
|
3735
|
-
<circle
|
|
3736
|
-
cx="${size / 2}"
|
|
3737
|
-
cy="${size / 2}"
|
|
3738
|
-
r="${radius}"
|
|
3739
|
-
fill="none"
|
|
3740
|
-
stroke="currentColor"
|
|
3741
|
-
stroke-width="${strokeWidth}"
|
|
3742
|
-
stroke-linecap="round"
|
|
3743
|
-
class="${variantColors[variant]}"
|
|
3744
|
-
style="stroke-dasharray: ${circumference}; stroke-dashoffset: ${offset}; transition: stroke-dashoffset 0.3s ease;"
|
|
3745
|
-
/>
|
|
3746
|
-
</svg>
|
|
3747
|
-
${showLabel ? `<span class="absolute text-sm font-semibold text-text-primary">${(0, import_utils2.escapeHtml)(displayLabel)}</span>` : ""}
|
|
3748
|
-
</div>`;
|
|
3749
|
-
}
|
|
3750
|
-
function statusIndicator(options) {
|
|
3751
|
-
const { status, label, size = "md", pulse = false, className = "" } = options;
|
|
3752
|
-
const sizeClasses = {
|
|
3753
|
-
sm: { dot: "w-2 h-2", text: "text-xs" },
|
|
3754
|
-
md: { dot: "w-2.5 h-2.5", text: "text-sm" },
|
|
3755
|
-
lg: { dot: "w-3 h-3", text: "text-base" }
|
|
3756
|
-
};
|
|
3757
|
-
const statusClasses = {
|
|
3758
|
-
online: { color: "bg-success", label: "Online" },
|
|
3759
|
-
offline: { color: "bg-gray-400", label: "Offline" },
|
|
3760
|
-
busy: { color: "bg-danger", label: "Busy" },
|
|
3761
|
-
away: { color: "bg-warning", label: "Away" },
|
|
3762
|
-
loading: { color: "bg-blue-500", label: "Loading" },
|
|
3763
|
-
error: { color: "bg-danger", label: "Error" },
|
|
3764
|
-
success: { color: "bg-success", label: "Success" }
|
|
3765
|
-
};
|
|
3766
|
-
const statusInfo = statusClasses[status];
|
|
3767
|
-
const sizeInfo = sizeClasses[size];
|
|
3768
|
-
const displayLabel = label || statusInfo.label;
|
|
3769
|
-
const pulseHtml = pulse || status === "loading" ? `<span class="absolute ${sizeInfo.dot} ${statusInfo.color} rounded-full animate-ping opacity-75"></span>` : "";
|
|
3770
|
-
return `<div class="status-indicator inline-flex items-center gap-2 ${className}">
|
|
3771
|
-
<span class="relative flex">
|
|
3772
|
-
${pulseHtml}
|
|
3773
|
-
<span class="relative ${sizeInfo.dot} ${statusInfo.color} rounded-full"></span>
|
|
3774
|
-
</span>
|
|
3775
|
-
${displayLabel ? `<span class="${sizeInfo.text} text-text-secondary">${(0, import_utils2.escapeHtml)(displayLabel)}</span>` : ""}
|
|
3776
|
-
</div>`;
|
|
3777
|
-
}
|
|
3778
|
-
function skeleton(options = {}) {
|
|
3779
|
-
const { type = "text", width, height, lines = 3, animated = true, className = "" } = options;
|
|
3780
|
-
const animateClass = animated ? "animate-pulse" : "";
|
|
3781
|
-
const baseClass = `bg-gray-200 ${animateClass}`;
|
|
3782
|
-
switch (type) {
|
|
3783
|
-
case "circle":
|
|
3784
|
-
return `<div class="${baseClass} rounded-full ${className}" style="width: ${width || "40px"}; height: ${height || "40px"}"></div>`;
|
|
3785
|
-
case "rect":
|
|
3786
|
-
return `<div class="${baseClass} rounded ${className}" style="width: ${width || "100%"}; height: ${height || "100px"}"></div>`;
|
|
3787
|
-
case "card":
|
|
3788
|
-
return `<div class="${animateClass} space-y-4 ${className}">
|
|
3789
|
-
<div class="bg-gray-200 rounded h-40"></div>
|
|
3790
|
-
<div class="space-y-2">
|
|
3791
|
-
<div class="bg-gray-200 h-4 rounded w-3/4"></div>
|
|
3792
|
-
<div class="bg-gray-200 h-4 rounded w-1/2"></div>
|
|
3793
|
-
</div>
|
|
3794
|
-
</div>`;
|
|
3795
|
-
case "text":
|
|
3796
|
-
default: {
|
|
3797
|
-
const linesHtml = Array(lines).fill(0).map((_, i) => {
|
|
3798
|
-
const lineWidth = i === lines - 1 ? "60%" : i === 0 ? "90%" : "80%";
|
|
3799
|
-
return `<div class="bg-gray-200 h-4 rounded" style="width: ${lineWidth}"></div>`;
|
|
3800
|
-
}).join("\n");
|
|
3801
|
-
return `<div class="${animateClass} space-y-2 ${className}" style="width: ${width || "100%"}">
|
|
3802
|
-
${linesHtml}
|
|
3803
|
-
</div>`;
|
|
3804
|
-
}
|
|
3805
|
-
}
|
|
3806
|
-
}
|
|
3807
|
-
function contentSkeleton(options = {}) {
|
|
3808
|
-
const { animated = true, className = "" } = options;
|
|
3809
|
-
const animateClass = animated ? "animate-pulse" : "";
|
|
3810
|
-
return `<div class="${animateClass} flex gap-4 ${className}">
|
|
3811
|
-
<div class="bg-gray-200 rounded-full w-12 h-12 flex-shrink-0"></div>
|
|
3812
|
-
<div class="flex-1 space-y-2 py-1">
|
|
3813
|
-
<div class="bg-gray-200 h-4 rounded w-3/4"></div>
|
|
3814
|
-
<div class="bg-gray-200 h-4 rounded w-1/2"></div>
|
|
3815
|
-
</div>
|
|
3816
|
-
</div>`;
|
|
3817
|
-
}
|
|
3818
|
-
|
|
3819
2868
|
// libs/ui/src/bridge/core/adapter-registry.ts
|
|
3820
2869
|
var AdapterRegistry = class {
|
|
3821
2870
|
_adapters = /* @__PURE__ */ new Map();
|
|
@@ -4180,50 +3229,50 @@ var FrontMcpBridge = class {
|
|
|
4180
3229
|
* Get current theme.
|
|
4181
3230
|
*/
|
|
4182
3231
|
getTheme() {
|
|
4183
|
-
this._ensureInitialized();
|
|
4184
|
-
return
|
|
3232
|
+
const adapter = this._ensureInitialized();
|
|
3233
|
+
return adapter.getTheme();
|
|
4185
3234
|
}
|
|
4186
3235
|
/**
|
|
4187
3236
|
* Get current display mode.
|
|
4188
3237
|
*/
|
|
4189
3238
|
getDisplayMode() {
|
|
4190
|
-
this._ensureInitialized();
|
|
4191
|
-
return
|
|
3239
|
+
const adapter = this._ensureInitialized();
|
|
3240
|
+
return adapter.getDisplayMode();
|
|
4192
3241
|
}
|
|
4193
3242
|
/**
|
|
4194
3243
|
* Get tool input arguments.
|
|
4195
3244
|
*/
|
|
4196
3245
|
getToolInput() {
|
|
4197
|
-
this._ensureInitialized();
|
|
4198
|
-
return
|
|
3246
|
+
const adapter = this._ensureInitialized();
|
|
3247
|
+
return adapter.getToolInput();
|
|
4199
3248
|
}
|
|
4200
3249
|
/**
|
|
4201
3250
|
* Get tool output/result.
|
|
4202
3251
|
*/
|
|
4203
3252
|
getToolOutput() {
|
|
4204
|
-
this._ensureInitialized();
|
|
4205
|
-
return
|
|
3253
|
+
const adapter = this._ensureInitialized();
|
|
3254
|
+
return adapter.getToolOutput();
|
|
4206
3255
|
}
|
|
4207
3256
|
/**
|
|
4208
3257
|
* Get structured content (parsed output).
|
|
4209
3258
|
*/
|
|
4210
3259
|
getStructuredContent() {
|
|
4211
|
-
this._ensureInitialized();
|
|
4212
|
-
return
|
|
3260
|
+
const adapter = this._ensureInitialized();
|
|
3261
|
+
return adapter.getStructuredContent();
|
|
4213
3262
|
}
|
|
4214
3263
|
/**
|
|
4215
3264
|
* Get persisted widget state.
|
|
4216
3265
|
*/
|
|
4217
3266
|
getWidgetState() {
|
|
4218
|
-
this._ensureInitialized();
|
|
4219
|
-
return
|
|
3267
|
+
const adapter = this._ensureInitialized();
|
|
3268
|
+
return adapter.getWidgetState();
|
|
4220
3269
|
}
|
|
4221
3270
|
/**
|
|
4222
3271
|
* Get full host context.
|
|
4223
3272
|
*/
|
|
4224
3273
|
getHostContext() {
|
|
4225
|
-
this._ensureInitialized();
|
|
4226
|
-
return
|
|
3274
|
+
const adapter = this._ensureInitialized();
|
|
3275
|
+
return adapter.getHostContext();
|
|
4227
3276
|
}
|
|
4228
3277
|
// ============================================
|
|
4229
3278
|
// Actions (delegate to adapter)
|
|
@@ -4234,2026 +3283,134 @@ var FrontMcpBridge = class {
|
|
|
4234
3283
|
* @param args - Tool arguments
|
|
4235
3284
|
*/
|
|
4236
3285
|
async callTool(name, args) {
|
|
4237
|
-
this._ensureInitialized();
|
|
3286
|
+
const adapter = this._ensureInitialized();
|
|
4238
3287
|
if (!this.hasCapability("canCallTools")) {
|
|
4239
3288
|
throw new Error("Tool calls are not supported by the current adapter");
|
|
4240
3289
|
}
|
|
4241
|
-
return
|
|
3290
|
+
return adapter.callTool(name, args);
|
|
4242
3291
|
}
|
|
4243
3292
|
/**
|
|
4244
3293
|
* Send a follow-up message to the conversation.
|
|
4245
3294
|
* @param content - Message content
|
|
4246
3295
|
*/
|
|
4247
3296
|
async sendMessage(content) {
|
|
4248
|
-
this._ensureInitialized();
|
|
3297
|
+
const adapter = this._ensureInitialized();
|
|
4249
3298
|
if (!this.hasCapability("canSendMessages")) {
|
|
4250
3299
|
throw new Error("Sending messages is not supported by the current adapter");
|
|
4251
3300
|
}
|
|
4252
|
-
return
|
|
3301
|
+
return adapter.sendMessage(content);
|
|
4253
3302
|
}
|
|
4254
3303
|
/**
|
|
4255
3304
|
* Open an external link.
|
|
4256
3305
|
* @param url - URL to open
|
|
4257
3306
|
*/
|
|
4258
3307
|
async openLink(url) {
|
|
4259
|
-
this._ensureInitialized();
|
|
4260
|
-
return
|
|
3308
|
+
const adapter = this._ensureInitialized();
|
|
3309
|
+
return adapter.openLink(url);
|
|
4261
3310
|
}
|
|
4262
3311
|
/**
|
|
4263
3312
|
* Request a display mode change.
|
|
4264
3313
|
* @param mode - Desired display mode
|
|
4265
3314
|
*/
|
|
4266
3315
|
async requestDisplayMode(mode) {
|
|
4267
|
-
this._ensureInitialized();
|
|
4268
|
-
return
|
|
3316
|
+
const adapter = this._ensureInitialized();
|
|
3317
|
+
return adapter.requestDisplayMode(mode);
|
|
4269
3318
|
}
|
|
4270
3319
|
/**
|
|
4271
3320
|
* Request widget close.
|
|
4272
3321
|
*/
|
|
4273
3322
|
async requestClose() {
|
|
4274
|
-
this._ensureInitialized();
|
|
4275
|
-
return
|
|
3323
|
+
const adapter = this._ensureInitialized();
|
|
3324
|
+
return adapter.requestClose();
|
|
4276
3325
|
}
|
|
4277
3326
|
/**
|
|
4278
3327
|
* Set widget state (persisted across sessions).
|
|
4279
3328
|
* @param state - State object to persist
|
|
4280
3329
|
*/
|
|
4281
|
-
setWidgetState(state) {
|
|
4282
|
-
this._ensureInitialized();
|
|
4283
|
-
|
|
4284
|
-
}
|
|
4285
|
-
// ============================================
|
|
4286
|
-
// Events (delegate to adapter)
|
|
4287
|
-
// ============================================
|
|
4288
|
-
/**
|
|
4289
|
-
* Subscribe to host context changes.
|
|
4290
|
-
* @param callback - Called when context changes
|
|
4291
|
-
* @returns Unsubscribe function
|
|
4292
|
-
*/
|
|
4293
|
-
onContextChange(callback) {
|
|
4294
|
-
this._ensureInitialized();
|
|
4295
|
-
return this._adapter.onContextChange(callback);
|
|
4296
|
-
}
|
|
4297
|
-
/**
|
|
4298
|
-
* Subscribe to tool result updates.
|
|
4299
|
-
* @param callback - Called when tool result is received
|
|
4300
|
-
* @returns Unsubscribe function
|
|
4301
|
-
*/
|
|
4302
|
-
onToolResult(callback) {
|
|
4303
|
-
this._ensureInitialized();
|
|
4304
|
-
return this._adapter.onToolResult(callback);
|
|
4305
|
-
}
|
|
4306
|
-
// ============================================
|
|
4307
|
-
// Private Helpers
|
|
4308
|
-
// ============================================
|
|
4309
|
-
/**
|
|
4310
|
-
* Ensure the bridge is initialized before operations.
|
|
4311
|
-
*/
|
|
4312
|
-
_ensureInitialized() {
|
|
4313
|
-
if (!this._initialized || !this._adapter) {
|
|
4314
|
-
throw new Error("FrontMcpBridge is not initialized. Call initialize() first.");
|
|
4315
|
-
}
|
|
4316
|
-
}
|
|
4317
|
-
/**
|
|
4318
|
-
* Wrap a promise with a timeout.
|
|
4319
|
-
*/
|
|
4320
|
-
_withTimeout(promise, ms) {
|
|
4321
|
-
return new Promise((resolve, reject) => {
|
|
4322
|
-
const timer = setTimeout(() => {
|
|
4323
|
-
reject(new Error(`Operation timed out after ${ms}ms`));
|
|
4324
|
-
}, ms);
|
|
4325
|
-
promise.then((result) => {
|
|
4326
|
-
clearTimeout(timer);
|
|
4327
|
-
resolve(result);
|
|
4328
|
-
}).catch((error) => {
|
|
4329
|
-
clearTimeout(timer);
|
|
4330
|
-
reject(error);
|
|
4331
|
-
});
|
|
4332
|
-
});
|
|
4333
|
-
}
|
|
4334
|
-
/**
|
|
4335
|
-
* Emit a bridge event via CustomEvent.
|
|
4336
|
-
*/
|
|
4337
|
-
_emitEvent(type, payload) {
|
|
4338
|
-
if (typeof window !== "undefined" && typeof CustomEvent !== "undefined") {
|
|
4339
|
-
try {
|
|
4340
|
-
const event = new CustomEvent(type, { detail: payload });
|
|
4341
|
-
window.dispatchEvent(event);
|
|
4342
|
-
} catch {
|
|
4343
|
-
}
|
|
4344
|
-
}
|
|
4345
|
-
}
|
|
4346
|
-
/**
|
|
4347
|
-
* Log debug message if debugging is enabled.
|
|
4348
|
-
*/
|
|
4349
|
-
_log(message) {
|
|
4350
|
-
if (this._config.debug) {
|
|
4351
|
-
console.log(`[FrontMcpBridge] ${message}`);
|
|
4352
|
-
}
|
|
4353
|
-
}
|
|
4354
|
-
};
|
|
4355
|
-
async function createBridge(config, registry) {
|
|
4356
|
-
const bridge = new FrontMcpBridge(config, registry);
|
|
4357
|
-
await bridge.initialize();
|
|
4358
|
-
return bridge;
|
|
4359
|
-
}
|
|
4360
|
-
|
|
4361
|
-
// libs/ui/src/bridge/adapters/base-adapter.ts
|
|
4362
|
-
var DEFAULT_CAPABILITIES = {
|
|
4363
|
-
canCallTools: false,
|
|
4364
|
-
canSendMessages: false,
|
|
4365
|
-
canOpenLinks: false,
|
|
4366
|
-
canPersistState: true,
|
|
4367
|
-
// localStorage fallback
|
|
4368
|
-
hasNetworkAccess: true,
|
|
4369
|
-
supportsDisplayModes: false,
|
|
4370
|
-
supportsTheme: true
|
|
4371
|
-
};
|
|
4372
|
-
var DEFAULT_SAFE_AREA = {
|
|
4373
|
-
top: 0,
|
|
4374
|
-
bottom: 0,
|
|
4375
|
-
left: 0,
|
|
4376
|
-
right: 0
|
|
4377
|
-
};
|
|
4378
|
-
var BaseAdapter = class {
|
|
4379
|
-
_capabilities = { ...DEFAULT_CAPABILITIES };
|
|
4380
|
-
_hostContext;
|
|
4381
|
-
_widgetState = {};
|
|
4382
|
-
_toolInput = {};
|
|
4383
|
-
_toolOutput = void 0;
|
|
4384
|
-
_structuredContent = void 0;
|
|
4385
|
-
_initialized = false;
|
|
4386
|
-
_contextListeners = /* @__PURE__ */ new Set();
|
|
4387
|
-
_toolResultListeners = /* @__PURE__ */ new Set();
|
|
4388
|
-
constructor() {
|
|
4389
|
-
this._hostContext = this._createDefaultHostContext();
|
|
4390
|
-
}
|
|
4391
|
-
get capabilities() {
|
|
4392
|
-
return this._capabilities;
|
|
4393
|
-
}
|
|
4394
|
-
async initialize() {
|
|
4395
|
-
if (this._initialized) return;
|
|
4396
|
-
this._loadWidgetState();
|
|
4397
|
-
this._readInjectedData();
|
|
4398
|
-
this._initialized = true;
|
|
4399
|
-
}
|
|
4400
|
-
dispose() {
|
|
4401
|
-
this._contextListeners.clear();
|
|
4402
|
-
this._toolResultListeners.clear();
|
|
4403
|
-
this._initialized = false;
|
|
4404
|
-
}
|
|
4405
|
-
// ============================================
|
|
4406
|
-
// Data Access
|
|
4407
|
-
// ============================================
|
|
4408
|
-
getTheme() {
|
|
4409
|
-
return this._hostContext.theme;
|
|
4410
|
-
}
|
|
4411
|
-
getDisplayMode() {
|
|
4412
|
-
return this._hostContext.displayMode;
|
|
4413
|
-
}
|
|
4414
|
-
getUserAgent() {
|
|
4415
|
-
return this._hostContext.userAgent;
|
|
4416
|
-
}
|
|
4417
|
-
getLocale() {
|
|
4418
|
-
return this._hostContext.locale;
|
|
4419
|
-
}
|
|
4420
|
-
getToolInput() {
|
|
4421
|
-
return this._toolInput;
|
|
4422
|
-
}
|
|
4423
|
-
getToolOutput() {
|
|
4424
|
-
return this._toolOutput;
|
|
4425
|
-
}
|
|
4426
|
-
getStructuredContent() {
|
|
4427
|
-
return this._structuredContent;
|
|
4428
|
-
}
|
|
4429
|
-
getWidgetState() {
|
|
4430
|
-
return this._widgetState;
|
|
4431
|
-
}
|
|
4432
|
-
getSafeArea() {
|
|
4433
|
-
return this._hostContext.safeArea;
|
|
4434
|
-
}
|
|
4435
|
-
getViewport() {
|
|
4436
|
-
return this._hostContext.viewport;
|
|
4437
|
-
}
|
|
4438
|
-
getHostContext() {
|
|
4439
|
-
return { ...this._hostContext };
|
|
4440
|
-
}
|
|
4441
|
-
// ============================================
|
|
4442
|
-
// Actions (override in subclasses for real functionality)
|
|
4443
|
-
// ============================================
|
|
4444
|
-
async callTool(_name, _args) {
|
|
4445
|
-
if (!this._capabilities.canCallTools) {
|
|
4446
|
-
throw new Error(`Tool calls are not supported by ${this.name} adapter`);
|
|
4447
|
-
}
|
|
4448
|
-
throw new Error("callTool not implemented");
|
|
4449
|
-
}
|
|
4450
|
-
async sendMessage(_content) {
|
|
4451
|
-
if (!this._capabilities.canSendMessages) {
|
|
4452
|
-
throw new Error(`Sending messages is not supported by ${this.name} adapter`);
|
|
4453
|
-
}
|
|
4454
|
-
throw new Error("sendMessage not implemented");
|
|
4455
|
-
}
|
|
4456
|
-
async openLink(url) {
|
|
4457
|
-
if (!this._capabilities.canOpenLinks) {
|
|
4458
|
-
if (typeof window !== "undefined") {
|
|
4459
|
-
window.open(url, "_blank", "noopener,noreferrer");
|
|
4460
|
-
return;
|
|
4461
|
-
}
|
|
4462
|
-
throw new Error(`Opening links is not supported by ${this.name} adapter`);
|
|
4463
|
-
}
|
|
4464
|
-
throw new Error("openLink not implemented");
|
|
4465
|
-
}
|
|
4466
|
-
async requestDisplayMode(_mode) {
|
|
4467
|
-
if (!this._capabilities.supportsDisplayModes) {
|
|
4468
|
-
return;
|
|
4469
|
-
}
|
|
4470
|
-
throw new Error("requestDisplayMode not implemented");
|
|
4471
|
-
}
|
|
4472
|
-
async requestClose() {
|
|
4473
|
-
}
|
|
4474
|
-
setWidgetState(state) {
|
|
4475
|
-
this._widgetState = { ...this._widgetState, ...state };
|
|
4476
|
-
this._persistWidgetState();
|
|
4477
|
-
}
|
|
4478
|
-
// ============================================
|
|
4479
|
-
// Events
|
|
4480
|
-
// ============================================
|
|
4481
|
-
onContextChange(callback) {
|
|
4482
|
-
this._contextListeners.add(callback);
|
|
4483
|
-
return () => {
|
|
4484
|
-
this._contextListeners.delete(callback);
|
|
4485
|
-
};
|
|
4486
|
-
}
|
|
4487
|
-
onToolResult(callback) {
|
|
4488
|
-
this._toolResultListeners.add(callback);
|
|
4489
|
-
return () => {
|
|
4490
|
-
this._toolResultListeners.delete(callback);
|
|
4491
|
-
};
|
|
4492
|
-
}
|
|
4493
|
-
// ============================================
|
|
4494
|
-
// Protected Helpers
|
|
4495
|
-
// ============================================
|
|
4496
|
-
/**
|
|
4497
|
-
* Create default host context from environment detection.
|
|
4498
|
-
*/
|
|
4499
|
-
_createDefaultHostContext() {
|
|
4500
|
-
return {
|
|
4501
|
-
theme: this._detectTheme(),
|
|
4502
|
-
displayMode: "inline",
|
|
4503
|
-
locale: this._detectLocale(),
|
|
4504
|
-
userAgent: this._detectUserAgent(),
|
|
4505
|
-
safeArea: DEFAULT_SAFE_AREA,
|
|
4506
|
-
viewport: this._detectViewport()
|
|
4507
|
-
};
|
|
4508
|
-
}
|
|
4509
|
-
/**
|
|
4510
|
-
* Detect theme from CSS media query.
|
|
4511
|
-
*/
|
|
4512
|
-
_detectTheme() {
|
|
4513
|
-
if (typeof window !== "undefined" && window.matchMedia) {
|
|
4514
|
-
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
4515
|
-
}
|
|
4516
|
-
return "light";
|
|
4517
|
-
}
|
|
4518
|
-
/**
|
|
4519
|
-
* Detect locale from navigator.
|
|
4520
|
-
*/
|
|
4521
|
-
_detectLocale() {
|
|
4522
|
-
if (typeof navigator !== "undefined") {
|
|
4523
|
-
return navigator.language || "en-US";
|
|
4524
|
-
}
|
|
4525
|
-
return "en-US";
|
|
4526
|
-
}
|
|
4527
|
-
/**
|
|
4528
|
-
* Detect user agent capabilities.
|
|
4529
|
-
*/
|
|
4530
|
-
_detectUserAgent() {
|
|
4531
|
-
if (typeof navigator === "undefined") {
|
|
4532
|
-
return { type: "web", hover: true, touch: false };
|
|
4533
|
-
}
|
|
4534
|
-
const ua = navigator.userAgent || "";
|
|
4535
|
-
const isMobile = /iPhone|iPad|iPod|Android/i.test(ua);
|
|
4536
|
-
const hasTouch = "ontouchstart" in window || navigator.maxTouchPoints > 0;
|
|
4537
|
-
const hasHover = typeof window !== "undefined" && window.matchMedia && window.matchMedia("(hover: hover)").matches;
|
|
4538
|
-
return {
|
|
4539
|
-
type: isMobile ? "mobile" : "web",
|
|
4540
|
-
hover: hasHover !== false,
|
|
4541
|
-
touch: hasTouch
|
|
4542
|
-
};
|
|
4543
|
-
}
|
|
4544
|
-
/**
|
|
4545
|
-
* Detect viewport dimensions.
|
|
4546
|
-
*/
|
|
4547
|
-
_detectViewport() {
|
|
4548
|
-
if (typeof window !== "undefined") {
|
|
4549
|
-
return {
|
|
4550
|
-
width: window.innerWidth,
|
|
4551
|
-
height: window.innerHeight
|
|
4552
|
-
};
|
|
4553
|
-
}
|
|
4554
|
-
return void 0;
|
|
4555
|
-
}
|
|
4556
|
-
/**
|
|
4557
|
-
* Read injected tool data from window globals.
|
|
4558
|
-
*/
|
|
4559
|
-
_readInjectedData() {
|
|
4560
|
-
if (typeof window === "undefined") return;
|
|
4561
|
-
const win = window;
|
|
4562
|
-
if (win.__mcpToolInput) {
|
|
4563
|
-
this._toolInput = win.__mcpToolInput;
|
|
4564
|
-
}
|
|
4565
|
-
if (win.__mcpToolOutput) {
|
|
4566
|
-
this._toolOutput = win.__mcpToolOutput;
|
|
4567
|
-
}
|
|
4568
|
-
if (win.__mcpStructuredContent) {
|
|
4569
|
-
this._structuredContent = win.__mcpStructuredContent;
|
|
4570
|
-
}
|
|
4571
|
-
if (win.__mcpHostContext) {
|
|
4572
|
-
this._hostContext = { ...this._hostContext, ...win.__mcpHostContext };
|
|
4573
|
-
}
|
|
4574
|
-
}
|
|
4575
|
-
/**
|
|
4576
|
-
* Load widget state from localStorage.
|
|
4577
|
-
*/
|
|
4578
|
-
_loadWidgetState() {
|
|
4579
|
-
if (typeof localStorage === "undefined") return;
|
|
4580
|
-
try {
|
|
4581
|
-
const key = this._getStateKey();
|
|
4582
|
-
const stored = localStorage.getItem(key);
|
|
4583
|
-
if (stored) {
|
|
4584
|
-
this._widgetState = JSON.parse(stored);
|
|
4585
|
-
}
|
|
4586
|
-
} catch {
|
|
4587
|
-
}
|
|
4588
|
-
}
|
|
4589
|
-
/**
|
|
4590
|
-
* Persist widget state to localStorage.
|
|
4591
|
-
*/
|
|
4592
|
-
_persistWidgetState() {
|
|
4593
|
-
if (typeof localStorage === "undefined") return;
|
|
4594
|
-
try {
|
|
4595
|
-
const key = this._getStateKey();
|
|
4596
|
-
localStorage.setItem(key, JSON.stringify(this._widgetState));
|
|
4597
|
-
} catch {
|
|
4598
|
-
}
|
|
4599
|
-
}
|
|
4600
|
-
/**
|
|
4601
|
-
* Get localStorage key for widget state.
|
|
4602
|
-
*/
|
|
4603
|
-
_getStateKey() {
|
|
4604
|
-
if (typeof window !== "undefined") {
|
|
4605
|
-
const toolName = window.__mcpToolName || "unknown";
|
|
4606
|
-
return `frontmcp:widget:${toolName}`;
|
|
4607
|
-
}
|
|
4608
|
-
return "frontmcp:widget:unknown";
|
|
4609
|
-
}
|
|
4610
|
-
/**
|
|
4611
|
-
* Notify context change listeners.
|
|
4612
|
-
*/
|
|
4613
|
-
_notifyContextChange(changes) {
|
|
4614
|
-
this._hostContext = { ...this._hostContext, ...changes };
|
|
4615
|
-
this._contextListeners.forEach((cb) => {
|
|
4616
|
-
try {
|
|
4617
|
-
cb(changes);
|
|
4618
|
-
} catch (e) {
|
|
4619
|
-
console.error("[FrontMcpBridge] Context change listener error:", e);
|
|
4620
|
-
}
|
|
4621
|
-
});
|
|
4622
|
-
}
|
|
4623
|
-
/**
|
|
4624
|
-
* Notify tool result listeners.
|
|
4625
|
-
*/
|
|
4626
|
-
_notifyToolResult(result) {
|
|
4627
|
-
this._toolOutput = result;
|
|
4628
|
-
this._toolResultListeners.forEach((cb) => {
|
|
4629
|
-
try {
|
|
4630
|
-
cb(result);
|
|
4631
|
-
} catch (e) {
|
|
4632
|
-
console.error("[FrontMcpBridge] Tool result listener error:", e);
|
|
4633
|
-
}
|
|
4634
|
-
});
|
|
4635
|
-
}
|
|
4636
|
-
};
|
|
4637
|
-
|
|
4638
|
-
// libs/ui/src/bridge/adapters/openai.adapter.ts
|
|
4639
|
-
var OpenAIAdapter = class extends BaseAdapter {
|
|
4640
|
-
id = "openai";
|
|
4641
|
-
name = "OpenAI ChatGPT";
|
|
4642
|
-
priority = 100;
|
|
4643
|
-
// Highest priority
|
|
4644
|
-
_openai;
|
|
4645
|
-
_unsubscribeContext;
|
|
4646
|
-
_unsubscribeToolResult;
|
|
4647
|
-
constructor() {
|
|
4648
|
-
super();
|
|
4649
|
-
this._capabilities = {
|
|
4650
|
-
...DEFAULT_CAPABILITIES,
|
|
4651
|
-
canCallTools: true,
|
|
4652
|
-
canSendMessages: true,
|
|
4653
|
-
canOpenLinks: true,
|
|
4654
|
-
canPersistState: true,
|
|
4655
|
-
hasNetworkAccess: true,
|
|
4656
|
-
supportsDisplayModes: true,
|
|
4657
|
-
supportsTheme: true
|
|
4658
|
-
};
|
|
4659
|
-
}
|
|
4660
|
-
/**
|
|
4661
|
-
* Check if OpenAI Apps SDK is available.
|
|
4662
|
-
*/
|
|
4663
|
-
canHandle() {
|
|
4664
|
-
if (typeof window === "undefined") return false;
|
|
4665
|
-
const win = window;
|
|
4666
|
-
return Boolean(win.openai?.canvas);
|
|
4667
|
-
}
|
|
4668
|
-
/**
|
|
4669
|
-
* Initialize the OpenAI adapter.
|
|
4670
|
-
*/
|
|
4671
|
-
async initialize() {
|
|
4672
|
-
if (this._initialized) return;
|
|
4673
|
-
this._openai = window.openai;
|
|
4674
|
-
await super.initialize();
|
|
4675
|
-
this._syncContextFromSDK();
|
|
4676
|
-
if (this._openai?.canvas?.onContextChange) {
|
|
4677
|
-
this._unsubscribeContext = this._openai.canvas.onContextChange((changes) => {
|
|
4678
|
-
this._notifyContextChange(changes);
|
|
4679
|
-
});
|
|
4680
|
-
}
|
|
4681
|
-
if (this._openai?.canvas?.onToolResult) {
|
|
4682
|
-
this._unsubscribeToolResult = this._openai.canvas.onToolResult((result) => {
|
|
4683
|
-
this._notifyToolResult(result);
|
|
4684
|
-
});
|
|
4685
|
-
}
|
|
4686
|
-
}
|
|
4687
|
-
/**
|
|
4688
|
-
* Dispose adapter resources.
|
|
4689
|
-
*/
|
|
4690
|
-
dispose() {
|
|
4691
|
-
if (this._unsubscribeContext) {
|
|
4692
|
-
this._unsubscribeContext();
|
|
4693
|
-
this._unsubscribeContext = void 0;
|
|
4694
|
-
}
|
|
4695
|
-
if (this._unsubscribeToolResult) {
|
|
4696
|
-
this._unsubscribeToolResult();
|
|
4697
|
-
this._unsubscribeToolResult = void 0;
|
|
4698
|
-
}
|
|
4699
|
-
this._openai = void 0;
|
|
4700
|
-
super.dispose();
|
|
4701
|
-
}
|
|
4702
|
-
// ============================================
|
|
4703
|
-
// Data Access (override with SDK calls)
|
|
4704
|
-
// ============================================
|
|
4705
|
-
getTheme() {
|
|
4706
|
-
if (this._openai?.canvas?.getTheme) {
|
|
4707
|
-
const theme = this._openai.canvas.getTheme();
|
|
4708
|
-
return theme === "dark" ? "dark" : "light";
|
|
4709
|
-
}
|
|
4710
|
-
return super.getTheme();
|
|
4711
|
-
}
|
|
4712
|
-
getDisplayMode() {
|
|
4713
|
-
if (this._openai?.canvas?.getDisplayMode) {
|
|
4714
|
-
const mode = this._openai.canvas.getDisplayMode();
|
|
4715
|
-
if (mode === "fullscreen" || mode === "pip" || mode === "carousel") {
|
|
4716
|
-
return mode;
|
|
4717
|
-
}
|
|
4718
|
-
return "inline";
|
|
4719
|
-
}
|
|
4720
|
-
return super.getDisplayMode();
|
|
4721
|
-
}
|
|
4722
|
-
// ============================================
|
|
4723
|
-
// Actions (proxy to SDK)
|
|
4724
|
-
// ============================================
|
|
4725
|
-
async callTool(name, args) {
|
|
4726
|
-
if (!this._openai?.canvas?.callServerTool) {
|
|
4727
|
-
throw new Error("callServerTool not available in OpenAI SDK");
|
|
4728
|
-
}
|
|
4729
|
-
return this._openai.canvas.callServerTool(name, args);
|
|
4730
|
-
}
|
|
4731
|
-
async sendMessage(content) {
|
|
4732
|
-
if (!this._openai?.canvas?.sendMessage) {
|
|
4733
|
-
throw new Error("sendMessage not available in OpenAI SDK");
|
|
4734
|
-
}
|
|
4735
|
-
await this._openai.canvas.sendMessage(content);
|
|
4736
|
-
}
|
|
4737
|
-
async openLink(url) {
|
|
4738
|
-
if (!this._openai?.canvas?.openLink) {
|
|
4739
|
-
return super.openLink(url);
|
|
4740
|
-
}
|
|
4741
|
-
await this._openai.canvas.openLink(url);
|
|
4742
|
-
}
|
|
4743
|
-
async requestDisplayMode(mode) {
|
|
4744
|
-
if (!this._openai?.canvas?.setDisplayMode) {
|
|
4745
|
-
return super.requestDisplayMode(mode);
|
|
4746
|
-
}
|
|
4747
|
-
await this._openai.canvas.setDisplayMode(mode);
|
|
4748
|
-
this._hostContext = { ...this._hostContext, displayMode: mode };
|
|
4749
|
-
}
|
|
4750
|
-
async requestClose() {
|
|
4751
|
-
if (this._openai?.canvas?.close) {
|
|
4752
|
-
await this._openai.canvas.close();
|
|
4753
|
-
}
|
|
4754
|
-
}
|
|
4755
|
-
// ============================================
|
|
4756
|
-
// Private Helpers
|
|
4757
|
-
// ============================================
|
|
4758
|
-
/**
|
|
4759
|
-
* Sync context from OpenAI SDK.
|
|
4760
|
-
*/
|
|
4761
|
-
_syncContextFromSDK() {
|
|
4762
|
-
if (!this._openai?.canvas) return;
|
|
4763
|
-
if (this._openai.canvas.getTheme) {
|
|
4764
|
-
const theme = this._openai.canvas.getTheme();
|
|
4765
|
-
this._hostContext.theme = theme === "dark" ? "dark" : "light";
|
|
4766
|
-
}
|
|
4767
|
-
if (this._openai.canvas.getDisplayMode) {
|
|
4768
|
-
const mode = this._openai.canvas.getDisplayMode();
|
|
4769
|
-
if (mode === "fullscreen" || mode === "pip" || mode === "carousel" || mode === "inline") {
|
|
4770
|
-
this._hostContext.displayMode = mode;
|
|
4771
|
-
}
|
|
4772
|
-
}
|
|
4773
|
-
if (this._openai.canvas.getContext) {
|
|
4774
|
-
const ctx = this._openai.canvas.getContext();
|
|
4775
|
-
if (ctx) {
|
|
4776
|
-
this._hostContext = { ...this._hostContext, ...ctx };
|
|
4777
|
-
}
|
|
4778
|
-
}
|
|
4779
|
-
}
|
|
4780
|
-
};
|
|
4781
|
-
function createOpenAIAdapter() {
|
|
4782
|
-
return new OpenAIAdapter();
|
|
4783
|
-
}
|
|
4784
|
-
|
|
4785
|
-
// libs/ui/src/bridge/adapters/ext-apps.adapter.ts
|
|
4786
|
-
var ExtAppsAdapter = class extends BaseAdapter {
|
|
4787
|
-
id = "ext-apps";
|
|
4788
|
-
name = "ext-apps (SEP-1865)";
|
|
4789
|
-
priority = 80;
|
|
4790
|
-
// High priority, but below OpenAI native
|
|
4791
|
-
_config;
|
|
4792
|
-
_messageListener;
|
|
4793
|
-
_pendingRequests = /* @__PURE__ */ new Map();
|
|
4794
|
-
_requestId = 0;
|
|
4795
|
-
_trustedOrigin;
|
|
4796
|
-
_hostCapabilities = {};
|
|
4797
|
-
constructor(config) {
|
|
4798
|
-
super();
|
|
4799
|
-
this._config = config || {};
|
|
4800
|
-
this._capabilities = {
|
|
4801
|
-
...DEFAULT_CAPABILITIES,
|
|
4802
|
-
canPersistState: true,
|
|
4803
|
-
hasNetworkAccess: true,
|
|
4804
|
-
// ext-apps usually allows network
|
|
4805
|
-
supportsTheme: true
|
|
4806
|
-
};
|
|
4807
|
-
}
|
|
4808
|
-
/**
|
|
4809
|
-
* Check if we're in an iframe (potential ext-apps context).
|
|
4810
|
-
*/
|
|
4811
|
-
canHandle() {
|
|
4812
|
-
if (typeof window === "undefined") return false;
|
|
4813
|
-
const inIframe = window.parent !== window;
|
|
4814
|
-
if (!inIframe) return false;
|
|
4815
|
-
const win = window;
|
|
4816
|
-
if (win.openai?.canvas) return false;
|
|
4817
|
-
if (win.__mcpPlatform === "ext-apps") return true;
|
|
4818
|
-
return true;
|
|
4819
|
-
}
|
|
4820
|
-
/**
|
|
4821
|
-
* Initialize the ext-apps adapter with protocol handshake.
|
|
4822
|
-
*/
|
|
4823
|
-
async initialize() {
|
|
4824
|
-
if (this._initialized) return;
|
|
4825
|
-
this._setupMessageListener();
|
|
4826
|
-
await super.initialize();
|
|
4827
|
-
await this._performHandshake();
|
|
4828
|
-
this._initialized = true;
|
|
4829
|
-
}
|
|
4830
|
-
/**
|
|
4831
|
-
* Dispose adapter resources.
|
|
4832
|
-
*/
|
|
4833
|
-
dispose() {
|
|
4834
|
-
if (this._messageListener && typeof window !== "undefined") {
|
|
4835
|
-
window.removeEventListener("message", this._messageListener);
|
|
4836
|
-
this._messageListener = void 0;
|
|
4837
|
-
}
|
|
4838
|
-
for (const [id, pending] of this._pendingRequests) {
|
|
4839
|
-
clearTimeout(pending.timeout);
|
|
4840
|
-
pending.reject(new Error("Adapter disposed"));
|
|
4841
|
-
}
|
|
4842
|
-
this._pendingRequests.clear();
|
|
4843
|
-
super.dispose();
|
|
4844
|
-
}
|
|
4845
|
-
// ============================================
|
|
4846
|
-
// Actions (via JSON-RPC)
|
|
4847
|
-
// ============================================
|
|
4848
|
-
async callTool(name, args) {
|
|
4849
|
-
if (!this._hostCapabilities.serverToolProxy) {
|
|
4850
|
-
throw new Error("Server tool proxy not supported by host");
|
|
4851
|
-
}
|
|
4852
|
-
return this._sendRequest("ui/callServerTool", {
|
|
4853
|
-
name,
|
|
4854
|
-
arguments: args
|
|
4855
|
-
});
|
|
4856
|
-
}
|
|
4857
|
-
async sendMessage(content) {
|
|
4858
|
-
await this._sendRequest("ui/message", { content });
|
|
4859
|
-
}
|
|
4860
|
-
async openLink(url) {
|
|
4861
|
-
if (!this._hostCapabilities.openLink) {
|
|
4862
|
-
return super.openLink(url);
|
|
4863
|
-
}
|
|
4864
|
-
await this._sendRequest("ui/openLink", { url });
|
|
4865
|
-
}
|
|
4866
|
-
async requestDisplayMode(mode) {
|
|
4867
|
-
await this._sendRequest("ui/setDisplayMode", { mode });
|
|
4868
|
-
this._hostContext = { ...this._hostContext, displayMode: mode };
|
|
4869
|
-
}
|
|
4870
|
-
async requestClose() {
|
|
4871
|
-
await this._sendRequest("ui/close", {});
|
|
4872
|
-
}
|
|
4873
|
-
// ============================================
|
|
4874
|
-
// Private: Message Handling
|
|
4875
|
-
// ============================================
|
|
4876
|
-
/**
|
|
4877
|
-
* Setup postMessage listener for incoming messages.
|
|
4878
|
-
*/
|
|
4879
|
-
_setupMessageListener() {
|
|
4880
|
-
if (typeof window === "undefined") return;
|
|
4881
|
-
this._messageListener = (event) => {
|
|
4882
|
-
this._handleMessage(event);
|
|
4883
|
-
};
|
|
4884
|
-
window.addEventListener("message", this._messageListener);
|
|
4885
|
-
}
|
|
4886
|
-
/**
|
|
4887
|
-
* Handle incoming postMessage events.
|
|
4888
|
-
*/
|
|
4889
|
-
_handleMessage(event) {
|
|
4890
|
-
if (!this._isOriginTrusted(event.origin)) {
|
|
4891
|
-
return;
|
|
4892
|
-
}
|
|
4893
|
-
const data = event.data;
|
|
4894
|
-
if (!data || typeof data !== "object") return;
|
|
4895
|
-
if (data.jsonrpc !== "2.0") return;
|
|
4896
|
-
if ("id" in data && (data.result !== void 0 || data.error !== void 0)) {
|
|
4897
|
-
this._handleResponse(data);
|
|
4898
|
-
return;
|
|
4899
|
-
}
|
|
4900
|
-
if ("method" in data && !("id" in data)) {
|
|
4901
|
-
this._handleNotification(data);
|
|
4902
|
-
return;
|
|
4903
|
-
}
|
|
4904
|
-
}
|
|
4905
|
-
/**
|
|
4906
|
-
* Handle JSON-RPC response.
|
|
4907
|
-
*/
|
|
4908
|
-
_handleResponse(response) {
|
|
4909
|
-
const pending = this._pendingRequests.get(response.id);
|
|
4910
|
-
if (!pending) return;
|
|
4911
|
-
clearTimeout(pending.timeout);
|
|
4912
|
-
this._pendingRequests.delete(response.id);
|
|
4913
|
-
if (response.error) {
|
|
4914
|
-
pending.reject(new Error(`${response.error.message} (code: ${response.error.code})`));
|
|
4915
|
-
} else {
|
|
4916
|
-
pending.resolve(response.result);
|
|
4917
|
-
}
|
|
4918
|
-
}
|
|
4919
|
-
/**
|
|
4920
|
-
* Handle JSON-RPC notification from host.
|
|
4921
|
-
*/
|
|
4922
|
-
_handleNotification(notification) {
|
|
4923
|
-
switch (notification.method) {
|
|
4924
|
-
case "ui/notifications/tool-input":
|
|
4925
|
-
this._handleToolInput(notification.params);
|
|
4926
|
-
break;
|
|
4927
|
-
case "ui/notifications/tool-input-partial":
|
|
4928
|
-
this._handleToolInputPartial(notification.params);
|
|
4929
|
-
break;
|
|
4930
|
-
case "ui/notifications/tool-result":
|
|
4931
|
-
this._handleToolResult(notification.params);
|
|
4932
|
-
break;
|
|
4933
|
-
case "ui/notifications/host-context-changed":
|
|
4934
|
-
this._handleHostContextChange(notification.params);
|
|
4935
|
-
break;
|
|
4936
|
-
case "ui/notifications/initialized":
|
|
4937
|
-
break;
|
|
4938
|
-
case "ui/notifications/cancelled":
|
|
4939
|
-
this._handleCancelled(notification.params);
|
|
4940
|
-
break;
|
|
4941
|
-
}
|
|
4942
|
-
}
|
|
4943
|
-
/**
|
|
4944
|
-
* Handle tool input notification.
|
|
4945
|
-
*/
|
|
4946
|
-
_handleToolInput(params) {
|
|
4947
|
-
this._toolInput = params.arguments || {};
|
|
4948
|
-
this._emitBridgeEvent("tool:input", { arguments: this._toolInput });
|
|
4949
|
-
}
|
|
4950
|
-
/**
|
|
4951
|
-
* Handle partial tool input (streaming).
|
|
4952
|
-
*/
|
|
4953
|
-
_handleToolInputPartial(params) {
|
|
4954
|
-
this._toolInput = { ...this._toolInput, ...params.arguments };
|
|
4955
|
-
this._emitBridgeEvent("tool:input-partial", { arguments: this._toolInput });
|
|
4956
|
-
}
|
|
4957
|
-
/**
|
|
4958
|
-
* Handle tool result notification.
|
|
4959
|
-
*/
|
|
4960
|
-
_handleToolResult(params) {
|
|
4961
|
-
this._toolOutput = params.content;
|
|
4962
|
-
this._structuredContent = params.structuredContent;
|
|
4963
|
-
this._notifyToolResult(params.content);
|
|
4964
|
-
this._emitBridgeEvent("tool:result", {
|
|
4965
|
-
content: params.content,
|
|
4966
|
-
structuredContent: params.structuredContent
|
|
4967
|
-
});
|
|
4968
|
-
}
|
|
4969
|
-
/**
|
|
4970
|
-
* Handle host context change notification.
|
|
4971
|
-
*/
|
|
4972
|
-
_handleHostContextChange(params) {
|
|
4973
|
-
const changes = {};
|
|
4974
|
-
if (params.theme !== void 0) {
|
|
4975
|
-
changes.theme = params.theme;
|
|
4976
|
-
}
|
|
4977
|
-
if (params.displayMode !== void 0) {
|
|
4978
|
-
changes.displayMode = params.displayMode;
|
|
4979
|
-
}
|
|
4980
|
-
if (params.viewport !== void 0) {
|
|
4981
|
-
changes.viewport = params.viewport;
|
|
4982
|
-
}
|
|
4983
|
-
if (params.locale !== void 0) {
|
|
4984
|
-
changes.locale = params.locale;
|
|
4985
|
-
}
|
|
4986
|
-
if (params.timezone !== void 0) {
|
|
4987
|
-
changes.timezone = params.timezone;
|
|
4988
|
-
}
|
|
4989
|
-
this._notifyContextChange(changes);
|
|
4990
|
-
}
|
|
4991
|
-
/**
|
|
4992
|
-
* Handle cancellation notification.
|
|
4993
|
-
*/
|
|
4994
|
-
_handleCancelled(params) {
|
|
4995
|
-
const reason = params?.reason;
|
|
4996
|
-
this._emitBridgeEvent("tool:cancelled", { reason });
|
|
4997
|
-
}
|
|
4998
|
-
// ============================================
|
|
4999
|
-
// Private: JSON-RPC Transport
|
|
5000
|
-
// ============================================
|
|
5001
|
-
/**
|
|
5002
|
-
* Send a JSON-RPC request to the host.
|
|
5003
|
-
*/
|
|
5004
|
-
_sendRequest(method, params) {
|
|
5005
|
-
return new Promise((resolve, reject) => {
|
|
5006
|
-
const id = ++this._requestId;
|
|
5007
|
-
const timeout = this._config.options?.initTimeout || 1e4;
|
|
5008
|
-
const request = {
|
|
5009
|
-
jsonrpc: "2.0",
|
|
5010
|
-
id,
|
|
5011
|
-
method,
|
|
5012
|
-
params
|
|
5013
|
-
};
|
|
5014
|
-
const timeoutHandle = setTimeout(() => {
|
|
5015
|
-
this._pendingRequests.delete(id);
|
|
5016
|
-
reject(new Error(`Request ${method} timed out after ${timeout}ms`));
|
|
5017
|
-
}, timeout);
|
|
5018
|
-
this._pendingRequests.set(id, {
|
|
5019
|
-
resolve,
|
|
5020
|
-
reject,
|
|
5021
|
-
timeout: timeoutHandle
|
|
5022
|
-
});
|
|
5023
|
-
this._postMessage(request);
|
|
5024
|
-
});
|
|
5025
|
-
}
|
|
5026
|
-
/**
|
|
5027
|
-
* Send a JSON-RPC notification (no response expected).
|
|
5028
|
-
*/
|
|
5029
|
-
_sendNotification(method, params) {
|
|
5030
|
-
const notification = {
|
|
5031
|
-
jsonrpc: "2.0",
|
|
5032
|
-
method,
|
|
5033
|
-
params
|
|
5034
|
-
};
|
|
5035
|
-
this._postMessage(notification);
|
|
5036
|
-
}
|
|
5037
|
-
/**
|
|
5038
|
-
* Post a message to the parent window.
|
|
5039
|
-
*/
|
|
5040
|
-
_postMessage(message) {
|
|
5041
|
-
if (typeof window === "undefined") return;
|
|
5042
|
-
const targetOrigin = this._trustedOrigin || "*";
|
|
5043
|
-
window.parent.postMessage(message, targetOrigin);
|
|
5044
|
-
}
|
|
5045
|
-
// ============================================
|
|
5046
|
-
// Private: Handshake
|
|
5047
|
-
// ============================================
|
|
5048
|
-
/**
|
|
5049
|
-
* Perform the ui/initialize handshake with the host.
|
|
5050
|
-
*/
|
|
5051
|
-
async _performHandshake() {
|
|
5052
|
-
const params = {
|
|
5053
|
-
appInfo: {
|
|
5054
|
-
name: this._config.options?.appName || "FrontMCP Widget",
|
|
5055
|
-
version: this._config.options?.appVersion || "1.0.0"
|
|
5056
|
-
},
|
|
5057
|
-
appCapabilities: {
|
|
5058
|
-
tools: {
|
|
5059
|
-
listChanged: false
|
|
5060
|
-
}
|
|
5061
|
-
},
|
|
5062
|
-
protocolVersion: this._config.options?.protocolVersion || "2024-11-05"
|
|
5063
|
-
};
|
|
5064
|
-
try {
|
|
5065
|
-
const result = await this._sendRequest("ui/initialize", params);
|
|
5066
|
-
this._hostCapabilities = result.hostCapabilities || {};
|
|
5067
|
-
this._capabilities = {
|
|
5068
|
-
...this._capabilities,
|
|
5069
|
-
canCallTools: Boolean(this._hostCapabilities.serverToolProxy),
|
|
5070
|
-
canSendMessages: true,
|
|
5071
|
-
canOpenLinks: Boolean(this._hostCapabilities.openLink),
|
|
5072
|
-
supportsDisplayModes: true
|
|
5073
|
-
};
|
|
5074
|
-
if (result.hostContext) {
|
|
5075
|
-
this._hostContext = {
|
|
5076
|
-
...this._hostContext,
|
|
5077
|
-
...result.hostContext
|
|
5078
|
-
};
|
|
5079
|
-
}
|
|
5080
|
-
if (!this._config.options?.trustedOrigins?.length) {
|
|
5081
|
-
}
|
|
5082
|
-
} catch (error) {
|
|
5083
|
-
throw new Error(`ext-apps handshake failed: ${error}`);
|
|
5084
|
-
}
|
|
5085
|
-
}
|
|
5086
|
-
// ============================================
|
|
5087
|
-
// Private: Origin Security
|
|
5088
|
-
// ============================================
|
|
5089
|
-
/**
|
|
5090
|
-
* Check if an origin is trusted.
|
|
5091
|
-
* Uses trust-on-first-use if no explicit origins configured.
|
|
5092
|
-
*/
|
|
5093
|
-
_isOriginTrusted(origin) {
|
|
5094
|
-
const trustedOrigins = this._config.options?.trustedOrigins;
|
|
5095
|
-
if (trustedOrigins && trustedOrigins.length > 0) {
|
|
5096
|
-
return trustedOrigins.includes(origin);
|
|
5097
|
-
}
|
|
5098
|
-
if (!this._trustedOrigin) {
|
|
5099
|
-
this._trustedOrigin = origin;
|
|
5100
|
-
return true;
|
|
5101
|
-
}
|
|
5102
|
-
return this._trustedOrigin === origin;
|
|
5103
|
-
}
|
|
5104
|
-
// ============================================
|
|
5105
|
-
// Private: Events
|
|
5106
|
-
// ============================================
|
|
5107
|
-
/**
|
|
5108
|
-
* Emit a bridge event via CustomEvent.
|
|
5109
|
-
*/
|
|
5110
|
-
_emitBridgeEvent(type, detail) {
|
|
5111
|
-
if (typeof window !== "undefined" && typeof CustomEvent !== "undefined") {
|
|
5112
|
-
try {
|
|
5113
|
-
const event = new CustomEvent(type, { detail });
|
|
5114
|
-
window.dispatchEvent(event);
|
|
5115
|
-
} catch {
|
|
5116
|
-
}
|
|
5117
|
-
}
|
|
5118
|
-
}
|
|
5119
|
-
};
|
|
5120
|
-
function createExtAppsAdapter(config) {
|
|
5121
|
-
return new ExtAppsAdapter(config);
|
|
5122
|
-
}
|
|
5123
|
-
|
|
5124
|
-
// libs/ui/src/bridge/adapters/claude.adapter.ts
|
|
5125
|
-
var ClaudeAdapter = class extends BaseAdapter {
|
|
5126
|
-
id = "claude";
|
|
5127
|
-
name = "Claude (Anthropic)";
|
|
5128
|
-
priority = 60;
|
|
5129
|
-
constructor() {
|
|
5130
|
-
super();
|
|
5131
|
-
this._capabilities = {
|
|
5132
|
-
...DEFAULT_CAPABILITIES,
|
|
5133
|
-
canCallTools: false,
|
|
5134
|
-
// Claude artifacts can't call tools
|
|
5135
|
-
canSendMessages: false,
|
|
5136
|
-
// Can't send messages back to conversation
|
|
5137
|
-
canOpenLinks: true,
|
|
5138
|
-
// Can open links via window.open
|
|
5139
|
-
canPersistState: true,
|
|
5140
|
-
// localStorage works
|
|
5141
|
-
hasNetworkAccess: false,
|
|
5142
|
-
// Network is blocked
|
|
5143
|
-
supportsDisplayModes: false,
|
|
5144
|
-
// No display mode control
|
|
5145
|
-
supportsTheme: true
|
|
5146
|
-
// Can detect system theme
|
|
5147
|
-
};
|
|
5148
|
-
}
|
|
5149
|
-
/**
|
|
5150
|
-
* Check if we're running in a Claude artifact/widget context.
|
|
5151
|
-
*/
|
|
5152
|
-
canHandle() {
|
|
5153
|
-
if (typeof window === "undefined") return false;
|
|
5154
|
-
const win = window;
|
|
5155
|
-
if (win.__mcpPlatform === "claude") return true;
|
|
5156
|
-
if (win.claude) return true;
|
|
5157
|
-
if (win.__claudeArtifact) return true;
|
|
5158
|
-
if (typeof location !== "undefined") {
|
|
5159
|
-
const href = location.href;
|
|
5160
|
-
if (href.includes("claude.ai") || href.includes("anthropic.com")) {
|
|
5161
|
-
return true;
|
|
5162
|
-
}
|
|
5163
|
-
}
|
|
5164
|
-
return false;
|
|
5165
|
-
}
|
|
5166
|
-
/**
|
|
5167
|
-
* Initialize the Claude adapter.
|
|
5168
|
-
*/
|
|
5169
|
-
async initialize() {
|
|
5170
|
-
if (this._initialized) return;
|
|
5171
|
-
await super.initialize();
|
|
5172
|
-
this._setupThemeListener();
|
|
5173
|
-
}
|
|
5174
|
-
/**
|
|
5175
|
-
* Open a link in a new tab.
|
|
5176
|
-
* This is one of the few actions available in Claude artifacts.
|
|
5177
|
-
*/
|
|
5178
|
-
async openLink(url) {
|
|
5179
|
-
if (typeof window !== "undefined") {
|
|
5180
|
-
window.open(url, "_blank", "noopener,noreferrer");
|
|
5181
|
-
}
|
|
5182
|
-
}
|
|
5183
|
-
/**
|
|
5184
|
-
* Request display mode change (no-op for Claude).
|
|
5185
|
-
*/
|
|
5186
|
-
async requestDisplayMode(_mode) {
|
|
5187
|
-
}
|
|
5188
|
-
/**
|
|
5189
|
-
* Request close (no-op for Claude).
|
|
5190
|
-
*/
|
|
5191
|
-
async requestClose() {
|
|
5192
|
-
}
|
|
5193
|
-
// ============================================
|
|
5194
|
-
// Private Helpers
|
|
5195
|
-
// ============================================
|
|
5196
|
-
/**
|
|
5197
|
-
* Setup listener for system theme changes.
|
|
5198
|
-
*/
|
|
5199
|
-
_setupThemeListener() {
|
|
5200
|
-
if (typeof window === "undefined" || !window.matchMedia) return;
|
|
5201
|
-
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
5202
|
-
const handleChange = (e) => {
|
|
5203
|
-
const newTheme = e.matches ? "dark" : "light";
|
|
5204
|
-
if (newTheme !== this._hostContext.theme) {
|
|
5205
|
-
this._notifyContextChange({ theme: newTheme });
|
|
5206
|
-
}
|
|
5207
|
-
};
|
|
5208
|
-
if (mediaQuery.addEventListener) {
|
|
5209
|
-
mediaQuery.addEventListener("change", handleChange);
|
|
5210
|
-
} else if (mediaQuery.addListener) {
|
|
5211
|
-
mediaQuery.addListener(handleChange);
|
|
5212
|
-
}
|
|
5213
|
-
}
|
|
5214
|
-
};
|
|
5215
|
-
function createClaudeAdapter() {
|
|
5216
|
-
return new ClaudeAdapter();
|
|
5217
|
-
}
|
|
5218
|
-
|
|
5219
|
-
// libs/ui/src/bridge/adapters/gemini.adapter.ts
|
|
5220
|
-
var GeminiAdapter = class extends BaseAdapter {
|
|
5221
|
-
id = "gemini";
|
|
5222
|
-
name = "Google Gemini";
|
|
5223
|
-
priority = 40;
|
|
5224
|
-
_gemini;
|
|
5225
|
-
constructor() {
|
|
5226
|
-
super();
|
|
5227
|
-
this._capabilities = {
|
|
5228
|
-
...DEFAULT_CAPABILITIES,
|
|
5229
|
-
canCallTools: false,
|
|
5230
|
-
// May be enabled if SDK supports it
|
|
5231
|
-
canSendMessages: false,
|
|
5232
|
-
// May be enabled if SDK supports it
|
|
5233
|
-
canOpenLinks: true,
|
|
5234
|
-
canPersistState: true,
|
|
5235
|
-
hasNetworkAccess: true,
|
|
5236
|
-
supportsDisplayModes: false,
|
|
5237
|
-
supportsTheme: true
|
|
5238
|
-
};
|
|
5239
|
-
}
|
|
5240
|
-
/**
|
|
5241
|
-
* Check if we're running in a Gemini context.
|
|
5242
|
-
*/
|
|
5243
|
-
canHandle() {
|
|
5244
|
-
if (typeof window === "undefined") return false;
|
|
5245
|
-
const win = window;
|
|
5246
|
-
if (win.__mcpPlatform === "gemini") return true;
|
|
5247
|
-
if (win.gemini) return true;
|
|
5248
|
-
if (typeof location !== "undefined") {
|
|
5249
|
-
const href = location.href;
|
|
5250
|
-
if (href.includes("gemini.google.com") || href.includes("bard.google.com")) {
|
|
5251
|
-
return true;
|
|
5252
|
-
}
|
|
5253
|
-
}
|
|
5254
|
-
return false;
|
|
5255
|
-
}
|
|
5256
|
-
/**
|
|
5257
|
-
* Initialize the Gemini adapter.
|
|
5258
|
-
*/
|
|
5259
|
-
async initialize() {
|
|
5260
|
-
if (this._initialized) return;
|
|
5261
|
-
const win = window;
|
|
5262
|
-
this._gemini = win.gemini;
|
|
5263
|
-
if (this._gemini?.ui) {
|
|
5264
|
-
if (this._gemini.ui.sendMessage) {
|
|
5265
|
-
this._capabilities = { ...this._capabilities, canSendMessages: true };
|
|
5266
|
-
}
|
|
5267
|
-
}
|
|
5268
|
-
await super.initialize();
|
|
5269
|
-
this._setupThemeListener();
|
|
5270
|
-
}
|
|
5271
|
-
/**
|
|
5272
|
-
* Get current theme.
|
|
5273
|
-
*/
|
|
5274
|
-
getTheme() {
|
|
5275
|
-
if (this._gemini?.ui?.getTheme) {
|
|
5276
|
-
const theme = this._gemini.ui.getTheme();
|
|
5277
|
-
return theme === "dark" ? "dark" : "light";
|
|
5278
|
-
}
|
|
5279
|
-
return super.getTheme();
|
|
5280
|
-
}
|
|
5281
|
-
/**
|
|
5282
|
-
* Send a message (if supported by SDK).
|
|
5283
|
-
*/
|
|
5284
|
-
async sendMessage(content) {
|
|
5285
|
-
if (this._gemini?.ui?.sendMessage) {
|
|
5286
|
-
await this._gemini.ui.sendMessage(content);
|
|
5287
|
-
return;
|
|
5288
|
-
}
|
|
5289
|
-
throw new Error("Sending messages is not supported by Gemini adapter");
|
|
5290
|
-
}
|
|
5291
|
-
/**
|
|
5292
|
-
* Open a link.
|
|
5293
|
-
*/
|
|
5294
|
-
async openLink(url) {
|
|
5295
|
-
if (this._gemini?.ui?.openLink) {
|
|
5296
|
-
await this._gemini.ui.openLink(url);
|
|
5297
|
-
return;
|
|
5298
|
-
}
|
|
5299
|
-
return super.openLink(url);
|
|
5300
|
-
}
|
|
5301
|
-
// ============================================
|
|
5302
|
-
// Private Helpers
|
|
5303
|
-
// ============================================
|
|
5304
|
-
/**
|
|
5305
|
-
* Setup listener for system theme changes.
|
|
5306
|
-
*/
|
|
5307
|
-
_setupThemeListener() {
|
|
5308
|
-
if (typeof window === "undefined" || !window.matchMedia) return;
|
|
5309
|
-
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
5310
|
-
const handleChange = (e) => {
|
|
5311
|
-
if (!this._gemini?.ui?.getTheme) {
|
|
5312
|
-
const newTheme = e.matches ? "dark" : "light";
|
|
5313
|
-
if (newTheme !== this._hostContext.theme) {
|
|
5314
|
-
this._notifyContextChange({ theme: newTheme });
|
|
5315
|
-
}
|
|
5316
|
-
}
|
|
5317
|
-
};
|
|
5318
|
-
if (mediaQuery.addEventListener) {
|
|
5319
|
-
mediaQuery.addEventListener("change", handleChange);
|
|
5320
|
-
} else if (mediaQuery.addListener) {
|
|
5321
|
-
mediaQuery.addListener(handleChange);
|
|
5322
|
-
}
|
|
5323
|
-
}
|
|
5324
|
-
};
|
|
5325
|
-
function createGeminiAdapter() {
|
|
5326
|
-
return new GeminiAdapter();
|
|
5327
|
-
}
|
|
5328
|
-
|
|
5329
|
-
// libs/ui/src/bridge/adapters/generic.adapter.ts
|
|
5330
|
-
var GenericAdapter = class extends BaseAdapter {
|
|
5331
|
-
id = "generic";
|
|
5332
|
-
name = "Generic Web";
|
|
5333
|
-
priority = 0;
|
|
5334
|
-
// Lowest priority - fallback only
|
|
5335
|
-
constructor() {
|
|
5336
|
-
super();
|
|
5337
|
-
this._capabilities = {
|
|
5338
|
-
...DEFAULT_CAPABILITIES,
|
|
5339
|
-
canCallTools: false,
|
|
5340
|
-
canSendMessages: false,
|
|
5341
|
-
canOpenLinks: true,
|
|
5342
|
-
// window.open works
|
|
5343
|
-
canPersistState: true,
|
|
5344
|
-
// localStorage works
|
|
5345
|
-
hasNetworkAccess: true,
|
|
5346
|
-
// Assume network available
|
|
5347
|
-
supportsDisplayModes: false,
|
|
5348
|
-
supportsTheme: true
|
|
5349
|
-
// System theme detection
|
|
5350
|
-
};
|
|
5351
|
-
}
|
|
5352
|
-
/**
|
|
5353
|
-
* Generic adapter can always handle the environment.
|
|
5354
|
-
* It serves as the fallback when no other adapter matches.
|
|
5355
|
-
*/
|
|
5356
|
-
canHandle() {
|
|
5357
|
-
return typeof window !== "undefined";
|
|
5358
|
-
}
|
|
5359
|
-
/**
|
|
5360
|
-
* Initialize the generic adapter.
|
|
5361
|
-
*/
|
|
5362
|
-
async initialize() {
|
|
5363
|
-
if (this._initialized) return;
|
|
5364
|
-
await super.initialize();
|
|
5365
|
-
this._setupThemeListener();
|
|
5366
|
-
}
|
|
5367
|
-
/**
|
|
5368
|
-
* Open a link using window.open.
|
|
5369
|
-
*/
|
|
5370
|
-
async openLink(url) {
|
|
5371
|
-
if (typeof window !== "undefined") {
|
|
5372
|
-
window.open(url, "_blank", "noopener,noreferrer");
|
|
5373
|
-
}
|
|
5374
|
-
}
|
|
5375
|
-
// ============================================
|
|
5376
|
-
// Private Helpers
|
|
5377
|
-
// ============================================
|
|
5378
|
-
/**
|
|
5379
|
-
* Setup listener for system theme changes.
|
|
5380
|
-
*/
|
|
5381
|
-
_setupThemeListener() {
|
|
5382
|
-
if (typeof window === "undefined" || !window.matchMedia) return;
|
|
5383
|
-
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
5384
|
-
const handleChange = (e) => {
|
|
5385
|
-
const newTheme = e.matches ? "dark" : "light";
|
|
5386
|
-
if (newTheme !== this._hostContext.theme) {
|
|
5387
|
-
this._notifyContextChange({ theme: newTheme });
|
|
5388
|
-
}
|
|
5389
|
-
};
|
|
5390
|
-
if (mediaQuery.addEventListener) {
|
|
5391
|
-
mediaQuery.addEventListener("change", handleChange);
|
|
5392
|
-
} else if (mediaQuery.addListener) {
|
|
5393
|
-
mediaQuery.addListener(handleChange);
|
|
5394
|
-
}
|
|
5395
|
-
}
|
|
5396
|
-
};
|
|
5397
|
-
function createGenericAdapter() {
|
|
5398
|
-
return new GenericAdapter();
|
|
5399
|
-
}
|
|
5400
|
-
|
|
5401
|
-
// libs/ui/src/bridge/adapters/index.ts
|
|
5402
|
-
function registerBuiltInAdapters() {
|
|
5403
|
-
defaultRegistry.register("openai", createOpenAIAdapter);
|
|
5404
|
-
defaultRegistry.register("ext-apps", createExtAppsAdapter);
|
|
5405
|
-
defaultRegistry.register("claude", createClaudeAdapter);
|
|
5406
|
-
defaultRegistry.register("gemini", createGeminiAdapter);
|
|
5407
|
-
defaultRegistry.register("generic", createGenericAdapter);
|
|
5408
|
-
}
|
|
5409
|
-
registerBuiltInAdapters();
|
|
5410
|
-
|
|
5411
|
-
// libs/ui/src/bridge/runtime/iife-generator.ts
|
|
5412
|
-
function generateBridgeIIFE(options = {}) {
|
|
5413
|
-
const { debug = false, trustedOrigins = [], minify = false } = options;
|
|
5414
|
-
const adapters = options.adapters || ["openai", "ext-apps", "claude", "gemini", "generic"];
|
|
5415
|
-
const parts = [];
|
|
5416
|
-
parts.push("(function() {");
|
|
5417
|
-
parts.push('"use strict";');
|
|
5418
|
-
parts.push("");
|
|
5419
|
-
if (debug) {
|
|
5420
|
-
parts.push('function log(msg) { console.log("[FrontMcpBridge] " + msg); }');
|
|
5421
|
-
} else {
|
|
5422
|
-
parts.push("function log() {}");
|
|
5423
|
-
}
|
|
5424
|
-
parts.push("");
|
|
5425
|
-
parts.push("var DEFAULT_SAFE_AREA = { top: 0, bottom: 0, left: 0, right: 0 };");
|
|
5426
|
-
parts.push("");
|
|
5427
|
-
parts.push(generateContextDetection());
|
|
5428
|
-
parts.push("");
|
|
5429
|
-
parts.push(generateBaseCapabilities());
|
|
5430
|
-
parts.push("");
|
|
5431
|
-
if (adapters.includes("openai")) {
|
|
5432
|
-
parts.push(generateOpenAIAdapter());
|
|
5433
|
-
parts.push("");
|
|
5434
|
-
}
|
|
5435
|
-
if (adapters.includes("ext-apps")) {
|
|
5436
|
-
parts.push(generateExtAppsAdapter(trustedOrigins));
|
|
5437
|
-
parts.push("");
|
|
5438
|
-
}
|
|
5439
|
-
if (adapters.includes("claude")) {
|
|
5440
|
-
parts.push(generateClaudeAdapter());
|
|
5441
|
-
parts.push("");
|
|
5442
|
-
}
|
|
5443
|
-
if (adapters.includes("gemini")) {
|
|
5444
|
-
parts.push(generateGeminiAdapter());
|
|
5445
|
-
parts.push("");
|
|
5446
|
-
}
|
|
5447
|
-
if (adapters.includes("generic")) {
|
|
5448
|
-
parts.push(generateGenericAdapter());
|
|
5449
|
-
parts.push("");
|
|
5450
|
-
}
|
|
5451
|
-
parts.push(generatePlatformDetection(adapters));
|
|
5452
|
-
parts.push("");
|
|
5453
|
-
parts.push(generateBridgeClass());
|
|
5454
|
-
parts.push("");
|
|
5455
|
-
parts.push("var bridge = new FrontMcpBridge();");
|
|
5456
|
-
parts.push("bridge.initialize().then(function() {");
|
|
5457
|
-
parts.push(' log("Bridge initialized with adapter: " + bridge.adapterId);');
|
|
5458
|
-
parts.push(' window.dispatchEvent(new CustomEvent("bridge:ready", { detail: { adapter: bridge.adapterId } }));');
|
|
5459
|
-
parts.push("}).catch(function(err) {");
|
|
5460
|
-
parts.push(' console.error("[FrontMcpBridge] Init failed:", err);');
|
|
5461
|
-
parts.push(' window.dispatchEvent(new CustomEvent("bridge:error", { detail: { error: err } }));');
|
|
5462
|
-
parts.push("});");
|
|
5463
|
-
parts.push("");
|
|
5464
|
-
parts.push("window.FrontMcpBridge = bridge;");
|
|
5465
|
-
parts.push("})();");
|
|
5466
|
-
const code = parts.join("\n");
|
|
5467
|
-
if (minify) {
|
|
5468
|
-
return minifyJS(code);
|
|
5469
|
-
}
|
|
5470
|
-
return code;
|
|
5471
|
-
}
|
|
5472
|
-
function generateContextDetection() {
|
|
5473
|
-
return `
|
|
5474
|
-
function detectTheme() {
|
|
5475
|
-
if (typeof window !== 'undefined' && window.matchMedia) {
|
|
5476
|
-
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
5477
|
-
}
|
|
5478
|
-
return 'light';
|
|
5479
|
-
}
|
|
5480
|
-
|
|
5481
|
-
function detectLocale() {
|
|
5482
|
-
if (typeof navigator !== 'undefined') {
|
|
5483
|
-
return navigator.language || 'en-US';
|
|
5484
|
-
}
|
|
5485
|
-
return 'en-US';
|
|
5486
|
-
}
|
|
5487
|
-
|
|
5488
|
-
function detectUserAgent() {
|
|
5489
|
-
if (typeof navigator === 'undefined') {
|
|
5490
|
-
return { type: 'web', hover: true, touch: false };
|
|
5491
|
-
}
|
|
5492
|
-
var ua = navigator.userAgent || '';
|
|
5493
|
-
var isMobile = /iPhone|iPad|iPod|Android/i.test(ua);
|
|
5494
|
-
var hasTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
|
5495
|
-
var hasHover = window.matchMedia && window.matchMedia('(hover: hover)').matches;
|
|
5496
|
-
return { type: isMobile ? 'mobile' : 'web', hover: hasHover !== false, touch: hasTouch };
|
|
5497
|
-
}
|
|
5498
|
-
|
|
5499
|
-
function detectViewport() {
|
|
5500
|
-
if (typeof window !== 'undefined') {
|
|
5501
|
-
return { width: window.innerWidth, height: window.innerHeight };
|
|
5502
|
-
}
|
|
5503
|
-
return undefined;
|
|
5504
|
-
}
|
|
5505
|
-
|
|
5506
|
-
function readInjectedData() {
|
|
5507
|
-
var data = { toolInput: {}, toolOutput: undefined, structuredContent: undefined };
|
|
5508
|
-
if (typeof window !== 'undefined') {
|
|
5509
|
-
if (window.__mcpToolInput) data.toolInput = window.__mcpToolInput;
|
|
5510
|
-
if (window.__mcpToolOutput) data.toolOutput = window.__mcpToolOutput;
|
|
5511
|
-
if (window.__mcpStructuredContent) data.structuredContent = window.__mcpStructuredContent;
|
|
5512
|
-
}
|
|
5513
|
-
return data;
|
|
5514
|
-
}
|
|
5515
|
-
`.trim();
|
|
5516
|
-
}
|
|
5517
|
-
function generateBaseCapabilities() {
|
|
5518
|
-
return `
|
|
5519
|
-
var DEFAULT_CAPABILITIES = {
|
|
5520
|
-
canCallTools: false,
|
|
5521
|
-
canSendMessages: false,
|
|
5522
|
-
canOpenLinks: false,
|
|
5523
|
-
canPersistState: true,
|
|
5524
|
-
hasNetworkAccess: true,
|
|
5525
|
-
supportsDisplayModes: false,
|
|
5526
|
-
supportsTheme: true
|
|
5527
|
-
};
|
|
5528
|
-
`.trim();
|
|
5529
|
-
}
|
|
5530
|
-
function generateOpenAIAdapter() {
|
|
5531
|
-
return `
|
|
5532
|
-
var OpenAIAdapter = {
|
|
5533
|
-
id: 'openai',
|
|
5534
|
-
name: 'OpenAI ChatGPT',
|
|
5535
|
-
priority: 100,
|
|
5536
|
-
capabilities: Object.assign({}, DEFAULT_CAPABILITIES, {
|
|
5537
|
-
canCallTools: true,
|
|
5538
|
-
canSendMessages: true,
|
|
5539
|
-
canOpenLinks: true,
|
|
5540
|
-
supportsDisplayModes: true
|
|
5541
|
-
}),
|
|
5542
|
-
canHandle: function() {
|
|
5543
|
-
if (typeof window === 'undefined') return false;
|
|
5544
|
-
// Check for window.openai.callTool (the actual OpenAI SDK API)
|
|
5545
|
-
if (window.openai && typeof window.openai.callTool === 'function') return true;
|
|
5546
|
-
// Also check if we're being injected with tool metadata (OpenAI injects toolOutput)
|
|
5547
|
-
if (window.openai && (window.openai.toolOutput !== undefined || window.openai.toolInput !== undefined)) return true;
|
|
5548
|
-
return false;
|
|
5549
|
-
},
|
|
5550
|
-
initialize: function(context) {
|
|
5551
|
-
var sdk = window.openai;
|
|
5552
|
-
context.sdk = sdk;
|
|
5553
|
-
// OpenAI SDK exposes theme and displayMode directly as properties
|
|
5554
|
-
if (sdk.theme) {
|
|
5555
|
-
context.hostContext.theme = sdk.theme;
|
|
5556
|
-
}
|
|
5557
|
-
if (sdk.displayMode) {
|
|
5558
|
-
context.hostContext.displayMode = sdk.displayMode;
|
|
5559
|
-
}
|
|
5560
|
-
// Note: OpenAI SDK does not have an onContextChange equivalent
|
|
5561
|
-
return Promise.resolve();
|
|
5562
|
-
},
|
|
5563
|
-
callTool: function(context, name, args) {
|
|
5564
|
-
return context.sdk.callTool(name, args);
|
|
5565
|
-
},
|
|
5566
|
-
sendMessage: function(context, content) {
|
|
5567
|
-
if (typeof context.sdk.sendFollowUpMessage === 'function') {
|
|
5568
|
-
return context.sdk.sendFollowUpMessage(content);
|
|
5569
|
-
}
|
|
5570
|
-
return Promise.reject(new Error('Messages not supported'));
|
|
5571
|
-
},
|
|
5572
|
-
openLink: function(context, url) {
|
|
5573
|
-
window.open(url, '_blank', 'noopener,noreferrer');
|
|
5574
|
-
return Promise.resolve();
|
|
5575
|
-
},
|
|
5576
|
-
requestDisplayMode: function(context, mode) {
|
|
5577
|
-
return Promise.resolve();
|
|
5578
|
-
},
|
|
5579
|
-
requestClose: function(context) {
|
|
5580
|
-
return Promise.resolve();
|
|
5581
|
-
}
|
|
5582
|
-
};
|
|
5583
|
-
`.trim();
|
|
5584
|
-
}
|
|
5585
|
-
function generateExtAppsAdapter(trustedOrigins) {
|
|
5586
|
-
const originsArray = trustedOrigins.length > 0 ? JSON.stringify(trustedOrigins) : "[]";
|
|
5587
|
-
return `
|
|
5588
|
-
var ExtAppsAdapter = {
|
|
5589
|
-
id: 'ext-apps',
|
|
5590
|
-
name: 'ext-apps (SEP-1865)',
|
|
5591
|
-
priority: 80,
|
|
5592
|
-
capabilities: Object.assign({}, DEFAULT_CAPABILITIES, { canPersistState: true, hasNetworkAccess: true }),
|
|
5593
|
-
trustedOrigins: ${originsArray},
|
|
5594
|
-
trustedOrigin: null,
|
|
5595
|
-
pendingRequests: {},
|
|
5596
|
-
requestId: 0,
|
|
5597
|
-
hostCapabilities: {},
|
|
5598
|
-
canHandle: function() {
|
|
5599
|
-
if (typeof window === 'undefined') return false;
|
|
5600
|
-
if (window.parent === window) return false;
|
|
5601
|
-
// Check for OpenAI SDK (window.openai.callTool) - defer to OpenAIAdapter
|
|
5602
|
-
if (window.openai && typeof window.openai.callTool === 'function') return false;
|
|
5603
|
-
if (window.__mcpPlatform === 'ext-apps') return true;
|
|
5604
|
-
return true;
|
|
5605
|
-
},
|
|
5606
|
-
initialize: function(context) {
|
|
5607
|
-
var self = this;
|
|
5608
|
-
context.extApps = this;
|
|
5609
|
-
|
|
5610
|
-
window.addEventListener('message', function(event) {
|
|
5611
|
-
self.handleMessage(context, event);
|
|
5612
|
-
});
|
|
5613
|
-
|
|
5614
|
-
return self.performHandshake(context);
|
|
5615
|
-
},
|
|
5616
|
-
handleMessage: function(context, event) {
|
|
5617
|
-
if (!this.isOriginTrusted(event.origin)) return;
|
|
5618
|
-
var data = event.data;
|
|
5619
|
-
if (!data || typeof data !== 'object' || data.jsonrpc !== '2.0') return;
|
|
5620
|
-
|
|
5621
|
-
if ('id' in data && (data.result !== undefined || data.error !== undefined)) {
|
|
5622
|
-
var pending = this.pendingRequests[data.id];
|
|
5623
|
-
if (pending) {
|
|
5624
|
-
clearTimeout(pending.timeout);
|
|
5625
|
-
delete this.pendingRequests[data.id];
|
|
5626
|
-
if (data.error) {
|
|
5627
|
-
pending.reject(new Error(data.error.message + ' (code: ' + data.error.code + ')'));
|
|
5628
|
-
} else {
|
|
5629
|
-
pending.resolve(data.result);
|
|
5630
|
-
}
|
|
5631
|
-
}
|
|
5632
|
-
return;
|
|
5633
|
-
}
|
|
5634
|
-
|
|
5635
|
-
if ('method' in data && !('id' in data)) {
|
|
5636
|
-
this.handleNotification(context, data);
|
|
5637
|
-
}
|
|
5638
|
-
},
|
|
5639
|
-
handleNotification: function(context, notification) {
|
|
5640
|
-
var params = notification.params || {};
|
|
5641
|
-
switch (notification.method) {
|
|
5642
|
-
case 'ui/notifications/tool-input':
|
|
5643
|
-
context.toolInput = params.arguments || {};
|
|
5644
|
-
window.dispatchEvent(new CustomEvent('tool:input', { detail: { arguments: context.toolInput } }));
|
|
5645
|
-
break;
|
|
5646
|
-
case 'ui/notifications/tool-result':
|
|
5647
|
-
context.toolOutput = params.content;
|
|
5648
|
-
context.structuredContent = params.structuredContent;
|
|
5649
|
-
context.notifyToolResult(params.content);
|
|
5650
|
-
window.dispatchEvent(new CustomEvent('tool:result', { detail: params }));
|
|
5651
|
-
break;
|
|
5652
|
-
case 'ui/notifications/host-context-changed':
|
|
5653
|
-
Object.assign(context.hostContext, params);
|
|
5654
|
-
context.notifyContextChange(params);
|
|
5655
|
-
break;
|
|
5656
|
-
}
|
|
5657
|
-
},
|
|
5658
|
-
isOriginTrusted: function(origin) {
|
|
5659
|
-
if (this.trustedOrigins.length > 0) {
|
|
5660
|
-
return this.trustedOrigins.indexOf(origin) !== -1;
|
|
5661
|
-
}
|
|
5662
|
-
// When no trusted origins configured, only trust first message in iframe context
|
|
5663
|
-
// This helps mitigate race conditions where a malicious iframe could establish trust
|
|
5664
|
-
if (!this.trustedOrigin) {
|
|
5665
|
-
if (window.parent !== window && origin) {
|
|
5666
|
-
this.trustedOrigin = origin;
|
|
5667
|
-
return true;
|
|
5668
|
-
}
|
|
5669
|
-
return false;
|
|
5670
|
-
}
|
|
5671
|
-
return this.trustedOrigin === origin;
|
|
5672
|
-
},
|
|
5673
|
-
sendRequest: function(method, params) {
|
|
5674
|
-
var self = this;
|
|
5675
|
-
return new Promise(function(resolve, reject) {
|
|
5676
|
-
// Security: Require trusted origin before sending requests to prevent message leaks
|
|
5677
|
-
if (!self.trustedOrigin && self.trustedOrigins.length === 0) {
|
|
5678
|
-
reject(new Error('Cannot send request: no trusted origin established'));
|
|
5679
|
-
return;
|
|
5680
|
-
}
|
|
5681
|
-
|
|
5682
|
-
var id = ++self.requestId;
|
|
5683
|
-
var timeout = setTimeout(function() {
|
|
5684
|
-
delete self.pendingRequests[id];
|
|
5685
|
-
reject(new Error('Request ' + method + ' timed out'));
|
|
5686
|
-
}, 10000);
|
|
5687
|
-
|
|
5688
|
-
self.pendingRequests[id] = { resolve: resolve, reject: reject, timeout: timeout };
|
|
5689
|
-
|
|
5690
|
-
var targetOrigin = self.trustedOrigin || self.trustedOrigins[0];
|
|
5691
|
-
window.parent.postMessage({ jsonrpc: '2.0', id: id, method: method, params: params }, targetOrigin);
|
|
5692
|
-
});
|
|
5693
|
-
},
|
|
5694
|
-
performHandshake: function(context) {
|
|
5695
|
-
var self = this;
|
|
5696
|
-
var params = {
|
|
5697
|
-
appInfo: { name: 'FrontMCP Widget', version: '1.0.0' },
|
|
5698
|
-
appCapabilities: { tools: { listChanged: false } },
|
|
5699
|
-
protocolVersion: '2024-11-05'
|
|
5700
|
-
};
|
|
5701
|
-
|
|
5702
|
-
return this.sendRequest('ui/initialize', params).then(function(result) {
|
|
5703
|
-
self.hostCapabilities = result.hostCapabilities || {};
|
|
5704
|
-
self.capabilities = Object.assign({}, self.capabilities, {
|
|
5705
|
-
canCallTools: Boolean(self.hostCapabilities.serverToolProxy),
|
|
5706
|
-
canSendMessages: true,
|
|
5707
|
-
canOpenLinks: Boolean(self.hostCapabilities.openLink),
|
|
5708
|
-
supportsDisplayModes: true
|
|
5709
|
-
});
|
|
5710
|
-
if (result.hostContext) {
|
|
5711
|
-
Object.assign(context.hostContext, result.hostContext);
|
|
5712
|
-
}
|
|
5713
|
-
});
|
|
5714
|
-
},
|
|
5715
|
-
callTool: function(context, name, args) {
|
|
5716
|
-
if (!this.hostCapabilities.serverToolProxy) {
|
|
5717
|
-
return Promise.reject(new Error('Server tool proxy not supported'));
|
|
5718
|
-
}
|
|
5719
|
-
return this.sendRequest('ui/callServerTool', { name: name, arguments: args });
|
|
5720
|
-
},
|
|
5721
|
-
sendMessage: function(context, content) {
|
|
5722
|
-
return this.sendRequest('ui/message', { content: content });
|
|
5723
|
-
},
|
|
5724
|
-
openLink: function(context, url) {
|
|
5725
|
-
if (!this.hostCapabilities.openLink) {
|
|
5726
|
-
window.open(url, '_blank', 'noopener,noreferrer');
|
|
5727
|
-
return Promise.resolve();
|
|
5728
|
-
}
|
|
5729
|
-
return this.sendRequest('ui/openLink', { url: url });
|
|
5730
|
-
},
|
|
5731
|
-
requestDisplayMode: function(context, mode) {
|
|
5732
|
-
return this.sendRequest('ui/setDisplayMode', { mode: mode });
|
|
5733
|
-
},
|
|
5734
|
-
requestClose: function(context) {
|
|
5735
|
-
return this.sendRequest('ui/close', {});
|
|
5736
|
-
}
|
|
5737
|
-
};
|
|
5738
|
-
`.trim();
|
|
5739
|
-
}
|
|
5740
|
-
function generateClaudeAdapter() {
|
|
5741
|
-
return `
|
|
5742
|
-
var ClaudeAdapter = {
|
|
5743
|
-
id: 'claude',
|
|
5744
|
-
name: 'Claude (Anthropic)',
|
|
5745
|
-
priority: 60,
|
|
5746
|
-
capabilities: Object.assign({}, DEFAULT_CAPABILITIES, {
|
|
5747
|
-
canCallTools: false,
|
|
5748
|
-
canSendMessages: false,
|
|
5749
|
-
canOpenLinks: true,
|
|
5750
|
-
hasNetworkAccess: false,
|
|
5751
|
-
supportsDisplayModes: false
|
|
5752
|
-
}),
|
|
5753
|
-
canHandle: function() {
|
|
5754
|
-
if (typeof window === 'undefined') return false;
|
|
5755
|
-
if (window.__mcpPlatform === 'claude') return true;
|
|
5756
|
-
if (window.claude) return true;
|
|
5757
|
-
if (window.__claudeArtifact) return true;
|
|
5758
|
-
if (typeof location !== 'undefined') {
|
|
5759
|
-
var href = location.href;
|
|
5760
|
-
if (href.indexOf('claude.ai') !== -1 || href.indexOf('anthropic.com') !== -1) return true;
|
|
5761
|
-
}
|
|
5762
|
-
return false;
|
|
5763
|
-
},
|
|
5764
|
-
initialize: function(context) {
|
|
5765
|
-
return Promise.resolve();
|
|
5766
|
-
},
|
|
5767
|
-
callTool: function() {
|
|
5768
|
-
return Promise.reject(new Error('Tool calls not supported in Claude'));
|
|
5769
|
-
},
|
|
5770
|
-
sendMessage: function() {
|
|
5771
|
-
return Promise.reject(new Error('Messages not supported in Claude'));
|
|
5772
|
-
},
|
|
5773
|
-
openLink: function(context, url) {
|
|
5774
|
-
window.open(url, '_blank', 'noopener,noreferrer');
|
|
5775
|
-
return Promise.resolve();
|
|
5776
|
-
},
|
|
5777
|
-
requestDisplayMode: function() {
|
|
5778
|
-
return Promise.resolve();
|
|
5779
|
-
},
|
|
5780
|
-
requestClose: function() {
|
|
5781
|
-
return Promise.resolve();
|
|
5782
|
-
}
|
|
5783
|
-
};
|
|
5784
|
-
`.trim();
|
|
5785
|
-
}
|
|
5786
|
-
function generateGeminiAdapter() {
|
|
5787
|
-
return `
|
|
5788
|
-
var GeminiAdapter = {
|
|
5789
|
-
id: 'gemini',
|
|
5790
|
-
name: 'Google Gemini',
|
|
5791
|
-
priority: 40,
|
|
5792
|
-
capabilities: Object.assign({}, DEFAULT_CAPABILITIES, {
|
|
5793
|
-
canOpenLinks: true,
|
|
5794
|
-
hasNetworkAccess: true
|
|
5795
|
-
}),
|
|
5796
|
-
canHandle: function() {
|
|
5797
|
-
if (typeof window === 'undefined') return false;
|
|
5798
|
-
if (window.__mcpPlatform === 'gemini') return true;
|
|
5799
|
-
if (window.gemini) return true;
|
|
5800
|
-
if (typeof location !== 'undefined') {
|
|
5801
|
-
var href = location.href;
|
|
5802
|
-
if (href.indexOf('gemini.google.com') !== -1 || href.indexOf('bard.google.com') !== -1) return true;
|
|
5803
|
-
}
|
|
5804
|
-
return false;
|
|
5805
|
-
},
|
|
5806
|
-
initialize: function(context) {
|
|
5807
|
-
if (window.gemini && window.gemini.ui && window.gemini.ui.getTheme) {
|
|
5808
|
-
context.hostContext.theme = window.gemini.ui.getTheme() === 'dark' ? 'dark' : 'light';
|
|
5809
|
-
}
|
|
5810
|
-
return Promise.resolve();
|
|
5811
|
-
},
|
|
5812
|
-
callTool: function() {
|
|
5813
|
-
return Promise.reject(new Error('Tool calls not supported in Gemini'));
|
|
5814
|
-
},
|
|
5815
|
-
sendMessage: function(context, content) {
|
|
5816
|
-
if (window.gemini && window.gemini.ui && window.gemini.ui.sendMessage) {
|
|
5817
|
-
return window.gemini.ui.sendMessage(content);
|
|
5818
|
-
}
|
|
5819
|
-
return Promise.reject(new Error('Messages not supported in Gemini'));
|
|
5820
|
-
},
|
|
5821
|
-
openLink: function(context, url) {
|
|
5822
|
-
if (window.gemini && window.gemini.ui && window.gemini.ui.openLink) {
|
|
5823
|
-
return window.gemini.ui.openLink(url);
|
|
5824
|
-
}
|
|
5825
|
-
window.open(url, '_blank', 'noopener,noreferrer');
|
|
5826
|
-
return Promise.resolve();
|
|
5827
|
-
},
|
|
5828
|
-
requestDisplayMode: function() {
|
|
5829
|
-
return Promise.resolve();
|
|
5830
|
-
},
|
|
5831
|
-
requestClose: function() {
|
|
5832
|
-
return Promise.resolve();
|
|
3330
|
+
setWidgetState(state) {
|
|
3331
|
+
const adapter = this._ensureInitialized();
|
|
3332
|
+
adapter.setWidgetState(state);
|
|
5833
3333
|
}
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
hasNetworkAccess: true
|
|
5846
|
-
}),
|
|
5847
|
-
canHandle: function() {
|
|
5848
|
-
return typeof window !== 'undefined';
|
|
5849
|
-
},
|
|
5850
|
-
initialize: function(context) {
|
|
5851
|
-
return Promise.resolve();
|
|
5852
|
-
},
|
|
5853
|
-
callTool: function() {
|
|
5854
|
-
return Promise.reject(new Error('Tool calls not supported'));
|
|
5855
|
-
},
|
|
5856
|
-
sendMessage: function() {
|
|
5857
|
-
return Promise.reject(new Error('Messages not supported'));
|
|
5858
|
-
},
|
|
5859
|
-
openLink: function(context, url) {
|
|
5860
|
-
window.open(url, '_blank', 'noopener,noreferrer');
|
|
5861
|
-
return Promise.resolve();
|
|
5862
|
-
},
|
|
5863
|
-
requestDisplayMode: function() {
|
|
5864
|
-
return Promise.resolve();
|
|
5865
|
-
},
|
|
5866
|
-
requestClose: function() {
|
|
5867
|
-
return Promise.resolve();
|
|
3334
|
+
// ============================================
|
|
3335
|
+
// Events (delegate to adapter)
|
|
3336
|
+
// ============================================
|
|
3337
|
+
/**
|
|
3338
|
+
* Subscribe to host context changes.
|
|
3339
|
+
* @param callback - Called when context changes
|
|
3340
|
+
* @returns Unsubscribe function
|
|
3341
|
+
*/
|
|
3342
|
+
onContextChange(callback) {
|
|
3343
|
+
const adapter = this._ensureInitialized();
|
|
3344
|
+
return adapter.onContextChange(callback);
|
|
5868
3345
|
}
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
case "ext-apps":
|
|
5878
|
-
return "ExtAppsAdapter";
|
|
5879
|
-
case "claude":
|
|
5880
|
-
return "ClaudeAdapter";
|
|
5881
|
-
case "gemini":
|
|
5882
|
-
return "GeminiAdapter";
|
|
5883
|
-
case "generic":
|
|
5884
|
-
return "GenericAdapter";
|
|
5885
|
-
default:
|
|
5886
|
-
return "";
|
|
5887
|
-
}
|
|
5888
|
-
}).filter(Boolean);
|
|
5889
|
-
return `
|
|
5890
|
-
var ADAPTERS = [${adapterVars.join(", ")}].sort(function(a, b) { return b.priority - a.priority; });
|
|
5891
|
-
|
|
5892
|
-
function detectPlatform() {
|
|
5893
|
-
for (var i = 0; i < ADAPTERS.length; i++) {
|
|
5894
|
-
if (ADAPTERS[i].canHandle()) {
|
|
5895
|
-
log('Detected platform: ' + ADAPTERS[i].id);
|
|
5896
|
-
return ADAPTERS[i];
|
|
5897
|
-
}
|
|
3346
|
+
/**
|
|
3347
|
+
* Subscribe to tool result updates.
|
|
3348
|
+
* @param callback - Called when tool result is received
|
|
3349
|
+
* @returns Unsubscribe function
|
|
3350
|
+
*/
|
|
3351
|
+
onToolResult(callback) {
|
|
3352
|
+
const adapter = this._ensureInitialized();
|
|
3353
|
+
return adapter.onToolResult(callback);
|
|
5898
3354
|
}
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
this._context = {
|
|
5910
|
-
hostContext: {
|
|
5911
|
-
theme: detectTheme(),
|
|
5912
|
-
displayMode: 'inline',
|
|
5913
|
-
locale: detectLocale(),
|
|
5914
|
-
userAgent: detectUserAgent(),
|
|
5915
|
-
safeArea: DEFAULT_SAFE_AREA,
|
|
5916
|
-
viewport: detectViewport()
|
|
5917
|
-
},
|
|
5918
|
-
toolInput: {},
|
|
5919
|
-
toolOutput: undefined,
|
|
5920
|
-
structuredContent: undefined,
|
|
5921
|
-
widgetState: {},
|
|
5922
|
-
contextListeners: [],
|
|
5923
|
-
toolResultListeners: [],
|
|
5924
|
-
notifyContextChange: function(changes) {
|
|
5925
|
-
Object.assign(this.hostContext, changes);
|
|
5926
|
-
for (var i = 0; i < this.contextListeners.length; i++) {
|
|
5927
|
-
try { this.contextListeners[i](changes); } catch(e) {}
|
|
5928
|
-
}
|
|
5929
|
-
},
|
|
5930
|
-
notifyToolResult: function(result) {
|
|
5931
|
-
this.toolOutput = result;
|
|
5932
|
-
for (var i = 0; i < this.toolResultListeners.length; i++) {
|
|
5933
|
-
try { this.toolResultListeners[i](result); } catch(e) {}
|
|
5934
|
-
}
|
|
3355
|
+
// ============================================
|
|
3356
|
+
// Private Helpers
|
|
3357
|
+
// ============================================
|
|
3358
|
+
/**
|
|
3359
|
+
* Ensure the bridge is initialized before operations.
|
|
3360
|
+
* Returns the adapter for type-safe access.
|
|
3361
|
+
*/
|
|
3362
|
+
_ensureInitialized() {
|
|
3363
|
+
if (!this._initialized || !this._adapter) {
|
|
3364
|
+
throw new Error("FrontMcpBridge is not initialized. Call initialize() first.");
|
|
5935
3365
|
}
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
var injected = readInjectedData();
|
|
5939
|
-
this._context.toolInput = injected.toolInput;
|
|
5940
|
-
this._context.toolOutput = injected.toolOutput;
|
|
5941
|
-
this._context.structuredContent = injected.structuredContent;
|
|
5942
|
-
|
|
5943
|
-
this._loadWidgetState();
|
|
5944
|
-
}
|
|
5945
|
-
|
|
5946
|
-
FrontMcpBridge.prototype._loadWidgetState = function() {
|
|
5947
|
-
try {
|
|
5948
|
-
var key = 'frontmcp:widget:' + (window.__mcpToolName || 'unknown');
|
|
5949
|
-
var stored = localStorage.getItem(key);
|
|
5950
|
-
if (stored) this._context.widgetState = JSON.parse(stored);
|
|
5951
|
-
} catch(e) {}
|
|
5952
|
-
};
|
|
5953
|
-
|
|
5954
|
-
FrontMcpBridge.prototype._saveWidgetState = function() {
|
|
5955
|
-
try {
|
|
5956
|
-
var key = 'frontmcp:widget:' + (window.__mcpToolName || 'unknown');
|
|
5957
|
-
localStorage.setItem(key, JSON.stringify(this._context.widgetState));
|
|
5958
|
-
} catch(e) {}
|
|
5959
|
-
};
|
|
5960
|
-
|
|
5961
|
-
FrontMcpBridge.prototype.initialize = function() {
|
|
5962
|
-
if (this._initialized) return Promise.resolve();
|
|
5963
|
-
var self = this;
|
|
5964
|
-
this._adapter = detectPlatform();
|
|
5965
|
-
return this._adapter.initialize(this._context).then(function() {
|
|
5966
|
-
self._initialized = true;
|
|
5967
|
-
// Set up the data-tool-call click handler after initialization
|
|
5968
|
-
self._setupDataToolCallHandler();
|
|
5969
|
-
});
|
|
5970
|
-
};
|
|
5971
|
-
|
|
5972
|
-
Object.defineProperty(FrontMcpBridge.prototype, 'initialized', { get: function() { return this._initialized; } });
|
|
5973
|
-
Object.defineProperty(FrontMcpBridge.prototype, 'adapterId', { get: function() { return this._adapter ? this._adapter.id : undefined; } });
|
|
5974
|
-
Object.defineProperty(FrontMcpBridge.prototype, 'capabilities', { get: function() { return this._adapter ? this._adapter.capabilities : DEFAULT_CAPABILITIES; } });
|
|
5975
|
-
|
|
5976
|
-
FrontMcpBridge.prototype.getTheme = function() { return this._context.hostContext.theme; };
|
|
5977
|
-
FrontMcpBridge.prototype.getDisplayMode = function() { return this._context.hostContext.displayMode; };
|
|
5978
|
-
FrontMcpBridge.prototype.getToolInput = function() { return this._context.toolInput; };
|
|
5979
|
-
FrontMcpBridge.prototype.getToolOutput = function() { return this._context.toolOutput; };
|
|
5980
|
-
FrontMcpBridge.prototype.getStructuredContent = function() { return this._context.structuredContent; };
|
|
5981
|
-
FrontMcpBridge.prototype.getWidgetState = function() { return this._context.widgetState; };
|
|
5982
|
-
FrontMcpBridge.prototype.getHostContext = function() { return Object.assign({}, this._context.hostContext); };
|
|
5983
|
-
FrontMcpBridge.prototype.hasCapability = function(cap) { return this._adapter && this._adapter.capabilities[cap] === true; };
|
|
5984
|
-
|
|
5985
|
-
// Get tool response metadata (platform-agnostic)
|
|
5986
|
-
// Used by inline mode widgets to detect when ui/html arrives
|
|
5987
|
-
FrontMcpBridge.prototype.getToolResponseMetadata = function() {
|
|
5988
|
-
// OpenAI injects toolResponseMetadata for widget-producing tools
|
|
5989
|
-
if (typeof window !== 'undefined' && window.openai && window.openai.toolResponseMetadata) {
|
|
5990
|
-
return window.openai.toolResponseMetadata;
|
|
5991
|
-
}
|
|
5992
|
-
// Claude (future support)
|
|
5993
|
-
if (typeof window !== 'undefined' && window.claude && window.claude.toolResponseMetadata) {
|
|
5994
|
-
return window.claude.toolResponseMetadata;
|
|
5995
|
-
}
|
|
5996
|
-
// FrontMCP direct injection (for testing/ext-apps)
|
|
5997
|
-
if (typeof window !== 'undefined' && window.__mcpToolResponseMetadata) {
|
|
5998
|
-
return window.__mcpToolResponseMetadata;
|
|
5999
|
-
}
|
|
6000
|
-
return null;
|
|
6001
|
-
};
|
|
6002
|
-
|
|
6003
|
-
// Subscribe to tool response metadata changes (for inline mode injection)
|
|
6004
|
-
FrontMcpBridge.prototype.onToolResponseMetadata = function(callback) {
|
|
6005
|
-
var self = this;
|
|
6006
|
-
var called = false;
|
|
6007
|
-
|
|
6008
|
-
// Check if already available
|
|
6009
|
-
var existing = self.getToolResponseMetadata();
|
|
6010
|
-
if (existing) {
|
|
6011
|
-
called = true;
|
|
6012
|
-
callback(existing);
|
|
3366
|
+
return this._adapter;
|
|
6013
3367
|
}
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
set: function(val) {
|
|
6029
|
-
originalMetadata = val;
|
|
6030
|
-
log('toolResponseMetadata set, notifying ' + window.__frontmcpMetadataCallbacks.length + ' listeners');
|
|
6031
|
-
for (var i = 0; i < window.__frontmcpMetadataCallbacks.length; i++) {
|
|
6032
|
-
try { window.__frontmcpMetadataCallbacks[i](val); } catch(e) {}
|
|
6033
|
-
}
|
|
6034
|
-
},
|
|
6035
|
-
configurable: true
|
|
3368
|
+
/**
|
|
3369
|
+
* Wrap a promise with a timeout.
|
|
3370
|
+
*/
|
|
3371
|
+
_withTimeout(promise, ms) {
|
|
3372
|
+
return new Promise((resolve, reject) => {
|
|
3373
|
+
const timer = setTimeout(() => {
|
|
3374
|
+
reject(new Error(`Operation timed out after ${ms}ms`));
|
|
3375
|
+
}, ms);
|
|
3376
|
+
promise.then((result) => {
|
|
3377
|
+
clearTimeout(timer);
|
|
3378
|
+
resolve(result);
|
|
3379
|
+
}).catch((error) => {
|
|
3380
|
+
clearTimeout(timer);
|
|
3381
|
+
reject(error);
|
|
6036
3382
|
});
|
|
6037
|
-
}
|
|
6038
|
-
|
|
6039
|
-
// Register callback wrapper (store reference for unsubscribe)
|
|
6040
|
-
var wrapper = function(metadata) {
|
|
6041
|
-
if (!called) {
|
|
6042
|
-
called = true;
|
|
6043
|
-
callback(metadata);
|
|
6044
|
-
}
|
|
6045
|
-
};
|
|
6046
|
-
window.__frontmcpMetadataCallbacks.push(wrapper);
|
|
6047
|
-
|
|
6048
|
-
// Return unsubscribe function that removes the wrapper (not the original callback)
|
|
6049
|
-
return function() {
|
|
6050
|
-
if (window.__frontmcpMetadataCallbacks) {
|
|
6051
|
-
var idx = window.__frontmcpMetadataCallbacks.indexOf(wrapper);
|
|
6052
|
-
if (idx !== -1) window.__frontmcpMetadataCallbacks.splice(idx, 1);
|
|
6053
|
-
}
|
|
6054
|
-
};
|
|
6055
|
-
}
|
|
6056
|
-
|
|
6057
|
-
// Return no-op unsubscribe for non-window environments
|
|
6058
|
-
return function() {};
|
|
6059
|
-
};
|
|
6060
|
-
|
|
6061
|
-
FrontMcpBridge.prototype.callTool = function(name, args) {
|
|
6062
|
-
// Priority 1: Direct OpenAI SDK call (most reliable in OpenAI iframe)
|
|
6063
|
-
// This bypasses adapter abstraction for maximum compatibility
|
|
6064
|
-
if (typeof window !== 'undefined' && window.openai && typeof window.openai.callTool === 'function') {
|
|
6065
|
-
log('callTool: Using OpenAI SDK directly');
|
|
6066
|
-
return window.openai.callTool(name, args);
|
|
6067
|
-
}
|
|
6068
|
-
|
|
6069
|
-
// Priority 2: Use adapter (if initialized and supports tool calls)
|
|
6070
|
-
if (this._adapter && this._adapter.capabilities && this._adapter.capabilities.canCallTools) {
|
|
6071
|
-
log('callTool: Using adapter ' + this._adapter.id);
|
|
6072
|
-
return this._adapter.callTool(this._context, name, args);
|
|
6073
|
-
}
|
|
6074
|
-
|
|
6075
|
-
// Not initialized or no tool support
|
|
6076
|
-
if (!this._adapter) {
|
|
6077
|
-
return Promise.reject(new Error('Bridge not initialized. Wait for bridge:ready event.'));
|
|
3383
|
+
});
|
|
6078
3384
|
}
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
};
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
if (!this._adapter) return Promise.reject(new Error('Not initialized'));
|
|
6089
|
-
return this._adapter.openLink(this._context, url);
|
|
6090
|
-
};
|
|
6091
|
-
|
|
6092
|
-
FrontMcpBridge.prototype.requestDisplayMode = function(mode) {
|
|
6093
|
-
if (!this._adapter) return Promise.reject(new Error('Not initialized'));
|
|
6094
|
-
var self = this;
|
|
6095
|
-
return this._adapter.requestDisplayMode(this._context, mode).then(function() {
|
|
6096
|
-
self._context.hostContext.displayMode = mode;
|
|
6097
|
-
});
|
|
6098
|
-
};
|
|
6099
|
-
|
|
6100
|
-
FrontMcpBridge.prototype.requestClose = function() {
|
|
6101
|
-
if (!this._adapter) return Promise.reject(new Error('Not initialized'));
|
|
6102
|
-
return this._adapter.requestClose(this._context);
|
|
6103
|
-
};
|
|
6104
|
-
|
|
6105
|
-
FrontMcpBridge.prototype.setWidgetState = function(state) {
|
|
6106
|
-
Object.assign(this._context.widgetState, state);
|
|
6107
|
-
this._saveWidgetState();
|
|
6108
|
-
};
|
|
6109
|
-
|
|
6110
|
-
FrontMcpBridge.prototype.onContextChange = function(callback) {
|
|
6111
|
-
var listeners = this._context.contextListeners;
|
|
6112
|
-
listeners.push(callback);
|
|
6113
|
-
return function() {
|
|
6114
|
-
var idx = listeners.indexOf(callback);
|
|
6115
|
-
if (idx !== -1) listeners.splice(idx, 1);
|
|
6116
|
-
};
|
|
6117
|
-
};
|
|
6118
|
-
|
|
6119
|
-
FrontMcpBridge.prototype.onToolResult = function(callback) {
|
|
6120
|
-
var listeners = this._context.toolResultListeners;
|
|
6121
|
-
listeners.push(callback);
|
|
6122
|
-
return function() {
|
|
6123
|
-
var idx = listeners.indexOf(callback);
|
|
6124
|
-
if (idx !== -1) listeners.splice(idx, 1);
|
|
6125
|
-
};
|
|
6126
|
-
};
|
|
6127
|
-
|
|
6128
|
-
// ==================== data-tool-call Click Handler ====================
|
|
6129
|
-
|
|
6130
|
-
FrontMcpBridge.prototype._setupDataToolCallHandler = function() {
|
|
6131
|
-
var self = this;
|
|
6132
|
-
|
|
6133
|
-
document.addEventListener('click', function(e) {
|
|
6134
|
-
// Find the closest element with data-tool-call attribute
|
|
6135
|
-
var target = e.target;
|
|
6136
|
-
while (target && target !== document) {
|
|
6137
|
-
if (target.hasAttribute && target.hasAttribute('data-tool-call')) {
|
|
6138
|
-
var toolName = target.getAttribute('data-tool-call');
|
|
6139
|
-
var argsAttr = target.getAttribute('data-tool-args');
|
|
6140
|
-
var args = {};
|
|
6141
|
-
|
|
6142
|
-
try {
|
|
6143
|
-
if (argsAttr) {
|
|
6144
|
-
args = JSON.parse(argsAttr);
|
|
6145
|
-
}
|
|
6146
|
-
} catch (parseErr) {
|
|
6147
|
-
console.error('[frontmcp] Failed to parse data-tool-args:', parseErr);
|
|
6148
|
-
}
|
|
6149
|
-
|
|
6150
|
-
log('data-tool-call clicked: ' + toolName);
|
|
6151
|
-
|
|
6152
|
-
// Show loading state - save original content first
|
|
6153
|
-
var originalContent = target.innerHTML;
|
|
6154
|
-
var originalDisabled = target.disabled;
|
|
6155
|
-
target.disabled = true;
|
|
6156
|
-
target.classList.add('opacity-50', 'cursor-not-allowed');
|
|
6157
|
-
|
|
6158
|
-
// Add spinner for buttons
|
|
6159
|
-
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>';
|
|
6160
|
-
if (target.tagName === 'BUTTON') {
|
|
6161
|
-
target.innerHTML = spinner + 'Loading...';
|
|
6162
|
-
}
|
|
6163
|
-
|
|
6164
|
-
// Helper to reset button state
|
|
6165
|
-
function resetButton() {
|
|
6166
|
-
target.innerHTML = originalContent;
|
|
6167
|
-
target.disabled = originalDisabled;
|
|
6168
|
-
target.classList.remove('opacity-50', 'cursor-not-allowed');
|
|
6169
|
-
}
|
|
6170
|
-
|
|
6171
|
-
// Determine how to call the tool
|
|
6172
|
-
var toolCallPromise;
|
|
6173
|
-
|
|
6174
|
-
// Priority 1: Direct OpenAI SDK call (bypasses adapter abstraction)
|
|
6175
|
-
if (typeof window !== 'undefined' && window.openai && typeof window.openai.callTool === 'function') {
|
|
6176
|
-
log('Using OpenAI SDK directly for tool call');
|
|
6177
|
-
toolCallPromise = window.openai.callTool(toolName, args);
|
|
6178
|
-
}
|
|
6179
|
-
// Priority 2: Use adapter (if it supports tool calls)
|
|
6180
|
-
else if (self.hasCapability('canCallTools')) {
|
|
6181
|
-
log('Using adapter for tool call');
|
|
6182
|
-
toolCallPromise = self.callTool(toolName, args);
|
|
6183
|
-
}
|
|
6184
|
-
// No tool call capability
|
|
6185
|
-
else {
|
|
6186
|
-
console.error('[frontmcp] Tool calls not supported on this platform (' + self.adapterId + ')');
|
|
6187
|
-
resetButton();
|
|
6188
|
-
target.dispatchEvent(new CustomEvent('tool:error', {
|
|
6189
|
-
detail: { name: toolName, args: args, error: 'Tool calls not supported on this platform' },
|
|
6190
|
-
bubbles: true
|
|
6191
|
-
}));
|
|
6192
|
-
e.preventDefault();
|
|
6193
|
-
return;
|
|
6194
|
-
}
|
|
6195
|
-
|
|
6196
|
-
// Handle the tool call result
|
|
6197
|
-
toolCallPromise.then(function(result) {
|
|
6198
|
-
log('Tool call succeeded: ' + toolName);
|
|
6199
|
-
resetButton();
|
|
6200
|
-
|
|
6201
|
-
// Update bridge state to trigger widget re-render
|
|
6202
|
-
// React isn't hydrated in OpenAI iframe, so useState doesn't work
|
|
6203
|
-
// Instead, we use the bridge's reactive state system
|
|
6204
|
-
if (result && window.__frontmcp && window.__frontmcp.bridge && typeof window.__frontmcp.bridge.setWidgetState === 'function') {
|
|
6205
|
-
var newData = result.structuredContent || result;
|
|
6206
|
-
log('Updating bridge state with new data');
|
|
6207
|
-
window.__frontmcp.bridge.setWidgetState(newData);
|
|
6208
|
-
}
|
|
6209
|
-
|
|
6210
|
-
// Dispatch success event
|
|
6211
|
-
target.dispatchEvent(new CustomEvent('tool:success', {
|
|
6212
|
-
detail: { name: toolName, args: args, result: result },
|
|
6213
|
-
bubbles: true
|
|
6214
|
-
}));
|
|
6215
|
-
}).catch(function(err) {
|
|
6216
|
-
console.error('[frontmcp] Tool call failed: ' + toolName, err);
|
|
6217
|
-
resetButton();
|
|
6218
|
-
// Dispatch error event
|
|
6219
|
-
target.dispatchEvent(new CustomEvent('tool:error', {
|
|
6220
|
-
detail: { name: toolName, args: args, error: err.message || err },
|
|
6221
|
-
bubbles: true
|
|
6222
|
-
}));
|
|
6223
|
-
});
|
|
6224
|
-
|
|
6225
|
-
// Prevent default behavior (e.g., form submission)
|
|
6226
|
-
e.preventDefault();
|
|
6227
|
-
return;
|
|
3385
|
+
/**
|
|
3386
|
+
* Emit a bridge event via CustomEvent.
|
|
3387
|
+
*/
|
|
3388
|
+
_emitEvent(type, payload) {
|
|
3389
|
+
if (typeof window !== "undefined" && typeof CustomEvent !== "undefined") {
|
|
3390
|
+
try {
|
|
3391
|
+
const event = new CustomEvent(type, { detail: payload });
|
|
3392
|
+
window.dispatchEvent(event);
|
|
3393
|
+
} catch {
|
|
6228
3394
|
}
|
|
6229
|
-
target = target.parentElement;
|
|
6230
3395
|
}
|
|
6231
|
-
}
|
|
3396
|
+
}
|
|
3397
|
+
/**
|
|
3398
|
+
* Log debug message if debugging is enabled.
|
|
3399
|
+
*/
|
|
3400
|
+
_log(message) {
|
|
3401
|
+
if (this._config.debug) {
|
|
3402
|
+
console.log(`[FrontMcpBridge] ${message}`);
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
6232
3405
|
};
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
return
|
|
6237
|
-
}
|
|
6238
|
-
function generatePlatformBundle(platform, options = {}) {
|
|
6239
|
-
const platformAdapters = {
|
|
6240
|
-
chatgpt: ["openai", "generic"],
|
|
6241
|
-
claude: ["claude", "generic"],
|
|
6242
|
-
gemini: ["gemini", "generic"],
|
|
6243
|
-
universal: ["openai", "ext-apps", "claude", "gemini", "generic"]
|
|
6244
|
-
};
|
|
6245
|
-
return generateBridgeIIFE({
|
|
6246
|
-
...options,
|
|
6247
|
-
adapters: platformAdapters[platform]
|
|
6248
|
-
});
|
|
3406
|
+
async function createBridge(config, registry) {
|
|
3407
|
+
const bridge = new FrontMcpBridge(config, registry);
|
|
3408
|
+
await bridge.initialize();
|
|
3409
|
+
return bridge;
|
|
6249
3410
|
}
|
|
6250
|
-
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
chatgpt: `<script>${generatePlatformBundle("chatgpt")}</script>`,
|
|
6254
|
-
claude: `<script>${generatePlatformBundle("claude")}</script>`,
|
|
6255
|
-
gemini: `<script>${generatePlatformBundle("gemini")}</script>`
|
|
6256
|
-
};
|
|
3411
|
+
|
|
3412
|
+
// libs/ui/src/bridge/runtime/index.ts
|
|
3413
|
+
var import_uipack = require("@frontmcp/uipack");
|
|
6257
3414
|
|
|
6258
3415
|
// libs/ui/src/web-components/core/base-element.ts
|
|
6259
3416
|
var import_validation2 = require("@frontmcp/uipack/validation");
|
|
@@ -7331,15 +4488,6 @@ function renderToStringSync(element) {
|
|
|
7331
4488
|
const ReactDOMServer = require("react-dom/server");
|
|
7332
4489
|
return ReactDOMServer.renderToStaticMarkup(element);
|
|
7333
4490
|
}
|
|
7334
|
-
function isReactAvailable() {
|
|
7335
|
-
try {
|
|
7336
|
-
require("react");
|
|
7337
|
-
require("react-dom/server");
|
|
7338
|
-
return true;
|
|
7339
|
-
} catch {
|
|
7340
|
-
return false;
|
|
7341
|
-
}
|
|
7342
|
-
}
|
|
7343
4491
|
|
|
7344
4492
|
// libs/ui/src/react/Card.tsx
|
|
7345
4493
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
@@ -7902,51 +5050,19 @@ function useOpenLink() {
|
|
|
7902
5050
|
);
|
|
7903
5051
|
}
|
|
7904
5052
|
|
|
7905
|
-
// libs/ui/src/react
|
|
7906
|
-
var
|
|
7907
|
-
var
|
|
7908
|
-
function
|
|
7909
|
-
|
|
7910
|
-
try {
|
|
7911
|
-
cachedReactDOMServer = require("react-dom/server");
|
|
7912
|
-
} catch {
|
|
7913
|
-
return null;
|
|
7914
|
-
}
|
|
7915
|
-
}
|
|
7916
|
-
return cachedReactDOMServer;
|
|
5053
|
+
// libs/ui/src/renderers/react.renderer.ts
|
|
5054
|
+
var import_renderers = require("@frontmcp/uipack/renderers");
|
|
5055
|
+
var VALID_JS_IDENTIFIER = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
5056
|
+
function isValidComponentName(name) {
|
|
5057
|
+
return VALID_JS_IDENTIFIER.test(name);
|
|
7917
5058
|
}
|
|
7918
|
-
function
|
|
7919
|
-
if (
|
|
7920
|
-
return
|
|
7921
|
-
}
|
|
7922
|
-
if (typeof children === "string") {
|
|
7923
|
-
return (0, import_utils3.escapeHtml)(children);
|
|
7924
|
-
}
|
|
7925
|
-
if (typeof children === "number") {
|
|
7926
|
-
return String(children);
|
|
7927
|
-
}
|
|
7928
|
-
if (typeof children === "boolean") {
|
|
7929
|
-
return "";
|
|
7930
|
-
}
|
|
7931
|
-
try {
|
|
7932
|
-
const server = getReactDOMServer();
|
|
7933
|
-
if (server) {
|
|
7934
|
-
return server.renderToStaticMarkup(children);
|
|
7935
|
-
}
|
|
7936
|
-
return String(children);
|
|
7937
|
-
} catch {
|
|
7938
|
-
return String(children);
|
|
5059
|
+
function sanitizeComponentName(name) {
|
|
5060
|
+
if (isValidComponentName(name)) {
|
|
5061
|
+
return name;
|
|
7939
5062
|
}
|
|
5063
|
+
const sanitized = name.replace(/[^a-zA-Z0-9_$]/g, "_").replace(/^[0-9]/, "_$&");
|
|
5064
|
+
return sanitized || "Component";
|
|
7940
5065
|
}
|
|
7941
|
-
function isBrowser() {
|
|
7942
|
-
return typeof window !== "undefined";
|
|
7943
|
-
}
|
|
7944
|
-
function isServer() {
|
|
7945
|
-
return typeof window === "undefined";
|
|
7946
|
-
}
|
|
7947
|
-
|
|
7948
|
-
// libs/ui/src/renderers/react.renderer.ts
|
|
7949
|
-
var import_renderers = require("@frontmcp/uipack/renderers");
|
|
7950
5066
|
var REACT_CDN = {
|
|
7951
5067
|
react: "https://esm.sh/react@19",
|
|
7952
5068
|
reactDom: "https://esm.sh/react-dom@19/client"
|
|
@@ -7960,13 +5076,6 @@ var ReactRenderer = class {
|
|
|
7960
5076
|
type = "react";
|
|
7961
5077
|
priority = 20;
|
|
7962
5078
|
// Higher priority than HTML
|
|
7963
|
-
/**
|
|
7964
|
-
* Lazy-loaded React modules.
|
|
7965
|
-
*/
|
|
7966
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7967
|
-
React = null;
|
|
7968
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7969
|
-
ReactDOMServer = null;
|
|
7970
5079
|
/**
|
|
7971
5080
|
* Check if this renderer can handle the given template.
|
|
7972
5081
|
*
|
|
@@ -8007,46 +5116,109 @@ var ReactRenderer = class {
|
|
|
8007
5116
|
throw new Error("Invalid template type for ReactRenderer");
|
|
8008
5117
|
}
|
|
8009
5118
|
/**
|
|
8010
|
-
* Render the template to HTML
|
|
5119
|
+
* Render the template to HTML for client-side rendering.
|
|
5120
|
+
*
|
|
5121
|
+
* Unlike SSR, this method generates HTML that will be rendered
|
|
5122
|
+
* client-side by React in the browser. No server-side React required.
|
|
5123
|
+
*
|
|
5124
|
+
* The generated HTML includes:
|
|
5125
|
+
* - A container div for the React root
|
|
5126
|
+
* - The component code (transpiled if needed)
|
|
5127
|
+
* - Props embedded as a data attribute
|
|
5128
|
+
* - A render script that initializes the component
|
|
8011
5129
|
*/
|
|
8012
|
-
async render(template, context,
|
|
8013
|
-
|
|
8014
|
-
|
|
5130
|
+
async render(template, context, _options) {
|
|
5131
|
+
const props = {
|
|
5132
|
+
input: context.input,
|
|
5133
|
+
output: context.output,
|
|
5134
|
+
structuredContent: context.structuredContent,
|
|
5135
|
+
helpers: context.helpers
|
|
5136
|
+
};
|
|
5137
|
+
const escapedProps = JSON.stringify(props).replace(/&/g, "&").replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
|
|
5138
|
+
const rootId = `frontmcp-react-${(0, import_renderers.hashString)(Date.now().toString()).slice(0, 8)}`;
|
|
5139
|
+
let componentCode;
|
|
5140
|
+
let componentName;
|
|
8015
5141
|
if (typeof template === "function") {
|
|
8016
|
-
|
|
5142
|
+
const rawName = template.name || "Component";
|
|
5143
|
+
componentName = sanitizeComponentName(rawName);
|
|
5144
|
+
componentCode = `
|
|
5145
|
+
// Component should be registered via window.__frontmcp_components['${componentName}']
|
|
5146
|
+
(function() {
|
|
5147
|
+
if (!window.__frontmcp_components || !window.__frontmcp_components['${componentName}']) {
|
|
5148
|
+
console.error('[FrontMCP] Component "${componentName}" not registered. Use buildHydrationScript() to register components.');
|
|
5149
|
+
}
|
|
5150
|
+
})();
|
|
5151
|
+
`;
|
|
8017
5152
|
} else if (typeof template === "string") {
|
|
8018
5153
|
const transpiled = await this.transpile(template);
|
|
8019
|
-
const
|
|
8020
|
-
|
|
8021
|
-
|
|
8022
|
-
|
|
8023
|
-
Component = await (0, import_renderers.executeTranspiledCode)(transpiled.code, {
|
|
8024
|
-
// Provide any additional MDX components if specified
|
|
8025
|
-
...options?.mdxComponents
|
|
8026
|
-
});
|
|
8027
|
-
import_renderers.transpileCache.setByKey(`exec:${transpiled.hash}`, {
|
|
8028
|
-
code: Component,
|
|
8029
|
-
hash: transpiled.hash,
|
|
8030
|
-
cached: false
|
|
8031
|
-
});
|
|
8032
|
-
}
|
|
5154
|
+
const match = transpiled.code.match(/function\s+(\w+)/);
|
|
5155
|
+
const rawName = match?.[1] || "Widget";
|
|
5156
|
+
componentName = sanitizeComponentName(rawName);
|
|
5157
|
+
componentCode = transpiled.code;
|
|
8033
5158
|
} else {
|
|
8034
5159
|
throw new Error("Invalid template type for ReactRenderer");
|
|
8035
5160
|
}
|
|
8036
|
-
const
|
|
8037
|
-
|
|
8038
|
-
|
|
8039
|
-
|
|
8040
|
-
|
|
5161
|
+
const html = `
|
|
5162
|
+
<div id="${rootId}" data-frontmcp-react data-component="${componentName}" data-props='${escapedProps}'>
|
|
5163
|
+
<div class="flex items-center justify-center p-4 text-gray-500">
|
|
5164
|
+
<svg class="animate-spin h-5 w-5 mr-2" viewBox="0 0 24 24">
|
|
5165
|
+
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" fill="none"></circle>
|
|
5166
|
+
<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>
|
|
5167
|
+
</svg>
|
|
5168
|
+
Loading...
|
|
5169
|
+
</div>
|
|
5170
|
+
</div>
|
|
5171
|
+
<script type="module">
|
|
5172
|
+
(function() {
|
|
5173
|
+
${componentCode}
|
|
5174
|
+
|
|
5175
|
+
// Wait for React to be available
|
|
5176
|
+
function waitForReact(callback, maxAttempts) {
|
|
5177
|
+
var attempts = 0;
|
|
5178
|
+
var check = function() {
|
|
5179
|
+
if (typeof React !== 'undefined' && typeof ReactDOM !== 'undefined') {
|
|
5180
|
+
callback();
|
|
5181
|
+
} else if (attempts < maxAttempts) {
|
|
5182
|
+
attempts++;
|
|
5183
|
+
setTimeout(check, 50);
|
|
5184
|
+
} else {
|
|
5185
|
+
console.error('[FrontMCP] React not loaded after ' + maxAttempts + ' attempts');
|
|
5186
|
+
}
|
|
8041
5187
|
};
|
|
8042
|
-
|
|
8043
|
-
|
|
8044
|
-
|
|
8045
|
-
|
|
8046
|
-
|
|
8047
|
-
|
|
5188
|
+
check();
|
|
5189
|
+
}
|
|
5190
|
+
|
|
5191
|
+
waitForReact(function() {
|
|
5192
|
+
try {
|
|
5193
|
+
var root = document.getElementById('${rootId}');
|
|
5194
|
+
if (!root) return;
|
|
5195
|
+
|
|
5196
|
+
var propsJson = root.getAttribute('data-props');
|
|
5197
|
+
var props = propsJson ? JSON.parse(propsJson.replace(/&/g, '&').replace(/'/g, "'").replace(/</g, '<').replace(/>/g, '>')) : {};
|
|
5198
|
+
|
|
5199
|
+
// Get the component
|
|
5200
|
+
var Component = ${componentName};
|
|
5201
|
+
|
|
5202
|
+
// Check if it's registered globally
|
|
5203
|
+
if (typeof Component === 'undefined' && window.__frontmcp_components) {
|
|
5204
|
+
Component = window.__frontmcp_components['${componentName}'];
|
|
5205
|
+
}
|
|
5206
|
+
|
|
5207
|
+
if (typeof Component === 'function') {
|
|
5208
|
+
var element = React.createElement(Component, props);
|
|
5209
|
+
var reactRoot = ReactDOM.createRoot(root);
|
|
5210
|
+
reactRoot.render(element);
|
|
5211
|
+
} else {
|
|
5212
|
+
console.error('[FrontMCP] Component "${componentName}" not found');
|
|
5213
|
+
}
|
|
5214
|
+
} catch (err) {
|
|
5215
|
+
console.error('[FrontMCP] React render error:', err);
|
|
8048
5216
|
}
|
|
8049
|
-
|
|
5217
|
+
}, 100);
|
|
5218
|
+
})();
|
|
5219
|
+
</script>
|
|
5220
|
+
`;
|
|
5221
|
+
return html.trim();
|
|
8050
5222
|
}
|
|
8051
5223
|
/**
|
|
8052
5224
|
* Get runtime scripts for client-side functionality.
|
|
@@ -8067,54 +5239,8 @@ var ReactRenderer = class {
|
|
|
8067
5239
|
isInline: false
|
|
8068
5240
|
};
|
|
8069
5241
|
}
|
|
8070
|
-
/**
|
|
8071
|
-
* Load React and ReactDOMServer modules.
|
|
8072
|
-
*/
|
|
8073
|
-
async loadReact() {
|
|
8074
|
-
if (this.React && this.ReactDOMServer) {
|
|
8075
|
-
return;
|
|
8076
|
-
}
|
|
8077
|
-
try {
|
|
8078
|
-
this.React = await import("react");
|
|
8079
|
-
this.ReactDOMServer = await import("react-dom/server");
|
|
8080
|
-
} catch {
|
|
8081
|
-
throw new Error("React is required for ReactRenderer. Install react and react-dom: npm install react react-dom");
|
|
8082
|
-
}
|
|
8083
|
-
}
|
|
8084
5242
|
};
|
|
8085
5243
|
var reactRenderer = new ReactRenderer();
|
|
8086
|
-
function buildHydrationScript() {
|
|
8087
|
-
return `
|
|
8088
|
-
<script>
|
|
8089
|
-
(function() {
|
|
8090
|
-
// Wait for React to be available
|
|
8091
|
-
if (typeof React === 'undefined' || typeof ReactDOM === 'undefined') {
|
|
8092
|
-
console.warn('[FrontMCP] React not available for hydration');
|
|
8093
|
-
return;
|
|
8094
|
-
}
|
|
8095
|
-
|
|
8096
|
-
// Find all elements marked for hydration
|
|
8097
|
-
document.querySelectorAll('[data-hydrate]').forEach(function(root) {
|
|
8098
|
-
var componentName = root.getAttribute('data-hydrate');
|
|
8099
|
-
var propsJson = root.getAttribute('data-props');
|
|
8100
|
-
var props = propsJson ? JSON.parse(propsJson) : {};
|
|
8101
|
-
|
|
8102
|
-
// Look for the component in the global scope
|
|
8103
|
-
if (window.__frontmcp_components && window.__frontmcp_components[componentName]) {
|
|
8104
|
-
try {
|
|
8105
|
-
ReactDOM.hydrateRoot(root, React.createElement(
|
|
8106
|
-
window.__frontmcp_components[componentName],
|
|
8107
|
-
props
|
|
8108
|
-
));
|
|
8109
|
-
} catch (e) {
|
|
8110
|
-
console.error('[FrontMCP] Hydration failed for', componentName, e);
|
|
8111
|
-
}
|
|
8112
|
-
}
|
|
8113
|
-
});
|
|
8114
|
-
})();
|
|
8115
|
-
</script>
|
|
8116
|
-
`;
|
|
8117
|
-
}
|
|
8118
5244
|
|
|
8119
5245
|
// libs/ui/src/renderers/react.adapter.ts
|
|
8120
5246
|
var mountedRoots = /* @__PURE__ */ new WeakMap();
|
|
@@ -8404,22 +5530,15 @@ async function loadReactAdapter() {
|
|
|
8404
5530
|
badgeGroup,
|
|
8405
5531
|
baseLayout,
|
|
8406
5532
|
betaBadge,
|
|
8407
|
-
buildHydrationScript,
|
|
8408
5533
|
busyDot,
|
|
8409
5534
|
button,
|
|
8410
5535
|
buttonGroup,
|
|
8411
5536
|
card,
|
|
8412
5537
|
cardGroup,
|
|
8413
5538
|
checkbox,
|
|
8414
|
-
circularProgress,
|
|
8415
|
-
codePreview,
|
|
8416
5539
|
confirmModal,
|
|
8417
|
-
consentDeniedPage,
|
|
8418
5540
|
consentLayout,
|
|
8419
5541
|
consentLayoutBuilder,
|
|
8420
|
-
consentPage,
|
|
8421
|
-
consentSuccessPage,
|
|
8422
|
-
contentSkeleton,
|
|
8423
5542
|
createBridge,
|
|
8424
5543
|
createLayoutBuilder,
|
|
8425
5544
|
createReactAdapter,
|
|
@@ -8432,10 +5551,8 @@ async function loadReactAdapter() {
|
|
|
8432
5551
|
errorBadge,
|
|
8433
5552
|
errorLayout,
|
|
8434
5553
|
errorLayoutBuilder,
|
|
8435
|
-
errorPage,
|
|
8436
5554
|
escapeHtml,
|
|
8437
5555
|
featureList,
|
|
8438
|
-
forbiddenPage,
|
|
8439
5556
|
form,
|
|
8440
5557
|
formActions,
|
|
8441
5558
|
formRow,
|
|
@@ -8444,33 +5561,23 @@ async function loadReactAdapter() {
|
|
|
8444
5561
|
generatePlatformBundle,
|
|
8445
5562
|
ghostButton,
|
|
8446
5563
|
hiddenInput,
|
|
8447
|
-
imagePreview,
|
|
8448
5564
|
inactiveBadge,
|
|
8449
5565
|
infoAlert,
|
|
8450
5566
|
input,
|
|
8451
|
-
isBrowser,
|
|
8452
|
-
isReactAvailable,
|
|
8453
|
-
isServer,
|
|
8454
5567
|
linkButton,
|
|
8455
5568
|
loadReactAdapter,
|
|
8456
5569
|
loadingLayout,
|
|
8457
|
-
maintenancePage,
|
|
8458
5570
|
modal,
|
|
8459
5571
|
modalTrigger,
|
|
8460
5572
|
newBadge,
|
|
8461
|
-
notFoundPage,
|
|
8462
|
-
oauthErrorPage,
|
|
8463
5573
|
offlineDot,
|
|
8464
|
-
offlinePage,
|
|
8465
5574
|
onlineDot,
|
|
8466
5575
|
outlineButton,
|
|
8467
5576
|
pagination,
|
|
8468
5577
|
pendingBadge,
|
|
8469
5578
|
permissionList,
|
|
8470
5579
|
primaryButton,
|
|
8471
|
-
progressBar,
|
|
8472
5580
|
radioGroup,
|
|
8473
|
-
rateLimitPage,
|
|
8474
5581
|
reactRenderer,
|
|
8475
5582
|
registerAdapter,
|
|
8476
5583
|
registerAllComponents,
|
|
@@ -8488,26 +5595,14 @@ async function loadReactAdapter() {
|
|
|
8488
5595
|
renderButtonSync,
|
|
8489
5596
|
renderCard,
|
|
8490
5597
|
renderCardSync,
|
|
8491
|
-
renderChildrenToString,
|
|
8492
|
-
renderToString,
|
|
8493
|
-
renderToStringSync,
|
|
8494
|
-
resourceItem,
|
|
8495
|
-
resourceList,
|
|
8496
|
-
resourceWidget,
|
|
8497
5598
|
secondaryButton,
|
|
8498
5599
|
select,
|
|
8499
|
-
serverErrorPage,
|
|
8500
|
-
sessionExpiredPage,
|
|
8501
|
-
skeleton,
|
|
8502
|
-
statusIndicator,
|
|
8503
|
-
stepProgress,
|
|
8504
5600
|
successAlert,
|
|
8505
5601
|
successLayout,
|
|
8506
5602
|
table,
|
|
8507
5603
|
textarea,
|
|
8508
5604
|
toast,
|
|
8509
5605
|
toastContainer,
|
|
8510
|
-
unauthorizedPage,
|
|
8511
5606
|
useCallTool,
|
|
8512
5607
|
useCapability,
|
|
8513
5608
|
useDisplayMode,
|