@seed-ship/mcp-ui-solid 6.0.0 → 6.2.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +176 -0
  2. package/dist/components/CarouselRenderer.cjs +41 -30
  3. package/dist/components/CarouselRenderer.cjs.map +1 -1
  4. package/dist/components/CarouselRenderer.d.ts.map +1 -1
  5. package/dist/components/CarouselRenderer.js +42 -31
  6. package/dist/components/CarouselRenderer.js.map +1 -1
  7. package/dist/components/ChartJSRenderer.cjs +27 -7
  8. package/dist/components/ChartJSRenderer.cjs.map +1 -1
  9. package/dist/components/ChartJSRenderer.d.ts.map +1 -1
  10. package/dist/components/ChartJSRenderer.js +29 -9
  11. package/dist/components/ChartJSRenderer.js.map +1 -1
  12. package/dist/components/CodeBlockRenderer.cjs +88 -25
  13. package/dist/components/CodeBlockRenderer.cjs.map +1 -1
  14. package/dist/components/CodeBlockRenderer.d.ts.map +1 -1
  15. package/dist/components/CodeBlockRenderer.js +89 -26
  16. package/dist/components/CodeBlockRenderer.js.map +1 -1
  17. package/dist/components/ExpandableWrapper.cjs +1 -1
  18. package/dist/components/ExpandableWrapper.cjs.map +1 -1
  19. package/dist/components/ExpandableWrapper.d.ts.map +1 -1
  20. package/dist/components/ExpandableWrapper.js +1 -1
  21. package/dist/components/ExpandableWrapper.js.map +1 -1
  22. package/dist/components/GraphRenderer.cjs +7 -4
  23. package/dist/components/GraphRenderer.cjs.map +1 -1
  24. package/dist/components/GraphRenderer.d.ts.map +1 -1
  25. package/dist/components/GraphRenderer.js +8 -5
  26. package/dist/components/GraphRenderer.js.map +1 -1
  27. package/dist/components/ImageGalleryRenderer.cjs +101 -77
  28. package/dist/components/ImageGalleryRenderer.cjs.map +1 -1
  29. package/dist/components/ImageGalleryRenderer.d.ts.map +1 -1
  30. package/dist/components/ImageGalleryRenderer.js +102 -78
  31. package/dist/components/ImageGalleryRenderer.js.map +1 -1
  32. package/dist/components/MapRenderer.cjs +94 -34
  33. package/dist/components/MapRenderer.cjs.map +1 -1
  34. package/dist/components/MapRenderer.d.ts.map +1 -1
  35. package/dist/components/MapRenderer.js +107 -47
  36. package/dist/components/MapRenderer.js.map +1 -1
  37. package/dist/components/UIResourceRenderer.cjs +66 -54
  38. package/dist/components/UIResourceRenderer.cjs.map +1 -1
  39. package/dist/components/UIResourceRenderer.d.ts.map +1 -1
  40. package/dist/components/UIResourceRenderer.js +66 -54
  41. package/dist/components/UIResourceRenderer.js.map +1 -1
  42. package/dist/components/VideoRenderer.cjs +95 -74
  43. package/dist/components/VideoRenderer.cjs.map +1 -1
  44. package/dist/components/VideoRenderer.d.ts.map +1 -1
  45. package/dist/components/VideoRenderer.js +96 -75
  46. package/dist/components/VideoRenderer.js.map +1 -1
  47. package/dist/index.cjs +3 -3
  48. package/dist/index.js +1 -1
  49. package/package.json +1 -1
  50. package/src/components/CarouselRenderer.tsx +9 -1
  51. package/src/components/ChartJSRenderer.tsx +30 -8
  52. package/src/components/CodeBlockRenderer.tsx +65 -5
  53. package/src/components/ExpandableWrapper.tsx +7 -2
  54. package/src/components/GraphRenderer.tsx +13 -4
  55. package/src/components/ImageGalleryRenderer.test.tsx +18 -7
  56. package/src/components/ImageGalleryRenderer.tsx +22 -3
  57. package/src/components/MapRenderer.tsx +68 -14
  58. package/src/components/UIResourceRenderer.fluidity.test.tsx +101 -0
  59. package/src/components/UIResourceRenderer.tsx +23 -9
  60. package/src/components/VideoRenderer.tsx +14 -4
  61. package/tsconfig.tsbuildinfo +1 -1
@@ -2,7 +2,8 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const web = require("solid-js/web");
4
4
  const solidJs = require("solid-js");
