sitepaige-mcp-server 0.5.0 → 0.7.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/components/menu.tsx +3 -0
- package/components/photogallery.tsx +20 -16
- package/components/videogallery.tsx +267 -0
- package/components/wrapped_menu.tsx +3 -1
- package/defaultapp/db.ts +1 -1
- package/dist/blueprintWriter.js +39 -10
- package/dist/blueprintWriter.js.map +1 -1
- package/dist/components/menu.tsx +3 -0
- package/dist/components/photogallery.tsx +20 -16
- package/dist/components/videogallery.tsx +267 -0
- package/dist/components/wrapped_menu.tsx +3 -1
- package/dist/defaultapp/db.ts +1 -1
- package/dist/generators/components.js +1 -0
- package/dist/generators/components.js.map +1 -1
- package/dist/generators/db-template.txt +1 -1
- package/dist/generators/pages.js +0 -9
- package/dist/generators/pages.js.map +1 -1
- package/dist/generators/sql.js +3 -3
- package/dist/generators/views.js +16 -3
- package/dist/generators/views.js.map +1 -1
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -1
- package/dist/sitepaige.js +343 -0
- package/dist/sitepaige.js.map +1 -1
- package/dist/types.js +2 -1
- package/dist/types.js.map +1 -1
- package/manifest.json +2 -2
- package/package.json +1 -1
package/components/menu.tsx
CHANGED
|
@@ -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
|
-
|
|
8
|
-
|
|
9
|
-
label: string;
|
|
7
|
+
fileName: string; // Required: filename in library/images
|
|
8
|
+
label?: string; // Optional label/caption
|
|
10
9
|
}>;
|
|
11
|
-
gridConfig
|
|
12
|
-
columns
|
|
13
|
-
rows?: number;
|
|
14
|
-
gap
|
|
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 = {
|
|
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(${
|
|
66
|
-
gap: `${
|
|
67
|
-
...(
|
|
68
|
-
gridTemplateRows: `repeat(${
|
|
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={
|
|
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={`/
|
|
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.
|
|
82
|
-
(e.target as HTMLImageElement).src = '/placeholder
|
|
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
|
|
package/defaultapp/db.ts
CHANGED
|
@@ -44,7 +44,7 @@ export interface DatabaseConfig {
|
|
|
44
44
|
|
|
45
45
|
// Get database configuration from environment
|
|
46
46
|
export function getDatabaseConfig(): DatabaseConfig {
|
|
47
|
-
const dbType = (process.env.DATABASE_TYPE || '
|
|
47
|
+
const dbType = (process.env.DATABASE_TYPE || 'postgres').toLowerCase() as DatabaseType;
|
|
48
48
|
|
|
49
49
|
switch (dbType) {
|
|
50
50
|
case 'postgres':
|
package/dist/blueprintWriter.js
CHANGED
|
@@ -49,14 +49,14 @@ export async function writeProjectFromBlueprint(project, options) {
|
|
|
49
49
|
}));
|
|
50
50
|
// package.json first to allow immediate npm install
|
|
51
51
|
const projectName = project.name || undefined;
|
|
52
|
-
await writePackageJson(targetDir, projectName, options.databaseType || "
|
|
52
|
+
await writePackageJson(targetDir, projectName, options.databaseType || "postgres");
|
|
53
53
|
await writeNextConfig(targetDir);
|
|
54
54
|
await writeBaseAppSkeleton(targetDir);
|
|
55
55
|
// Copy default app files after base skeleton
|
|
56
|
-
await writeDefaultApp(targetDir, options.databaseType || "
|
|
56
|
+
await writeDefaultApp(targetDir, options.databaseType || "postgres");
|
|
57
57
|
await writeComponents(targetDir, blueprint);
|
|
58
|
-
await writeModelsSql(targetDir, blueprint, options.databaseType || "
|
|
59
|
-
await writeIncrementalMigrations(targetDir, blueprint, options.databaseType || "
|
|
58
|
+
await writeModelsSql(targetDir, blueprint, options.databaseType || "postgres");
|
|
59
|
+
await writeIncrementalMigrations(targetDir, blueprint, options.databaseType || "postgres");
|
|
60
60
|
// Process images: download to public/images and replace refs in blueprint before views/pages
|
|
61
61
|
const bpWithImages = await processBlueprintImages(targetDir, blueprint);
|
|
62
62
|
const viewMap = await writeViews(targetDir, bpWithImages, projectCode, project.AuthProviders);
|
|
@@ -99,18 +99,18 @@ export async function writeProjectPagesOnly(project, options) {
|
|
|
99
99
|
}
|
|
100
100
|
// package.json first to allow immediate npm install
|
|
101
101
|
const projectName = project.name || undefined;
|
|
102
|
-
await writePackageJson(targetDir, projectName, options.databaseType || "
|
|
102
|
+
await writePackageJson(targetDir, projectName, options.databaseType || "postgres");
|
|
103
103
|
await writeNextConfig(targetDir);
|
|
104
104
|
await writeBaseAppSkeleton(targetDir);
|
|
105
105
|
// Copy default app files after base skeleton
|
|
106
|
-
await writeDefaultApp(targetDir, options.databaseType || "
|
|
106
|
+
await writeDefaultApp(targetDir, options.databaseType || "postgres");
|
|
107
107
|
await writeComponents(targetDir, blueprint);
|
|
108
108
|
// Write database/models if requested and they exist in blueprint
|
|
109
109
|
const hasModels = blueprint.models && blueprint.models.length > 0;
|
|
110
110
|
if (options.writeApis && hasModels) {
|
|
111
111
|
await debugLog('[writeProjectPagesOnly] Writing models/migrations (writeApis=true and models exist)');
|
|
112
|
-
await writeModelsSql(targetDir, blueprint, options.databaseType || "
|
|
113
|
-
await writeIncrementalMigrations(targetDir, blueprint, options.databaseType || "
|
|
112
|
+
await writeModelsSql(targetDir, blueprint, options.databaseType || "postgres");
|
|
113
|
+
await writeIncrementalMigrations(targetDir, blueprint, options.databaseType || "postgres");
|
|
114
114
|
}
|
|
115
115
|
else {
|
|
116
116
|
await debugLog('[writeProjectPagesOnly] Skipping models/migrations (writeApis=' + options.writeApis + ', hasModels=' + hasModels + ')');
|
|
@@ -145,10 +145,39 @@ export async function writeProjectBackendOnly(project, options) {
|
|
|
145
145
|
throw new Error("No code found in project data");
|
|
146
146
|
}
|
|
147
147
|
// Only write backend-specific files
|
|
148
|
-
await writeModelsSql(targetDir, blueprint, options.databaseType || "
|
|
149
|
-
await writeIncrementalMigrations(targetDir, blueprint, options.databaseType || "
|
|
148
|
+
await writeModelsSql(targetDir, blueprint, options.databaseType || "postgres");
|
|
149
|
+
await writeIncrementalMigrations(targetDir, blueprint, options.databaseType || "postgres");
|
|
150
150
|
await writeApis(targetDir, blueprint, projectCode);
|
|
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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"}
|
package/dist/components/menu.tsx
CHANGED
|
@@ -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
|
-
|
|
8
|
-
|
|
9
|
-
label: string;
|
|
7
|
+
fileName: string; // Required: filename in library/images
|
|
8
|
+
label?: string; // Optional label/caption
|
|
10
9
|
}>;
|
|
11
|
-
gridConfig
|
|
12
|
-
columns
|
|
13
|
-
rows?: number;
|
|
14
|
-
gap
|
|
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 = {
|
|
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(${
|
|
66
|
-
gap: `${
|
|
67
|
-
...(
|
|
68
|
-
gridTemplateRows: `repeat(${
|
|
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={
|
|
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={`/
|
|
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.
|
|
82
|
-
(e.target as HTMLImageElement).src = '/placeholder
|
|
85
|
+
console.error('Error loading image:', photo.fileName);
|
|
86
|
+
(e.target as HTMLImageElement).src = '/placeholder.png';
|
|
83
87
|
}}
|
|
84
88
|
/>
|
|
85
89
|
</div>
|