@seed-ship/mcp-ui-solid 6.20.0 → 6.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +144 -0
- package/README.md +120 -3
- package/dist/components/IframeFallbackLink.cjs +37 -0
- package/dist/components/IframeFallbackLink.cjs.map +1 -0
- package/dist/components/IframeFallbackLink.d.ts +23 -0
- package/dist/components/IframeFallbackLink.d.ts.map +1 -0
- package/dist/components/IframeFallbackLink.js +37 -0
- package/dist/components/IframeFallbackLink.js.map +1 -0
- package/dist/components/UIResourceRenderer.cjs +150 -127
- package/dist/components/UIResourceRenderer.cjs.map +1 -1
- package/dist/components/UIResourceRenderer.d.ts.map +1 -1
- package/dist/components/UIResourceRenderer.js +151 -128
- package/dist/components/UIResourceRenderer.js.map +1 -1
- package/dist/components/VideoRenderer.cjs +45 -19
- package/dist/components/VideoRenderer.cjs.map +1 -1
- package/dist/components/VideoRenderer.d.ts.map +1 -1
- package/dist/components/VideoRenderer.js +45 -19
- package/dist/components/VideoRenderer.js.map +1 -1
- package/dist/context/MCPUIConfigContext.cjs +29 -0
- package/dist/context/MCPUIConfigContext.cjs.map +1 -0
- package/dist/context/MCPUIConfigContext.d.ts +124 -0
- package/dist/context/MCPUIConfigContext.d.ts.map +1 -0
- package/dist/context/MCPUIConfigContext.js +29 -0
- package/dist/context/MCPUIConfigContext.js.map +1 -0
- package/dist/context/MCPUIStringsContext.cjs +3 -1
- package/dist/context/MCPUIStringsContext.cjs.map +1 -1
- package/dist/context/MCPUIStringsContext.d.ts +7 -0
- package/dist/context/MCPUIStringsContext.d.ts.map +1 -1
- package/dist/context/MCPUIStringsContext.js +3 -1
- package/dist/context/MCPUIStringsContext.js.map +1 -1
- package/dist/index.cjs +6 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/services/index.d.ts +1 -1
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/validation.cjs +11 -10
- package/dist/services/validation.cjs.map +1 -1
- package/dist/services/validation.d.ts +20 -0
- package/dist/services/validation.d.ts.map +1 -1
- package/dist/services/validation.js +11 -10
- package/dist/services/validation.js.map +1 -1
- package/dist/utils/iframe-coep.cjs +30 -0
- package/dist/utils/iframe-coep.cjs.map +1 -0
- package/dist/utils/iframe-coep.d.ts +46 -0
- package/dist/utils/iframe-coep.d.ts.map +1 -0
- package/dist/utils/iframe-coep.js +30 -0
- package/dist/utils/iframe-coep.js.map +1 -0
- package/dist/validation.cjs +2 -0
- package/dist/validation.cjs.map +1 -1
- package/dist/validation.d.cts +1 -1
- package/dist/validation.d.ts +1 -1
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +3 -1
- package/package.json +1 -1
- package/src/components/IframeFallbackLink.tsx +57 -0
- package/src/components/UIResourceRenderer.tsx +31 -4
- package/src/components/VideoRenderer.tsx +20 -0
- package/src/context/MCPUIConfigContext.tsx +151 -0
- package/src/context/MCPUIStringsContext.tsx +10 -0
- package/src/index.ts +15 -0
- package/src/services/index.ts +1 -0
- package/src/services/validation.ts +38 -16
- package/src/solid-jsx-attrs.d.ts +24 -0
- package/src/utils/iframe-coep.ts +74 -0
- package/src/validation.ts +4 -0
|
@@ -4,7 +4,10 @@ const web = require("solid-js/web");
|
|
|
4
4
|
const solidJs = require("solid-js");
|
|
5
5
|
const ExpandableWrapper = require("./ExpandableWrapper.cjs");
|
|
6
6
|
const MCPUIStringsContext = require("../context/MCPUIStringsContext.cjs");
|
|
7
|
-
|
|
7
|
+
const MCPUIConfigContext = require("../context/MCPUIConfigContext.cjs");
|
|
8
|
+
const iframeCoep = require("../utils/iframe-coep.cjs");
|
|
9
|
+
const IframeFallbackLink = require("./IframeFallbackLink.cjs");
|
|
10
|
+
var _tmpl$ = /* @__PURE__ */ web.template(`<div class="px-4 py-3 border-b border-gray-200 dark:border-gray-700"><h3 class="text-sm font-semibold text-gray-900 dark:text-white">`), _tmpl$2 = /* @__PURE__ */ web.template(`<iframe class="absolute inset-0 w-full h-full"allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"allowfullscreen loading=lazy>`, true, false, false), _tmpl$3 = /* @__PURE__ */ web.template(`<div class="px-4 py-3 border-t border-gray-200 dark:border-gray-700 flex-shrink-0"><p class="text-sm text-gray-600 dark:text-gray-400">`), _tmpl$4 = /* @__PURE__ */ web.template(`<div><!$><!/><div></div><!$><!/><!$><!/>`), _tmpl$5 = /* @__PURE__ */ web.template(`<video playsinline class="absolute inset-0 w-full h-full object-contain"><track kind=captions><!$><!/>`);
|
|
8
11
|
function parseVideoUrl(url) {
|
|
9
12
|
const youtubeMatch = url.match(/(?:youtube\.com\/(?:watch\?v=|embed\/|v\/)|youtu\.be\/)([a-zA-Z0-9_-]{11})/);
|
|
10
13
|
if (youtubeMatch) {
|
|
@@ -26,6 +29,7 @@ function parseVideoUrl(url) {
|
|
|
26
29
|
}
|
|
27
30
|
const VideoRenderer = (props) => {
|
|
28
31
|
const strings = MCPUIStringsContext.useMCPUIStrings();
|
|
32
|
+
const config = MCPUIConfigContext.useMCPUIConfig();
|
|
29
33
|
const params = () => {
|
|
30
34
|
var _a;
|
|
31
35
|
return props.params || ((_a = props.component) == null ? void 0 : _a.params);
|
|
@@ -99,7 +103,7 @@ const VideoRenderer = (props) => {
|
|
|
99
103
|
return props.toolbarVariant;
|
|
100
104
|
},
|
|
101
105
|
get children() {
|
|
102
|
-
var _el$ = web.getNextElement(_tmpl$4), _el$8 = _el$.firstChild, [_el$9, _co$] = web.getNextMarker(_el$8.nextSibling), _el$4 = _el$9.nextSibling, _el$0 = _el$4.nextSibling, [_el$1, _co$2] = web.getNextMarker(_el$0.nextSibling);
|
|
106
|
+
var _el$ = web.getNextElement(_tmpl$4), _el$8 = _el$.firstChild, [_el$9, _co$] = web.getNextMarker(_el$8.nextSibling), _el$4 = _el$9.nextSibling, _el$0 = _el$4.nextSibling, [_el$1, _co$2] = web.getNextMarker(_el$0.nextSibling), _el$10 = _el$1.nextSibling, [_el$11, _co$3] = web.getNextMarker(_el$10.nextSibling);
|
|
103
107
|
web.insert(_el$, web.createComponent(solidJs.Show, {
|
|
104
108
|
get when() {
|
|
105
109
|
var _a;
|
|
@@ -119,18 +123,18 @@ const VideoRenderer = (props) => {
|
|
|
119
123
|
return (
|
|
120
124
|
// Direct video file
|
|
121
125
|
(() => {
|
|
122
|
-
var _el$
|
|
123
|
-
_el$
|
|
124
|
-
web.insert(_el$
|
|
126
|
+
var _el$12 = web.getNextElement(_tmpl$5), _el$13 = _el$12.firstChild, _el$14 = _el$13.nextSibling, [_el$15, _co$4] = web.getNextMarker(_el$14.nextSibling);
|
|
127
|
+
_el$12.addEventListener("error", handleVideoError);
|
|
128
|
+
web.insert(_el$12, () => strings.videoUnsupported, _el$15, _co$4);
|
|
125
129
|
web.effect((_p$) => {
|
|
126
130
|
var _a, _b, _c, _d, _e, _f;
|
|
127
|
-
var _v$
|
|
128
|
-
_v$
|
|
129
|
-
_v$
|
|
130
|
-
_v$
|
|
131
|
-
_v$
|
|
132
|
-
_v$
|
|
133
|
-
_v$
|
|
131
|
+
var _v$6 = (_a = params()) == null ? void 0 : _a.url, _v$7 = (_b = params()) == null ? void 0 : _b.poster, _v$8 = (_c = params()) == null ? void 0 : _c.autoplay, _v$9 = ((_d = params()) == null ? void 0 : _d.controls) !== false, _v$0 = (_e = params()) == null ? void 0 : _e.loop, _v$1 = (_f = params()) == null ? void 0 : _f.muted;
|
|
132
|
+
_v$6 !== _p$.e && web.setAttribute(_el$12, "src", _p$.e = _v$6);
|
|
133
|
+
_v$7 !== _p$.t && web.setAttribute(_el$12, "poster", _p$.t = _v$7);
|
|
134
|
+
_v$8 !== _p$.a && web.setProperty(_el$12, "autoplay", _p$.a = _v$8);
|
|
135
|
+
_v$9 !== _p$.o && web.setProperty(_el$12, "controls", _p$.o = _v$9);
|
|
136
|
+
_v$0 !== _p$.i && web.setProperty(_el$12, "loop", _p$.i = _v$0);
|
|
137
|
+
_v$1 !== _p$.n && web.setProperty(_el$12, "muted", _p$.n = _v$1);
|
|
134
138
|
return _p$;
|
|
135
139
|
}, {
|
|
136
140
|
e: void 0,
|
|
@@ -140,7 +144,7 @@ const VideoRenderer = (props) => {
|
|
|
140
144
|
i: void 0,
|
|
141
145
|
n: void 0
|
|
142
146
|
});
|
|
143
|
-
return _el$
|
|
147
|
+
return _el$12;
|
|
144
148
|
})()
|
|
145
149
|
);
|
|
146
150
|
},
|
|
@@ -148,17 +152,39 @@ const VideoRenderer = (props) => {
|
|
|
148
152
|
var _el$5 = web.getNextElement(_tmpl$2);
|
|
149
153
|
web.effect((_p$) => {
|
|
150
154
|
var _a;
|
|
151
|
-
var _v$ = embedUrl(), _v$2 = ((_a = params()) == null ? void 0 : _a.title) || strings.videoTitle
|
|
155
|
+
var _v$ = embedUrl(), _v$2 = ((_a = params()) == null ? void 0 : _a.title) || strings.videoTitle, _v$3 = (
|
|
156
|
+
// No sandbox here: a YouTube/Vimeo embed keeps its provider
|
|
157
|
+
// cookies, so `'auto'` leaves it alone and a COEP host opts in
|
|
158
|
+
// with `iframeCredentialless: 'always'`.
|
|
159
|
+
iframeCoep.shouldSetCredentialless(embedUrl(), config, {
|
|
160
|
+
sandboxedWithoutSameOrigin: false
|
|
161
|
+
}) || void 0
|
|
162
|
+
);
|
|
152
163
|
_v$ !== _p$.e && web.setAttribute(_el$5, "src", _p$.e = _v$);
|
|
153
164
|
_v$2 !== _p$.t && web.setAttribute(_el$5, "title", _p$.t = _v$2);
|
|
165
|
+
_v$3 !== _p$.a && web.setAttribute(_el$5, "credentialless", _p$.a = _v$3);
|
|
154
166
|
return _p$;
|
|
155
167
|
}, {
|
|
156
168
|
e: void 0,
|
|
157
|
-
t: void 0
|
|
169
|
+
t: void 0,
|
|
170
|
+
a: void 0
|
|
158
171
|
});
|
|
159
172
|
return _el$5;
|
|
160
173
|
}
|
|
161
174
|
}));
|
|
175
|
+
web.insert(_el$, web.createComponent(solidJs.Show, {
|
|
176
|
+
get when() {
|
|
177
|
+
return embedUrl();
|
|
178
|
+
},
|
|
179
|
+
get children() {
|
|
180
|
+
return web.createComponent(IframeFallbackLink.IframeFallbackLink, {
|
|
181
|
+
get url() {
|
|
182
|
+
var _a;
|
|
183
|
+
return ((_a = params()) == null ? void 0 : _a.url) || "";
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}), _el$1, _co$2);
|
|
162
188
|
web.insert(_el$, web.createComponent(solidJs.Show, {
|
|
163
189
|
get when() {
|
|
164
190
|
var _a;
|
|
@@ -169,11 +195,11 @@ const VideoRenderer = (props) => {
|
|
|
169
195
|
web.insert(_el$7, () => params().caption);
|
|
170
196
|
return _el$6;
|
|
171
197
|
}
|
|
172
|
-
}), _el$
|
|
198
|
+
}), _el$11, _co$3);
|
|
173
199
|
web.effect((_p$) => {
|
|
174
|
-
var _v$
|
|
175
|
-
_v$
|
|
176
|
-
_v$
|
|
200
|
+
var _v$4 = `w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden ${isExpanded() ? "flex-1 min-h-0 flex flex-col" : ""}`, _v$5 = `relative bg-black ${isExpanded() ? "flex-1 min-h-0" : aspectClass()}`;
|
|
201
|
+
_v$4 !== _p$.e && web.className(_el$, _p$.e = _v$4);
|
|
202
|
+
_v$5 !== _p$.t && web.className(_el$4, _p$.t = _v$5);
|
|
177
203
|
return _p$;
|
|
178
204
|
}, {
|
|
179
205
|
e: void 0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoRenderer.cjs","sources":["../../src/components/VideoRenderer.tsx"],"sourcesContent":["/**\n * VideoRenderer - Video embed component\n * Sprint 5: Media Components\n *\n * Supports YouTube, Vimeo, and direct video files\n */\n\nimport { Component, createMemo, Show } from 'solid-js'\nimport type { UIComponent, VideoComponentParams } from '../types'\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper'\nimport { useMCPUIStrings } from '../context/MCPUIStringsContext'\n\nexport interface VideoRendererProps {\n /**\n * UIComponent containing video params\n */\n component?: UIComponent\n\n /**\n * Direct video params (alternative to component)\n */\n params?: VideoComponentParams\n\n /**\n * Error callback\n */\n onError?: (error: Error) => void\n\n /**\n * Forwarded to the underlying `<ExpandableWrapper>` (v6.3.1).\n * @see ExpandableWrapperProps.toolbarVariant\n */\n toolbarVariant?: 'hover' | 'always-visible'\n}\n\n/**\n * Video provider type\n */\ntype VideoProvider = 'youtube' | 'vimeo' | 'direct'\n\n/**\n * Parsed video info\n */\ninterface VideoInfo {\n provider: VideoProvider\n videoId?: string\n}\n\n/**\n * Extract video ID and provider from URL\n */\nfunction parseVideoUrl(url: string): VideoInfo {\n // YouTube patterns:\n // - youtube.com/watch?v=VIDEO_ID\n // - youtube.com/embed/VIDEO_ID\n // - youtube.com/v/VIDEO_ID\n // - youtu.be/VIDEO_ID\n const youtubeMatch = url.match(\n /(?:youtube\\.com\\/(?:watch\\?v=|embed\\/|v\\/)|youtu\\.be\\/)([a-zA-Z0-9_-]{11})/\n )\n if (youtubeMatch) {\n return { provider: 'youtube', videoId: youtubeMatch[1] }\n }\n\n // Vimeo patterns:\n // - vimeo.com/VIDEO_ID\n // - player.vimeo.com/video/VIDEO_ID\n const vimeoMatch = url.match(/(?:vimeo\\.com\\/|player\\.vimeo\\.com\\/video\\/)(\\d+)/)\n if (vimeoMatch) {\n return { provider: 'vimeo', videoId: vimeoMatch[1] }\n }\n\n // Direct video file\n return { provider: 'direct' }\n}\n\nexport const VideoRenderer: Component<VideoRendererProps> = (props) => {\n const strings = useMCPUIStrings()\n const params = () => props.params || (props.component?.params as VideoComponentParams)\n const isExpanded = useExpanded()\n\n const videoInfo = createMemo(() => parseVideoUrl(params()?.url || ''))\n\n const embedUrl = createMemo(() => {\n const info = videoInfo()\n const p = params()\n\n if (!p?.url) return null\n\n switch (info.provider) {\n case 'youtube': {\n const ytParams = new URLSearchParams({\n autoplay: p.autoplay ? '1' : '0',\n controls: p.controls !== false ? '1' : '0',\n loop: p.loop ? '1' : '0',\n mute: p.muted ? '1' : '0',\n })\n if (p.startTime) {\n ytParams.set('start', String(p.startTime))\n }\n // Use youtube-nocookie.com for privacy\n return `https://www.youtube-nocookie.com/embed/${info.videoId}?${ytParams}`\n }\n\n case 'vimeo': {\n const vParams = new URLSearchParams({\n autoplay: p.autoplay ? '1' : '0',\n loop: p.loop ? '1' : '0',\n muted: p.muted ? '1' : '0',\n })\n return `https://player.vimeo.com/video/${info.videoId}?${vParams}`\n }\n\n default:\n return null\n }\n })\n\n const aspectClass = () => {\n switch (params()?.aspectRatio) {\n case '1:1':\n return 'aspect-square'\n case '4:3':\n return 'aspect-[4/3]'\n case '21:9':\n return 'aspect-[21/9]'\n default:\n return 'aspect-video' // 16:9\n }\n }\n\n const handleVideoError = (e: Event) => {\n const error = new Error('Video failed to load')\n console.error('Video error:', e)\n props.onError?.(error)\n }\n\n return (\n <ExpandableWrapper\n title={params()?.title || strings.videoTitle}\n copyData={params()?.url || ''}\n copyLabel={strings.videoCopy}\n toolbarVariant={props.toolbarVariant}\n >\n <div class={`w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden ${\n isExpanded() ? 'flex-1 min-h-0 flex flex-col' : ''\n }`}>\n {/* Title */}\n <Show when={params()?.title}>\n <div class=\"px-4 py-3 border-b border-gray-200 dark:border-gray-700\">\n <h3 class=\"text-sm font-semibold text-gray-900 dark:text-white\">{params()!.title}</h3>\n </div>\n </Show>\n\n {/* Video Container — when expanded, fill remaining space (override aspect ratio) */}\n <div class={`relative bg-black ${isExpanded() ? 'flex-1 min-h-0' : aspectClass()}`}>\n <Show\n when={embedUrl()}\n fallback={\n // Direct video file\n <video\n src={params()?.url}\n poster={params()?.poster}\n autoplay={params()?.autoplay}\n controls={params()?.controls !== false}\n loop={params()?.loop}\n muted={params()?.muted}\n playsinline\n class=\"absolute inset-0 w-full h-full object-contain\"\n onError={handleVideoError}\n >\n <track kind=\"captions\" />\n {strings.videoUnsupported}\n </video>\n }\n >\n {/* YouTube/Vimeo embed */}\n <iframe\n src={embedUrl()!}\n title={params()?.title || strings.videoTitle}\n class=\"absolute inset-0 w-full h-full\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowfullscreen\n loading=\"lazy\"\n />\n </Show>\n </div>\n\n {/* Caption */}\n <Show when={params()?.caption}>\n <div class=\"px-4 py-3 border-t border-gray-200 dark:border-gray-700 flex-shrink-0\">\n <p class=\"text-sm text-gray-600 dark:text-gray-400\">{params()!.caption}</p>\n </div>\n </Show>\n </div>\n </ExpandableWrapper>\n )\n}\n\n/**\n * Check if a URL is from a supported video provider\n */\nexport function isSupportedVideoUrl(url: string): boolean {\n const info = parseVideoUrl(url)\n return info.provider !== 'direct' || url.match(/\\.(mp4|webm|ogg|mov)$/i) !== null\n}\n\n/**\n * Get video provider from URL\n */\nexport function getVideoProvider(url: string): VideoProvider {\n return parseVideoUrl(url).provider\n}\n"],"names":["parseVideoUrl","url","youtubeMatch","match","provider","videoId","vimeoMatch","VideoRenderer","props","strings","useMCPUIStrings","params","component","isExpanded","useExpanded","videoInfo","createMemo","embedUrl","info","p","ytParams","URLSearchParams","autoplay","controls","loop","mute","muted","startTime","set","String","vParams","aspectClass","aspectRatio","handleVideoError","e","error","Error","console","onError","_$createComponent","ExpandableWrapper","title","videoTitle","copyData","copyLabel","videoCopy","toolbarVariant","children","_el$","_$getNextElement","_tmpl$4","_el$8","firstChild","_el$9","_co$","_$getNextMarker","nextSibling","_el$4","_el$0","_el$1","_co$2","_$insert","Show","when","_el$2","_tmpl$","_el$3","fallback","_el$10","_tmpl$5","_el$11","_el$12","_el$13","_co$3","addEventListener","videoUnsupported","_$effect","_p$","_v$5","_v$6","poster","_v$7","_v$8","_v$9","_v$0","_$setAttribute","t","a","_$setProperty","o","i","n","undefined","_el$5","_tmpl$2","_v$","_v$2","caption","_el$6","_tmpl$3","_el$7","_v$3","_v$4","_$className","isSupportedVideoUrl","getVideoProvider"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VideoRenderer.cjs","sources":["../../src/components/VideoRenderer.tsx"],"sourcesContent":["/**\n * VideoRenderer - Video embed component\n * Sprint 5: Media Components\n *\n * Supports YouTube, Vimeo, and direct video files\n */\n\nimport { Component, createMemo, Show } from 'solid-js'\nimport type { UIComponent, VideoComponentParams } from '../types'\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper'\nimport { useMCPUIStrings } from '../context/MCPUIStringsContext'\nimport { useMCPUIConfig } from '../context/MCPUIConfigContext'\nimport { shouldSetCredentialless } from '../utils/iframe-coep'\nimport { IframeFallbackLink } from './IframeFallbackLink'\n\nexport interface VideoRendererProps {\n /**\n * UIComponent containing video params\n */\n component?: UIComponent\n\n /**\n * Direct video params (alternative to component)\n */\n params?: VideoComponentParams\n\n /**\n * Error callback\n */\n onError?: (error: Error) => void\n\n /**\n * Forwarded to the underlying `<ExpandableWrapper>` (v6.3.1).\n * @see ExpandableWrapperProps.toolbarVariant\n */\n toolbarVariant?: 'hover' | 'always-visible'\n}\n\n/**\n * Video provider type\n */\ntype VideoProvider = 'youtube' | 'vimeo' | 'direct'\n\n/**\n * Parsed video info\n */\ninterface VideoInfo {\n provider: VideoProvider\n videoId?: string\n}\n\n/**\n * Extract video ID and provider from URL\n */\nfunction parseVideoUrl(url: string): VideoInfo {\n // YouTube patterns:\n // - youtube.com/watch?v=VIDEO_ID\n // - youtube.com/embed/VIDEO_ID\n // - youtube.com/v/VIDEO_ID\n // - youtu.be/VIDEO_ID\n const youtubeMatch = url.match(\n /(?:youtube\\.com\\/(?:watch\\?v=|embed\\/|v\\/)|youtu\\.be\\/)([a-zA-Z0-9_-]{11})/\n )\n if (youtubeMatch) {\n return { provider: 'youtube', videoId: youtubeMatch[1] }\n }\n\n // Vimeo patterns:\n // - vimeo.com/VIDEO_ID\n // - player.vimeo.com/video/VIDEO_ID\n const vimeoMatch = url.match(/(?:vimeo\\.com\\/|player\\.vimeo\\.com\\/video\\/)(\\d+)/)\n if (vimeoMatch) {\n return { provider: 'vimeo', videoId: vimeoMatch[1] }\n }\n\n // Direct video file\n return { provider: 'direct' }\n}\n\nexport const VideoRenderer: Component<VideoRendererProps> = (props) => {\n const strings = useMCPUIStrings()\n const config = useMCPUIConfig()\n const params = () => props.params || (props.component?.params as VideoComponentParams)\n const isExpanded = useExpanded()\n\n const videoInfo = createMemo(() => parseVideoUrl(params()?.url || ''))\n\n const embedUrl = createMemo(() => {\n const info = videoInfo()\n const p = params()\n\n if (!p?.url) return null\n\n switch (info.provider) {\n case 'youtube': {\n const ytParams = new URLSearchParams({\n autoplay: p.autoplay ? '1' : '0',\n controls: p.controls !== false ? '1' : '0',\n loop: p.loop ? '1' : '0',\n mute: p.muted ? '1' : '0',\n })\n if (p.startTime) {\n ytParams.set('start', String(p.startTime))\n }\n // Use youtube-nocookie.com for privacy\n return `https://www.youtube-nocookie.com/embed/${info.videoId}?${ytParams}`\n }\n\n case 'vimeo': {\n const vParams = new URLSearchParams({\n autoplay: p.autoplay ? '1' : '0',\n loop: p.loop ? '1' : '0',\n muted: p.muted ? '1' : '0',\n })\n return `https://player.vimeo.com/video/${info.videoId}?${vParams}`\n }\n\n default:\n return null\n }\n })\n\n const aspectClass = () => {\n switch (params()?.aspectRatio) {\n case '1:1':\n return 'aspect-square'\n case '4:3':\n return 'aspect-[4/3]'\n case '21:9':\n return 'aspect-[21/9]'\n default:\n return 'aspect-video' // 16:9\n }\n }\n\n const handleVideoError = (e: Event) => {\n const error = new Error('Video failed to load')\n console.error('Video error:', e)\n props.onError?.(error)\n }\n\n return (\n <ExpandableWrapper\n title={params()?.title || strings.videoTitle}\n copyData={params()?.url || ''}\n copyLabel={strings.videoCopy}\n toolbarVariant={props.toolbarVariant}\n >\n <div class={`w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden ${\n isExpanded() ? 'flex-1 min-h-0 flex flex-col' : ''\n }`}>\n {/* Title */}\n <Show when={params()?.title}>\n <div class=\"px-4 py-3 border-b border-gray-200 dark:border-gray-700\">\n <h3 class=\"text-sm font-semibold text-gray-900 dark:text-white\">{params()!.title}</h3>\n </div>\n </Show>\n\n {/* Video Container — when expanded, fill remaining space (override aspect ratio) */}\n <div class={`relative bg-black ${isExpanded() ? 'flex-1 min-h-0' : aspectClass()}`}>\n <Show\n when={embedUrl()}\n fallback={\n // Direct video file\n <video\n src={params()?.url}\n poster={params()?.poster}\n autoplay={params()?.autoplay}\n controls={params()?.controls !== false}\n loop={params()?.loop}\n muted={params()?.muted}\n playsinline\n class=\"absolute inset-0 w-full h-full object-contain\"\n onError={handleVideoError}\n >\n <track kind=\"captions\" />\n {strings.videoUnsupported}\n </video>\n }\n >\n {/* YouTube/Vimeo embed */}\n <iframe\n src={embedUrl()!}\n title={params()?.title || strings.videoTitle}\n class=\"absolute inset-0 w-full h-full\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowfullscreen\n // v6.21.0 — youtube-nocookie / player.vimeo are not trusted hosts,\n // so under COEP `credentialless` is what lets them load at all.\n // `attr:` + `true | undefined` — see the note in `IframeRenderer`.\n attr:credentialless={\n // No sandbox here: a YouTube/Vimeo embed keeps its provider\n // cookies, so `'auto'` leaves it alone and a COEP host opts in\n // with `iframeCredentialless: 'always'`.\n shouldSetCredentialless(embedUrl()!, config, {\n sandboxedWithoutSameOrigin: false,\n }) || undefined\n }\n loading=\"lazy\"\n />\n </Show>\n </div>\n\n {/* v6.21.0 — a COEP-blocked embed is silent; this link is the way out. */}\n <Show when={embedUrl()}>\n <IframeFallbackLink url={params()?.url || ''} />\n </Show>\n\n {/* Caption */}\n <Show when={params()?.caption}>\n <div class=\"px-4 py-3 border-t border-gray-200 dark:border-gray-700 flex-shrink-0\">\n <p class=\"text-sm text-gray-600 dark:text-gray-400\">{params()!.caption}</p>\n </div>\n </Show>\n </div>\n </ExpandableWrapper>\n )\n}\n\n/**\n * Check if a URL is from a supported video provider\n */\nexport function isSupportedVideoUrl(url: string): boolean {\n const info = parseVideoUrl(url)\n return info.provider !== 'direct' || url.match(/\\.(mp4|webm|ogg|mov)$/i) !== null\n}\n\n/**\n * Get video provider from URL\n */\nexport function getVideoProvider(url: string): VideoProvider {\n return parseVideoUrl(url).provider\n}\n"],"names":["parseVideoUrl","url","youtubeMatch","match","provider","videoId","vimeoMatch","VideoRenderer","props","strings","useMCPUIStrings","config","useMCPUIConfig","params","component","isExpanded","useExpanded","videoInfo","createMemo","embedUrl","info","p","ytParams","URLSearchParams","autoplay","controls","loop","mute","muted","startTime","set","String","vParams","aspectClass","aspectRatio","handleVideoError","e","error","Error","console","onError","_$createComponent","ExpandableWrapper","title","videoTitle","copyData","copyLabel","videoCopy","toolbarVariant","children","_el$","_$getNextElement","_tmpl$4","_el$8","firstChild","_el$9","_co$","_$getNextMarker","nextSibling","_el$4","_el$0","_el$1","_co$2","_el$10","_el$11","_co$3","_$insert","Show","when","_el$2","_tmpl$","_el$3","fallback","_el$12","_tmpl$5","_el$13","_el$14","_el$15","_co$4","addEventListener","videoUnsupported","_$effect","_p$","_v$6","_v$7","poster","_v$8","_v$9","_v$0","_v$1","_$setAttribute","t","a","_$setProperty","o","i","n","undefined","_el$5","_tmpl$2","_v$","_v$2","_v$3","shouldSetCredentialless","sandboxedWithoutSameOrigin","IframeFallbackLink","caption","_el$6","_tmpl$3","_el$7","_v$4","_v$5","_$className","isSupportedVideoUrl","getVideoProvider"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoRenderer.d.ts","sourceRoot":"","sources":["../../src/components/VideoRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAoB,MAAM,UAAU,CAAA;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"VideoRenderer.d.ts","sourceRoot":"","sources":["../../src/components/VideoRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAoB,MAAM,UAAU,CAAA;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA;AAOjE,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,SAAS,CAAC,EAAE,WAAW,CAAA;IAEvB;;OAEG;IACH,MAAM,CAAC,EAAE,oBAAoB,CAAA;IAE7B;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IAEhC;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAA;CAC5C;AAED;;GAEG;AACH,KAAK,aAAa,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAA;AAsCnD,eAAO,MAAM,aAAa,EAAE,SAAS,CAAC,kBAAkB,CA0IvD,CAAA;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAGxD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAE3D"}
|
|
@@ -2,7 +2,10 @@ import { createComponent, getNextElement, getNextMarker, insert, effect, setAttr
|
|
|
2
2
|
import { createMemo, Show } from "solid-js";
|
|
3
3
|
import { useExpanded, ExpandableWrapper } from "./ExpandableWrapper.js";
|
|
4
4
|
import { useMCPUIStrings } from "../context/MCPUIStringsContext.js";
|
|
5
|
-
|
|
5
|
+
import { useMCPUIConfig } from "../context/MCPUIConfigContext.js";
|
|
6
|
+
import { shouldSetCredentialless } from "../utils/iframe-coep.js";
|
|
7
|
+
import { IframeFallbackLink } from "./IframeFallbackLink.js";
|
|
8
|
+
var _tmpl$ = /* @__PURE__ */ template(`<div class="px-4 py-3 border-b border-gray-200 dark:border-gray-700"><h3 class="text-sm font-semibold text-gray-900 dark:text-white">`), _tmpl$2 = /* @__PURE__ */ template(`<iframe class="absolute inset-0 w-full h-full"allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"allowfullscreen loading=lazy>`, true, false, false), _tmpl$3 = /* @__PURE__ */ template(`<div class="px-4 py-3 border-t border-gray-200 dark:border-gray-700 flex-shrink-0"><p class="text-sm text-gray-600 dark:text-gray-400">`), _tmpl$4 = /* @__PURE__ */ template(`<div><!$><!/><div></div><!$><!/><!$><!/>`), _tmpl$5 = /* @__PURE__ */ template(`<video playsinline class="absolute inset-0 w-full h-full object-contain"><track kind=captions><!$><!/>`);
|
|
6
9
|
function parseVideoUrl(url) {
|
|
7
10
|
const youtubeMatch = url.match(/(?:youtube\.com\/(?:watch\?v=|embed\/|v\/)|youtu\.be\/)([a-zA-Z0-9_-]{11})/);
|
|
8
11
|
if (youtubeMatch) {
|
|
@@ -24,6 +27,7 @@ function parseVideoUrl(url) {
|
|
|
24
27
|
}
|
|
25
28
|
const VideoRenderer = (props) => {
|
|
26
29
|
const strings = useMCPUIStrings();
|
|
30
|
+
const config = useMCPUIConfig();
|
|
27
31
|
const params = () => {
|
|
28
32
|
var _a;
|
|
29
33
|
return props.params || ((_a = props.component) == null ? void 0 : _a.params);
|
|
@@ -97,7 +101,7 @@ const VideoRenderer = (props) => {
|
|
|
97
101
|
return props.toolbarVariant;
|
|
98
102
|
},
|
|
99
103
|
get children() {
|
|
100
|
-
var _el$ = getNextElement(_tmpl$4), _el$8 = _el$.firstChild, [_el$9, _co$] = getNextMarker(_el$8.nextSibling), _el$4 = _el$9.nextSibling, _el$0 = _el$4.nextSibling, [_el$1, _co$2] = getNextMarker(_el$0.nextSibling);
|
|
104
|
+
var _el$ = getNextElement(_tmpl$4), _el$8 = _el$.firstChild, [_el$9, _co$] = getNextMarker(_el$8.nextSibling), _el$4 = _el$9.nextSibling, _el$0 = _el$4.nextSibling, [_el$1, _co$2] = getNextMarker(_el$0.nextSibling), _el$10 = _el$1.nextSibling, [_el$11, _co$3] = getNextMarker(_el$10.nextSibling);
|
|
101
105
|
insert(_el$, createComponent(Show, {
|
|
102
106
|
get when() {
|
|
103
107
|
var _a;
|
|
@@ -117,18 +121,18 @@ const VideoRenderer = (props) => {
|
|
|
117
121
|
return (
|
|
118
122
|
// Direct video file
|
|
119
123
|
(() => {
|
|
120
|
-
var _el$
|
|
121
|
-
_el$
|
|
122
|
-
insert(_el$
|
|
124
|
+
var _el$12 = getNextElement(_tmpl$5), _el$13 = _el$12.firstChild, _el$14 = _el$13.nextSibling, [_el$15, _co$4] = getNextMarker(_el$14.nextSibling);
|
|
125
|
+
_el$12.addEventListener("error", handleVideoError);
|
|
126
|
+
insert(_el$12, () => strings.videoUnsupported, _el$15, _co$4);
|
|
123
127
|
effect((_p$) => {
|
|
124
128
|
var _a, _b, _c, _d, _e, _f;
|
|
125
|
-
var _v$
|
|
126
|
-
_v$
|
|
127
|
-
_v$
|
|
128
|
-
_v$
|
|
129
|
-
_v$
|
|
130
|
-
_v$
|
|
131
|
-
_v$
|
|
129
|
+
var _v$6 = (_a = params()) == null ? void 0 : _a.url, _v$7 = (_b = params()) == null ? void 0 : _b.poster, _v$8 = (_c = params()) == null ? void 0 : _c.autoplay, _v$9 = ((_d = params()) == null ? void 0 : _d.controls) !== false, _v$0 = (_e = params()) == null ? void 0 : _e.loop, _v$1 = (_f = params()) == null ? void 0 : _f.muted;
|
|
130
|
+
_v$6 !== _p$.e && setAttribute(_el$12, "src", _p$.e = _v$6);
|
|
131
|
+
_v$7 !== _p$.t && setAttribute(_el$12, "poster", _p$.t = _v$7);
|
|
132
|
+
_v$8 !== _p$.a && setProperty(_el$12, "autoplay", _p$.a = _v$8);
|
|
133
|
+
_v$9 !== _p$.o && setProperty(_el$12, "controls", _p$.o = _v$9);
|
|
134
|
+
_v$0 !== _p$.i && setProperty(_el$12, "loop", _p$.i = _v$0);
|
|
135
|
+
_v$1 !== _p$.n && setProperty(_el$12, "muted", _p$.n = _v$1);
|
|
132
136
|
return _p$;
|
|
133
137
|
}, {
|
|
134
138
|
e: void 0,
|
|
@@ -138,7 +142,7 @@ const VideoRenderer = (props) => {
|
|
|
138
142
|
i: void 0,
|
|
139
143
|
n: void 0
|
|
140
144
|
});
|
|
141
|
-
return _el$
|
|
145
|
+
return _el$12;
|
|
142
146
|
})()
|
|
143
147
|
);
|
|
144
148
|
},
|
|
@@ -146,17 +150,39 @@ const VideoRenderer = (props) => {
|
|
|
146
150
|
var _el$5 = getNextElement(_tmpl$2);
|
|
147
151
|
effect((_p$) => {
|
|
148
152
|
var _a;
|
|
149
|
-
var _v$ = embedUrl(), _v$2 = ((_a = params()) == null ? void 0 : _a.title) || strings.videoTitle
|
|
153
|
+
var _v$ = embedUrl(), _v$2 = ((_a = params()) == null ? void 0 : _a.title) || strings.videoTitle, _v$3 = (
|
|
154
|
+
// No sandbox here: a YouTube/Vimeo embed keeps its provider
|
|
155
|
+
// cookies, so `'auto'` leaves it alone and a COEP host opts in
|
|
156
|
+
// with `iframeCredentialless: 'always'`.
|
|
157
|
+
shouldSetCredentialless(embedUrl(), config, {
|
|
158
|
+
sandboxedWithoutSameOrigin: false
|
|
159
|
+
}) || void 0
|
|
160
|
+
);
|
|
150
161
|
_v$ !== _p$.e && setAttribute(_el$5, "src", _p$.e = _v$);
|
|
151
162
|
_v$2 !== _p$.t && setAttribute(_el$5, "title", _p$.t = _v$2);
|
|
163
|
+
_v$3 !== _p$.a && setAttribute(_el$5, "credentialless", _p$.a = _v$3);
|
|
152
164
|
return _p$;
|
|
153
165
|
}, {
|
|
154
166
|
e: void 0,
|
|
155
|
-
t: void 0
|
|
167
|
+
t: void 0,
|
|
168
|
+
a: void 0
|
|
156
169
|
});
|
|
157
170
|
return _el$5;
|
|
158
171
|
}
|
|
159
172
|
}));
|
|
173
|
+
insert(_el$, createComponent(Show, {
|
|
174
|
+
get when() {
|
|
175
|
+
return embedUrl();
|
|
176
|
+
},
|
|
177
|
+
get children() {
|
|
178
|
+
return createComponent(IframeFallbackLink, {
|
|
179
|
+
get url() {
|
|
180
|
+
var _a;
|
|
181
|
+
return ((_a = params()) == null ? void 0 : _a.url) || "";
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}), _el$1, _co$2);
|
|
160
186
|
insert(_el$, createComponent(Show, {
|
|
161
187
|
get when() {
|
|
162
188
|
var _a;
|
|
@@ -167,11 +193,11 @@ const VideoRenderer = (props) => {
|
|
|
167
193
|
insert(_el$7, () => params().caption);
|
|
168
194
|
return _el$6;
|
|
169
195
|
}
|
|
170
|
-
}), _el$
|
|
196
|
+
}), _el$11, _co$3);
|
|
171
197
|
effect((_p$) => {
|
|
172
|
-
var _v$
|
|
173
|
-
_v$
|
|
174
|
-
_v$
|
|
198
|
+
var _v$4 = `w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden ${isExpanded() ? "flex-1 min-h-0 flex flex-col" : ""}`, _v$5 = `relative bg-black ${isExpanded() ? "flex-1 min-h-0" : aspectClass()}`;
|
|
199
|
+
_v$4 !== _p$.e && className(_el$, _p$.e = _v$4);
|
|
200
|
+
_v$5 !== _p$.t && className(_el$4, _p$.t = _v$5);
|
|
175
201
|
return _p$;
|
|
176
202
|
}, {
|
|
177
203
|
e: void 0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoRenderer.js","sources":["../../src/components/VideoRenderer.tsx"],"sourcesContent":["/**\n * VideoRenderer - Video embed component\n * Sprint 5: Media Components\n *\n * Supports YouTube, Vimeo, and direct video files\n */\n\nimport { Component, createMemo, Show } from 'solid-js'\nimport type { UIComponent, VideoComponentParams } from '../types'\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper'\nimport { useMCPUIStrings } from '../context/MCPUIStringsContext'\n\nexport interface VideoRendererProps {\n /**\n * UIComponent containing video params\n */\n component?: UIComponent\n\n /**\n * Direct video params (alternative to component)\n */\n params?: VideoComponentParams\n\n /**\n * Error callback\n */\n onError?: (error: Error) => void\n\n /**\n * Forwarded to the underlying `<ExpandableWrapper>` (v6.3.1).\n * @see ExpandableWrapperProps.toolbarVariant\n */\n toolbarVariant?: 'hover' | 'always-visible'\n}\n\n/**\n * Video provider type\n */\ntype VideoProvider = 'youtube' | 'vimeo' | 'direct'\n\n/**\n * Parsed video info\n */\ninterface VideoInfo {\n provider: VideoProvider\n videoId?: string\n}\n\n/**\n * Extract video ID and provider from URL\n */\nfunction parseVideoUrl(url: string): VideoInfo {\n // YouTube patterns:\n // - youtube.com/watch?v=VIDEO_ID\n // - youtube.com/embed/VIDEO_ID\n // - youtube.com/v/VIDEO_ID\n // - youtu.be/VIDEO_ID\n const youtubeMatch = url.match(\n /(?:youtube\\.com\\/(?:watch\\?v=|embed\\/|v\\/)|youtu\\.be\\/)([a-zA-Z0-9_-]{11})/\n )\n if (youtubeMatch) {\n return { provider: 'youtube', videoId: youtubeMatch[1] }\n }\n\n // Vimeo patterns:\n // - vimeo.com/VIDEO_ID\n // - player.vimeo.com/video/VIDEO_ID\n const vimeoMatch = url.match(/(?:vimeo\\.com\\/|player\\.vimeo\\.com\\/video\\/)(\\d+)/)\n if (vimeoMatch) {\n return { provider: 'vimeo', videoId: vimeoMatch[1] }\n }\n\n // Direct video file\n return { provider: 'direct' }\n}\n\nexport const VideoRenderer: Component<VideoRendererProps> = (props) => {\n const strings = useMCPUIStrings()\n const params = () => props.params || (props.component?.params as VideoComponentParams)\n const isExpanded = useExpanded()\n\n const videoInfo = createMemo(() => parseVideoUrl(params()?.url || ''))\n\n const embedUrl = createMemo(() => {\n const info = videoInfo()\n const p = params()\n\n if (!p?.url) return null\n\n switch (info.provider) {\n case 'youtube': {\n const ytParams = new URLSearchParams({\n autoplay: p.autoplay ? '1' : '0',\n controls: p.controls !== false ? '1' : '0',\n loop: p.loop ? '1' : '0',\n mute: p.muted ? '1' : '0',\n })\n if (p.startTime) {\n ytParams.set('start', String(p.startTime))\n }\n // Use youtube-nocookie.com for privacy\n return `https://www.youtube-nocookie.com/embed/${info.videoId}?${ytParams}`\n }\n\n case 'vimeo': {\n const vParams = new URLSearchParams({\n autoplay: p.autoplay ? '1' : '0',\n loop: p.loop ? '1' : '0',\n muted: p.muted ? '1' : '0',\n })\n return `https://player.vimeo.com/video/${info.videoId}?${vParams}`\n }\n\n default:\n return null\n }\n })\n\n const aspectClass = () => {\n switch (params()?.aspectRatio) {\n case '1:1':\n return 'aspect-square'\n case '4:3':\n return 'aspect-[4/3]'\n case '21:9':\n return 'aspect-[21/9]'\n default:\n return 'aspect-video' // 16:9\n }\n }\n\n const handleVideoError = (e: Event) => {\n const error = new Error('Video failed to load')\n console.error('Video error:', e)\n props.onError?.(error)\n }\n\n return (\n <ExpandableWrapper\n title={params()?.title || strings.videoTitle}\n copyData={params()?.url || ''}\n copyLabel={strings.videoCopy}\n toolbarVariant={props.toolbarVariant}\n >\n <div class={`w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden ${\n isExpanded() ? 'flex-1 min-h-0 flex flex-col' : ''\n }`}>\n {/* Title */}\n <Show when={params()?.title}>\n <div class=\"px-4 py-3 border-b border-gray-200 dark:border-gray-700\">\n <h3 class=\"text-sm font-semibold text-gray-900 dark:text-white\">{params()!.title}</h3>\n </div>\n </Show>\n\n {/* Video Container — when expanded, fill remaining space (override aspect ratio) */}\n <div class={`relative bg-black ${isExpanded() ? 'flex-1 min-h-0' : aspectClass()}`}>\n <Show\n when={embedUrl()}\n fallback={\n // Direct video file\n <video\n src={params()?.url}\n poster={params()?.poster}\n autoplay={params()?.autoplay}\n controls={params()?.controls !== false}\n loop={params()?.loop}\n muted={params()?.muted}\n playsinline\n class=\"absolute inset-0 w-full h-full object-contain\"\n onError={handleVideoError}\n >\n <track kind=\"captions\" />\n {strings.videoUnsupported}\n </video>\n }\n >\n {/* YouTube/Vimeo embed */}\n <iframe\n src={embedUrl()!}\n title={params()?.title || strings.videoTitle}\n class=\"absolute inset-0 w-full h-full\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowfullscreen\n loading=\"lazy\"\n />\n </Show>\n </div>\n\n {/* Caption */}\n <Show when={params()?.caption}>\n <div class=\"px-4 py-3 border-t border-gray-200 dark:border-gray-700 flex-shrink-0\">\n <p class=\"text-sm text-gray-600 dark:text-gray-400\">{params()!.caption}</p>\n </div>\n </Show>\n </div>\n </ExpandableWrapper>\n )\n}\n\n/**\n * Check if a URL is from a supported video provider\n */\nexport function isSupportedVideoUrl(url: string): boolean {\n const info = parseVideoUrl(url)\n return info.provider !== 'direct' || url.match(/\\.(mp4|webm|ogg|mov)$/i) !== null\n}\n\n/**\n * Get video provider from URL\n */\nexport function getVideoProvider(url: string): VideoProvider {\n return parseVideoUrl(url).provider\n}\n"],"names":["parseVideoUrl","url","youtubeMatch","match","provider","videoId","vimeoMatch","VideoRenderer","props","strings","useMCPUIStrings","params","component","isExpanded","useExpanded","videoInfo","createMemo","embedUrl","info","p","ytParams","URLSearchParams","autoplay","controls","loop","mute","muted","startTime","set","String","vParams","aspectClass","aspectRatio","handleVideoError","e","error","Error","console","onError","_$createComponent","ExpandableWrapper","title","videoTitle","copyData","copyLabel","videoCopy","toolbarVariant","children","_el$","_$getNextElement","_tmpl$4","_el$8","firstChild","_el$9","_co$","_$getNextMarker","nextSibling","_el$4","_el$0","_el$1","_co$2","_$insert","Show","when","_el$2","_tmpl$","_el$3","fallback","_el$10","_tmpl$5","_el$11","_el$12","_el$13","_co$3","addEventListener","videoUnsupported","_$effect","_p$","_v$5","_v$6","poster","_v$7","_v$8","_v$9","_v$0","_$setAttribute","t","a","_$setProperty","o","i","n","undefined","_el$5","_tmpl$2","_v$","_v$2","caption","_el$6","_tmpl$3","_el$7","_v$3","_v$4","_$className","isSupportedVideoUrl","getVideoProvider"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VideoRenderer.js","sources":["../../src/components/VideoRenderer.tsx"],"sourcesContent":["/**\n * VideoRenderer - Video embed component\n * Sprint 5: Media Components\n *\n * Supports YouTube, Vimeo, and direct video files\n */\n\nimport { Component, createMemo, Show } from 'solid-js'\nimport type { UIComponent, VideoComponentParams } from '../types'\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper'\nimport { useMCPUIStrings } from '../context/MCPUIStringsContext'\nimport { useMCPUIConfig } from '../context/MCPUIConfigContext'\nimport { shouldSetCredentialless } from '../utils/iframe-coep'\nimport { IframeFallbackLink } from './IframeFallbackLink'\n\nexport interface VideoRendererProps {\n /**\n * UIComponent containing video params\n */\n component?: UIComponent\n\n /**\n * Direct video params (alternative to component)\n */\n params?: VideoComponentParams\n\n /**\n * Error callback\n */\n onError?: (error: Error) => void\n\n /**\n * Forwarded to the underlying `<ExpandableWrapper>` (v6.3.1).\n * @see ExpandableWrapperProps.toolbarVariant\n */\n toolbarVariant?: 'hover' | 'always-visible'\n}\n\n/**\n * Video provider type\n */\ntype VideoProvider = 'youtube' | 'vimeo' | 'direct'\n\n/**\n * Parsed video info\n */\ninterface VideoInfo {\n provider: VideoProvider\n videoId?: string\n}\n\n/**\n * Extract video ID and provider from URL\n */\nfunction parseVideoUrl(url: string): VideoInfo {\n // YouTube patterns:\n // - youtube.com/watch?v=VIDEO_ID\n // - youtube.com/embed/VIDEO_ID\n // - youtube.com/v/VIDEO_ID\n // - youtu.be/VIDEO_ID\n const youtubeMatch = url.match(\n /(?:youtube\\.com\\/(?:watch\\?v=|embed\\/|v\\/)|youtu\\.be\\/)([a-zA-Z0-9_-]{11})/\n )\n if (youtubeMatch) {\n return { provider: 'youtube', videoId: youtubeMatch[1] }\n }\n\n // Vimeo patterns:\n // - vimeo.com/VIDEO_ID\n // - player.vimeo.com/video/VIDEO_ID\n const vimeoMatch = url.match(/(?:vimeo\\.com\\/|player\\.vimeo\\.com\\/video\\/)(\\d+)/)\n if (vimeoMatch) {\n return { provider: 'vimeo', videoId: vimeoMatch[1] }\n }\n\n // Direct video file\n return { provider: 'direct' }\n}\n\nexport const VideoRenderer: Component<VideoRendererProps> = (props) => {\n const strings = useMCPUIStrings()\n const config = useMCPUIConfig()\n const params = () => props.params || (props.component?.params as VideoComponentParams)\n const isExpanded = useExpanded()\n\n const videoInfo = createMemo(() => parseVideoUrl(params()?.url || ''))\n\n const embedUrl = createMemo(() => {\n const info = videoInfo()\n const p = params()\n\n if (!p?.url) return null\n\n switch (info.provider) {\n case 'youtube': {\n const ytParams = new URLSearchParams({\n autoplay: p.autoplay ? '1' : '0',\n controls: p.controls !== false ? '1' : '0',\n loop: p.loop ? '1' : '0',\n mute: p.muted ? '1' : '0',\n })\n if (p.startTime) {\n ytParams.set('start', String(p.startTime))\n }\n // Use youtube-nocookie.com for privacy\n return `https://www.youtube-nocookie.com/embed/${info.videoId}?${ytParams}`\n }\n\n case 'vimeo': {\n const vParams = new URLSearchParams({\n autoplay: p.autoplay ? '1' : '0',\n loop: p.loop ? '1' : '0',\n muted: p.muted ? '1' : '0',\n })\n return `https://player.vimeo.com/video/${info.videoId}?${vParams}`\n }\n\n default:\n return null\n }\n })\n\n const aspectClass = () => {\n switch (params()?.aspectRatio) {\n case '1:1':\n return 'aspect-square'\n case '4:3':\n return 'aspect-[4/3]'\n case '21:9':\n return 'aspect-[21/9]'\n default:\n return 'aspect-video' // 16:9\n }\n }\n\n const handleVideoError = (e: Event) => {\n const error = new Error('Video failed to load')\n console.error('Video error:', e)\n props.onError?.(error)\n }\n\n return (\n <ExpandableWrapper\n title={params()?.title || strings.videoTitle}\n copyData={params()?.url || ''}\n copyLabel={strings.videoCopy}\n toolbarVariant={props.toolbarVariant}\n >\n <div class={`w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden ${\n isExpanded() ? 'flex-1 min-h-0 flex flex-col' : ''\n }`}>\n {/* Title */}\n <Show when={params()?.title}>\n <div class=\"px-4 py-3 border-b border-gray-200 dark:border-gray-700\">\n <h3 class=\"text-sm font-semibold text-gray-900 dark:text-white\">{params()!.title}</h3>\n </div>\n </Show>\n\n {/* Video Container — when expanded, fill remaining space (override aspect ratio) */}\n <div class={`relative bg-black ${isExpanded() ? 'flex-1 min-h-0' : aspectClass()}`}>\n <Show\n when={embedUrl()}\n fallback={\n // Direct video file\n <video\n src={params()?.url}\n poster={params()?.poster}\n autoplay={params()?.autoplay}\n controls={params()?.controls !== false}\n loop={params()?.loop}\n muted={params()?.muted}\n playsinline\n class=\"absolute inset-0 w-full h-full object-contain\"\n onError={handleVideoError}\n >\n <track kind=\"captions\" />\n {strings.videoUnsupported}\n </video>\n }\n >\n {/* YouTube/Vimeo embed */}\n <iframe\n src={embedUrl()!}\n title={params()?.title || strings.videoTitle}\n class=\"absolute inset-0 w-full h-full\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowfullscreen\n // v6.21.0 — youtube-nocookie / player.vimeo are not trusted hosts,\n // so under COEP `credentialless` is what lets them load at all.\n // `attr:` + `true | undefined` — see the note in `IframeRenderer`.\n attr:credentialless={\n // No sandbox here: a YouTube/Vimeo embed keeps its provider\n // cookies, so `'auto'` leaves it alone and a COEP host opts in\n // with `iframeCredentialless: 'always'`.\n shouldSetCredentialless(embedUrl()!, config, {\n sandboxedWithoutSameOrigin: false,\n }) || undefined\n }\n loading=\"lazy\"\n />\n </Show>\n </div>\n\n {/* v6.21.0 — a COEP-blocked embed is silent; this link is the way out. */}\n <Show when={embedUrl()}>\n <IframeFallbackLink url={params()?.url || ''} />\n </Show>\n\n {/* Caption */}\n <Show when={params()?.caption}>\n <div class=\"px-4 py-3 border-t border-gray-200 dark:border-gray-700 flex-shrink-0\">\n <p class=\"text-sm text-gray-600 dark:text-gray-400\">{params()!.caption}</p>\n </div>\n </Show>\n </div>\n </ExpandableWrapper>\n )\n}\n\n/**\n * Check if a URL is from a supported video provider\n */\nexport function isSupportedVideoUrl(url: string): boolean {\n const info = parseVideoUrl(url)\n return info.provider !== 'direct' || url.match(/\\.(mp4|webm|ogg|mov)$/i) !== null\n}\n\n/**\n * Get video provider from URL\n */\nexport function getVideoProvider(url: string): VideoProvider {\n return parseVideoUrl(url).provider\n}\n"],"names":["parseVideoUrl","url","youtubeMatch","match","provider","videoId","vimeoMatch","VideoRenderer","props","strings","useMCPUIStrings","config","useMCPUIConfig","params","component","isExpanded","useExpanded","videoInfo","createMemo","embedUrl","info","p","ytParams","URLSearchParams","autoplay","controls","loop","mute","muted","startTime","set","String","vParams","aspectClass","aspectRatio","handleVideoError","e","error","Error","console","onError","_$createComponent","ExpandableWrapper","title","videoTitle","copyData","copyLabel","videoCopy","toolbarVariant","children","_el$","_$getNextElement","_tmpl$4","_el$8","firstChild","_el$9","_co$","_$getNextMarker","nextSibling","_el$4","_el$0","_el$1","_co$2","_el$10","_el$11","_co$3","_$insert","Show","when","_el$2","_tmpl$","_el$3","fallback","_el$12","_tmpl$5","_el$13","_el$14","_el$15","_co$4","addEventListener","videoUnsupported","_$effect","_p$","_v$6","_v$7","poster","_v$8","_v$9","_v$0","_v$1","_$setAttribute","t","a","_$setProperty","o","i","n","undefined","_el$5","_tmpl$2","_v$","_v$2","_v$3","shouldSetCredentialless","sandboxedWithoutSameOrigin","IframeFallbackLink","caption","_el$6","_tmpl$3","_el$7","_v$4","_v$5","_$className","isSupportedVideoUrl","getVideoProvider"],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const web = require("solid-js/web");
|
|
4
|
+
const solidJs = require("solid-js");
|
|
5
|
+
const DEFAULT_MCPUI_CONFIG = {
|
|
6
|
+
iframeCredentialless: "auto",
|
|
7
|
+
customTrustedIframeDomains: [],
|
|
8
|
+
iframePolicy: "strict",
|
|
9
|
+
customIframeDomains: [],
|
|
10
|
+
iframeFallbackLink: "auto"
|
|
11
|
+
};
|
|
12
|
+
const MCPUIConfigContext = solidJs.createContext(DEFAULT_MCPUI_CONFIG);
|
|
13
|
+
function useMCPUIConfig() {
|
|
14
|
+
return solidJs.mergeProps(DEFAULT_MCPUI_CONFIG, solidJs.useContext(MCPUIConfigContext));
|
|
15
|
+
}
|
|
16
|
+
function MCPUIConfigProvider(props) {
|
|
17
|
+
const value = solidJs.mergeProps(DEFAULT_MCPUI_CONFIG, () => props.config);
|
|
18
|
+
return web.createComponent(MCPUIConfigContext.Provider, {
|
|
19
|
+
value,
|
|
20
|
+
get children() {
|
|
21
|
+
return props.children;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
exports.DEFAULT_MCPUI_CONFIG = DEFAULT_MCPUI_CONFIG;
|
|
26
|
+
exports.MCPUIConfigContext = MCPUIConfigContext;
|
|
27
|
+
exports.MCPUIConfigProvider = MCPUIConfigProvider;
|
|
28
|
+
exports.useMCPUIConfig = useMCPUIConfig;
|
|
29
|
+
//# sourceMappingURL=MCPUIConfigContext.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MCPUIConfigContext.cjs","sources":["../../src/context/MCPUIConfigContext.tsx"],"sourcesContent":["/**\n * MCPUIConfigContext — host-level rendering policy for the library's iframes.\n *\n * @since v6.21.0\n *\n * ## Why\n *\n * A host that serves `Cross-Origin-Embedder-Policy: credentialless` makes the\n * browser refuse every cross-origin iframe whose own document does not send a\n * COEP header — which is YouTube, Vimeo, Google Docs, Notion and nearly all of\n * `DEFAULT_IFRAME_DOMAINS`. The escape hatch is the boolean HTML attribute\n * `credentialless` on the `<iframe>` itself: the document then loads, but\n * without access to the cookies and storage of its own origin.\n *\n * That trade-off is free for a public video or chart and fatal for an\n * authenticated embed, so the library keys it on the distinction it already\n * makes: `TRUSTED_IFRAME_DOMAINS` (the hosts that receive `allow-same-origin`\n * because they need their own cookies) do NOT get the attribute; everything\n * else does. This context lets a host override that, add its own trusted\n * hosts, and control the \"open in a new tab\" fallback link.\n *\n * ## Scope — policy only, NOT strings\n *\n * Text lives in `MCPUIStrings` (`MCPUIStringsContext`). This context carries\n * only behavioural switches, and like the strings context every renderer works\n * standalone: with no provider mounted, `DEFAULT_MCPUI_CONFIG` applies.\n *\n * @example\n * ```tsx\n * import { MCPUIConfigProvider } from '@seed-ship/mcp-ui-solid'\n *\n * <MCPUIConfigProvider config={{ customTrustedIframeDomains: ['embed.acme.com'] }}>\n * <App />\n * </MCPUIConfigProvider>\n * ```\n */\n\nimport { createContext, mergeProps, useContext, type JSX } from 'solid-js'\nimport type { IframePolicy } from '../types'\n\n/**\n * Host-level rendering policy. Every field has a default — a provider passes\n * a partial override.\n */\nexport interface MCPUIConfig {\n /**\n * When the library puts the boolean `credentialless` attribute on an\n * `<iframe>` it renders.\n *\n * - `'auto'` (default) — on every iframe whose host is NOT in\n * `TRUSTED_IFRAME_DOMAINS` (plus `customTrustedIframeDomains`). Those\n * hosts already ran without cookies, since their sandbox has no\n * `allow-same-origin`; the attribute is what unblocks them under COEP.\n * - `'always'` — on every iframe, trusted hosts included. An authenticated\n * embed will then render its login screen.\n * - `'never'` — never. The right value for a host that sends no COEP header\n * and does not want its embeds to lose their cookies.\n *\n * Chrome/Edge 110+ honour the attribute; Firefox ignores it (iframes stay\n * blocked while the host sends COEP); Safari ignores the COEP value itself,\n * so nothing is blocked there in the first place.\n */\n iframeCredentialless: 'auto' | 'always' | 'never'\n\n /**\n * Extra hosts treated as trusted, on top of `TRUSTED_IFRAME_DOMAINS`:\n * no `credentialless` attribute, and `allow-same-origin` in the sandbox.\n * A subdomain of a listed host matches too.\n *\n * This only RECLASSIFIES a host the allow-list already accepts. To render an\n * iframe whose host is outside `DEFAULT_IFRAME_DOMAINS`, add it to\n * {@link MCPUIConfig.customIframeDomains} with `iframePolicy: 'extend'`.\n */\n customTrustedIframeDomains: string[]\n\n /**\n * How `UIResourceRenderer` validates an `iframe` component's host:\n * `'strict'` (default) accepts `DEFAULT_IFRAME_DOMAINS` only, `'extend'`\n * also accepts {@link MCPUIConfig.customIframeDomains}.\n */\n iframePolicy: IframePolicy\n\n /**\n * Extra hosts the allow-list accepts when `iframePolicy` is `'extend'`.\n * Without this, a component pointing at an unlisted host is replaced by the\n * validation card before any renderer runs.\n */\n customIframeDomains: string[]\n\n /**\n * When the library renders an \"open in a new tab\" link under an embed\n * (`strings.iframeOpenInNewTab`).\n *\n * A COEP-blocked iframe fires no event the parent page can observe, so\n * without this the user sees an unexplained blank frame.\n *\n * - `'auto'` (default) — only when the page is cross-origin isolated\n * (`window.crossOriginIsolated`). That flag needs COOP `same-origin` AND\n * COEP, so a host that sends COEP alone still blocks embeds while the\n * flag is false: set `'always'` there. Read after mount, never during\n * render, so SSR markup and hydration agree.\n * - `'always'` / `'never'` — unconditional.\n */\n iframeFallbackLink: 'auto' | 'always' | 'never'\n}\n\n/**\n * Defaults. Chosen so a host that sends no COEP header sees no change from\n * 6.20.0 apart from the fallback link, which `'auto'` keeps hidden there.\n */\nexport const DEFAULT_MCPUI_CONFIG: MCPUIConfig = {\n iframeCredentialless: 'auto',\n customTrustedIframeDomains: [],\n iframePolicy: 'strict',\n customIframeDomains: [],\n iframeFallbackLink: 'auto',\n}\n\nexport const MCPUIConfigContext = createContext<MCPUIConfig>(DEFAULT_MCPUI_CONFIG)\n\n/**\n * Reads the active host config. Returns `DEFAULT_MCPUI_CONFIG` when no\n * `<MCPUIConfigProvider>` is mounted above.\n */\nexport function useMCPUIConfig(): MCPUIConfig {\n return mergeProps(DEFAULT_MCPUI_CONFIG, useContext(MCPUIConfigContext))\n}\n\nexport interface MCPUIConfigProviderProps {\n /**\n * Partial override of the host config. Any key left unset falls back to\n * `DEFAULT_MCPUI_CONFIG`.\n */\n config?: Partial<MCPUIConfig>\n children: JSX.Element\n}\n\n/**\n * Provides the host config to every MCP-UI renderer below it.\n * Merges the partial `config` override over the defaults.\n */\nexport function MCPUIConfigProvider(props: MCPUIConfigProviderProps): JSX.Element {\n // The function source keeps replacement `config` objects reactive while\n // mergeProps fills any omitted key without taking a one-time snapshot.\n const value = mergeProps(DEFAULT_MCPUI_CONFIG, () => props.config)\n return (\n <MCPUIConfigContext.Provider value={value}>\n {props.children}\n </MCPUIConfigContext.Provider>\n )\n}\n"],"names":["DEFAULT_MCPUI_CONFIG","iframeCredentialless","customTrustedIframeDomains","iframePolicy","customIframeDomains","iframeFallbackLink","MCPUIConfigContext","createContext","useMCPUIConfig","mergeProps","useContext","MCPUIConfigProvider","props","value","config","_$createComponent","Provider","children"],"mappings":";;;;AA8GO,MAAMA,uBAAoC;AAAA,EAC/CC,sBAAsB;AAAA,EACtBC,4BAA4B,CAAA;AAAA,EAC5BC,cAAc;AAAA,EACdC,qBAAqB,CAAA;AAAA,EACrBC,oBAAoB;AACtB;AAEO,MAAMC,qBAAqBC,QAAAA,cAA2BP,oBAAoB;AAM1E,SAASQ,iBAA8B;AAC5C,SAAOC,mBAAWT,sBAAsBU,QAAAA,WAAWJ,kBAAkB,CAAC;AACxE;AAeO,SAASK,oBAAoBC,OAA8C;AAGhF,QAAMC,QAAQJ,QAAAA,WAAWT,sBAAsB,MAAMY,MAAME,MAAM;AACjE,SAAAC,IAAAA,gBACGT,mBAAmBU,UAAQ;AAAA,IAACH;AAAAA,IAAY,IAAAI,WAAA;AAAA,aACtCL,MAAMK;AAAAA,IAAQ;AAAA,EAAA,CAAA;AAGrB;;;;;"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCPUIConfigContext — host-level rendering policy for the library's iframes.
|
|
3
|
+
*
|
|
4
|
+
* @since v6.21.0
|
|
5
|
+
*
|
|
6
|
+
* ## Why
|
|
7
|
+
*
|
|
8
|
+
* A host that serves `Cross-Origin-Embedder-Policy: credentialless` makes the
|
|
9
|
+
* browser refuse every cross-origin iframe whose own document does not send a
|
|
10
|
+
* COEP header — which is YouTube, Vimeo, Google Docs, Notion and nearly all of
|
|
11
|
+
* `DEFAULT_IFRAME_DOMAINS`. The escape hatch is the boolean HTML attribute
|
|
12
|
+
* `credentialless` on the `<iframe>` itself: the document then loads, but
|
|
13
|
+
* without access to the cookies and storage of its own origin.
|
|
14
|
+
*
|
|
15
|
+
* That trade-off is free for a public video or chart and fatal for an
|
|
16
|
+
* authenticated embed, so the library keys it on the distinction it already
|
|
17
|
+
* makes: `TRUSTED_IFRAME_DOMAINS` (the hosts that receive `allow-same-origin`
|
|
18
|
+
* because they need their own cookies) do NOT get the attribute; everything
|
|
19
|
+
* else does. This context lets a host override that, add its own trusted
|
|
20
|
+
* hosts, and control the "open in a new tab" fallback link.
|
|
21
|
+
*
|
|
22
|
+
* ## Scope — policy only, NOT strings
|
|
23
|
+
*
|
|
24
|
+
* Text lives in `MCPUIStrings` (`MCPUIStringsContext`). This context carries
|
|
25
|
+
* only behavioural switches, and like the strings context every renderer works
|
|
26
|
+
* standalone: with no provider mounted, `DEFAULT_MCPUI_CONFIG` applies.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```tsx
|
|
30
|
+
* import { MCPUIConfigProvider } from '@seed-ship/mcp-ui-solid'
|
|
31
|
+
*
|
|
32
|
+
* <MCPUIConfigProvider config={{ customTrustedIframeDomains: ['embed.acme.com'] }}>
|
|
33
|
+
* <App />
|
|
34
|
+
* </MCPUIConfigProvider>
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
import { type JSX } from 'solid-js';
|
|
38
|
+
import type { IframePolicy } from '../types';
|
|
39
|
+
/**
|
|
40
|
+
* Host-level rendering policy. Every field has a default — a provider passes
|
|
41
|
+
* a partial override.
|
|
42
|
+
*/
|
|
43
|
+
export interface MCPUIConfig {
|
|
44
|
+
/**
|
|
45
|
+
* When the library puts the boolean `credentialless` attribute on an
|
|
46
|
+
* `<iframe>` it renders.
|
|
47
|
+
*
|
|
48
|
+
* - `'auto'` (default) — on every iframe whose host is NOT in
|
|
49
|
+
* `TRUSTED_IFRAME_DOMAINS` (plus `customTrustedIframeDomains`). Those
|
|
50
|
+
* hosts already ran without cookies, since their sandbox has no
|
|
51
|
+
* `allow-same-origin`; the attribute is what unblocks them under COEP.
|
|
52
|
+
* - `'always'` — on every iframe, trusted hosts included. An authenticated
|
|
53
|
+
* embed will then render its login screen.
|
|
54
|
+
* - `'never'` — never. The right value for a host that sends no COEP header
|
|
55
|
+
* and does not want its embeds to lose their cookies.
|
|
56
|
+
*
|
|
57
|
+
* Chrome/Edge 110+ honour the attribute; Firefox ignores it (iframes stay
|
|
58
|
+
* blocked while the host sends COEP); Safari ignores the COEP value itself,
|
|
59
|
+
* so nothing is blocked there in the first place.
|
|
60
|
+
*/
|
|
61
|
+
iframeCredentialless: 'auto' | 'always' | 'never';
|
|
62
|
+
/**
|
|
63
|
+
* Extra hosts treated as trusted, on top of `TRUSTED_IFRAME_DOMAINS`:
|
|
64
|
+
* no `credentialless` attribute, and `allow-same-origin` in the sandbox.
|
|
65
|
+
* A subdomain of a listed host matches too.
|
|
66
|
+
*
|
|
67
|
+
* This only RECLASSIFIES a host the allow-list already accepts. To render an
|
|
68
|
+
* iframe whose host is outside `DEFAULT_IFRAME_DOMAINS`, add it to
|
|
69
|
+
* {@link MCPUIConfig.customIframeDomains} with `iframePolicy: 'extend'`.
|
|
70
|
+
*/
|
|
71
|
+
customTrustedIframeDomains: string[];
|
|
72
|
+
/**
|
|
73
|
+
* How `UIResourceRenderer` validates an `iframe` component's host:
|
|
74
|
+
* `'strict'` (default) accepts `DEFAULT_IFRAME_DOMAINS` only, `'extend'`
|
|
75
|
+
* also accepts {@link MCPUIConfig.customIframeDomains}.
|
|
76
|
+
*/
|
|
77
|
+
iframePolicy: IframePolicy;
|
|
78
|
+
/**
|
|
79
|
+
* Extra hosts the allow-list accepts when `iframePolicy` is `'extend'`.
|
|
80
|
+
* Without this, a component pointing at an unlisted host is replaced by the
|
|
81
|
+
* validation card before any renderer runs.
|
|
82
|
+
*/
|
|
83
|
+
customIframeDomains: string[];
|
|
84
|
+
/**
|
|
85
|
+
* When the library renders an "open in a new tab" link under an embed
|
|
86
|
+
* (`strings.iframeOpenInNewTab`).
|
|
87
|
+
*
|
|
88
|
+
* A COEP-blocked iframe fires no event the parent page can observe, so
|
|
89
|
+
* without this the user sees an unexplained blank frame.
|
|
90
|
+
*
|
|
91
|
+
* - `'auto'` (default) — only when the page is cross-origin isolated
|
|
92
|
+
* (`window.crossOriginIsolated`). That flag needs COOP `same-origin` AND
|
|
93
|
+
* COEP, so a host that sends COEP alone still blocks embeds while the
|
|
94
|
+
* flag is false: set `'always'` there. Read after mount, never during
|
|
95
|
+
* render, so SSR markup and hydration agree.
|
|
96
|
+
* - `'always'` / `'never'` — unconditional.
|
|
97
|
+
*/
|
|
98
|
+
iframeFallbackLink: 'auto' | 'always' | 'never';
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Defaults. Chosen so a host that sends no COEP header sees no change from
|
|
102
|
+
* 6.20.0 apart from the fallback link, which `'auto'` keeps hidden there.
|
|
103
|
+
*/
|
|
104
|
+
export declare const DEFAULT_MCPUI_CONFIG: MCPUIConfig;
|
|
105
|
+
export declare const MCPUIConfigContext: import("solid-js").Context<MCPUIConfig>;
|
|
106
|
+
/**
|
|
107
|
+
* Reads the active host config. Returns `DEFAULT_MCPUI_CONFIG` when no
|
|
108
|
+
* `<MCPUIConfigProvider>` is mounted above.
|
|
109
|
+
*/
|
|
110
|
+
export declare function useMCPUIConfig(): MCPUIConfig;
|
|
111
|
+
export interface MCPUIConfigProviderProps {
|
|
112
|
+
/**
|
|
113
|
+
* Partial override of the host config. Any key left unset falls back to
|
|
114
|
+
* `DEFAULT_MCPUI_CONFIG`.
|
|
115
|
+
*/
|
|
116
|
+
config?: Partial<MCPUIConfig>;
|
|
117
|
+
children: JSX.Element;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Provides the host config to every MCP-UI renderer below it.
|
|
121
|
+
* Merges the partial `config` override over the defaults.
|
|
122
|
+
*/
|
|
123
|
+
export declare function MCPUIConfigProvider(props: MCPUIConfigProviderProps): JSX.Element;
|
|
124
|
+
//# sourceMappingURL=MCPUIConfigContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MCPUIConfigContext.d.ts","sourceRoot":"","sources":["../../src/context/MCPUIConfigContext.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,EAAyC,KAAK,GAAG,EAAE,MAAM,UAAU,CAAA;AAC1E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAE5C;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;;;;;;;;;;;;;OAgBG;IACH,oBAAoB,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAA;IAEjD;;;;;;;;OAQG;IACH,0BAA0B,EAAE,MAAM,EAAE,CAAA;IAEpC;;;;OAIG;IACH,YAAY,EAAE,YAAY,CAAA;IAE1B;;;;OAIG;IACH,mBAAmB,EAAE,MAAM,EAAE,CAAA;IAE7B;;;;;;;;;;;;;OAaG;IACH,kBAAkB,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAA;CAChD;AAED;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,WAMlC,CAAA;AAED,eAAO,MAAM,kBAAkB,yCAAmD,CAAA;AAElF;;;GAGG;AACH,wBAAgB,cAAc,IAAI,WAAW,CAE5C;AAED,MAAM,WAAW,wBAAwB;IACvC;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;IAC7B,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAA;CACtB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,GAAG,GAAG,CAAC,OAAO,CAShF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createComponent } from "solid-js/web";
|
|
2
|
+
import { createContext, mergeProps, useContext } from "solid-js";
|
|
3
|
+
const DEFAULT_MCPUI_CONFIG = {
|
|
4
|
+
iframeCredentialless: "auto",
|
|
5
|
+
customTrustedIframeDomains: [],
|
|
6
|
+
iframePolicy: "strict",
|
|
7
|
+
customIframeDomains: [],
|
|
8
|
+
iframeFallbackLink: "auto"
|
|
9
|
+
};
|
|
10
|
+
const MCPUIConfigContext = createContext(DEFAULT_MCPUI_CONFIG);
|
|
11
|
+
function useMCPUIConfig() {
|
|
12
|
+
return mergeProps(DEFAULT_MCPUI_CONFIG, useContext(MCPUIConfigContext));
|
|
13
|
+
}
|
|
14
|
+
function MCPUIConfigProvider(props) {
|
|
15
|
+
const value = mergeProps(DEFAULT_MCPUI_CONFIG, () => props.config);
|
|
16
|
+
return createComponent(MCPUIConfigContext.Provider, {
|
|
17
|
+
value,
|
|
18
|
+
get children() {
|
|
19
|
+
return props.children;
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
export {
|
|
24
|
+
DEFAULT_MCPUI_CONFIG,
|
|
25
|
+
MCPUIConfigContext,
|
|
26
|
+
MCPUIConfigProvider,
|
|
27
|
+
useMCPUIConfig
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=MCPUIConfigContext.js.map
|