5
- 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"><p class="text-sm text-gray-600 dark:text-gray-400">`), _tmpl$4 = /* @__PURE__ */ web.template(`<div class="w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden"><!$><!/><div></div><!$><!/>`), _tmpl$5 = /* @__PURE__ */ web.template(`<video playsinline class="absolute inset-0 w-full h-full object-contain"><track kind=captions>Your browser does not support the video tag.`);
5
+ const ExpandableWrapper = require("./ExpandableWrapper.cjs");
6
+ 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>Your browser does not support the video tag.`);
6
7
  function parseVideoUrl(url) {
7
8
  const youtubeMatch = url.match(/(?:youtube\.com\/(?:watch\?v=|embed\/|v\/)|youtu\.be\/)([a-zA-Z0-9_-]{11})/);
8
9
  if (youtubeMatch) {
@@ -27,6 +28,7 @@ const VideoRenderer = (props) => {
27
28
  var _a;
28
29
  return props.params || ((_a = props.component) == null ? void 0 : _a.params);
29
30
  };
31
+ const isExpanded = ExpandableWrapper.useExpanded();
30
32
  const videoInfo = solidJs.createMemo(() => {
31
33
  var _a;
32
34
  return parseVideoUrl(((_a = params()) == null ? void 0 : _a.url) || "");
@@ -79,80 +81,99 @@ const VideoRenderer = (props) => {
79
81
  console.error("Video error:", e);
80
82
  (_a = props.onError) == null ? void 0 : _a.call(props, error);
81
83
  };
82
- return (() => {
83
- 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);
84
- web.insert(_el$, web.createComponent(solidJs.Show, {
85
- get when() {
86
- var _a;
87
- return (_a = params()) == null ? void 0 : _a.title;
88
- },
89
- get children() {
90
- var _el$2 = web.getNextElement(_tmpl$), _el$3 = _el$2.firstChild;
91
- web.insert(_el$3, () => params().title);
92
- return _el$2;
93
- }
94
- }), _el$9, _co$);
95
- web.insert(_el$4, web.createComponent(solidJs.Show, {
96
- get when() {
97
- return embedUrl();
98
- },
99
- get fallback() {
100
- return (
101
- // Direct video file
102
- (() => {
103
- var _el$10 = web.getNextElement(_tmpl$5);
104
- _el$10.addEventListener("error", handleVideoError);
105
- web.effect((_p$) => {
106
- var _a, _b, _c, _d, _e, _f;
107
- var _v$3 = (_a = params()) == null ? void 0 : _a.url, _v$4 = (_b = params()) == null ? void 0 : _b.poster, _v$5 = (_c = params()) == null ? void 0 : _c.autoplay, _v$6 = ((_d = params()) == null ? void 0 : _d.controls) !== false, _v$7 = (_e = params()) == null ? void 0 : _e.loop, _v$8 = (_f = params()) == null ? void 0 : _f.muted;
108
- _v$3 !== _p$.e && web.setAttribute(_el$10, "src", _p$.e = _v$3);
109
- _v$4 !== _p$.t && web.setAttribute(_el$10, "poster", _p$.t = _v$4);
110
- _v$5 !== _p$.a && web.setProperty(_el$10, "autoplay", _p$.a = _v$5);
111
- _v$6 !== _p$.o && web.setProperty(_el$10, "controls", _p$.o = _v$6);
112
- _v$7 !== _p$.i && web.setProperty(_el$10, "loop", _p$.i = _v$7);
113
- _v$8 !== _p$.n && web.setProperty(_el$10, "muted", _p$.n = _v$8);
114
- return _p$;
115
- }, {
116
- e: void 0,
117
- t: void 0,
118
- a: void 0,
119
- o: void 0,
120
- i: void 0,
121
- n: void 0
122
- });
123
- return _el$10;
124
- })()
125
- );
126
- },
127
- get children() {
128
- var _el$5 = web.getNextElement(_tmpl$2);
129
- web.effect((_p$) => {
84
+ return web.createComponent(ExpandableWrapper.ExpandableWrapper, {
85
+ get title() {
86
+ var _a;
87
+ return ((_a = params()) == null ? void 0 : _a.title) || "Video";
88
+ },
89
+ get copyData() {
90
+ var _a;
91
+ return ((_a = params()) == null ? void 0 : _a.url) || "";
92
+ },
93
+ copyLabel: "Copy video URL",
94
+ get children() {
95
+ 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);
96
+ web.insert(_el$, web.createComponent(solidJs.Show, {
97
+ get when() {
130
98
  var _a;
131
- var _v$ = embedUrl(), _v$2 = ((_a = params()) == null ? void 0 : _a.title) || "Video";
132
- _v$ !== _p$.e && web.setAttribute(_el$5, "src", _p$.e = _v$);
133
- _v$2 !== _p$.t && web.setAttribute(_el$5, "title", _p$.t = _v$2);
134
- return _p$;
135
- }, {
136
- e: void 0,
137
- t: void 0
138
- });
139
- return _el$5;
140
- }
141
- }));
142
- web.insert(_el$, web.createComponent(solidJs.Show, {
143
- get when() {
144
- var _a;
145
- return (_a = params()) == null ? void 0 : _a.caption;
146
- },
147
- get children() {
148
- var _el$6 = web.getNextElement(_tmpl$3), _el$7 = _el$6.firstChild;
149
- web.insert(_el$7, () => params().caption);
150
- return _el$6;
151
- }
152
- }), _el$1, _co$2);
153
- web.effect(() => web.className(_el$4, `relative ${aspectClass()} bg-black`));
154
- return _el$;
155
- })();
99
+ return (_a = params()) == null ? void 0 : _a.title;
100
+ },
101
+ get children() {
102
+ var _el$2 = web.getNextElement(_tmpl$), _el$3 = _el$2.firstChild;
103
+ web.insert(_el$3, () => params().title);
104
+ return _el$2;
105
+ }
106
+ }), _el$9, _co$);
107
+ web.insert(_el$4, web.createComponent(solidJs.Show, {
108
+ get when() {
109
+ return embedUrl();
110
+ },
111
+ get fallback() {
112
+ return (
113
+ // Direct video file
114
+ (() => {
115
+ var _el$10 = web.getNextElement(_tmpl$5);
116
+ _el$10.addEventListener("error", handleVideoError);
117
+ web.effect((_p$) => {
118
+ var _a, _b, _c, _d, _e, _f;
119
+ var _v$5 = (_a = params()) == null ? void 0 : _a.url, _v$6 = (_b = params()) == null ? void 0 : _b.poster, _v$7 = (_c = params()) == null ? void 0 : _c.autoplay, _v$8 = ((_d = params()) == null ? void 0 : _d.controls) !== false, _v$9 = (_e = params()) == null ? void 0 : _e.loop, _v$0 = (_f = params()) == null ? void 0 : _f.muted;
120
+ _v$5 !== _p$.e && web.setAttribute(_el$10, "src", _p$.e = _v$5);
121
+ _v$6 !== _p$.t && web.setAttribute(_el$10, "poster", _p$.t = _v$6);
122
+ _v$7 !== _p$.a && web.setProperty(_el$10, "autoplay", _p$.a = _v$7);
123
+ _v$8 !== _p$.o && web.setProperty(_el$10, "controls", _p$.o = _v$8);
124
+ _v$9 !== _p$.i && web.setProperty(_el$10, "loop", _p$.i = _v$9);
125
+ _v$0 !== _p$.n && web.setProperty(_el$10, "muted", _p$.n = _v$0);
126
+ return _p$;
127
+ }, {
128
+ e: void 0,
129
+ t: void 0,
130
+ a: void 0,
131
+ o: void 0,
132
+ i: void 0,
133
+ n: void 0
134
+ });
135
+ return _el$10;
136
+ })()
137
+ );
138
+ },
139
+ get children() {
140
+ var _el$5 = web.getNextElement(_tmpl$2);
141
+ web.effect((_p$) => {
142
+ var _a;
143
+ var _v$ = embedUrl(), _v$2 = ((_a = params()) == null ? void 0 : _a.title) || "Video";
144
+ _v$ !== _p$.e && web.setAttribute(_el$5, "src", _p$.e = _v$);
145
+ _v$2 !== _p$.t && web.setAttribute(_el$5, "title", _p$.t = _v$2);
146
+ return _p$;
147
+ }, {
148
+ e: void 0,
149
+ t: void 0
150
+ });
151
+ return _el$5;
152
+ }
153
+ }));
154
+ web.insert(_el$, web.createComponent(solidJs.Show, {
155
+ get when() {
156
+ var _a;
157
+ return (_a = params()) == null ? void 0 : _a.caption;
158
+ },
159
+ get children() {
160
+ var _el$6 = web.getNextElement(_tmpl$3), _el$7 = _el$6.firstChild;
161
+ web.insert(_el$7, () => params().caption);
162
+ return _el$6;
163
+ }
164
+ }), _el$1, _co$2);
165
+ web.effect((_p$) => {
166
+ var _v$3 = `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$4 = `relative bg-black ${isExpanded() ? "flex-1 min-h-0" : aspectClass()}`;
167
+ _v$3 !== _p$.e && web.className(_el$, _p$.e = _v$3);
168
+ _v$4 !== _p$.t && web.className(_el$4, _p$.t = _v$4);
169
+ return _p$;
170
+ }, {
171
+ e: void 0,
172
+ t: void 0
173
+ });
174
+ return _el$;
175
+ }
176
+ });
156
177
  };
