Compare commits
4 Commits
v1.3.1
...
906699f023
Author | SHA1 | Date | |
---|---|---|---|
906699f023 | |||
643a729f94 | |||
a02da47e73 | |||
f7f2356510 |
@ -52,7 +52,7 @@ start_server() {
|
|||||||
|
|
||||||
if [ $# -eq 0 ]; then
|
if [ $# -eq 0 ]; then
|
||||||
echo -e "${CYAN}╭───────────────────────────────────────────╮${NC}"
|
echo -e "${CYAN}╭───────────────────────────────────────────╮${NC}"
|
||||||
echo -e "${CYAN}│${NC} Lum${CYAN}eex${NC} - Version 1.3.1${NC} ${CYAN}│${NC}"
|
echo -e "${CYAN}│${NC} Lum${CYAN}eex${NC} - Version 1.3.2${NC} ${CYAN}│${NC}"
|
||||||
echo -e "${CYAN}├───────────────────────────────────────────┤${NC}"
|
echo -e "${CYAN}├───────────────────────────────────────────┤${NC}"
|
||||||
echo -e "${CYAN}│${NC} Source: https://git.djeex.fr/Djeex/lumeex ${CYAN}│${NC}"
|
echo -e "${CYAN}│${NC} Source: https://git.djeex.fr/Djeex/lumeex ${CYAN}│${NC}"
|
||||||
echo -e "${CYAN}│${NC} Mirror: https://github.com/Djeex/lumeex ${CYAN}│${NC}"
|
echo -e "${CYAN}│${NC} Mirror: https://github.com/Djeex/lumeex ${CYAN}│${NC}"
|
||||||
|
@ -69,7 +69,8 @@ const setupTagFilter = () => {
|
|||||||
const allSections = document.querySelectorAll('.section[data-tags]');
|
const allSections = document.querySelectorAll('.section[data-tags]');
|
||||||
const allTags = document.querySelectorAll('.tag');
|
const allTags = document.querySelectorAll('.tag');
|
||||||
let activeTags = [];
|
let activeTags = [];
|
||||||
let lastClickedTag = null; // mémorise le dernier tag cliqué
|
let lastClickedTag = null; // remembers the last clicked tag
|
||||||
|
let lastClickedSection = null; // remembers the last clicked section (photo)
|
||||||
|
|
||||||
const applyFilter = () => {
|
const applyFilter = () => {
|
||||||
let filteredSections = [];
|
let filteredSections = [];
|
||||||
@ -81,30 +82,41 @@ const setupTagFilter = () => {
|
|||||||
section.style.display = hasAllTags ? '' : 'none';
|
section.style.display = hasAllTags ? '' : 'none';
|
||||||
|
|
||||||
if (hasAllTags) {
|
if (hasAllTags) {
|
||||||
filteredSections.push(section);
|
if (lastClickedSection === section) {
|
||||||
if (lastClickedTag && sectionTags.includes(lastClickedTag) && !matchingSection) {
|
|
||||||
matchingSection = section;
|
matchingSection = section;
|
||||||
|
} else {
|
||||||
|
filteredSections.push(section);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Réorganise : la photo correspondante au dernier tag cliqué en premier
|
// Remove all filtered sections from DOM before reordering
|
||||||
if (matchingSection && galleryContainer.contains(matchingSection)) {
|
if (galleryContainer) {
|
||||||
|
[matchingSection, ...filteredSections].forEach(section => {
|
||||||
|
if (section && galleryContainer.contains(section)) {
|
||||||
|
galleryContainer.removeChild(section);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (matchingSection) {
|
||||||
galleryContainer.prepend(matchingSection);
|
galleryContainer.prepend(matchingSection);
|
||||||
}
|
}
|
||||||
|
filteredSections.forEach(section => {
|
||||||
|
galleryContainer.appendChild(section);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Met à jour le style des tags
|
// Update tag styles
|
||||||
allTags.forEach((tagEl) => {
|
allTags.forEach((tagEl) => {
|
||||||
const tagText = tagEl.textContent.replace('#', '').toLowerCase();
|
const tagText = tagEl.textContent.replace('#', '').toLowerCase();
|
||||||
tagEl.classList.toggle('active', activeTags.includes(tagText));
|
tagEl.classList.toggle('active', activeTags.includes(tagText));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Met à jour l'URL
|
// Update the URL
|
||||||
const base = window.location.pathname;
|
const base = window.location.pathname;
|
||||||
const query = activeTags.length > 0 ? `?tag=${activeTags.join(',')}` : '';
|
const query = activeTags.length > 0 ? `?tag=${activeTags.join(',')}` : '';
|
||||||
window.history.pushState({}, '', base + query);
|
window.history.pushState({}, '', base + query);
|
||||||
|
|
||||||
// Scroll jusqu'à la galerie
|
// Scroll to the gallery
|
||||||
if (galleryContainer) {
|
if (galleryContainer) {
|
||||||
galleryContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
galleryContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
}
|
}
|
||||||
@ -113,7 +125,8 @@ const setupTagFilter = () => {
|
|||||||
allTags.forEach((tagEl) => {
|
allTags.forEach((tagEl) => {
|
||||||
tagEl.addEventListener('click', () => {
|
tagEl.addEventListener('click', () => {
|
||||||
const tagText = tagEl.textContent.replace('#', '').toLowerCase();
|
const tagText = tagEl.textContent.replace('#', '').toLowerCase();
|
||||||
lastClickedTag = tagText; // mémorise le dernier tag cliqué
|
lastClickedTag = tagText; // remembers the last clicked tag
|
||||||
|
lastClickedSection = tagEl.closest('.section'); // remembers the last clicked section
|
||||||
|
|
||||||
if (activeTags.includes(tagText)) {
|
if (activeTags.includes(tagText)) {
|
||||||
activeTags = activeTags.filter((t) => t !== tagText);
|
activeTags = activeTags.filter((t) => t !== tagText);
|
||||||
@ -130,6 +143,7 @@ const setupTagFilter = () => {
|
|||||||
if (urlTags) {
|
if (urlTags) {
|
||||||
activeTags = urlTags.split(',').map((t) => t.toLowerCase());
|
activeTags = urlTags.split(',').map((t) => t.toLowerCase());
|
||||||
lastClickedTag = activeTags[activeTags.length - 1] || null;
|
lastClickedTag = activeTags[activeTags.length - 1] || null;
|
||||||
|
lastClickedSection = null; // No section selected from URL
|
||||||
applyFilter();
|
applyFilter();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -141,12 +155,14 @@ const disableRightClickAndDrag = () => {
|
|||||||
document.addEventListener('dragstart', (e) => e.preventDefault());
|
document.addEventListener('dragstart', (e) => e.preventDefault());
|
||||||
};
|
};
|
||||||
|
|
||||||
// Scroll to top button
|
// Scroll-to-top button functionality
|
||||||
const setupScrollToTopButton = () => {
|
const setupScrollToTopButton = () => {
|
||||||
const scrollToTopButton = document.querySelector('.scroll-to-top');
|
const scrollBtn = document.getElementById("scrollToTop");
|
||||||
if (!scrollToTopButton) return;
|
window.addEventListener("scroll", () => {
|
||||||
scrollToTopButton.addEventListener('click', () => {
|
scrollBtn.style.display = window.scrollY > 300 ? "block" : "none";
|
||||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
});
|
||||||
|
scrollBtn.addEventListener("click", () => {
|
||||||
|
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ from pathlib import Path
|
|||||||
from shutil import copyfile
|
from shutil import copyfile
|
||||||
|
|
||||||
def generate_css_variables(colors_dict, output_path):
|
def generate_css_variables(colors_dict, output_path):
|
||||||
|
"""Generate css variables for theme colors"""
|
||||||
css_lines = [":root {"]
|
css_lines = [":root {"]
|
||||||
for key, value in colors_dict.items():
|
for key, value in colors_dict.items():
|
||||||
css_lines.append(f" --color-{key.replace('_', '-')}: {value};")
|
css_lines.append(f" --color-{key.replace('_', '-')}: {value};")
|
||||||
@ -13,6 +14,7 @@ def generate_css_variables(colors_dict, output_path):
|
|||||||
logging.info(f"[✓] CSS variables written to {output_path}")
|
logging.info(f"[✓] CSS variables written to {output_path}")
|
||||||
|
|
||||||
def generate_fonts_css(fonts_dir, output_path, fonts_cfg=None):
|
def generate_fonts_css(fonts_dir, output_path, fonts_cfg=None):
|
||||||
|
"""Generate css variables fonts"""
|
||||||
font_files = list(fonts_dir.glob("*"))
|
font_files = list(fonts_dir.glob("*"))
|
||||||
font_faces = {}
|
font_faces = {}
|
||||||
preload_links = []
|
preload_links = []
|
||||||
@ -57,6 +59,7 @@ def generate_fonts_css(fonts_dir, output_path, fonts_cfg=None):
|
|||||||
return preload_links
|
return preload_links
|
||||||
|
|
||||||
def generate_google_fonts_link(fonts):
|
def generate_google_fonts_link(fonts):
|
||||||
|
"""Generate src link for Google fonts"""
|
||||||
if not fonts:
|
if not fonts:
|
||||||
return ""
|
return ""
|
||||||
families = []
|
families = []
|
||||||
|
@ -10,6 +10,7 @@ GALLERY_DIR = Path("config/photos/gallery")
|
|||||||
HERO_DIR = Path("config/photos/hero")
|
HERO_DIR = Path("config/photos/hero")
|
||||||
|
|
||||||
def load_yaml(path):
|
def load_yaml(path):
|
||||||
|
"""Load gallery config .yaml file"""
|
||||||
print(f"[→] Loading {path}...")
|
print(f"[→] Loading {path}...")
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
print(f"[✗] File not found: {path}")
|
print(f"[✗] File not found: {path}")
|
||||||
@ -21,11 +22,13 @@ def load_yaml(path):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
def save_yaml(data, path):
|
def save_yaml(data, path):
|
||||||
|
"""Save modified gallery config .yaml file"""
|
||||||
with open(path, "w", encoding="utf-8") as f:
|
with open(path, "w", encoding="utf-8") as f:
|
||||||
yaml.dump(data, f, sort_keys=False, allow_unicode=True)
|
yaml.dump(data, f, sort_keys=False, allow_unicode=True)
|
||||||
print(f"[✓] Saved updated YAML to {path}")
|
print(f"[✓] Saved updated YAML to {path}")
|
||||||
|
|
||||||
def get_all_image_paths(directory):
|
def get_all_image_paths(directory):
|
||||||
|
"""Get the path to record for builded site"""
|
||||||
return sorted([
|
return sorted([
|
||||||
str(p.relative_to(directory.parent)).replace("\\", "/")
|
str(p.relative_to(directory.parent)).replace("\\", "/")
|
||||||
for p in directory.rglob("*")
|
for p in directory.rglob("*")
|
||||||
@ -33,6 +36,7 @@ def get_all_image_paths(directory):
|
|||||||
])
|
])
|
||||||
|
|
||||||
def update_gallery():
|
def update_gallery():
|
||||||
|
"""Update the gallery photo list"""
|
||||||
print("\n=== Updating gallery.yaml (gallery section) ===")
|
print("\n=== Updating gallery.yaml (gallery section) ===")
|
||||||
gallery = load_yaml(GALLERY_YAML)
|
gallery = load_yaml(GALLERY_YAML)
|
||||||
|
|
||||||
@ -71,6 +75,7 @@ def update_gallery():
|
|||||||
print("[✓] No changes to gallery.yaml (gallery)")
|
print("[✓] No changes to gallery.yaml (gallery)")
|
||||||
|
|
||||||
def update_hero():
|
def update_hero():
|
||||||
|
"""Update the hero photo list"""
|
||||||
print("\n=== Updating gallery.yaml (hero section) ===")
|
print("\n=== Updating gallery.yaml (hero section) ===")
|
||||||
gallery = load_yaml(GALLERY_YAML)
|
gallery = load_yaml(GALLERY_YAML)
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import logging
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
def render_template(template_path, context):
|
def render_template(template_path, context):
|
||||||
|
"""Render html templates"""
|
||||||
with open(template_path, encoding="utf-8") as f:
|
with open(template_path, encoding="utf-8") as f:
|
||||||
content = f.read()
|
content = f.read()
|
||||||
for key, value in context.items():
|
for key, value in context.items():
|
||||||
@ -11,6 +12,7 @@ def render_template(template_path, context):
|
|||||||
return content
|
return content
|
||||||
|
|
||||||
def render_gallery_images(images):
|
def render_gallery_images(images):
|
||||||
|
"""Render the photo gallery"""
|
||||||
html = ""
|
html = ""
|
||||||
for img in images:
|
for img in images:
|
||||||
tags = " ".join(img.get("tags", []))
|
tags = " ".join(img.get("tags", []))
|
||||||
@ -24,6 +26,7 @@ def render_gallery_images(images):
|
|||||||
return html
|
return html
|
||||||
|
|
||||||
def generate_gallery_json_from_images(images, output_dir):
|
def generate_gallery_json_from_images(images, output_dir):
|
||||||
|
"""Generte the hero carrousel photo list"""
|
||||||
try:
|
try:
|
||||||
img_list = [img["src"] for img in images]
|
img_list = [img["src"] for img in images]
|
||||||
output_path = output_dir / "data" / "gallery.json"
|
output_path = output_dir / "data" / "gallery.json"
|
||||||
@ -35,6 +38,7 @@ def generate_gallery_json_from_images(images, output_dir):
|
|||||||
logging.error(f"[✗] Error generating gallery JSON: {e}")
|
logging.error(f"[✗] Error generating gallery JSON: {e}")
|
||||||
|
|
||||||
def generate_robots_txt(canonical_url, allowed_paths, output_dir):
|
def generate_robots_txt(canonical_url, allowed_paths, output_dir):
|
||||||
|
"""Generate the robot.txt"""
|
||||||
robots_lines = ["User-agent: *"]
|
robots_lines = ["User-agent: *"]
|
||||||
|
|
||||||
# Block everything by default
|
# Block everything by default
|
||||||
@ -62,6 +66,7 @@ def generate_robots_txt(canonical_url, allowed_paths, output_dir):
|
|||||||
logging.error(f"[✗] Failed to write robots.txt: {e}")
|
logging.error(f"[✗] Failed to write robots.txt: {e}")
|
||||||
|
|
||||||
def generate_sitemap_xml(canonical_url, allowed_paths, output_dir):
|
def generate_sitemap_xml(canonical_url, allowed_paths, output_dir):
|
||||||
|
"""Generate the sitemap"""
|
||||||
urlset_start = '<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n'
|
urlset_start = '<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n'
|
||||||
urlset_end = '</urlset>\n'
|
urlset_end = '</urlset>\n'
|
||||||
urls = ""
|
urls = ""
|
||||||
|
@ -23,7 +23,7 @@ SITE_FILE = SRC_DIR / "config/site.yaml"
|
|||||||
THEMES_DIR = SRC_DIR / "config/themes"
|
THEMES_DIR = SRC_DIR / "config/themes"
|
||||||
|
|
||||||
def build():
|
def build():
|
||||||
build_version = "v1.3.1"
|
build_version = "v1.3.2"
|
||||||
logging.info("\n")
|
logging.info("\n")
|
||||||
logging.info("=" * 24)
|
logging.info("=" * 24)
|
||||||
logging.info(f"🚀 Lumeex builder {build_version}")
|
logging.info(f"🚀 Lumeex builder {build_version}")
|
||||||
|
@ -4,6 +4,7 @@ from pathlib import Path
|
|||||||
from shutil import copytree, rmtree, copyfile
|
from shutil import copytree, rmtree, copyfile
|
||||||
|
|
||||||
def load_yaml(path):
|
def load_yaml(path):
|
||||||
|
"""Load gallery and site .yaml conf"""
|
||||||
if not path.exists():
|
if not path.exists():
|
||||||
logging.warning(f"[!] YAML file not found: {path}")
|
logging.warning(f"[!] YAML file not found: {path}")
|
||||||
return {}
|
return {}
|
||||||
@ -11,6 +12,7 @@ def load_yaml(path):
|
|||||||
return yaml.safe_load(f)
|
return yaml.safe_load(f)
|
||||||
|
|
||||||
def load_theme_config(theme_name, themes_dir):
|
def load_theme_config(theme_name, themes_dir):
|
||||||
|
"""Load theme.yaml"""
|
||||||
theme_dir = themes_dir / theme_name
|
theme_dir = themes_dir / theme_name
|
||||||
theme_config_path = theme_dir / "theme.yaml"
|
theme_config_path = theme_dir / "theme.yaml"
|
||||||
if not theme_config_path.exists():
|
if not theme_config_path.exists():
|
||||||
@ -20,26 +22,25 @@ def load_theme_config(theme_name, themes_dir):
|
|||||||
return theme_vars, theme_dir
|
return theme_vars, theme_dir
|
||||||
|
|
||||||
def clear_dir(path: Path):
|
def clear_dir(path: Path):
|
||||||
|
"""Clear the output dir"""
|
||||||
if not path.exists():
|
if not path.exists():
|
||||||
path.mkdir(parents=True)
|
path.mkdir(parents=True)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Remove all files and subdirectories inside path, but not path itself
|
|
||||||
for child in path.iterdir():
|
for child in path.iterdir():
|
||||||
if child.is_file() or child.is_symlink():
|
if child.is_file() or child.is_symlink():
|
||||||
child.unlink() # delete file or symlink
|
child.unlink()
|
||||||
elif child.is_dir():
|
elif child.is_dir():
|
||||||
rmtree(child) # delete directory and contents
|
rmtree(child)
|
||||||
|
|
||||||
# Then replace your ensure_dir with this:
|
|
||||||
|
|
||||||
def ensure_dir(path: Path):
|
def ensure_dir(path: Path):
|
||||||
|
"""Create the output dir if it does not exist"""
|
||||||
if not path.exists():
|
if not path.exists():
|
||||||
path.mkdir(parents=True)
|
path.mkdir(parents=True)
|
||||||
else:
|
else:
|
||||||
clear_dir(path)
|
clear_dir(path)
|
||||||
|
|
||||||
def copy_assets(js_dir, style_dir, build_dir):
|
def copy_assets(js_dir, style_dir, build_dir):
|
||||||
|
"""Copy public assets to output dir"""
|
||||||
for folder in [js_dir, style_dir]:
|
for folder in [js_dir, style_dir]:
|
||||||
if folder.exists():
|
if folder.exists():
|
||||||
dest = build_dir / folder.name
|
dest = build_dir / folder.name
|
||||||
|
Reference in New Issue
Block a user