This commit is contained in:
AngelJumbo
2025-07-28 00:34:06 -05:00
parent eb5b979f52
commit 6d4f195bf8
270 changed files with 591 additions and 142 deletions
+414 -81
View File
@@ -26,59 +26,206 @@ generate_thumbnails() {
rm -rf thumbnails/*
# Count total number of images
total_images=$(find ./wallpapers -type f | wc -l)
total_images=$(find ./wallpapers -type f \( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" \) | wc -l)
current_image=0
for section_dir in ./wallpapers/*; do
section_name="${section_dir##*/}"
mkdir -p "thumbnails/$section_name"
# Process dark images (base directory)
for img in "$section_dir"/*; do
[ -f "$img" ] || continue
case "$img" in
*.jpg|*.jpeg|*.png|*.JPG|*.JPEG|*.PNG) ;;
*) continue ;;
esac
current_image=$((current_image + 1))
local img_filename="${img##*/}"
local thumbnail="thumbnails/$section_name/$img_filename"
echo "($current_image/$total_images): $img -> $thumbnail"
convert "$img" -resize 300x300 "$thumbnail"
done
# Process light images if light folder exists
if [ -d "$section_dir/light" ]; then
mkdir -p "thumbnails/$section_name/light"
for img in "$section_dir/light"/*; do
[ -f "$img" ] || continue
case "$img" in
*.jpg|*.jpeg|*.png|*.JPG|*.JPEG|*.PNG) ;;
*) continue ;;
esac
current_image=$((current_image + 1))
local img_filename="${img##*/}"
local thumbnail="thumbnails/$section_name/light/$img_filename"
echo "($current_image/$total_images): $img -> $thumbnail"
convert "$img" -resize 300x300 "$thumbnail"
done
fi
done
echo "Thumbnail generation complete: $total_images images processed"
}
write_section_header() {
echo "write section header ..."
echo "<h2 class='s$1 clickable' onclick='activeSection(\"$2\")' >" >>$3
echo "$2" | tr a-z A-Z >>$3
echo "</h2>" >>$3
check_has_light_folder() {
local section_dir=$1
[ -d "$section_dir/light" ] && echo "true" || echo "false"
}
write_img() {
echo "write image ..."
# Remove leading './' from path if present
local img_path="${1#./}"
local section_name=$(echo "$img_path" | cut -d'/' -f2)
local img_filename="${1##*/}"
# Use web-friendly paths (no leading ./)
local thumbnail_path="thumbnails/$section_name/$img_filename"
echo " <a target='_blank' href='$img_path'>
<img loading='lazy' src='$thumbnail_path' alt='$img_filename' width='200'></a>" >>$2
count_images_in_dir() {
local dir=$1
find "$dir" -maxdepth 1 -type f \( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" \) | wc -l
}
rm *.html
create_section_data() {
local section=$1
local subdir=$2
local maxPerPage=8
local has_light=$(check_has_light_folder "$subdir")
echo "Creating data for section: $section"
# Create JSON data file for the section
local data_file="${section}_data.js"
cat > "$data_file" << EOF
// Data for $section section
window.sectionData = window.sectionData || {};
window.sectionData['$section'] = {
hasLight: $has_light,
maxPerPage: $maxPerPage,
dark: [
EOF
touch ./index.html
# Generate dark images data
for wallpaper in "$subdir"/*; do
[ -f "$wallpaper" ] || continue
case "$wallpaper" in
*.jpg|*.jpeg|*.png|*.JPG|*.JPEG|*.PNG) ;;
*) continue ;;
esac
local img_path="${wallpaper#./}"
local img_filename="${wallpaper##*/}"
local thumbnail_path="thumbnails/$section/$img_filename"
echo " { src: '$img_path', thumb: '$thumbnail_path', alt: '$img_filename' }," >> "$data_file"
done
echo "<!DOCTYPE html>
# Generate light images data if exists
echo " ]," >> "$data_file"
if [ "$has_light" = "true" ]; then
echo " light: [" >> "$data_file"
for wallpaper in "$subdir/light"/*; do
[ -f "$wallpaper" ] || continue
case "$wallpaper" in
*.jpg|*.jpeg|*.png|*.JPG|*.JPEG|*.PNG) ;;
*) continue ;;
esac
local img_path="${wallpaper#./}"
local img_filename="${wallpaper##*/}"
local thumbnail_path="thumbnails/$section/light/$img_filename"
echo " { src: '$img_path', thumb: '$thumbnail_path', alt: '$img_filename' }," >> "$data_file"
done
echo " ]" >> "$data_file"
else
echo " light: []" >> "$data_file"
fi
echo "};" >> "$data_file"
}
# Clean up old files
rm -f *.html *.js
# Generate thumbnails
generate_thumbnails
# Create main index.html
cat > index.html << 'EOF'
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<link rel='stylesheet' type='text/css' href='style.css'>
<title>Gruvbox wallpapers</title>
<script src='app.js' defer></script>
<script src='https://kit.fontawesome.com/13865d7982.js' crossorigin='anonymous' defer></script>
<style>
.section-content {
margin: 20px 0;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease-in-out;
transform: translateY(10px);
}
.section.selected .section-content {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
/* Theme switcher styles */
.section-theme-switcher {
text-align: center;
margin: 15px 0;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease-in-out;
transform: translateY(-10px);
}
.section.selected .section-theme-switcher {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.theme-toggle {
display: inline-flex;
gap: 0;
}
.theme-btn {
padding: 10px 20px;
background-color: var(--bg-color-reverse);
color: var(--fg-color-reverse);
border: 2px solid transparent;
cursor: pointer;
transition: border-color 0.3s ease;
font-weight: 600;
font-size: 14px;
argin: 0 5px 0 5px;
}
.theme-btn:hover {
background-color: var(--bg-color-reverse);
}
.theme-btn.active {
border: 4px solid var(--fg-color);
background-color: var(--bg-color);
color: var(--fg-color);
}
.subsection-content {
display: none;
}
.subsection-content.active {
display: block;
}
.loading {
text-align: center;
padding: 40px;
color: var(--fg-color);
}
</style>
</head>
<body>
<div class='float-btns'>
@@ -87,7 +234,7 @@ echo "<!DOCTYPE html>
<i class='fa-brands fa-github'></i>
</span>
</a>
<button onclick='switchTheme()' class='btn float-btn' title='Switch theme'>
<button onclick='switchMainTheme()' class='btn float-btn' title='Switch theme'>
<span>
<i id='light-icon' class='fa-solid fa-sun'></i>
<i id='dark-icon' class='fa-solid fa-moon'></i>
@@ -95,78 +242,264 @@ echo "<!DOCTYPE html>
</button>
</div>
<main>
<h1>Gruvbox Wallpapers</h1>" >./index.html
<h1>Gruvbox Wallpapers</h1>
EOF
# Generate sections
color=1
maxPerPage=8
declare -a sections
generate_thumbnails
declare -a sections_with_light
for subdir in ./wallpapers/*; do
section="${subdir##*/}"
sections+=("$section")
write_section_header $color "$section" ./index.html
page=1
img_count=0
subhtml="${section}_page${page}.html"
touch ./$subhtml
echo "<div class='section' id='$section'>" >>./index.html
echo "<div class='pager'>" >>./index.html
countImgs=$(find "$subdir" -type f | wc -l)
countImgs=$((countImgs - 1))
for i in $(seq 1 $((($countImgs / $maxPerPage) + 1))); do
echo "<button class='btn pager-btn' onclick='loadPage(\"$section\", $i)'>$i</button>" >>./index.html
done
echo "</div>" >>./index.html
echo "<div id='$section-content'>" >>./index.html
echo "<div class='c'>" >>./$subhtml
for wallpaper in ${subdir}/*; do
if [ "$img_count" -ge $maxPerPage ]; then
echo "</div>" >>./$subhtml
page=$((page + 1))
subhtml="${section}_page${page}.html"
touch ./$subhtml
echo "<div class='c'>" >>./$subhtml
img_count=0
fi
write_img "$wallpaper" "./$subhtml"
img_count=$((img_count + 1))
done
echo "</div>" >>./$subhtml
echo "</div>" >>./index.html
echo "</div>" >>./index.html
has_light=$(check_has_light_folder "$subdir")
if [ "$has_light" = "true" ]; then
sections_with_light+=("$section")
fi
# Create the data file
create_section_data "$section" "$subdir"
# Add section to main page
echo " <h2 class='s$color clickable' onclick='activeSection(\"$section\")'>" >> index.html
echo " $(echo "$section" | tr a-z A-Z)" >> index.html
echo " </h2>" >> index.html
echo " <div class='section' id='$section'>" >> index.html
# Add theme switcher if section has light variant
if [ "$has_light" = "true" ]; then
echo " <div class='section-theme-switcher'>" >> index.html
echo " <div class='theme-toggle'>" >> index.html
echo " <button class='theme-btn active' onclick='switchSectionTheme(\"$section\", \"dark\")' id='$section-dark-btn'>Dark</button>" >> index.html
echo " <button class='theme-btn' onclick='switchSectionTheme(\"$section\", \"light\")' id='$section-light-btn'>Light</button>" >> index.html
echo " </div>" >> index.html
echo " </div>" >> index.html
fi
echo " <div class='section-content' id='$section-content'>" >> index.html
echo " <div class='loading'>Loading...</div>" >> index.html
echo " </div>" >> index.html
echo " </div>" >> index.html
color=$((color + 1))
if [ "$color" -eq 8 ]; then
color=1
fi
done
echo "</main>" >>./index.html
echo "<script>
window.onload = () => {" >>./index.html
# Add all section data scripts
for section in "${sections[@]}"; do
echo " loadPage('$section', 1);" >>./index.html
echo " <script src='${section}_data.js'></script>" >> index.html
done
echo "
activeSection('${sections[0]}');
if (window.matchMedia('(prefers-color-scheme: dark)').matches){
setTheme('dark');
}else{
setTheme('light');
}
}
</script>" >>./index.html
# Add main JavaScript
cat >> index.html << 'EOF'
<script>
// Global state
let activeSectionName = null;
let sectionStates = {};
echo "</body>
</html>" >>./index.html
// Initialize section state
function initSectionState(section) {
if (!sectionStates[section]) {
sectionStates[section] = {
currentTheme: 'dark',
currentPages: { dark: 1, light: 1 },
loaded: false
};
}
}
function activeSection(section) {
hideAllSections();
const targetSection = document.getElementById(section);
targetSection.classList.add('selected');
activeSectionName = section;
initSectionState(section);
// Load content if not already loaded
if (!sectionStates[section].loaded) {
loadSectionContent(section);
}
// Reset to dark theme
if (window.sectionData[section].hasLight) {
switchSectionTheme(section, 'dark');
}
setTimeout(() => {
targetSection.scrollIntoView({ block: 'start', behavior: 'smooth' });
}, 100);
}
function hideAllSections() {
document.querySelectorAll('.section').forEach(s => {
s.classList.remove('selected');
});
activeSectionName = null;
}
function loadSectionContent(section) {
const contentDiv = document.getElementById(section + '-content');
const data = window.sectionData[section];
if (!data) {
contentDiv.innerHTML = '<div class="loading">Error: Section data not found</div>';
return;
}
let html = '';
// Dark content
if (data.dark.length > 0) {
html += `<div class="subsection-content active" id="${section}-dark">`;
// Dark pagination
const darkPages = Math.ceil(data.dark.length / data.maxPerPage);
if (darkPages > 1) {
html += '<div class="pager">';
for (let i = 1; i <= darkPages; i++) {
const selectedClass = i === 1 ? ' selected' : '';
html += `<button class="btn pager-btn${selectedClass}" onclick="loadPage('${section}', 'dark', ${i})">${i}</button>`;
}
html += '</div>';
}
html += `<div id="${section}-dark-content">`;
html += generateImageGrid(data.dark.slice(0, data.maxPerPage));
html += '</div>';
html += '</div>';
}
// Light content
if (data.hasLight && data.light.length > 0) {
html += `<div class="subsection-content" id="${section}-light">`;
// Light pagination
const lightPages = Math.ceil(data.light.length / data.maxPerPage);
if (lightPages > 1) {
html += '<div class="pager">';
for (let i = 1; i <= lightPages; i++) {
const selectedClass = i === 1 ? ' selected' : '';
html += `<button class="btn pager-btn${selectedClass}" onclick="loadPage('${section}', 'light', ${i})">${i}</button>`;
}
html += '</div>';
}
html += `<div id="${section}-light-content">`;
html += generateImageGrid(data.light.slice(0, data.maxPerPage));
html += '</div>';
html += '</div>';
}
contentDiv.innerHTML = html;
sectionStates[section].loaded = true;
}
function generateImageGrid(images) {
let html = '<div class="c">';
images.forEach(img => {
html += `<a target="_blank" href="${img.src}">
<img loading="lazy" src="${img.thumb}" alt="${img.alt}" width="200">
</a>`;
});
html += '</div>';
return html;
}
function switchSectionTheme(section, theme) {
const darkBtn = document.getElementById(section + '-dark-btn');
const lightBtn = document.getElementById(section + '-light-btn');
const darkContent = document.getElementById(section + '-dark');
const lightContent = document.getElementById(section + '-light');
initSectionState(section);
sectionStates[section].currentTheme = theme;
// Update button states
if (darkBtn && lightBtn) {
darkBtn.classList.toggle('active', theme === 'dark');
lightBtn.classList.toggle('active', theme === 'light');
}
// Switch content visibility
if (darkContent && lightContent) {
darkContent.classList.toggle('active', theme === 'dark');
lightContent.classList.toggle('active', theme === 'light');
}
// Load first page of selected theme
loadPage(section, theme, 1);
}
function loadPage(section, theme, page) {
const data = window.sectionData[section];
const images = data[theme];
const startIdx = (page - 1) * data.maxPerPage;
const endIdx = Math.min(startIdx + data.maxPerPage, images.length);
const pageImages = images.slice(startIdx, endIdx);
const contentDiv = document.getElementById(`${section}-${theme}-content`);
if (contentDiv) {
contentDiv.innerHTML = generateImageGrid(pageImages);
}
// Update pagination buttons
const pagerButtons = document.getElementById(`${section}-${theme}`)?.getElementsByClassName('pager-btn');
if (pagerButtons) {
for (let i = 0; i < pagerButtons.length; i++) {
pagerButtons[i].classList.toggle('selected', i === page - 1);
}
}
initSectionState(section);
sectionStates[section].currentPages[theme] = page;
}
// Main theme functions
const HIDDEN_CLASS = 'hidden';
function hideThemeSwitcher(currentTheme) {
if (currentTheme === 'dark') {
document.getElementById('dark-icon')?.classList.add(HIDDEN_CLASS);
document.getElementById('light-icon')?.classList.remove(HIDDEN_CLASS);
} else {
document.getElementById('light-icon')?.classList.add(HIDDEN_CLASS);
document.getElementById('dark-icon')?.classList.remove(HIDDEN_CLASS);
}
}
function setMainTheme(newTheme) {
document.documentElement.style.setProperty('color-scheme', newTheme);
hideThemeSwitcher(newTheme);
}
function switchMainTheme() {
const current = document.documentElement.style.getPropertyValue('color-scheme');
setMainTheme(current === 'dark' ? 'light' : 'dark');
}
// Initialize
document.addEventListener('DOMContentLoaded', function() {
EOF
echo " activeSection('${sections[0]}');" >> index.html
cat >> index.html << 'EOF'
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
setMainTheme('dark');
} else {
setMainTheme('light');
}
});
</script>
</body>
</html>
EOF
echo "Build complete! Generated $(echo ${#sections[@]}) sections with dynamic content loading."
echo "Sections with light variants: ${sections_with_light[*]}"