157
178
  function isSupportedVideoUrl(url) {
158
179
  const info = parseVideoUrl(url);
@@ -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'\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/**\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 params = () => props.params || (props.component?.params as VideoComponentParams)\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 <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 {/* 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 */}\n <div class={`relative ${aspectClass()} bg-black`}>\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 Your browser does not support the video tag.\n </video>\n }\n >\n {/* YouTube/Vimeo embed */}\n <iframe\n src={embedUrl()!}\n title={params()?.title || 'Video'}\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\">\n <p class=\"text-sm text-gray-600 dark:text-gray-400\">{params()!.caption}</p>\n </div>\n </Show>\n </div>\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","params","component","videoInfo","createMemo","embedUrl","info","p","ytParams","URLSearchParams","autoplay","controls","loop","mute","muted","startTime","set","String","vParams","aspectClass","aspectRatio","handleVideoError","e","error","Error","console","onError","_el$","_$getNextElement","_tmpl$4","_el$8","firstChild","_el$9","_co$","_$getNextMarker","nextSibling","_el$4","_el$0","_el$1","_co$2","_$insert","_$createComponent","Show","when","title","children","_el$2","_tmpl$","_el$3","fallback","_el$10","_tmpl$5","addEventListener","_$effect","_p$","_v$3","_v$4","poster","_v$5","_v$6","_v$7","_v$8","_$setAttribute","t","a","_$setProperty","o","i","n","undefined","_el$5","_tmpl$2","_v$","_v$2","caption","_el$6","_tmpl$3","_el$7","_$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'\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/**\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 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 || 'Video'}\n copyData={params()?.url || ''}\n copyLabel=\"Copy video URL\"\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 Your browser does not support the video tag.\n </video>\n }\n >\n {/* YouTube/Vimeo embed */}\n <iframe\n src={embedUrl()!}\n title={params()?.title || 'Video'}\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","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","copyData","copyLabel","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","addEventListener","_$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":";;;;;;AA4CA,SAASA,cAAcC,KAAwB;AAM7C,QAAMC,eAAeD,IAAIE,MACvB,4EACF;AACA,MAAID,cAAc;AAChB,WAAO;AAAA,MAAEE,UAAU;AAAA,MAAWC,SAASH,aAAa,CAAC;AAAA,IAAA;AAAA,EACvD;AAKA,QAAMI,aAAaL,IAAIE,MAAM,mDAAmD;AAChF,MAAIG,YAAY;AACd,WAAO;AAAA,MAAEF,UAAU;AAAA,MAASC,SAASC,WAAW,CAAC;AAAA,IAAA;AAAA,EACnD;AAGA,SAAO;AAAA,IAAEF,UAAU;AAAA,EAAA;AACrB;AAEO,MAAMG,gBAAgDC,CAAAA,UAAU;AACrE,QAAMC,SAASA,MAAAA;;AAAMD,iBAAMC,YAAWD,WAAME,cAANF,mBAAiBC;AAAAA;AACvD,QAAME,aAAaC,kBAAAA,YAAAA;AAEnB,QAAMC,YAAYC,QAAAA,WAAW,MAAA;;AAAMd,2BAAcS,kBAAAA,mBAAUR,QAAO,EAAE;AAAA,GAAC;AAErE,QAAMc,WAAWD,QAAAA,WAAW,MAAM;AAChC,UAAME,OAAOH,UAAAA;AACb,UAAMI,IAAIR,OAAAA;AAEV,QAAI,EAACQ,uBAAGhB,KAAK,QAAO;AAEpB,YAAQe,KAAKZ,UAAAA;AAAAA,MACX,KAAK,WAAW;AACd,cAAMc,WAAW,IAAIC,gBAAgB;AAAA,UACnCC,UAAUH,EAAEG,WAAW,MAAM;AAAA,UAC7BC,UAAUJ,EAAEI,aAAa,QAAQ,MAAM;AAAA,UACvCC,MAAML,EAAEK,OAAO,MAAM;AAAA,UACrBC,MAAMN,EAAEO,QAAQ,MAAM;AAAA,QAAA,CACvB;AACD,YAAIP,EAAEQ,WAAW;AACfP,mBAASQ,IAAI,SAASC,OAAOV,EAAEQ,SAAS,CAAC;AAAA,QAC3C;AAEA,eAAO,0CAA0CT,KAAKX,OAAO,IAAIa,QAAQ;AAAA,MAC3E;AAAA,MAEA,KAAK,SAAS;AACZ,cAAMU,UAAU,IAAIT,gBAAgB;AAAA,UAClCC,UAAUH,EAAEG,WAAW,MAAM;AAAA,UAC7BE,MAAML,EAAEK,OAAO,MAAM;AAAA,UACrBE,OAAOP,EAAEO,QAAQ,MAAM;AAAA,QAAA,CACxB;AACD,eAAO,kCAAkCR,KAAKX,OAAO,IAAIuB,OAAO;AAAA,MAClE;AAAA,MAEA;AACE,eAAO;AAAA,IAAA;AAAA,EAEb,CAAC;AAED,QAAMC,cAAcA,MAAM;;AACxB,aAAQpB,YAAAA,MAAAA,mBAAUqB,aAAAA;AAAAA,MAChB,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT;AACE,eAAO;AAAA,IAAA;AAAA,EAEb;AAEA,QAAMC,mBAAmBA,CAACC,MAAa;;AACrC,UAAMC,QAAQ,IAAIC,MAAM,sBAAsB;AAC9CC,YAAQF,MAAM,gBAAgBD,CAAC;AAC/BxB,gBAAM4B,YAAN5B,+BAAgByB;AAAAA,EAClB;AAEA,SAAAI,IAAAA,gBACGC,kBAAAA,mBAAiB;AAAA,IAAA,IAChBC,QAAK;;AAAA,eAAE9B,YAAAA,MAAAA,mBAAU8B,UAAS;AAAA,IAAO;AAAA,IAAA,IACjCC,WAAQ;;AAAA,eAAE/B,YAAAA,MAAAA,mBAAUR,QAAO;AAAA,IAAE;AAAA,IAC7BwC,WAAS;AAAA,IAAA,IAAAC,WAAA;AAAA,UAAAC,OAAAC,IAAAA,eAAAC,OAAA,GAAAC,QAAAH,KAAAI,YAAA,CAAAC,OAAAC,IAAA,IAAAC,IAAAA,cAAAJ,MAAAK,WAAA,GAAAC,QAAAJ,MAAAG,aAAAE,QAAAD,MAAAD,aAAA,CAAAG,OAAAC,KAAA,IAAAL,kBAAAG,MAAAF,WAAA;AAAAK,iBAAAb,MAAAN,IAAAA,gBAMRoB,cAAI;AAAA,QAAA,IAACC,OAAI;;AAAA,kBAAEjD,kBAAAA,mBAAU8B;AAAAA,QAAK;AAAA,QAAA,IAAAG,WAAA;AAAA,cAAAiB,QAAAf,IAAAA,eAAAgB,MAAA,GAAAC,QAAAF,MAAAZ;AAAAS,cAAAA,OAAAK,OAAA,MAE0CpD,OAAAA,EAAU8B,KAAK;AAAA,iBAAAoB;AAAAA,QAAA;AAAA,MAAA,CAAA,GAAAX,OAAAC,IAAA;AAAAO,iBAAAJ,OAAAf,IAAAA,gBAMjFoB,cAAI;AAAA,QAAA,IACHC,OAAI;AAAA,iBAAE3C,SAAAA;AAAAA,QAAU;AAAA,QAAA,IAChB+C,WAAQ;AAAA;AAAA;AAAA,aACN,MAAA;AAAA,kBAAAC,SAAAnB,IAAAA,eAAAoB,OAAA;AAAAD,qBAAAE,iBAAA,SAUWlC,gBAAgB;AAAAmC,kBAAAA,OAAAC,CAAAA,QAAA;;AAAA,oBAAAC,QARpB3D,kBAAAA,mBAAUR,KAAGoE,QACV5D,YAAAA,MAAAA,mBAAU6D,QAAMC,QACd9D,kBAAAA,mBAAUW,UAAQoD,SAClB/D,YAAAA,MAAAA,mBAAUY,cAAa,OAAKoD,QAChChE,kBAAAA,mBAAUa,MAAIoD,QACbjE,YAAAA,MAAAA,mBAAUe;AAAK4C,yBAAAD,IAAAnC,KAAA2C,IAAAA,aAAAZ,QAAA,OAAAI,IAAAnC,IAAAoC,IAAA;AAAAC,yBAAAF,IAAAS,KAAAD,IAAAA,aAAAZ,QAAA,UAAAI,IAAAS,IAAAP,IAAA;AAAAE,yBAAAJ,IAAAU,KAAAC,IAAAA,YAAAf,QAAA,YAAAI,IAAAU,IAAAN,IAAA;AAAAC,yBAAAL,IAAAY,KAAAD,IAAAA,YAAAf,QAAA,YAAAI,IAAAY,IAAAP,IAAA;AAAAC,yBAAAN,IAAAa,KAAAF,IAAAA,YAAAf,QAAA,QAAAI,IAAAa,IAAAP,IAAA;AAAAC,yBAAAP,IAAAc,KAAAH,IAAAA,YAAAf,QAAA,SAAAI,IAAAc,IAAAP,IAAA;AAAA,uBAAAP;AAAAA,cAAA,GAAA;AAAA,gBAAAnC,GAAAkD;AAAAA,gBAAAN,GAAAM;AAAAA,gBAAAL,GAAAK;AAAAA,gBAAAH,GAAAG;AAAAA,gBAAAF,GAAAE;AAAAA,gBAAAD,GAAAC;AAAAA,cAAAA,CAAA;AAAA,qBAAAnB;AAAAA,YAAA,GAAA;AAAA;AAAA,QAAA;AAAA,QAAA,IAAArB,WAAA;AAAA,cAAAyC,QAAAvC,IAAAA,eAAAwC,OAAA;AAAAlB,cAAAA,OAAAC,CAAAA,QAAA;;AAAA,gBAAAkB,MAYnBtE,SAAAA,GAAWuE,SACT7E,YAAAA,MAAAA,mBAAU8B,UAAS;AAAO8C,oBAAAlB,IAAAnC,KAAA2C,IAAAA,aAAAQ,OAAA,OAAAhB,IAAAnC,IAAAqD,GAAA;AAAAC,qBAAAnB,IAAAS,KAAAD,IAAAA,aAAAQ,OAAA,SAAAhB,IAAAS,IAAAU,IAAA;AAAA,mBAAAnB;AAAAA,UAAA,GAAA;AAAA,YAAAnC,GAAAkD;AAAAA,YAAAN,GAAAM;AAAAA,UAAAA,CAAA;AAAA,iBAAAC;AAAAA,QAAA;AAAA,MAAA,CAAA,CAAA;AAAA3B,iBAAAb,MAAAN,IAAAA,gBAUtCoB,cAAI;AAAA,QAAA,IAACC,OAAI;;AAAA,kBAAEjD,kBAAAA,mBAAU8E;AAAAA,QAAO;AAAA,QAAA,IAAA7C,WAAA;AAAA,cAAA8C,QAAA5C,IAAAA,eAAA6C,OAAA,GAAAC,QAAAF,MAAAzC;AAAAS,cAAAA,OAAAkC,OAAA,MAE4BjF,OAAAA,EAAU8E,OAAO;AAAA,iBAAAC;AAAAA,QAAA;AAAA,MAAA,CAAA,GAAAlC,OAAAC,KAAA;AAAAW,UAAAA,OAAAC,CAAAA,QAAA;AAAA,YAAAwB,OA/ChE,qHACVhF,WAAAA,IAAe,iCAAiC,EAAE,IAClDiF,OASY,qBAAqBjF,WAAAA,IAAe,mBAAmBkB,aAAa;AAAE8D,iBAAAxB,IAAAnC,KAAA6D,IAAAA,UAAAlD,MAAAwB,IAAAnC,IAAA2D,IAAA;AAAAC,iBAAAzB,IAAAS,KAAAiB,IAAAA,UAAAzC,OAAAe,IAAAS,IAAAgB,IAAA;AAAA,eAAAzB;AAAAA,MAAA,GAAA;AAAA,QAAAnC,GAAAkD;AAAAA,QAAAN,GAAAM;AAAAA,MAAAA,CAAA;AAAA,aAAAvC;AAAAA,IAAA;AAAA,EAAA,CAAA;AA0CxF;AAKO,SAASmD,oBAAoB7F,KAAsB;AACxD,QAAMe,OAAOhB,cAAcC,GAAG;AAC9B,SAAOe,KAAKZ,aAAa,YAAYH,IAAIE,MAAM,wBAAwB,MAAM;AAC/E;AAKO,SAAS4F,iBAAiB9F,KAA4B;AAC3D,SAAOD,cAAcC,GAAG,EAAEG;AAC5B;;;;"}
@@ -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;AAEjE,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;CACjC;AAED;;GAEG;AACH,KAAK,aAAa,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAA;AAsCnD,eAAO,MAAM,aAAa,EAAE,SAAS,CAAC,kBAAkB,CA8GvD,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"}
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;AAGjE,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;CACjC;AAED;;GAEG;AACH,KAAK,aAAa,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAA;AAsCnD,eAAO,MAAM,aAAa,EAAE,SAAS,CAAC,kBAAkB,CAuHvD,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"}
@@ -1,6 +1,7 @@
1
- import { getNextElement, template, getNextMarker, insert, createComponent, effect, setAttribute, setProperty, className } from "solid-js/web";
1
+ import { createComponent, getNextElement, template, getNextMarker, insert, effect, setAttribute, setProperty, className } from "solid-js/web";
2
2
  import { createMemo, Show } from "solid-js";
