@mtldev514/retro-portfolio-engine 1.0.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/build-and-deploy.yml +105 -0
- package/ADMIN_SETUP.md +306 -0
- package/README.md +193 -301
- package/USAGE.md +451 -0
- package/build.sh +326 -0
- package/index.html +85 -0
- package/js/manifest-loader.js +313 -0
- package/manifest.json +74 -0
- package/package.json +14 -38
- package/setup-admin.sh +134 -0
- package/sync-after-admin.sh +58 -0
- package/bin/cli.js +0 -103
- package/engine/admin/admin.css +0 -720
- package/engine/admin/admin.html +0 -801
- package/engine/admin/admin_api.py +0 -230
- package/engine/admin/scripts/backup.sh +0 -116
- package/engine/admin/scripts/config_loader.py +0 -180
- package/engine/admin/scripts/init.sh +0 -141
- package/engine/admin/scripts/manager.py +0 -308
- package/engine/admin/scripts/restore.sh +0 -121
- package/engine/admin/scripts/server.py +0 -41
- package/engine/admin/scripts/update.sh +0 -321
- package/engine/admin/scripts/validate_json.py +0 -62
- package/engine/fonts.css +0 -37
- package/engine/index.html +0 -190
- package/engine/js/config-loader.js +0 -370
- package/engine/js/config.js +0 -173
- package/engine/js/counter.js +0 -17
- package/engine/js/effects.js +0 -97
- package/engine/js/i18n.js +0 -68
- package/engine/js/init.js +0 -107
- package/engine/js/media.js +0 -264
- package/engine/js/render.js +0 -282
- package/engine/js/router.js +0 -133
- package/engine/js/sparkle.js +0 -123
- package/engine/js/themes.js +0 -607
- package/engine/style.css +0 -2037
- package/index.js +0 -35
- package/scripts/admin.js +0 -67
- package/scripts/build.js +0 -142
- package/scripts/init.js +0 -237
- package/scripts/post-install.js +0 -16
- package/scripts/serve.js +0 -54
- package/templates/user-portfolio/.github/workflows/deploy.yml +0 -57
- package/templates/user-portfolio/config/app.json +0 -36
- package/templates/user-portfolio/config/categories.json +0 -241
- package/templates/user-portfolio/config/languages.json +0 -15
- package/templates/user-portfolio/config/media-types.json +0 -59
- package/templates/user-portfolio/data/painting.json +0 -3
- package/templates/user-portfolio/data/projects.json +0 -3
- package/templates/user-portfolio/lang/en.json +0 -114
- package/templates/user-portfolio/lang/fr.json +0 -114
package/build.sh
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
##############################################################################
|
|
4
|
+
# Site-as-a-Package Build Script
|
|
5
|
+
# Fetches content from remote data repository and builds static site
|
|
6
|
+
##############################################################################
|
|
7
|
+
|
|
8
|
+
set -e # Exit on error
|
|
9
|
+
|
|
10
|
+
# Colors for output
|
|
11
|
+
RED='\033[0;31m'
|
|
12
|
+
GREEN='\033[0;32m'
|
|
13
|
+
YELLOW='\033[1;33m'
|
|
14
|
+
BLUE='\033[0;34m'
|
|
15
|
+
NC='\033[0m' # No Color
|
|
16
|
+
|
|
17
|
+
# Configuration
|
|
18
|
+
MANIFEST_URL="${MANIFEST_URL:-https://raw.githubusercontent.com/YOUR_USERNAME/retro-portfolio-data/main/manifest.json}"
|
|
19
|
+
CACHE_DIR=".cache"
|
|
20
|
+
DIST_DIR="dist"
|
|
21
|
+
FORCE_DOWNLOAD=false
|
|
22
|
+
|
|
23
|
+
##############################################################################
|
|
24
|
+
# Helper Functions
|
|
25
|
+
##############################################################################
|
|
26
|
+
|
|
27
|
+
print_header() {
|
|
28
|
+
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
29
|
+
echo -e "${BLUE} 🎨 Site-as-a-Package Builder${NC}"
|
|
30
|
+
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
print_step() {
|
|
34
|
+
echo -e "\n${GREEN}▶${NC} $1"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
print_warning() {
|
|
38
|
+
echo -e "${YELLOW}⚠${NC} $1"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
print_error() {
|
|
42
|
+
echo -e "${RED}✗${NC} $1"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
print_success() {
|
|
46
|
+
echo -e "${GREEN}✓${NC} $1"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
##############################################################################
|
|
50
|
+
# Parse Arguments
|
|
51
|
+
##############################################################################
|
|
52
|
+
|
|
53
|
+
parse_args() {
|
|
54
|
+
while [[ $# -gt 0 ]]; do
|
|
55
|
+
case $1 in
|
|
56
|
+
-f|--force)
|
|
57
|
+
FORCE_DOWNLOAD=true
|
|
58
|
+
shift
|
|
59
|
+
;;
|
|
60
|
+
-m|--manifest)
|
|
61
|
+
MANIFEST_URL="$2"
|
|
62
|
+
shift 2
|
|
63
|
+
;;
|
|
64
|
+
-c|--cache-dir)
|
|
65
|
+
CACHE_DIR="$2"
|
|
66
|
+
shift 2
|
|
67
|
+
;;
|
|
68
|
+
-o|--output)
|
|
69
|
+
DIST_DIR="$2"
|
|
70
|
+
shift 2
|
|
71
|
+
;;
|
|
72
|
+
-h|--help)
|
|
73
|
+
show_help
|
|
74
|
+
exit 0
|
|
75
|
+
;;
|
|
76
|
+
*)
|
|
77
|
+
print_error "Unknown option: $1"
|
|
78
|
+
show_help
|
|
79
|
+
exit 1
|
|
80
|
+
;;
|
|
81
|
+
esac
|
|
82
|
+
done
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
show_help() {
|
|
86
|
+
cat << EOF
|
|
87
|
+
Usage: ./build.sh [OPTIONS]
|
|
88
|
+
|
|
89
|
+
Options:
|
|
90
|
+
-f, --force Force re-download of all files (ignore cache)
|
|
91
|
+
-m, --manifest URL Specify custom manifest URL
|
|
92
|
+
-c, --cache-dir DIR Cache directory (default: .cache)
|
|
93
|
+
-o, --output DIR Output directory (default: dist)
|
|
94
|
+
-h, --help Show this help message
|
|
95
|
+
|
|
96
|
+
Examples:
|
|
97
|
+
./build.sh # Build with cache
|
|
98
|
+
./build.sh --force # Force fresh download
|
|
99
|
+
./build.sh --manifest https://... # Use custom manifest
|
|
100
|
+
|
|
101
|
+
EOF
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
##############################################################################
|
|
105
|
+
# Cache Management
|
|
106
|
+
##############################################################################
|
|
107
|
+
|
|
108
|
+
check_cache() {
|
|
109
|
+
local file_path="$1"
|
|
110
|
+
local cached_file="${CACHE_DIR}/${file_path}"
|
|
111
|
+
|
|
112
|
+
if [[ "$FORCE_DOWNLOAD" == true ]]; then
|
|
113
|
+
return 1 # Don't use cache
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
if [[ -f "$cached_file" ]]; then
|
|
117
|
+
# Check if cache is still valid (less than 1 hour old)
|
|
118
|
+
local cache_age=$(($(date +%s) - $(date -r "$cached_file" +%s)))
|
|
119
|
+
if [[ $cache_age -lt 3600 ]]; then
|
|
120
|
+
return 0 # Cache is valid
|
|
121
|
+
fi
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
return 1 # Cache invalid or doesn't exist
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
##############################################################################
|
|
128
|
+
# Download Functions
|
|
129
|
+
##############################################################################
|
|
130
|
+
|
|
131
|
+
download_file() {
|
|
132
|
+
local url="$1"
|
|
133
|
+
local output_path="$2"
|
|
134
|
+
local use_cache="${3:-true}"
|
|
135
|
+
|
|
136
|
+
# Create directory if needed
|
|
137
|
+
mkdir -p "$(dirname "$output_path")"
|
|
138
|
+
|
|
139
|
+
# Check cache first
|
|
140
|
+
if [[ "$use_cache" == true ]] && check_cache "$output_path"; then
|
|
141
|
+
local cached_file="${CACHE_DIR}/${output_path}"
|
|
142
|
+
cp "$cached_file" "$output_path"
|
|
143
|
+
echo -e " ${BLUE}↻${NC} $(basename "$output_path") (cached)"
|
|
144
|
+
return 0
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
# Download file
|
|
148
|
+
if curl -sfL "$url" -o "$output_path" 2>/dev/null; then
|
|
149
|
+
# Save to cache
|
|
150
|
+
if [[ "$use_cache" == true ]]; then
|
|
151
|
+
local cache_file="${CACHE_DIR}/${output_path}"
|
|
152
|
+
mkdir -p "$(dirname "$cache_file")"
|
|
153
|
+
cp "$output_path" "$cache_file"
|
|
154
|
+
fi
|
|
155
|
+
echo -e " ${GREEN}↓${NC} $(basename "$output_path")"
|
|
156
|
+
return 0
|
|
157
|
+
else
|
|
158
|
+
print_error "Failed to download: $url"
|
|
159
|
+
return 1
|
|
160
|
+
fi
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
##############################################################################
|
|
164
|
+
# Build Process
|
|
165
|
+
##############################################################################
|
|
166
|
+
|
|
167
|
+
load_manifest() {
|
|
168
|
+
print_step "Loading manifest from remote..."
|
|
169
|
+
|
|
170
|
+
local temp_manifest=$(mktemp)
|
|
171
|
+
|
|
172
|
+
if ! curl -sfL "$MANIFEST_URL" -o "$temp_manifest"; then
|
|
173
|
+
print_error "Failed to download manifest from: $MANIFEST_URL"
|
|
174
|
+
rm -f "$temp_manifest"
|
|
175
|
+
exit 1
|
|
176
|
+
fi
|
|
177
|
+
|
|
178
|
+
# Validate JSON
|
|
179
|
+
if ! jq empty "$temp_manifest" 2>/dev/null; then
|
|
180
|
+
print_error "Invalid JSON in manifest"
|
|
181
|
+
rm -f "$temp_manifest"
|
|
182
|
+
exit 1
|
|
183
|
+
fi
|
|
184
|
+
|
|
185
|
+
# Parse manifest
|
|
186
|
+
DATA_REPO_BASE=$(jq -r '.dataRepository.baseUrl' "$temp_manifest")
|
|
187
|
+
|
|
188
|
+
if [[ "$DATA_REPO_BASE" == "null" || -z "$DATA_REPO_BASE" ]]; then
|
|
189
|
+
print_error "Invalid manifest: missing dataRepository.baseUrl"
|
|
190
|
+
rm -f "$temp_manifest"
|
|
191
|
+
exit 1
|
|
192
|
+
fi
|
|
193
|
+
|
|
194
|
+
# Store manifest for later use
|
|
195
|
+
MANIFEST_FILE="$temp_manifest"
|
|
196
|
+
|
|
197
|
+
print_success "Manifest loaded successfully"
|
|
198
|
+
echo " Data source: $DATA_REPO_BASE"
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
download_structure() {
|
|
202
|
+
local section="$1"
|
|
203
|
+
local use_cache="$2"
|
|
204
|
+
|
|
205
|
+
print_step "Downloading $section files..."
|
|
206
|
+
|
|
207
|
+
# Get files from manifest
|
|
208
|
+
local files=$(jq -r ".structure.${section} | to_entries[] | .value" "$MANIFEST_FILE")
|
|
209
|
+
|
|
210
|
+
while IFS= read -r file_path; do
|
|
211
|
+
if [[ -n "$file_path" && "$file_path" != "null" ]]; then
|
|
212
|
+
local url="${DATA_REPO_BASE}${file_path}"
|
|
213
|
+
download_file "$url" "${DIST_DIR}/${file_path}" "$use_cache"
|
|
214
|
+
fi
|
|
215
|
+
done <<< "$files"
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
copy_engine_files() {
|
|
219
|
+
print_step "Copying engine files..."
|
|
220
|
+
|
|
221
|
+
# Copy index.html
|
|
222
|
+
if [[ -f "index.html" ]]; then
|
|
223
|
+
cp index.html "${DIST_DIR}/"
|
|
224
|
+
echo -e " ${GREEN}✓${NC} index.html"
|
|
225
|
+
fi
|
|
226
|
+
|
|
227
|
+
# Copy local JS files
|
|
228
|
+
if [[ -d "js" ]]; then
|
|
229
|
+
mkdir -p "${DIST_DIR}/js"
|
|
230
|
+
cp -r js/* "${DIST_DIR}/js/"
|
|
231
|
+
echo -e " ${GREEN}✓${NC} js/ ($(ls -1 js | wc -l) files)"
|
|
232
|
+
fi
|
|
233
|
+
|
|
234
|
+
# Copy local CSS files
|
|
235
|
+
for css_file in style.css fonts.css; do
|
|
236
|
+
if [[ -f "$css_file" ]]; then
|
|
237
|
+
cp "$css_file" "${DIST_DIR}/"
|
|
238
|
+
echo -e " ${GREEN}✓${NC} $css_file"
|
|
239
|
+
fi
|
|
240
|
+
done
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
create_build_info() {
|
|
244
|
+
print_step "Creating build info..."
|
|
245
|
+
|
|
246
|
+
cat > "${DIST_DIR}/build-info.json" << EOF
|
|
247
|
+
{
|
|
248
|
+
"buildDate": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
|
|
249
|
+
"manifestUrl": "$MANIFEST_URL",
|
|
250
|
+
"dataSource": "$DATA_REPO_BASE",
|
|
251
|
+
"cacheUsed": $([ "$FORCE_DOWNLOAD" == true ] && echo "false" || echo "true"),
|
|
252
|
+
"version": "$(jq -r '.version' "$MANIFEST_FILE")"
|
|
253
|
+
}
|
|
254
|
+
EOF
|
|
255
|
+
|
|
256
|
+
print_success "Build info created"
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
##############################################################################
|
|
260
|
+
# Main Build Process
|
|
261
|
+
##############################################################################
|
|
262
|
+
|
|
263
|
+
main() {
|
|
264
|
+
print_header
|
|
265
|
+
|
|
266
|
+
# Parse arguments
|
|
267
|
+
parse_args "$@"
|
|
268
|
+
|
|
269
|
+
# Show build configuration
|
|
270
|
+
echo ""
|
|
271
|
+
echo "Configuration:"
|
|
272
|
+
echo " Manifest URL: $MANIFEST_URL"
|
|
273
|
+
echo " Cache dir: $CACHE_DIR"
|
|
274
|
+
echo " Output dir: $DIST_DIR"
|
|
275
|
+
echo " Force reload: $FORCE_DOWNLOAD"
|
|
276
|
+
echo ""
|
|
277
|
+
|
|
278
|
+
# Check dependencies
|
|
279
|
+
if ! command -v jq &> /dev/null; then
|
|
280
|
+
print_error "jq is required but not installed. Install with: apt-get install jq"
|
|
281
|
+
exit 1
|
|
282
|
+
fi
|
|
283
|
+
|
|
284
|
+
# Clean output directory if needed
|
|
285
|
+
if [[ -d "$DIST_DIR" ]]; then
|
|
286
|
+
print_step "Cleaning output directory..."
|
|
287
|
+
rm -rf "$DIST_DIR"
|
|
288
|
+
fi
|
|
289
|
+
mkdir -p "$DIST_DIR"
|
|
290
|
+
|
|
291
|
+
# Create cache directory
|
|
292
|
+
mkdir -p "$CACHE_DIR"
|
|
293
|
+
|
|
294
|
+
# Load manifest
|
|
295
|
+
load_manifest
|
|
296
|
+
|
|
297
|
+
# Download data files
|
|
298
|
+
local use_cache=$([ "$FORCE_DOWNLOAD" == true ] && echo "false" || echo "true")
|
|
299
|
+
|
|
300
|
+
download_structure "config" "$use_cache"
|
|
301
|
+
download_structure "data" "$use_cache"
|
|
302
|
+
download_structure "lang" "$use_cache"
|
|
303
|
+
|
|
304
|
+
# Copy engine files
|
|
305
|
+
copy_engine_files
|
|
306
|
+
|
|
307
|
+
# Create build info
|
|
308
|
+
create_build_info
|
|
309
|
+
|
|
310
|
+
# Cleanup
|
|
311
|
+
rm -f "$MANIFEST_FILE"
|
|
312
|
+
|
|
313
|
+
# Success!
|
|
314
|
+
echo ""
|
|
315
|
+
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
316
|
+
print_success "Build completed successfully!"
|
|
317
|
+
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
318
|
+
echo ""
|
|
319
|
+
echo "Next steps:"
|
|
320
|
+
echo " 1. Test locally: cd ${DIST_DIR} && python3 -m http.server 8000"
|
|
321
|
+
echo " 2. Deploy: Deploy the ${DIST_DIR}/ folder to GitHub Pages"
|
|
322
|
+
echo ""
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
# Run main function
|
|
326
|
+
main "$@"
|
package/index.html
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title data-i18n="header_title">Retro Portfolio</title>
|
|
7
|
+
<link rel="stylesheet" href="style.css">
|
|
8
|
+
<link rel="stylesheet" href="fonts.css">
|
|
9
|
+
|
|
10
|
+
<!-- Load manifest-based config loader -->
|
|
11
|
+
<script src="js/manifest-loader.js"></script>
|
|
12
|
+
|
|
13
|
+
<!-- Load application modules -->
|
|
14
|
+
<script src="js/i18n.js" defer></script>
|
|
15
|
+
<script src="js/themes.js" defer></script>
|
|
16
|
+
<script src="js/render.js" defer></script>
|
|
17
|
+
<script src="js/router.js" defer></script>
|
|
18
|
+
<script src="js/media.js" defer></script>
|
|
19
|
+
<script src="js/sparkle.js" defer></script>
|
|
20
|
+
<script src="js/effects.js" defer></script>
|
|
21
|
+
|
|
22
|
+
<!-- Initialize app after all modules loaded -->
|
|
23
|
+
<script src="js/init.js" defer></script>
|
|
24
|
+
</head>
|
|
25
|
+
<body>
|
|
26
|
+
<!-- Votre contenu HTML existant ici -->
|
|
27
|
+
<div id="rotate-overlay">
|
|
28
|
+
<div class="rotate-content">
|
|
29
|
+
<div class="rotate-icon">📺↩️</div>
|
|
30
|
+
<h2>ROTATE YOUR DEVICE!</h2>
|
|
31
|
+
<p>This website was designed for<br><b>DESKTOP COMPUTERS</b></p>
|
|
32
|
+
<p class="rotate-sub">Please turn your phone sideways<br>for the full retro experience!</p>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<div class="container">
|
|
37
|
+
<header>
|
|
38
|
+
<div class="settings-switcher">
|
|
39
|
+
<button class="settings-btn">⚙</button>
|
|
40
|
+
<div class="settings-dropdown">
|
|
41
|
+
<div class="settings-section-label">Theme</div>
|
|
42
|
+
<div class="settings-option" onclick="themes.changeTheme('jr16')">
|
|
43
|
+
<span class="theme-icon">🌿</span> JR-16
|
|
44
|
+
</div>
|
|
45
|
+
<div class="settings-divider"></div>
|
|
46
|
+
<div class="settings-section-label">Language</div>
|
|
47
|
+
<div class="settings-option" onclick="i18n.changeLang('en')">
|
|
48
|
+
<span class="lang-flag">🇬🇧</span> English
|
|
49
|
+
</div>
|
|
50
|
+
<div class="settings-option" onclick="i18n.changeLang('fr')">
|
|
51
|
+
<span class="lang-flag">🇫🇷</span> Français
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
<h1 id="page-title" data-i18n="header_title">Portfolio</h1>
|
|
56
|
+
</header>
|
|
57
|
+
|
|
58
|
+
<div class="content">
|
|
59
|
+
<main id="app">
|
|
60
|
+
<div class="loading-screen">
|
|
61
|
+
<div class="loading-spinner"></div>
|
|
62
|
+
<p>Loading site engine...</p>
|
|
63
|
+
</div>
|
|
64
|
+
</main>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<footer class="footer-terminal">
|
|
68
|
+
<div class="terminal-window">
|
|
69
|
+
<div class="terminal-titlebar">
|
|
70
|
+
<span class="terminal-titlebar-text">Built with Site-as-a-Package</span>
|
|
71
|
+
</div>
|
|
72
|
+
<div class="terminal-body">
|
|
73
|
+
<div class="terminal-line">
|
|
74
|
+
<span class="terminal-prompt">$</span>
|
|
75
|
+
<span data-i18n="footer_copy">© 2026</span>
|
|
76
|
+
<span class="terminal-cursor">█</span>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</footer>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
<script src="js/counter.js"></script>
|
|
84
|
+
</body>
|
|
85
|
+
</html>
|