sitepaige-mcp-server 0.5.0 → 0.6.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.
@@ -106,6 +106,9 @@ export default function Menu({ menu, onClick, pages = [] }: MenuProps) {
106
106
  if (item.link_type === 'external' && item.external_url) {
107
107
  linkUrl = item.external_url;
108
108
  isExternal = true;
109
+ } else if (item.link_type === 'file' && item.file_name) {
110
+ linkUrl = `/library/files/${item.file_name}`;
111
+ isExternal = false;
109
112
  } else if (item.page && pages.length > 0) {
110
113
  const page = pages.find(p => p.id === item.page);
111
114
  if (page) {
@@ -4,14 +4,13 @@ import React from 'react';
4
4
 
5
5
  export interface PhotoGalleryData {
6
6
  photos: Array<{
7
- id: string;
8
- imageId: string;
9
- label: string;
7
+ fileName: string; // Required: filename in library/images
8
+ label?: string; // Optional label/caption
10
9
  }>;
11
- gridConfig: {
12
- columns: number;
13
- rows?: number; // Optional: if not set, rows are dynamic based on photo count
14
- gap: number; // Gap between photos in pixels
10
+ gridConfig?: {
11
+ columns?: number;
12
+ rows?: number;
13
+ gap?: number;
15
14
  };
16
15
  }
17
16
 
@@ -36,7 +35,12 @@ export default function RPhotoGallery({
36
35
  galleryData = { photos: [], gridConfig: { columns: 3, gap: 16 } };
37
36
  }
38
37
 
39
- const { photos = [], gridConfig = { columns: 3, gap: 16 } } = galleryData;
38
+ const { photos = [], gridConfig = {} } = galleryData;
39
+
40
+ // Default grid config
41
+ const columns = gridConfig.columns || 3;
42
+ const gap = gridConfig.gap || 16;
43
+ const rows = gridConfig.rows;
40
44
 
41
45
  // If no photos, show placeholder
42
46
  if (!photos || photos.length === 0) {
@@ -62,24 +66,24 @@ export default function RPhotoGallery({
62
66
  <div
63
67
  className="grid w-full"
64
68
  style={{
65
- gridTemplateColumns: `repeat(${gridConfig.columns}, minmax(0, 1fr))`,
66
- gap: `${gridConfig.gap}px`,
67
- ...(gridConfig.rows && {
68
- gridTemplateRows: `repeat(${gridConfig.rows}, minmax(0, 1fr))`
69
+ gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
70
+ gap: `${gap}px`,
71
+ ...(rows && {
72
+ gridTemplateRows: `repeat(${rows}, minmax(0, 1fr))`
69
73
  })
70
74
  }}
71
75
  >
72
76
  {photos.map((photo, index) => (
73
- <div key={photo.id || index} className="relative group">
77
+ <div key={index} className="relative group">
74
78
  <div className="aspect-square overflow-hidden rounded-lg bg-gray-100">
75
79
  <img
76
- src={`/api/image?imageid=${photo.imageId}`}
80
+ src={`/library/images/${photo.fileName}`}
77
81
  alt={photo.label || `Photo ${index + 1}`}
78
82
  className="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"
79
83
  loading="lazy"
80
84
  onError={(e) => {
81
- console.error('Error loading image:', photo.imageId);
82
- (e.target as HTMLImageElement).src = '/placeholder-gray.png';
85
+ console.error('Error loading image:', photo.fileName);
86
+ (e.target as HTMLImageElement).src = '/placeholder.png';
83
87
  }}
84
88
  />
85
89
  </div>
@@ -0,0 +1,267 @@
1
+ 'use client';
2
+
3
+ import React, { useState } from 'react';
4
+
5
+ export interface VideoGalleryData {
6
+ videos: Array<{
7
+ type: 'upload' | 'youtube' | 'vimeo' | 'external';
8
+ fileName?: string; // For uploaded videos - filename in library/videos
9
+ url?: string; // For YouTube/Vimeo/external videos
10
+ title?: string;
11
+ description?: string;
12
+ thumbnailFileName?: string; // Optional thumbnail filename in library/videos
13
+ }>;
14
+ gridConfig?: {
15
+ columns?: number;
16
+ gap?: number;
17
+ };
18
+ playerConfig?: {
19
+ autoplay?: boolean;
20
+ muted?: boolean;
21
+ controls?: boolean;
22
+ loop?: boolean;
23
+ };
24
+ displayConfig?: {
25
+ showTitle?: boolean;
26
+ showDescription?: boolean;
27
+ };
28
+ }
29
+
30
+ export default function RVideoGallery({
31
+ config,
32
+ name
33
+ }: {
34
+ config: string | VideoGalleryData;
35
+ name: string;
36
+ }) {
37
+ const [playingVideo, setPlayingVideo] = useState<string | null>(null);
38
+
39
+ // Parse the gallery data
40
+ let galleryData: VideoGalleryData;
41
+
42
+ try {
43
+ if (typeof config === 'string') {
44
+ galleryData = config ? JSON.parse(config) : {
45
+ videos: [],
46
+ gridConfig: { columns: 2, gap: 16 },
47
+ playerConfig: { autoplay: false, muted: false, controls: true, loop: false }
48
+ };
49
+ } else {
50
+ galleryData = config;
51
+ }
52
+ } catch (e) {
53
+ console.error('Error parsing video gallery data:', e);
54
+ galleryData = {
55
+ videos: [],
56
+ gridConfig: { columns: 2, gap: 16 },
57
+ playerConfig: { autoplay: false, muted: false, controls: true, loop: false }
58
+ };
59
+ }
60
+
61
+ const {
62
+ videos = [],
63
+ gridConfig = {},
64
+ playerConfig = {},
65
+ displayConfig = {}
66
+ } = galleryData;
67
+
68
+ // Default config values
69
+ const columns = gridConfig.columns || 2;
70
+ const gap = gridConfig.gap || 16;
71
+ const autoplay = playerConfig.autoplay ?? false;
72
+ const muted = playerConfig.muted ?? false;
73
+ const controls = playerConfig.controls ?? true;
74
+ const loop = playerConfig.loop ?? false;
75
+ const showTitle = displayConfig.showTitle ?? true;
76
+ const showDescription = displayConfig.showDescription ?? true;
77
+
78
+ // If no videos, show placeholder
79
+ if (!videos || videos.length === 0) {
80
+ return (
81
+ <div className="w-full">
82
+ <h1>{name}</h1>
83
+ <div className="p-8 text-center bg-gray-50 rounded-lg border-2 border-dashed border-gray-300">
84
+ <div className="text-gray-400">
85
+ <svg className="mx-auto h-12 w-12 mb-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
86
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" />
87
+ </svg>
88
+ <p className="text-sm font-medium">No videos in gallery</p>
89
+ <p className="text-xs mt-1">Add videos in the editor</p>
90
+ </div>
91
+ </div>
92
+ </div>
93
+ );
94
+ }
95
+
96
+ const renderVideo = (video: VideoGalleryData['videos'][0], index: number) => {
97
+ const videoKey = `${video.type}-${index}`;
98
+ const isPlaying = playingVideo === videoKey;
99
+
100
+ if (video.type === 'youtube' && video.url) {
101
+ // Extract YouTube video ID
102
+ const videoId = extractYouTubeId(video.url);
103
+ if (!videoId) return null;
104
+
105
+ return (
106
+ <div className="aspect-video bg-black rounded-lg overflow-hidden">
107
+ {isPlaying ? (
108
+ <iframe
109
+ src={`https://www.youtube.com/embed/${videoId}?autoplay=1${autoplay ? '&mute=1' : ''}${loop ? '&loop=1&playlist=' + videoId : ''}${controls ? '' : '&controls=0'}`}
110
+ className="w-full h-full"
111
+ allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
112
+ allowFullScreen
113
+ />
114
+ ) : (
115
+ <div
116
+ className="relative w-full h-full cursor-pointer group"
117
+ onClick={() => setPlayingVideo(videoKey)}
118
+ >
119
+ <img
120
+ src={`https://img.youtube.com/vi/${videoId}/maxresdefault.jpg`}
121
+ alt={video.title || 'Video'}
122
+ className="w-full h-full object-cover"
123
+ onError={(e) => {
124
+ // Fallback to lower quality thumbnail
125
+ (e.target as HTMLImageElement).src = `https://img.youtube.com/vi/${videoId}/hqdefault.jpg`;
126
+ }}
127
+ />
128
+ <div className="absolute inset-0 flex items-center justify-center bg-black bg-opacity-30 group-hover:bg-opacity-50 transition-all">
129
+ <div className="w-16 h-16 bg-red-600 rounded-full flex items-center justify-center">
130
+ <svg className="w-8 h-8 text-white ml-1" fill="currentColor" viewBox="0 0 20 20">
131
+ <path d="M6.3 2.841A1.5 1.5 0 004 4.11v11.78a1.5 1.5 0 002.3 1.269l9.344-5.89a1.5 1.5 0 000-2.538L6.3 2.84z" />
132
+ </svg>
133
+ </div>
134
+ </div>
135
+ </div>
136
+ )}
137
+ </div>
138
+ );
139
+ } else if (video.type === 'vimeo' && video.url) {
140
+ // Extract Vimeo video ID
141
+ const videoId = extractVimeoId(video.url);
142
+ if (!videoId) return null;
143
+
144
+ return (
145
+ <div className="aspect-video bg-black rounded-lg overflow-hidden">
146
+ {isPlaying ? (
147
+ <iframe
148
+ src={`https://player.vimeo.com/video/${videoId}?autoplay=1${muted ? '&muted=1' : ''}${loop ? '&loop=1' : ''}${!controls ? '&controls=0' : ''}`}
149
+ className="w-full h-full"
150
+ allow="autoplay; fullscreen; picture-in-picture"
151
+ allowFullScreen
152
+ />
153
+ ) : (
154
+ <div
155
+ className="relative w-full h-full cursor-pointer group"
156
+ onClick={() => setPlayingVideo(videoKey)}
157
+ >
158
+ <div className="w-full h-full bg-gray-800 flex items-center justify-center">
159
+ <div className="text-center">
160
+ <div className="w-16 h-16 bg-blue-500 rounded-full flex items-center justify-center mx-auto mb-2">
161
+ <svg className="w-8 h-8 text-white ml-1" fill="currentColor" viewBox="0 0 20 20">
162
+ <path d="M6.3 2.841A1.5 1.5 0 004 4.11v11.78a1.5 1.5 0 002.3 1.269l9.344-5.89a1.5 1.5 0 000-2.538L6.3 2.84z" />
163
+ </svg>
164
+ </div>
165
+ <p className="text-white text-sm">Click to play</p>
166
+ </div>
167
+ </div>
168
+ </div>
169
+ )}
170
+ </div>
171
+ );
172
+ } else if (video.type === 'upload' && video.fileName) {
173
+ // Uploaded video
174
+ const videoSrc = `/library/videos/${video.fileName}`;
175
+ const posterSrc = video.thumbnailFileName ? `/library/videos/${video.thumbnailFileName}` : undefined;
176
+
177
+ return (
178
+ <div className="aspect-video bg-black rounded-lg overflow-hidden">
179
+ <video
180
+ src={videoSrc}
181
+ poster={posterSrc}
182
+ controls={controls}
183
+ autoPlay={autoplay}
184
+ muted={autoplay || muted}
185
+ loop={loop}
186
+ className="w-full h-full object-contain"
187
+ onError={(e) => {
188
+ console.error('Error loading video:', video.fileName);
189
+ }}
190
+ >
191
+ Your browser does not support the video tag.
192
+ </video>
193
+ </div>
194
+ );
195
+ } else if (video.type === 'external' && video.url) {
196
+ // External video URL
197
+ return (
198
+ <div className="aspect-video bg-black rounded-lg overflow-hidden">
199
+ <video
200
+ src={video.url}
201
+ controls={controls}
202
+ autoPlay={autoplay}
203
+ muted={autoplay || muted}
204
+ loop={loop}
205
+ className="w-full h-full object-contain"
206
+ onError={(e) => {
207
+ console.error('Error loading video:', video.url);
208
+ }}
209
+ >
210
+ Your browser does not support the video tag.
211
+ </video>
212
+ </div>
213
+ );
214
+ }
215
+
216
+ return null;
217
+ };
218
+
219
+ return (
220
+ <div className="w-full">
221
+ <h1>{name}</h1>
222
+ <div
223
+ className="grid w-full"
224
+ style={{
225
+ gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
226
+ gap: `${gap}px`
227
+ }}
228
+ >
229
+ {videos.map((video, index) => (
230
+ <div key={index} className="relative">
231
+ {renderVideo(video, index)}
232
+ {showTitle && video.title && (
233
+ <h3 className="mt-2 text-lg font-semibold text-gray-800 text-center">
234
+ {video.title}
235
+ </h3>
236
+ )}
237
+ {showDescription && video.description && (
238
+ <p className="mt-1 text-sm text-gray-600 text-center">
239
+ {video.description}
240
+ </p>
241
+ )}
242
+ </div>
243
+ ))}
244
+ </div>
245
+ </div>
246
+ );
247
+ }
248
+
249
+ // Helper functions to extract video IDs
250
+ function extractYouTubeId(url: string): string | null {
251
+ const patterns = [
252
+ /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)/,
253
+ /youtube\.com\/watch\?.*v=([^&\n?#]+)/
254
+ ];
255
+
256
+ for (const pattern of patterns) {
257
+ const match = url.match(pattern);
258
+ if (match && match[1]) return match[1];
259
+ }
260
+
261
+ return null;
262
+ }
263
+
264
+ function extractVimeoId(url: string): string | null {
265
+ const match = url.match(/vimeo\.com\/(\d+)/);
266
+ return match ? match[1] : null;
267
+ }
@@ -27,8 +27,10 @@ interface MenuItem {
27
27
  page: string | null;
28
28
  menu: string | null;
29
29
  untouchable: boolean;
30
- link_type?: 'page' | 'external';
30
+ link_type?: 'page' | 'external' | 'file';
31
31
  external_url?: string | null;
32
+ file_id?: string | null;
33
+ file_name?: string | null;
32
34
  hiddenOnDesktop?: boolean; // New field to hide item on desktop (shows in icon bar instead)
33
35
  }
34
36
 
@@ -151,4 +151,33 @@ export async function writeProjectBackendOnly(project, options) {
151
151
  // Write architecture documentation (now includes backend)
152
152
  await writeArchitectureDoc(targetDir, blueprint);
153
153
  }
154
+ // Write a library file (image, document, video) to the public/library directory
155
+ export async function writeLibraryFile(targetDir, fileType, fileName, base64Data) {
156
+ // Determine the library path based on file type
157
+ const libraryPaths = {
158
+ 'image': 'library/images',
159
+ 'video': 'library/videos',
160
+ 'file': 'library/files'
161
+ };
162
+ const libraryPath = libraryPaths[fileType];
163
+ const fullPath = path.join(targetDir, 'public', libraryPath);
164
+ ensureDir(fullPath);
165
+ // Ensure filename is safe and unique
166
+ const safeFileName = fileName.replace(/[^a-zA-Z0-9._-]/g, '_');
167
+ const filePath = path.join(fullPath, safeFileName);
168
+ try {
169
+ // Remove data URL prefix if present
170
+ const base64Clean = base64Data.replace(/^data:[^;]+;base64,/, '');
171
+ // Convert base64 to buffer and write file
172
+ const buffer = Buffer.from(base64Clean, 'base64');
173
+ await fsp.writeFile(filePath, buffer);
174
+ await debugLog(`[writeLibraryFile] Wrote ${fileType} file: ${filePath}`);
175
+ // Return the actual filename that was written
176
+ return safeFileName;
177
+ }
178
+ catch (error) {
179
+ await debugLog(`[writeLibraryFile] Error writing ${fileType} file ${filePath}: ${error}`);
180
+ throw error;
181
+ }
182
+ }
154
183
  //# sourceMappingURL=blueprintWriter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"blueprintWriter.js","sourceRoot":"","sources":["../src/blueprintWriter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"blueprintWriter.js","sourceRoot":"","sources":["../src/blueprintWriter.ts"],"names":[],"mappings":"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"}
@@ -106,6 +106,9 @@ export default function Menu({ menu, onClick, pages = [] }: MenuProps) {
106
106
  if (item.link_type === 'external' && item.external_url) {
107
107
  linkUrl = item.external_url;
108
108
  isExternal = true;
109
+ } else if (item.link_type === 'file' && item.file_name) {
110
+ linkUrl = `/library/files/${item.file_name}`;
111
+ isExternal = false;
109
112
  } else if (item.page && pages.length > 0) {
110
113
  const page = pages.find(p => p.id === item.page);
111
114
  if (page) {
@@ -4,14 +4,13 @@ import React from 'react';
4
4
 
5
5
  export interface PhotoGalleryData {
6
6
  photos: Array<{
7
- id: string;
8
- imageId: string;
9
- label: string;
7
+ fileName: string; // Required: filename in library/images
8
+ label?: string; // Optional label/caption
10
9
  }>;
11
- gridConfig: {
12
- columns: number;
13
- rows?: number; // Optional: if not set, rows are dynamic based on photo count
14
- gap: number; // Gap between photos in pixels
10
+ gridConfig?: {
11
+ columns?: number;
12
+ rows?: number;
13
+ gap?: number;
15
14
  };
16
15
  }
17
16
 
@@ -36,7 +35,12 @@ export default function RPhotoGallery({
36
35
  galleryData = { photos: [], gridConfig: { columns: 3, gap: 16 } };
37
36
  }
38
37
 
39
- const { photos = [], gridConfig = { columns: 3, gap: 16 } } = galleryData;
38
+ const { photos = [], gridConfig = {} } = galleryData;
39
+
40
+ // Default grid config
41
+ const columns = gridConfig.columns || 3;
42
+ const gap = gridConfig.gap || 16;
43
+ const rows = gridConfig.rows;
40
44
 
41
45
  // If no photos, show placeholder
42
46
  if (!photos || photos.length === 0) {
@@ -62,24 +66,24 @@ export default function RPhotoGallery({
62
66
  <div
63
67
  className="grid w-full"
64
68
  style={{
65
- gridTemplateColumns: `repeat(${gridConfig.columns}, minmax(0, 1fr))`,
66
- gap: `${gridConfig.gap}px`,
67
- ...(gridConfig.rows && {
68
- gridTemplateRows: `repeat(${gridConfig.rows}, minmax(0, 1fr))`
69
+ gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
70
+ gap: `${gap}px`,
71
+ ...(rows && {
72
+ gridTemplateRows: `repeat(${rows}, minmax(0, 1fr))`
69
73
  })
70
74
  }}
71
75
  >
72
76
  {photos.map((photo, index) => (
73
- <div key={photo.id || index} className="relative group">
77
+ <div key={index} className="relative group">
74
78
  <div className="aspect-square overflow-hidden rounded-lg bg-gray-100">
75
79
  <img
76
- src={`/api/image?imageid=${photo.imageId}`}
80
+ src={`/library/images/${photo.fileName}`}
77
81
  alt={photo.label || `Photo ${index + 1}`}
78
82
  className="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"
79
83
  loading="lazy"
80
84
  onError={(e) => {
81
- console.error('Error loading image:', photo.imageId);
82
- (e.target as HTMLImageElement).src = '/placeholder-gray.png';
85
+ console.error('Error loading image:', photo.fileName);
86
+ (e.target as HTMLImageElement).src = '/placeholder.png';
83
87
  }}
84
88
  />
85
89
  </div>