3
- 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"><p class="text-sm text-gray-600 dark:text-gray-400">`), _tmpl$4 = /* @__PURE__ */ template(`<div class="w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden"><!$><!/><div></div><!$><!/>`), _tmpl$5 = /* @__PURE__ */ template(`<video playsinline class="absolute inset-0 w-full h-full object-contain"><track kind=captions>Your browser does not support the video tag.`);
3
+ import { useExpanded, ExpandableWrapper } from "./ExpandableWrapper.js";
4
+ 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>Your browser does not support the video tag.`);
4
5
  function parseVideoUrl(url) {
5
6
  const youtubeMatch = url.match(/(?:youtube\.com\/(?:watch\?v=|embed\/|v\/)|youtu\.be\/)([a-zA-Z0-9_-]{11})/);
6
7
  if (youtubeMatch) {
@@ -25,6 +26,7 @@ const VideoRenderer = (props) => {
25
26
  var _a;
26
27
  return props.params || ((_a = props.component) == null ? void 0 : _a.params);
27
28
  };
29
+ const isExpanded = useExpanded();
28
30
  const videoInfo = createMemo(() => {
29
31
  var _a;
30
32
  return parseVideoUrl(((_a = params()) == null ? void 0 : _a.url) || "");
@@ -77,80 +79,99 @@ const VideoRenderer = (props) => {
77
79
  console.error("Video error:", e);
78
80
  (_a = props.onError) == null ? void 0 : _a.call(props, error);
79
81
  };
80
- return (() => {
81
- 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);
82
- insert(_el$, createComponent(Show, {
83
- get when() {
84
- var _a;
85
- return (_a = params()) == null ? void 0 : _a.title;
86
- },
87
- get children() {
88
- var _el$2 = getNextElement(_tmpl$), _el$3 = _el$2.firstChild;
89
- insert(_el$3, () => params().title);
90
- return _el$2;
91
- }
92
- }), _el$9, _co$);
93
- insert(_el$4, createComponent(Show, {
94
- get when() {
95
- return embedUrl();
96
- },
97
- get fallback() {
98
- return (
99
- // Direct video file
100
- (() => {
101
- var _el$10 = getNextElement(_tmpl$5);
102
- _el$10.addEventListener("error", handleVideoError);
103
- effect((_p$) => {
104
- var _a, _b, _c, _d, _e, _f;
105
- var _v$3 = (_a = params()) == null ? void 0 : _a.url, _v$4 = (_b = params()) == null ? void 0 : _b.poster, _v$5 = (_c = params()) == null ? void 0 : _c.autoplay, _v$6 = ((_d = params()) == null ? void 0 : _d.controls) !== false, _v$7 = (_e = params()) == null ? void 0 : _e.loop, _v$8 = (_f = params()) == null ? void 0 : _f.muted;
106
- _v$3 !== _p$.e && setAttribute(_el$10, "src", _p$.e = _v$3);
107
- _v$4 !== _p$.t && setAttribute(_el$10, "poster", _p$.t = _v$4);
108
- _v$5 !== _p$.a && setProperty(_el$10, "autoplay", _p$.a = _v$5);
109
- _v$6 !== _p$.o && setProperty(_el$10, "controls", _p$.o = _v$6);
110
- _v$7 !== _p$.i && setProperty(_el$10, "loop", _p$.i = _v$7);
111
- _v$8 !== _p$.n && setProperty(_el$10, "muted", _p$.n = _v$8);
112
- return _p$;
113
- }, {
114
- e: void 0,
115
- t: void 0,
116
- a: void 0,
117
- o: void 0,
118
- i: void 0,
119
- n: void 0
120
- });
121
- return _el$10;
122
- })()
123
- );
124
- },
125
- get children() {
126
- var _el$5 = getNextElement(_tmpl$2);
127
- effect((_p$) => {
82
+ return createComponent(ExpandableWrapper, {
83
+ get title() {
84
+ var _a;
85
+ return ((_a = params()) == null ? void 0 : _a.title) || "Video";
86
+ },
87
+ get copyData() {
88
+ var _a;
89
+ return ((_a = params()) == null ? void 0 : _a.url) || "";
90
+ },
91
+ copyLabel: "Copy video URL",
92
+ get children() {
93
+ 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);
94
+ insert(_el$, createComponent(Show, {
95
+ get when() {
128
96
  var _a;
129
- var _v$ = embedUrl(), _v$2 = ((_a = params()) == null ? void 0 : _a.title) || "Video";
130
- _v$ !== _p$.e && setAttribute(_el$5, "src", _p$.e = _v$);
131
- _v$2 !== _p$.t && setAttribute(_el$5, "title", _p$.t = _v$2);
132
- return _p$;
133
- }, {
134
- e: void 0,
135
- t: void 0
136
- });
137
- return _el$5;
138
- }
139
- }));
140
- insert(_el$, createComponent(Show, {
141
- get when() {
142
- var _a;
143
- return (_a = params()) == null ? void 0 : _a.caption;
144
- },
145
- get children() {
146
- var _el$6 = getNextElement(_tmpl$3), _el$7 = _el$6.firstChild;
147
- insert(_el$7, () => params().caption);
148
- return _el$6;
149
- }
150
- }), _el$1, _co$2);
151
- effect(() => className(_el$4, `relative ${aspectClass()} bg-black`));
152
- return _el$;
153
- })();
97
+ return (_a = params()) == null ? void 0 : _a.title;
98
+ },
99
+ get children() {
100
+ var _el$2 = getNextElement(_tmpl$), _el$3 = _el$2.firstChild;
101
+ insert(_el$3, () => params().title);
102
+ return _el$2;
103
+ }
104
+ }), _el$9, _co$);
105
+ insert(_el$4, createComponent(Show, {
106
+ get when() {
107
+ return embedUrl();
108
+ },
109
+ get fallback() {
110
+ return (
111
+ // Direct video file
112
+ (() => {
113
+ var _el$10 = getNextElement(_tmpl$5);
114
+ _el$10.addEventListener("error", handleVideoError);
115
+ effect((_p$) => {
116
+ var _a, _b, _c, _d, _e, _f;
117
+ var _v$5 = (_a = params()) == null ? void 0 : _a.url, _v$6 = (_b = params()) == null ? void 0 : _b.poster, _v$7 = (_c = params()) == null ? void 0 : _c.autoplay, _v$8 = ((_d = params()) == null ? void 0 : _d.controls) !== false, _v$9 = (_e = params()) == null ? void 0 : _e.loop, _v$0 = (_f = params()) == null ? void 0 : _f.muted;
118
+ _v$5 !== _p$.e && setAttribute(_el$10, "src", _p$.e = _v$5);
119
+ _v$6 !== _p$.t && setAttribute(_el$10, "poster", _p$.t = _v$6);
120
+ _v$7 !== _p$.a && setProperty(_el$10, "autoplay", _p$.a = _v$7);
121
+ _v$8 !== _p$.o && setProperty(_el$10, "controls", _p$.o = _v$8);
122
+ _v$9 !== _p$.i && setProperty(_el$10, "loop", _p$.i = _v$9);
123
+ _v$0 !== _p$.n && setProperty(_el$10, "muted", _p$.n = _v$0);
124
+ return _p$;
125
+ }, {
126
+ e: void 0,
127
+ t: void 0,
128
+ a: void 0,
129
+ o: void 0,
130
+ i: void 0,
131
+ n: void 0
132
+ });
133
+ return _el$10;
134
+ })()
135
+ );
136
+ },
137
+ get children() {
138
+ var _el$5 = getNextElement(_tmpl$2);
139
+ effect((_p$) => {
140
+ var _a;
141
+ var _v$ = embedUrl(), _v$2 = ((_a = params()) == null ? void 0 : _a.title) || "Video";
142
+ _v$ !== _p$.e && setAttribute(_el$5, "src", _p$.e = _v$);
143
+ _v$2 !== _p$.t && setAttribute(_el$5, "title", _p$.t = _v$2);
144
+ return _p$;
145
+ }, {
146
+ e: void 0,
147
+ t: void 0
148
+ });
149
+ return _el$5;
150
+ }
151
+ }));
152
+ insert(_el$, createComponent(Show, {
153
+ get when() {
154
+ var _a;
155
+ return (_a = params()) == null ? void 0 : _a.caption;
156
+ },
157
+ get children() {
158
+ var _el$6 = getNextElement(_tmpl$3), _el$7 = _el$6.firstChild;
159
+ insert(_el$7, () => params().caption);
160
+ return _el$6;
161
+ }
162
+ }), _el$1, _co$2);
163
+ effect((_p$) => {
164
+ var _v$3 = `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$4 = `relative bg-black ${isExpanded() ? "flex-1 min-h-0" : aspectClass()}`;
165
+ _v$3 !== _p$.e && className(_el$, _p$.e = _v$3);
166
+ _v$4 !== _p$.t && className(_el$4, _p$.t = _v$4);
167
+ return _p$;
168
+ }, {
169
+ e: void 0,
170
+ t: void 0
171
+ });
172
+ return _el$;
173
+ }
174
+ });
154
175
  };
155
176
  function isSupportedVideoUrl(url) {
156
177
  const info = parseVideoUrl(url);
@@ -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'\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/**\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 params = () => props.params || (props.component?.params as VideoComponentParams)\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 <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 {/* 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 */}\n <div class={`relative ${aspectClass()} bg-black`}>\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 Your browser does not support the video tag.\n </video>\n }\n >\n {/* YouTube/Vimeo embed */}\n <iframe\n src={embedUrl()!}\n title={params()?.title || 'Video'}\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\">\n <p class=\"text-sm text-gray-600 dark:text-gray-400\">{params()!.caption}</p>\n </div>\n </Show>\n </div>\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","params","component","videoInfo","createMemo","embedUrl","info","p","ytParams","URLSearchParams","autoplay","controls","loop","mute","muted","startTime","set","String","vParams","aspectClass","aspectRatio","handleVideoError","e","error","Error","console","onError","_el$","_$getNextElement","_tmpl$4","_el$8","firstChild","_el$9","_co$","_$getNextMarker","nextSibling","_el$4","_el$0","_el$1","_co$2","_$insert","_$createComponent","Show","when","title","children","_el$2","_tmpl$","_el$3","fallback","_el$10","_tmpl$5","addEventListener","_$effect","_p$","_v$3","_v$4","poster","_v$5","_v$6","_v$7","_v$8","_$setAttribute","t","a","_$setProperty","o","i","n","undefined","_el$5","_tmpl$2","_v$","_v$2","caption","_el$6","_tmpl$3","_el$7","_$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'\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/**\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 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 || 'Video'}\n copyData={params()?.url || ''}\n copyLabel=\"Copy video URL\"\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 Your browser does not support the video tag.\n </video>\n }\n >\n {/* YouTube/Vimeo embed */}\n <iframe\n src={embedUrl()!}\n title={params()?.title || 'Video'}\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","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","copyData","copyLabel","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","addEventListener","_$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":";;;;AA4CA,SAASA,cAAcC,KAAwB;AAM7C,QAAMC,eAAeD,IAAIE,MACvB,4EACF;AACA,MAAID,cAAc;AAChB,WAAO;AAAA,MAAEE,UAAU;AAAA,MAAWC,SAASH,aAAa,CAAC;AAAA,IAAA;AAAA,EACvD;AAKA,QAAMI,aAAaL,IAAIE,MAAM,mDAAmD;AAChF,MAAIG,YAAY;AACd,WAAO;AAAA,MAAEF,UAAU;AAAA,MAASC,SAASC,WAAW,CAAC;AAAA,IAAA;AAAA,EACnD;AAGA,SAAO;AAAA,IAAEF,UAAU;AAAA,EAAA;AACrB;AAEO,MAAMG,gBAAgDC,CAAAA,UAAU;AACrE,QAAMC,SAASA,MAAAA;;AAAMD,iBAAMC,YAAWD,WAAME,cAANF,mBAAiBC;AAAAA;AACvD,QAAME,aAAaC,YAAAA;AAEnB,QAAMC,YAAYC,WAAW,MAAA;;AAAMd,2BAAcS,kBAAAA,mBAAUR,QAAO,EAAE;AAAA,GAAC;AAErE,QAAMc,WAAWD,WAAW,MAAM;AAChC,UAAME,OAAOH,UAAAA;AACb,UAAMI,IAAIR,OAAAA;AAEV,QAAI,EAACQ,uBAAGhB,KAAK,QAAO;AAEpB,YAAQe,KAAKZ,UAAAA;AAAAA,MACX,KAAK,WAAW;AACd,cAAMc,WAAW,IAAIC,gBAAgB;AAAA,UACnCC,UAAUH,EAAEG,WAAW,MAAM;AAAA,UAC7BC,UAAUJ,EAAEI,aAAa,QAAQ,MAAM;AAAA,UACvCC,MAAML,EAAEK,OAAO,MAAM;AAAA,UACrBC,MAAMN,EAAEO,QAAQ,MAAM;AAAA,QAAA,CACvB;AACD,YAAIP,EAAEQ,WAAW;AACfP,mBAASQ,IAAI,SAASC,OAAOV,EAAEQ,SAAS,CAAC;AAAA,QAC3C;AAEA,eAAO,0CAA0CT,KAAKX,OAAO,IAAIa,QAAQ;AAAA,MAC3E;AAAA,MAEA,KAAK,SAAS;AACZ,cAAMU,UAAU,IAAIT,gBAAgB;AAAA,UAClCC,UAAUH,EAAEG,WAAW,MAAM;AAAA,UAC7BE,MAAML,EAAEK,OAAO,MAAM;AAAA,UACrBE,OAAOP,EAAEO,QAAQ,MAAM;AAAA,QAAA,CACxB;AACD,eAAO,kCAAkCR,KAAKX,OAAO,IAAIuB,OAAO;AAAA,MAClE;AAAA,MAEA;AACE,eAAO;AAAA,IAAA;AAAA,EAEb,CAAC;AAED,QAAMC,cAAcA,MAAM;;AACxB,aAAQpB,YAAAA,MAAAA,mBAAUqB,aAAAA;AAAAA,MAChB,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT;AACE,eAAO;AAAA,IAAA;AAAA,EAEb;AAEA,QAAMC,mBAAmBA,CAACC,MAAa;;AACrC,UAAMC,QAAQ,IAAIC,MAAM,sBAAsB;AAC9CC,YAAQF,MAAM,gBAAgBD,CAAC;AAC/BxB,gBAAM4B,YAAN5B,+BAAgByB;AAAAA,EAClB;AAEA,SAAAI,gBACGC,mBAAiB;AAAA,IAAA,IAChBC,QAAK;;AAAA,eAAE9B,YAAAA,MAAAA,mBAAU8B,UAAS;AAAA,IAAO;AAAA,IAAA,IACjCC,WAAQ;;AAAA,eAAE/B,YAAAA,MAAAA,mBAAUR,QAAO;AAAA,IAAE;AAAA,IAC7BwC,WAAS;AAAA,IAAA,IAAAC,WAAA;AAAA,UAAAC,OAAAC,eAAAC,OAAA,GAAAC,QAAAH,KAAAI,YAAA,CAAAC,OAAAC,IAAA,IAAAC,cAAAJ,MAAAK,WAAA,GAAAC,QAAAJ,MAAAG,aAAAE,QAAAD,MAAAD,aAAA,CAAAG,OAAAC,KAAA,IAAAL,cAAAG,MAAAF,WAAA;AAAAK,aAAAb,MAAAN,gBAMRoB,MAAI;AAAA,QAAA,IAACC,OAAI;;AAAA,kBAAEjD,kBAAAA,mBAAU8B;AAAAA,QAAK;AAAA,QAAA,IAAAG,WAAA;AAAA,cAAAiB,QAAAf,eAAAgB,MAAA,GAAAC,QAAAF,MAAAZ;AAAAS,iBAAAK,OAAA,MAE0CpD,OAAAA,EAAU8B,KAAK;AAAA,iBAAAoB;AAAAA,QAAA;AAAA,MAAA,CAAA,GAAAX,OAAAC,IAAA;AAAAO,aAAAJ,OAAAf,gBAMjFoB,MAAI;AAAA,QAAA,IACHC,OAAI;AAAA,iBAAE3C,SAAAA;AAAAA,QAAU;AAAA,QAAA,IAChB+C,WAAQ;AAAA;AAAA;AAAA,aACN,MAAA;AAAA,kBAAAC,SAAAnB,eAAAoB,OAAA;AAAAD,qBAAAE,iBAAA,SAUWlC,gBAAgB;AAAAmC,qBAAAC,CAAAA,QAAA;;AAAA,oBAAAC,QARpB3D,kBAAAA,mBAAUR,KAAGoE,QACV5D,YAAAA,MAAAA,mBAAU6D,QAAMC,QACd9D,kBAAAA,mBAAUW,UAAQoD,SAClB/D,YAAAA,MAAAA,mBAAUY,cAAa,OAAKoD,QAChChE,kBAAAA,mBAAUa,MAAIoD,QACbjE,YAAAA,MAAAA,mBAAUe;AAAK4C,yBAAAD,IAAAnC,KAAA2C,aAAAZ,QAAA,OAAAI,IAAAnC,IAAAoC,IAAA;AAAAC,yBAAAF,IAAAS,KAAAD,aAAAZ,QAAA,UAAAI,IAAAS,IAAAP,IAAA;AAAAE,yBAAAJ,IAAAU,KAAAC,YAAAf,QAAA,YAAAI,IAAAU,IAAAN,IAAA;AAAAC,yBAAAL,IAAAY,KAAAD,YAAAf,QAAA,YAAAI,IAAAY,IAAAP,IAAA;AAAAC,yBAAAN,IAAAa,KAAAF,YAAAf,QAAA,QAAAI,IAAAa,IAAAP,IAAA;AAAAC,yBAAAP,IAAAc,KAAAH,YAAAf,QAAA,SAAAI,IAAAc,IAAAP,IAAA;AAAA,uBAAAP;AAAAA,cAAA,GAAA;AAAA,gBAAAnC,GAAAkD;AAAAA,gBAAAN,GAAAM;AAAAA,gBAAAL,GAAAK;AAAAA,gBAAAH,GAAAG;AAAAA,gBAAAF,GAAAE;AAAAA,gBAAAD,GAAAC;AAAAA,cAAAA,CAAA;AAAA,qBAAAnB;AAAAA,YAAA,GAAA;AAAA;AAAA,QAAA;AAAA,QAAA,IAAArB,WAAA;AAAA,cAAAyC,QAAAvC,eAAAwC,OAAA;AAAAlB,iBAAAC,CAAAA,QAAA;;AAAA,gBAAAkB,MAYnBtE,SAAAA,GAAWuE,SACT7E,YAAAA,MAAAA,mBAAU8B,UAAS;AAAO8C,oBAAAlB,IAAAnC,KAAA2C,aAAAQ,OAAA,OAAAhB,IAAAnC,IAAAqD,GAAA;AAAAC,qBAAAnB,IAAAS,KAAAD,aAAAQ,OAAA,SAAAhB,IAAAS,IAAAU,IAAA;AAAA,mBAAAnB;AAAAA,UAAA,GAAA;AAAA,YAAAnC,GAAAkD;AAAAA,YAAAN,GAAAM;AAAAA,UAAAA,CAAA;AAAA,iBAAAC;AAAAA,QAAA;AAAA,MAAA,CAAA,CAAA;AAAA3B,aAAAb,MAAAN,gBAUtCoB,MAAI;AAAA,QAAA,IAACC,OAAI;;AAAA,kBAAEjD,kBAAAA,mBAAU8E;AAAAA,QAAO;AAAA,QAAA,IAAA7C,WAAA;AAAA,cAAA8C,QAAA5C,eAAA6C,OAAA,GAAAC,QAAAF,MAAAzC;AAAAS,iBAAAkC,OAAA,MAE4BjF,OAAAA,EAAU8E,OAAO;AAAA,iBAAAC;AAAAA,QAAA;AAAA,MAAA,CAAA,GAAAlC,OAAAC,KAAA;AAAAW,aAAAC,CAAAA,QAAA;AAAA,YAAAwB,OA/ChE,qHACVhF,WAAAA,IAAe,iCAAiC,EAAE,IAClDiF,OASY,qBAAqBjF,WAAAA,IAAe,mBAAmBkB,aAAa;AAAE8D,iBAAAxB,IAAAnC,KAAA6D,UAAAlD,MAAAwB,IAAAnC,IAAA2D,IAAA;AAAAC,iBAAAzB,IAAAS,KAAAiB,UAAAzC,OAAAe,IAAAS,IAAAgB,IAAA;AAAA,eAAAzB;AAAAA,MAAA,GAAA;AAAA,QAAAnC,GAAAkD;AAAAA,QAAAN,GAAAM;AAAAA,MAAAA,CAAA;AAAA,aAAAvC;AAAAA,IAAA;AAAA,EAAA,CAAA;AA0CxF;AAKO,SAASmD,oBAAoB7F,KAAsB;AACxD,QAAMe,OAAOhB,cAAcC,GAAG;AAC9B,SAAOe,KAAKZ,aAAa,YAAYH,IAAIE,MAAM,wBAAwB,MAAM;AAC/E;AAKO,SAAS4F,iBAAiB9F,KAA4B;AAC3D,SAAOD,cAAcC,GAAG,EAAEG;AAC5B;"}
package/dist/index.cjs CHANGED
@@ -15,6 +15,7 @@ require("./components/ActionGroupRenderer.cjs");
15
15
  require("./components/ChartJSRenderer.cjs");
16
16
  require("./components/LightboxOverlay.cjs");
17
17
  require("./components/ImageGalleryRenderer.cjs");
18
+ const ExpandableWrapper = require("./components/ExpandableWrapper.cjs");
18
19
  require("./components/CodeBlockRenderer.cjs");
19
20
  const AgentCard = require("./components/AgentCard.cjs");
20
21
  const SplitStepper = require("./components/SplitStepper.cjs");
@@ -26,7 +27,6 @@ const ElicitationForm = require("./components/ElicitationForm.cjs");
26
27
  const DraggableGridItem = require("./components/DraggableGridItem.cjs");
27
28
  const ResizeHandle = require("./components/ResizeHandle.cjs");
28
29
  const EditableUIResourceRenderer = require("./components/EditableUIResourceRenderer.cjs");
29
- const ExpandableWrapper = require("./components/ExpandableWrapper.cjs");
30
30
  const ComponentToolbar = require("./components/ComponentToolbar.cjs");
31
31
  const FeedbackInline = require("./components/FeedbackInline.cjs");
32
32
  const useChatBus = require("./hooks/useChatBus.cjs");
@@ -66,6 +66,8 @@ exports.UIResourceRenderer = UIResourceRenderer.UIResourceRenderer;
66
66
  exports.renderCellValue = UIResourceRenderer.renderCellValue;
67
67
  exports.StreamingUIRenderer = StreamingUIRenderer.StreamingUIRenderer;
68
68
  exports.GenerativeUIErrorBoundary = GenerativeUIErrorBoundary.GenerativeUIErrorBoundary;
69
+ exports.ExpandableWrapper = ExpandableWrapper.ExpandableWrapper;
70
+ exports.useExpanded = ExpandableWrapper.useExpanded;
69
71
  exports.AgentCard = AgentCard.AgentCard;
70
72
  exports.AgentStatusBadge = AgentCard.AgentStatusBadge;
71
73
  exports.SplitStepper = SplitStepper.SplitStepper;
@@ -77,8 +79,6 @@ exports.ElicitationForm = ElicitationForm.ElicitationForm;
77
79
  exports.DraggableGridItem = DraggableGridItem.DraggableGridItem;
78
80
  exports.ResizeHandle = ResizeHandle.ResizeHandle;
79
81
  exports.EditableUIResourceRenderer = EditableUIResourceRenderer.EditableUIResourceRenderer;
80
- exports.ExpandableWrapper = ExpandableWrapper.ExpandableWrapper;
81
- exports.useExpanded = ExpandableWrapper.useExpanded;
82
82
  exports.ComponentToolbar = ComponentToolbar.ComponentToolbar;
83
83
  exports.FeedbackInline = FeedbackInline.FeedbackInline;
84
84
  exports.ChatBusProvider = useChatBus.ChatBusProvider;
package/dist/index.js CHANGED
@@ -13,6 +13,7 @@ import "./components/ActionGroupRenderer.js";
13
13
  import "./components/ChartJSRenderer.js";
14
14
  import "./components/LightboxOverlay.js";
15
15
  import "./components/ImageGalleryRenderer.js";
16
+ import { ExpandableWrapper, useExpanded } from "./components/ExpandableWrapper.js";
16
17
  import "./components/CodeBlockRenderer.js";
17
18
  import { AgentCard, AgentStatusBadge } from "./components/AgentCard.js";
18
19
  import { SplitStepper } from "./components/SplitStepper.js";
@@ -24,7 +25,6 @@ import { ElicitationForm } from "./components/ElicitationForm.js";
24
25
  import { DraggableGridItem } from "./components/DraggableGridItem.js";
25
26
  import { ResizeHandle } from "./components/ResizeHandle.js";
26
27
  import { EditableUIResourceRenderer } from "./components/EditableUIResourceRenderer.js";
27
- import { ExpandableWrapper, useExpanded } from "./components/ExpandableWrapper.js";
28
28
  import { ComponentToolbar } from "./components/ComponentToolbar.js";
29
29
  import { FeedbackInline } from "./components/FeedbackInline.js";
30
30
  import { ChatBusProvider, useChatBus } from "./hooks/useChatBus.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seed-ship/mcp-ui-solid",
3
- "version": "6.0.0",
3
+ "version": "6.2.0",
4
4
  "description": "SolidJS components for rendering MCP-generated UI resources",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -2,6 +2,7 @@ import { Component, For, createSignal } from 'solid-js'
2
2
  import { isServer } from 'solid-js/web'
3
3
  import { useRenderContext } from './RenderContext'
4
4
  import type { UIComponent } from '../types'
5
+ import { ExpandableWrapper, useExpanded } from './ExpandableWrapper'
5
6
 
6
7
  export interface CarouselRendererProps {
7
8
  items: UIComponent[]
@@ -12,6 +13,7 @@ export const CarouselRenderer: Component<CarouselRendererProps> = (props) => {
12
13
  let scrollContainer: HTMLDivElement | undefined
13
14
  const [canScrollLeft, setCanScrollLeft] = createSignal(false)
14
15
  const [canScrollRight, setCanScrollRight] = createSignal(true)
16
+ const isExpanded = useExpanded()
15
17
 
16
18
  // Use render context to avoid circular dependency
17
19
  const { renderComponent } = useRenderContext()
@@ -34,7 +36,12 @@ export const CarouselRenderer: Component<CarouselRendererProps> = (props) => {
34
36
  }
35
37
 
36
38
  return (
37
- <div class="relative group">
39
+ <ExpandableWrapper
40
+ title={'Carousel'}
41
+ copyData={JSON.stringify(props.items, null, 2)}
42
+ copyLabel="Copy items (JSON)"
43
+ >
44
+ <div class={`relative group ${isExpanded() ? 'flex-1 min-h-0 flex flex-col' : ''}`}>
38
45
  {/* Navigation Buttons */}
39
46
  <button
40
47
  onClick={() => scroll('left')}
@@ -74,5 +81,6 @@ export const CarouselRenderer: Component<CarouselRendererProps> = (props) => {
74
81
  </For>
75
82
  </div>
76
83
  </div>
84
+ </ExpandableWrapper>
77
85
  )
78
86
  }