Compare commits
26 Commits
b2ba1d7c7f
...
v2.0.1
Author | SHA1 | Date | |
---|---|---|---|
df96782500 | |||
febf4d2be5 | |||
617545e1bb | |||
922ce99679 | |||
cd0428990a | |||
d3af86be8c | |||
c825798b13 | |||
b5375343a8 | |||
757e676d2d | |||
0079c166e8 | |||
ee6d4a1fa2 | |||
c6c3162b83 | |||
04c1214cd1 | |||
b03779b487 | |||
b5f8ceeb31 | |||
1591886505 | |||
a6b63c2d2b | |||
8a04fe5aa6 | |||
2cb171806c | |||
ded97700d9 | |||
8533ce72e9 | |||
7a95ef0255 | |||
906699f023 | |||
643a729f94 | |||
a02da47e73 | |||
f7f2356510 |
22
README.MD
22
README.MD
@ -18,7 +18,7 @@ The project includes two thoughtfully designed themes—one modern, one minimali
|
||||
- **Typewriter** — [View Demo](https://typewriter.djeex.fr)
|
||||
|
||||
> [!NOTE]
|
||||
> This GitHub repository is a mirror of the primary source at [git.djeex.fr/Djeex/lumeex](https://git.djeex.fr/Djeex/lumeex). The main repository includes the full history, releases, and bug-checking assisted by an LLM.
|
||||
> _This GitHub repository is a mirror of the primary source at [git.djeex.fr/Djeex/lumeex](https://git.djeex.fr/Djeex/lumeex). The main repository includes the full history and releases_.
|
||||
|
||||
|
||||
## 📌 Table of Contents
|
||||
@ -41,20 +41,26 @@ The project includes two thoughtfully designed themes—one modern, one minimali
|
||||
- Typewriter — [Demo](https://typewriter.djeex.fr)
|
||||
- Supports Google Fonts and locally hosted fonts
|
||||
|
||||
### No-Code Builder (YAML Based)
|
||||
### No-Code Builder (WebUI Manager)
|
||||
|
||||
- Configure site info, SEO, colors, fonts, and more through simple YAML files
|
||||
- Reference and tag photos without any coding required
|
||||
- *(Optional)* Automatically update photo references via script
|
||||
|
||||
<div align="center">
|
||||
<img src="https://git.djeex.fr/Djeex/lumeex/raw/branch/main/illustration/lumeex-webui.png" alt="Lumeex Screenshot" />
|
||||
</div>
|
||||
|
||||
|
||||
### Simple Build Process
|
||||
|
||||
- Compiles static site from YAML configuration files (themes, templates, fonts, colors)
|
||||
- Configure site info, SEO, colors, fonts, and more through a simple convenient WebUI
|
||||
- Add and tag your photo photos without any coding required
|
||||
- Converts favicon automatically to all required formats
|
||||
- Resizes social sharing thumbnails
|
||||
- *(Optional)* Automatically resizes photos to a maximum width of 1140px
|
||||
- *(Optional)* Converts images to WebP format for optimized performance
|
||||
- Outputs a complete static website ready to deploy on any web server
|
||||
- Build your static site in one click and get a zip archive or an output folder, ready to deploy to your preferred webserver
|
||||
|
||||
### Don't want a WebUI ?
|
||||
|
||||
- CLI process is documented
|
||||
|
||||
## 🐳 Docker or 🐍 Python Installation
|
||||
For comprehensive documentation on installation, configuration options, customization, and demos, please visit:
|
||||
|
BIN
illustration/lumeex-webui.png
Normal file
BIN
illustration/lumeex-webui.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 620 KiB |
@ -3,6 +3,13 @@
|
||||
|
||||
// Fade in effect for elements with class 'appear'
|
||||
const setupIntersectionObserver = () => {
|
||||
document.querySelectorAll('.appear').forEach(parent => {
|
||||
const children = parent.querySelectorAll('.appear');
|
||||
children.forEach((child, i) => {
|
||||
child.style.transitionDelay = `${i * 0.2}s`;
|
||||
});
|
||||
});
|
||||
|
||||
const items = document.querySelectorAll('.appear');
|
||||
const io = new IntersectionObserver((entries) => {
|
||||
entries.forEach((entry) => {
|
||||
@ -32,6 +39,7 @@ const randomizeHeroBackground = () => {
|
||||
if (images.length === 0) return;
|
||||
let currentIndex = Math.floor(Math.random() * images.length);
|
||||
heroBg.style.backgroundImage = `url(/img/${images[currentIndex]})`;
|
||||
if (images.length < 2) return; // <-- Prevent interval if only one image
|
||||
setInterval(() => {
|
||||
let nextIndex;
|
||||
do {
|
||||
|
@ -192,36 +192,6 @@ h2 {
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.appear.inview:nth-child(1) {
|
||||
-webkit-transition-delay: 0s;
|
||||
transition-delay: 0s;
|
||||
}
|
||||
|
||||
.appear.inview:nth-child(2) {
|
||||
-webkit-transition-delay: 0.2s;
|
||||
transition-delay: 0.2s;
|
||||
}
|
||||
|
||||
.appear.inview:nth-child(3) {
|
||||
-webkit-transition-delay: 0.4s;
|
||||
transition-delay: 0.4s;
|
||||
}
|
||||
|
||||
.appear.inview:nth-child(4) {
|
||||
-webkit-transition-delay: 0.6s;
|
||||
transition-delay: 0.6s;
|
||||
}
|
||||
|
||||
.appear.inview:nth-child(5) {
|
||||
-webkit-transition-delay: 0.8s;
|
||||
transition-delay: 0.8s;
|
||||
}
|
||||
|
||||
.appear.inview:nth-child(6) {
|
||||
-webkit-transition-delay: 1s;
|
||||
transition-delay: 1s;
|
||||
}
|
||||
|
||||
/* img fade in */
|
||||
|
||||
.fade-in-img {
|
||||
@ -491,7 +461,7 @@ h2 {
|
||||
}
|
||||
|
||||
.section img {
|
||||
margin: 0px 0 60px 0;
|
||||
margin: 0px 0 40px 0;
|
||||
}
|
||||
|
||||
.tag {
|
||||
|
@ -12,7 +12,7 @@
|
||||
</div>
|
||||
<div class="inner bottom-link appear">
|
||||
<p><span class="navigation-subtitle appear">{{ copyright }}</span><span class="nav-separator"> • </span><span class="navigation-bottom-link appear"><a href="{{ legal_link }}">{{ legal_label }}</a></span></p>
|
||||
<p class="navigation-subtitle appear"> Built with <a href="https://git.djeex.fr/Djeex/lumeex">Lumeex</a></p>
|
||||
<p class="navigation-subtitle appear"> Built with <a href="https://lumeex.djeex.fr">Lumeex</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,43 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="UTF-8">
|
||||
<title>Lumeex</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='style/style.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<!-- Top bar -->
|
||||
<div class="nav-bar">
|
||||
<div class="content-inner nav">
|
||||
<input type="checkbox" id="nav-check">
|
||||
<div class="nav-header">
|
||||
<div class="nav-title">
|
||||
<a href="/"><img src="{{ url_for('static', filename='img/logo.svg') }}"></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="nav-btn">
|
||||
<label for="nav-check">
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="nav-links">
|
||||
<ul class="nav-list">
|
||||
<li class="nav-item"><a href="/gallery-editor">Gallery</a></li>
|
||||
<li class="nav-item"><a href="/site-info">Site info</a></li>
|
||||
<li class="nav-item"><a href="/theme-editor">Theme info</a></li>
|
||||
<li class="nav-item">
|
||||
<button id="build-btn" class="button">🚀 Build !</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Toast container for notifications -->
|
||||
<div class="content-inner">
|
||||
<div id="toast-container"></div>
|
||||
|
||||
{% extends "template/base.html" %}
|
||||
|
||||
{% block title %}Lumeex - Gallery Editor{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>Gallery editor</h1>
|
||||
|
||||
@ -74,53 +40,31 @@
|
||||
<p> Follow the steps to generate your static gallery</p>
|
||||
<ul id="stepper">
|
||||
<li><a class="step-active" href="/gallery-editor">Upload your photos</a></li>
|
||||
<div>→</div>
|
||||
<div></div>
|
||||
<li><a href="/site-info">Configure site info</a></li>
|
||||
<div>→</div>
|
||||
<div></div>
|
||||
<li><a href="/theme-editor">Customize your theme</a></li>
|
||||
<div>→</div>
|
||||
<div></div>
|
||||
<li><button id="stepper-build">Generate your static site!</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<script src="{{ url_for('static', filename='js/main.js') }}" defer></script>
|
||||
<script src="{{ url_for('static', filename='js/upload.js') }}" defer></script>
|
||||
<script src="{{ url_for('static', filename='js/build.js') }}" defer></script>
|
||||
<!-- Delete confirmation modal -->
|
||||
<div id="delete-modal" class="modal" style="display:none;">
|
||||
<div class="modal-content">
|
||||
<span id="delete-modal-close" class="modal-close">×</span>
|
||||
<h3>Confirm Deletion</h3>
|
||||
<p id="delete-modal-text">Are you sure you want to delete this image?</p>
|
||||
<div class="modal-actions">
|
||||
<button id="delete-modal-confirm" class="modal-btn danger">Delete</button>
|
||||
<button id="delete-modal-cancel" class="modal-btn">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Build success modal -->
|
||||
<div id="build-success-modal" class="modal" style="display:none;">
|
||||
<div class="modal-content">
|
||||
<span id="build-success-modal-close" class="modal-close">×</span>
|
||||
<h3>✅ Build completed!</h3>
|
||||
<p>Your files are available in the output folder.</p>
|
||||
<button id="download-zip-btn" class="modal-btn">Download ZIP</button>
|
||||
<div id="zip-loader" style="display:none;">Creating ZIP...</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="footer">
|
||||
<div class="content-inner">
|
||||
<div class="footer-container">
|
||||
<div class="footer-credit">
|
||||
<p><a href="https//lumeex.djeex.fr"><span class="lum-first">Lum</span><span class="lum-second">eex</span> v{{ lumeex_version }}</a> — © 2025</p>
|
||||
</div>
|
||||
<div class="footer-links">
|
||||
<a class="footer-link documentation" href="https://lumeex.djeex.fr"><span class="icon"><img src="/img/favicon.svg"></span><span class="icon-text">Documentation</span></a>
|
||||
<a class="footer-link gitea" href="https://gitea.com/Djeex/lumeex"><span class="icon"><img src="/img/gitea.svg"></span><span class="icon-text">Giteex</span></a>
|
||||
<a class="footer-link github" href="https://github.com/Djeex/lumeex"><span class="icon"><img src="/img/github.svg"></span><span class="icon-text">Github</span></a>
|
||||
<!-- Delete confirmation modal -->
|
||||
<div id="delete-modal" class="modal" style="display:none;">
|
||||
<div class="modal-content">
|
||||
<span id="delete-modal-close" class="modal-close">×</span>
|
||||
<h3>Confirm Deletion</h3>
|
||||
<p id="delete-modal-text">Are you sure you want to delete this image?</p>
|
||||
<div class="modal-actions">
|
||||
<button id="delete-modal-confirm" class="modal-btn danger">Delete</button>
|
||||
<button id="delete-modal-cancel" class="modal-btn">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="{{ url_for('static', filename='js/gallery-editor.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/upload.js') }}"></script>
|
||||
{% endblock %}
|
@ -1,43 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="UTF-8">
|
||||
<title>Lumeex</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='style/style.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<!-- Top bar -->
|
||||
<div class="nav-bar">
|
||||
<div class="content-inner nav">
|
||||
<input type="checkbox" id="nav-check">
|
||||
<div class="nav-header">
|
||||
<div class="nav-title">
|
||||
<a href="/"><img src="{{ url_for('static', filename='img/logo.svg') }}"></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="nav-btn">
|
||||
<label for="nav-check">
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="nav-links">
|
||||
<ul class="nav-list">
|
||||
<li class="nav-item"><a href="/gallery-editor">Gallery</a></li>
|
||||
<li class="nav-item"><a href="/site-info">Site info</a></li>
|
||||
<li class="nav-item"><a href="/theme-editor">Theme info</a></li>
|
||||
<li class="nav-item">
|
||||
<button id="build-btn" class="button">🚀 Build !</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Toast container for notifications -->
|
||||
<div class="content-inner first-content">
|
||||
<div id="toast-container"></div>
|
||||
{% extends "template/base.html" %}
|
||||
|
||||
{% block title %}Lumeex{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>Static Gallery Generator</h1>
|
||||
<p>Use this generator to create a static gallery from your photos. Then, upload the static files to your preferred web server.</p>
|
||||
@ -46,40 +11,21 @@
|
||||
<div class="section">
|
||||
<h2>Steps</h2>
|
||||
<p> Follow the steps to generate your static gallery</p>
|
||||
<ul id="stepper">
|
||||
<li><a href="/gallery-editor">Upload your photos</a></li>
|
||||
<div>→</div>
|
||||
<li><a href="/site-info">Configure site info</a></li>
|
||||
<div>→</div>
|
||||
<li><a href="/theme-editor">Customize your theme</a></li>
|
||||
<div>→</div>
|
||||
<li><button id="stepper-build">Generate your static site!</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Build success modal -->
|
||||
<div id="build-success-modal" class="modal" style="display:none;">
|
||||
<div class="modal-content">
|
||||
<span id="build-success-modal-close" class="modal-close">×</span>
|
||||
<h3>✅ Build completed!</h3>
|
||||
<p>Your files are available in the output folder.</p>
|
||||
<button id="download-zip-btn" class="modal-btn">Download ZIP</button>
|
||||
<div id="zip-loader" style="display:none;">Creating ZIP...</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="footer">
|
||||
<div class="content-inner">
|
||||
<div class="footer-container">
|
||||
<div class="footer-credit">
|
||||
<p><a href="https//lumeex.djeex.fr"><span class="lum-first">Lum</span><span class="lum-second">eex</span> v{{ lumeex_version }}</a> — © 2025</p>
|
||||
</div>
|
||||
<div class="footer-links">
|
||||
<a class="footer-link documentation" href="https://lumeex.djeex.fr"><span class="icon"><img src="/img/favicon.svg"></span><span class="icon-text">Documentation</span></a>
|
||||
<a class="footer-link gitea" href="https://gitea.com/Djeex/lumeex"><span class="icon"><img src="/img/gitea.svg"></span><span class="icon-text">Giteex</span></a>
|
||||
<a class="footer-link github" href="https://github.com/Djeex/lumeex"><span class="icon"><img src="/img/github.svg"></span><span class="icon-text">Github</span></a>
|
||||
</div>
|
||||
<div class="stepper">
|
||||
<ul id="stepper">
|
||||
<li><a href="/gallery-editor">Upload your photos</a></li>
|
||||
<div></div>
|
||||
<li><a href="/site-info">Configure site info</a></li>
|
||||
<div></div>
|
||||
<li><a href="/theme-editor">Customize your theme</a></li>
|
||||
<div></div>
|
||||
<li><button id="stepper-build">Generate your static site!</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
|
||||
{% endblock %}
|
@ -4,6 +4,21 @@
|
||||
* @param {string} type - "success" or "error".
|
||||
* @param {number} duration - Duration in ms.
|
||||
*/
|
||||
|
||||
// --- Loader helpers ---
|
||||
function showLoader(text = "Uploading...") {
|
||||
const loader = document.getElementById("global-loader");
|
||||
if (loader) {
|
||||
loader.classList.add("active");
|
||||
document.getElementById("loader-text").textContent = text;
|
||||
}
|
||||
}
|
||||
function hideLoader() {
|
||||
const loader = document.getElementById("global-loader");
|
||||
if (loader) loader.classList.remove("active");
|
||||
}
|
||||
|
||||
// --- Toast helpers ---
|
||||
function showToast(message, type = "success", duration = 3000) {
|
||||
const container = document.getElementById("toast-container");
|
||||
if (!container) return;
|
||||
@ -29,9 +44,11 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
|
||||
// Build action handler
|
||||
async function handleBuildClick() {
|
||||
showLoader("Building static site...");
|
||||
// Trigger build on backend
|
||||
const res = await fetch("/api/build", { method: "POST" });
|
||||
const result = await res.json();
|
||||
hideLoader();
|
||||
if (result.status === "ok") {
|
||||
// Show build success modal
|
||||
if (buildModal) buildModal.style.display = "flex";
|
||||
|
@ -92,6 +92,14 @@ function renderTags(imgIndex, tags) {
|
||||
input.placeholder = 'Add tag...';
|
||||
inputContainer.appendChild(input);
|
||||
|
||||
// --- Validate button ---
|
||||
const validateBtn = document.createElement('button');
|
||||
validateBtn.textContent = '✔️';
|
||||
validateBtn.className = 'validate-tag-btn';
|
||||
validateBtn.style.display = 'none'; // hidden by default
|
||||
validateBtn.style.marginLeft = '4px';
|
||||
inputContainer.appendChild(validateBtn);
|
||||
|
||||
const suggestionBox = document.createElement('ul');
|
||||
suggestionBox.className = 'suggestions';
|
||||
inputContainer.appendChild(suggestionBox);
|
||||
@ -148,8 +156,14 @@ function renderTags(imgIndex, tags) {
|
||||
}
|
||||
};
|
||||
|
||||
input.addEventListener('input', updateSuggestions);
|
||||
input.addEventListener('focus', updateSuggestions);
|
||||
input.addEventListener('input', () => {
|
||||
updateSuggestions();
|
||||
validateBtn.style.display = input.value.trim() ? 'inline-block' : 'none';
|
||||
});
|
||||
input.addEventListener('focus', () => {
|
||||
updateSuggestions();
|
||||
validateBtn.style.display = input.value.trim() ? 'inline-block' : 'none';
|
||||
});
|
||||
|
||||
input.addEventListener('keydown', (e) => {
|
||||
const items = suggestionBox.querySelectorAll('li');
|
||||
@ -172,11 +186,13 @@ function renderTags(imgIndex, tags) {
|
||||
}
|
||||
input.value = '';
|
||||
updateSuggestions();
|
||||
validateBtn.style.display = 'none';
|
||||
} else if ([' ', ','].includes(e.key)) {
|
||||
e.preventDefault();
|
||||
addTag(input.value);
|
||||
input.value = '';
|
||||
updateSuggestions();
|
||||
validateBtn.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
@ -184,9 +200,20 @@ function renderTags(imgIndex, tags) {
|
||||
setTimeout(() => {
|
||||
suggestionBox.style.display = 'none';
|
||||
input.value = '';
|
||||
validateBtn.style.display = 'none';
|
||||
}, 150);
|
||||
});
|
||||
|
||||
// --- Validate button action ---
|
||||
validateBtn.onclick = () => {
|
||||
if (input.value.trim()) {
|
||||
addTag(input.value.trim());
|
||||
input.value = '';
|
||||
updateSuggestions();
|
||||
validateBtn.style.display = 'none';
|
||||
}
|
||||
};
|
||||
|
||||
input.focus();
|
||||
updateSuggestions();
|
||||
}
|
@ -12,6 +12,19 @@ function showToast(message, type = "success", duration = 3000) {
|
||||
}, duration);
|
||||
}
|
||||
|
||||
// --- Loader helpers ---
|
||||
function showLoader(text = "Uploading...") {
|
||||
const loader = document.getElementById("global-loader");
|
||||
if (loader) {
|
||||
loader.classList.add("active");
|
||||
document.getElementById("loader-text").textContent = text;
|
||||
}
|
||||
}
|
||||
function hideLoader() {
|
||||
const loader = document.getElementById("global-loader");
|
||||
if (loader) loader.classList.remove("active");
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
// Form and menu logic
|
||||
const form = document.getElementById("site-info-form");
|
||||
@ -130,10 +143,12 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
thumbnailUpload.addEventListener("change", async (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (!file) return;
|
||||
showLoader("Uploading thumbnail...");
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
const res = await fetch("/api/thumbnail/upload", { method: "POST", body: formData });
|
||||
const result = await res.json();
|
||||
hideLoader();
|
||||
if (result.status === "ok") {
|
||||
if (thumbnailInput) thumbnailInput.value = result.filename;
|
||||
updateThumbnailPreview(`/photos/${result.filename}?t=${Date.now()}`);
|
||||
@ -183,12 +198,14 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
themeUpload.addEventListener("change", async (e) => {
|
||||
const files = Array.from(e.target.files);
|
||||
if (files.length === 0) return;
|
||||
showLoader("Uploading theme...");
|
||||
const formData = new FormData();
|
||||
files.forEach(file => {
|
||||
formData.append("files", file, file.webkitRelativePath || file.name);
|
||||
});
|
||||
const res = await fetch("/api/theme/upload", { method: "POST", body: formData });
|
||||
const result = await res.json();
|
||||
hideLoader();
|
||||
if (result.status === "ok") {
|
||||
showToast("✅ Theme uploaded!", "success");
|
||||
// Refresh theme select after upload
|
||||
@ -239,12 +256,14 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
};
|
||||
deleteThemeModalConfirm.onclick = async () => {
|
||||
if (!themeToDelete) return;
|
||||
showLoader("Removing theme...");
|
||||
const res = await fetch("/api/theme/remove", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ theme: themeToDelete })
|
||||
});
|
||||
const result = await res.json();
|
||||
hideLoader();
|
||||
if (result.status === "ok") {
|
||||
showToast("✅ Theme removed!", "success");
|
||||
// Refresh theme select
|
||||
@ -379,7 +398,9 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
|
||||
// Check if thumbnail is set before saving (uploaded or present in input)
|
||||
if (!thumbnailInput || !thumbnailInput.value) {
|
||||
showLoader("Saving...");
|
||||
showToast("❌ Thumbnail is required.", "error");
|
||||
hideLoader();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -417,6 +438,8 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
intellectual_property: ipParagraphs
|
||||
}
|
||||
};
|
||||
// --- REMOVE loader for save ---
|
||||
// showLoader("Saving...");
|
||||
const res = await fetch("/api/site-info", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
|
@ -31,6 +31,20 @@ function showToast(message, type = "success", duration = 3000) {
|
||||
}, duration);
|
||||
}
|
||||
|
||||
// --- Loader helpers ---
|
||||
function showLoader(text = "Uploading...") {
|
||||
const loader = document.getElementById("global-loader");
|
||||
if (loader) {
|
||||
loader.classList.add("active");
|
||||
document.getElementById("loader-text").textContent = text;
|
||||
}
|
||||
}
|
||||
function hideLoader() {
|
||||
const loader = document.getElementById("global-loader");
|
||||
if (loader) loader.classList.remove("active");
|
||||
}
|
||||
|
||||
// --- Color Picker
|
||||
function setupColorPicker(colorId, btnId, textId, initial) {
|
||||
const colorInput = document.getElementById(colorId);
|
||||
const colorBtn = document.getElementById(btnId);
|
||||
@ -40,7 +54,6 @@ function setupColorPicker(colorId, btnId, textId, initial) {
|
||||
colorBtn.style.background = initial;
|
||||
textInput.value = initial.toUpperCase();
|
||||
|
||||
// Color input is positioned over the button and is clickable
|
||||
colorInput.addEventListener("input", () => {
|
||||
colorBtn.style.background = colorInput.value;
|
||||
textInput.value = colorInput.value.toUpperCase();
|
||||
@ -178,11 +191,13 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
showToast("Only .woff and .woff2 fonts are allowed.", "error");
|
||||
return;
|
||||
}
|
||||
showLoader("Uploading font...");
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
formData.append("theme", themeInfo.theme_name);
|
||||
const res = await fetch("/api/font/upload", { method: "POST", body: formData });
|
||||
const result = await res.json();
|
||||
hideLoader();
|
||||
if (result.status === "ok") {
|
||||
showToast("✅ Font uploaded!", "success");
|
||||
localFonts = await fetchLocalFonts(themeInfo.theme_name);
|
||||
@ -219,9 +234,11 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
};
|
||||
deleteFontModalConfirm.onclick = async () => {
|
||||
if (!fontToDelete) return;
|
||||
showLoader("Removing font...");
|
||||
const result = await removeFont(themeInfo.theme_name, fontToDelete);
|
||||
hideLoader();
|
||||
if (result.status === "ok") {
|
||||
showToast("Font removed!", "✅ success");
|
||||
showToast("Font removed!", "success");
|
||||
localFonts = await fetchLocalFonts(themeInfo.theme_name);
|
||||
refreshLocalFonts();
|
||||
} else {
|
||||
@ -272,11 +289,13 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
showToast("Invalid file type for favicon.", "error");
|
||||
return;
|
||||
}
|
||||
showLoader("Uploading favicon...");
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
formData.append("theme", themeInfo.theme_name);
|
||||
const res = await fetch("/api/favicon/upload", { method: "POST", body: formData });
|
||||
const result = await res.json();
|
||||
hideLoader();
|
||||
if (result.status === "ok") {
|
||||
faviconInput.value = result.filename;
|
||||
updateFaviconPreview(`/themes/${themeInfo.theme_name}/${result.filename}?t=${Date.now()}`);
|
||||
@ -303,12 +322,14 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
}
|
||||
};
|
||||
deleteFaviconModalConfirm.onclick = async () => {
|
||||
showLoader("Removing favicon...");
|
||||
const res = await fetch("/api/favicon/remove", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ theme: themeInfo.theme_name })
|
||||
});
|
||||
const result = await res.json();
|
||||
hideLoader();
|
||||
if (result.status === "ok") {
|
||||
faviconInput.value = "";
|
||||
updateFaviconPreview("");
|
||||
@ -335,13 +356,11 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
if (addGoogleFontBtn) {
|
||||
addGoogleFontBtn.addEventListener("click", async () => {
|
||||
googleFonts.push({ family: "", weights: [] });
|
||||
// Save immediately to backend
|
||||
await fetch("/api/theme-google-fonts", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ theme_name: themeInfo.theme_name, google_fonts: googleFonts })
|
||||
});
|
||||
// Fetch updated theme info and refresh dropdowns
|
||||
const updatedThemeInfo = await fetchThemeInfo();
|
||||
const updatedGoogleFonts = updatedThemeInfo.theme_yaml.google_fonts || [];
|
||||
googleFonts.length = 0;
|
||||
@ -353,13 +372,11 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
|
||||
const googleFontsFields = document.getElementById("google-fonts-fields");
|
||||
if (googleFontsFields) {
|
||||
// Save on blur for family/weights fields
|
||||
googleFontsFields.addEventListener("blur", async (e) => {
|
||||
if (
|
||||
e.target.name &&
|
||||
(e.target.name.endsWith("[family]") || e.target.name.endsWith("[weights]"))
|
||||
) {
|
||||
// Update googleFonts array from the form fields
|
||||
const fontFields = googleFontsFields.querySelectorAll(".input-field");
|
||||
googleFonts.length = 0;
|
||||
fontFields.forEach(field => {
|
||||
@ -368,13 +385,11 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
.split(",").map(w => w.trim()).filter(Boolean);
|
||||
googleFonts.push({ family, weights });
|
||||
});
|
||||
// Save immediately to backend
|
||||
await fetch("/api/theme-google-fonts", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ theme_name: themeInfo.theme_name, google_fonts: googleFonts })
|
||||
});
|
||||
// Fetch updated theme info and refresh dropdowns
|
||||
const updatedThemeInfo = await fetchThemeInfo();
|
||||
const updatedGoogleFonts = updatedThemeInfo.theme_yaml.google_fonts || [];
|
||||
googleFonts.length = 0;
|
||||
@ -382,20 +397,17 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
renderGoogleFonts(googleFonts);
|
||||
refreshFontDropdowns();
|
||||
}
|
||||
}, true); // Use capture phase to catch blur from children
|
||||
}, true);
|
||||
|
||||
// Delegate remove button click for Google Fonts
|
||||
googleFontsFields.addEventListener("click", async (e) => {
|
||||
if (e.target.classList.contains("remove-google-font")) {
|
||||
const idx = Number(e.target.dataset.idx);
|
||||
googleFonts.splice(idx, 1);
|
||||
// Save immediately to backend
|
||||
await fetch("/api/theme-google-fonts", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ theme_name: themeInfo.theme_name, google_fonts: googleFonts })
|
||||
});
|
||||
// Fetch updated theme info and refresh dropdowns
|
||||
const updatedThemeInfo = await fetchThemeInfo();
|
||||
const updatedGoogleFonts = updatedThemeInfo.theme_yaml.google_fonts || [];
|
||||
googleFonts.length = 0;
|
||||
@ -406,9 +418,9 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
});
|
||||
}
|
||||
|
||||
// Form submit
|
||||
document.getElementById("theme-editor-form").addEventListener("submit", async (e) => {
|
||||
e.preventDefault();
|
||||
showLoader("Saving theme...");
|
||||
const data = {};
|
||||
data.colors = {
|
||||
primary: document.getElementById("color-primary-text").value,
|
||||
@ -445,6 +457,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ theme_name: themeInfo.theme_name, theme_yaml: data })
|
||||
});
|
||||
hideLoader();
|
||||
if (res.ok) {
|
||||
showToast("✅ Theme saved!", "success");
|
||||
} else {
|
||||
|
@ -1,41 +1,64 @@
|
||||
// --- Loader helpers ---
|
||||
function showLoader(text = "Uploading...") {
|
||||
const loader = document.getElementById("global-loader");
|
||||
if (loader) {
|
||||
loader.classList.add("active");
|
||||
document.getElementById("loader-text").textContent = text;
|
||||
}
|
||||
}
|
||||
function hideLoader() {
|
||||
const loader = document.getElementById("global-loader");
|
||||
if (loader) loader.classList.remove("active");
|
||||
}
|
||||
|
||||
// --- Upload gallery images ---
|
||||
document.getElementById('upload-gallery').addEventListener('change', async (e) => {
|
||||
const files = e.target.files;
|
||||
if (!files.length) return;
|
||||
const galleryInput = document.getElementById('upload-gallery');
|
||||
if (galleryInput) {
|
||||
galleryInput.addEventListener('change', async (e) => {
|
||||
const files = e.target.files;
|
||||
if (!files.length) return;
|
||||
showLoader("Uploading photos...");
|
||||
const formData = new FormData();
|
||||
for (const file of files) formData.append('files', file);
|
||||
|
||||
const formData = new FormData();
|
||||
for (const file of files) formData.append('files', file);
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/gallery/upload', { method: 'POST', body: formData });
|
||||
const data = await res.json();
|
||||
if (res.ok) {
|
||||
showToast(`✅ ${data.uploaded.length} gallery image(s) uploaded!`, "success");
|
||||
refreshGallery();
|
||||
} else showToast('Error: ' + data.error, "error");
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
showToast('Server error!', "error");
|
||||
} finally { e.target.value = ''; }
|
||||
});
|
||||
try {
|
||||
const res = await fetch('/api/gallery/upload', { method: 'POST', body: formData });
|
||||
const data = await res.json();
|
||||
hideLoader();
|
||||
if (res.ok) {
|
||||
showToast(`✅ ${data.uploaded.length} gallery image(s) uploaded!`, "success");
|
||||
if (typeof refreshGallery === "function") refreshGallery();
|
||||
} else showToast('Error: ' + data.error, "error");
|
||||
} catch(err) {
|
||||
hideLoader();
|
||||
console.error(err);
|
||||
showToast('Server error!', "error");
|
||||
} finally { e.target.value = ''; }
|
||||
});
|
||||
}
|
||||
|
||||
// --- Upload hero images ---
|
||||
document.getElementById('upload-hero').addEventListener('change', async (e) => {
|
||||
const files = e.target.files;
|
||||
if (!files.length) return;
|
||||
const heroInput = document.getElementById('upload-hero');
|
||||
if (heroInput) {
|
||||
heroInput.addEventListener('change', async (e) => {
|
||||
const files = e.target.files;
|
||||
if (!files.length) return;
|
||||
showLoader("Uploading hero photos...");
|
||||
const formData = new FormData();
|
||||
for (const file of files) formData.append('files', file);
|
||||
|
||||
const formData = new FormData();
|
||||
for (const file of files) formData.append('files', file);
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/hero/upload', { method: 'POST', body: formData });
|
||||
const data = await res.json();
|
||||
if (res.ok) {
|
||||
showToast(`✅ ${data.uploaded.length} hero image(s) uploaded!`, "success");
|
||||
refreshHero();
|
||||
} else showToast('Error: ' + data.error, "error");
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
showToast('Server error!', "error");
|
||||
} finally { e.target.value = ''; }
|
||||
});
|
||||
try {
|
||||
const res = await fetch('/api/hero/upload', { method: 'POST', body: formData });
|
||||
const data = await res.json();
|
||||
hideLoader();
|
||||
if (res.ok) {
|
||||
showToast(`✅ ${data.uploaded.length} hero image(s) uploaded!`, "success");
|
||||
if (typeof refreshHero === "function") refreshHero();
|
||||
} else showToast('Error: ' + data.error, "error");
|
||||
} catch(err) {
|
||||
hideLoader();
|
||||
console.error(err);
|
||||
showToast('Server error!', "error");
|
||||
} finally { e.target.value = ''; }
|
||||
});
|
||||
}
|
@ -1,43 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="UTF-8">
|
||||
<title>Lumeex</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='style/style.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<!-- Top bar -->
|
||||
<div class="nav-bar">
|
||||
<div class="content-inner nav">
|
||||
<input type="checkbox" id="nav-check">
|
||||
<div class="nav-header">
|
||||
<div class="nav-title">
|
||||
<a href="/"><img src="{{ url_for('static', filename='img/logo.svg') }}"></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="nav-btn">
|
||||
<label for="nav-check">
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="nav-links">
|
||||
<ul class="nav-list">
|
||||
<li class="nav-item"><a href="/gallery-editor">Gallery</a></li>
|
||||
<li class="nav-item"><a href="/site-info">Site info</a></li>
|
||||
<li class="nav-item"><a href="/theme-editor">Theme info</a></li>
|
||||
<li class="nav-item">
|
||||
<button id="build-btn" class="button">🚀 Build !</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Toast container for notifications -->
|
||||
<div id="site-info" class="content-inner">
|
||||
<div id="toast-container"></div>
|
||||
{% extends "template/base.html" %}
|
||||
|
||||
{% block title %}Lumeex - Site Info{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>Edit Site Info</h1>
|
||||
<form id="site-info-form">
|
||||
<!-- Info Section -->
|
||||
@ -171,64 +137,46 @@
|
||||
<p> Follow the steps to generate your static gallery</p>
|
||||
<ul id="stepper">
|
||||
<li><a href="/gallery-editor">Upload your photos</a></li>
|
||||
<div>→</div>
|
||||
<div></div>
|
||||
<li><a class="step-active" href="/site-info">Configure site info</a></li>
|
||||
<div>→</div>
|
||||
<div></div>
|
||||
<li><a href="/theme-editor">Customize your theme</a></li>
|
||||
<div>→</div>
|
||||
<div></div>
|
||||
<li><button id="stepper-build">Generate your static site!</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Delete confirmation modal (now outside .content-inner) -->
|
||||
<div id="delete-modal" class="modal" style="display:none;">
|
||||
<div class="modal-content">
|
||||
<span id="delete-modal-close" class="modal-close">×</span>
|
||||
<h3>Confirm Deletion</h3>
|
||||
<p id="delete-modal-text">Are you sure you want to remove this thumbnail?</p>
|
||||
<div class="modal-actions">
|
||||
<button id="delete-modal-confirm" class="modal-btn danger">Remove</button>
|
||||
<button id="delete-modal-cancel" class="modal-btn">Cancel</button>
|
||||
<!-- Delete thumbnail confirmation modal-->
|
||||
<div class="content-inner">
|
||||
<div id="delete-modal" class="modal" style="display:none;">
|
||||
<div class="modal-content">
|
||||
<span id="delete-modal-close" class="modal-close">×</span>
|
||||
<h3>Confirm Deletion</h3>
|
||||
<p id="delete-modal-text">Are you sure you want to remove this thumbnail?</p>
|
||||
<div class="modal-actions">
|
||||
<button id="delete-modal-confirm" class="modal-btn danger">Remove</button>
|
||||
<button id="delete-modal-cancel" class="modal-btn">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Delete theme confirmation modal -->
|
||||
<div id="delete-theme-modal" class="modal" style="display:none;">
|
||||
<div class="modal-content">
|
||||
<span id="delete-theme-modal-close" class="modal-close">×</span>
|
||||
<h3>Confirm Theme Deletion</h3>
|
||||
<p id="delete-theme-modal-text">Are you sure you want to remove this theme?</p>
|
||||
<div class="modal-actions">
|
||||
<button id="delete-theme-modal-confirm" class="modal-btn danger">Remove</button>
|
||||
<button id="delete-theme-modal-cancel" class="modal-btn">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Build success modal -->
|
||||
<div id="build-success-modal" class="modal" style="display:none;">
|
||||
<div class="modal-content">
|
||||
<span id="build-success-modal-close" class="modal-close">×</span>
|
||||
<h3>✅ Build completed!</h3>
|
||||
<p>Your files are available in the output folder.</p>
|
||||
<button id="download-zip-btn" class="modal-btn">Download ZIP</button>
|
||||
<div id="zip-loader" style="display:none;">Creating ZIP...</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="footer">
|
||||
<div class="content-inner">
|
||||
<div class="footer-container">
|
||||
<div class="footer-credit">
|
||||
<p><a href="https//lumeex.djeex.fr"><span class="lum-first">Lum</span><span class="lum-second">eex</span> v{{ lumeex_version }}</a> — © 2025</p>
|
||||
</div>
|
||||
<div class="footer-links">
|
||||
<a class="footer-link documentation" href="https://lumeex.djeex.fr"><span class="icon"><img src="/img/favicon.svg"></span><span class="icon-text">Documentation</span></a>
|
||||
<a class="footer-link gitea" href="https://gitea.com/Djeex/lumeex"><span class="icon"><img src="/img/gitea.svg"></span><span class="icon-text">Giteex</span></a>
|
||||
<a class="footer-link github" href="https://github.com/Djeex/lumeex"><span class="icon"><img src="/img/github.svg"></span><span class="icon-text">Github</span></a>
|
||||
<div class="content-inner">
|
||||
<div id="delete-theme-modal" class="modal" style="display:none;">
|
||||
<div class="modal-content">
|
||||
<span id="delete-theme-modal-close" class="modal-close">×</span>
|
||||
<h3>Confirm Theme Deletion</h3>
|
||||
<p id="delete-theme-modal-text">Are you sure you want to remove this theme?</p>
|
||||
<div class="modal-actions">
|
||||
<button id="delete-theme-modal-confirm" class="modal-btn danger">Remove</button>
|
||||
<button id="delete-theme-modal-cancel" class="modal-btn">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="{{ url_for('static', filename='js/site-info.js')}}"></script>
|
||||
<script src="{{ url_for('static', filename='js/build.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="{{ url_for('static', filename='js/site-info.js') }}"></script>
|
||||
{% endblock %}
|
@ -1,14 +1,13 @@
|
||||
/* --- Base Styles --- */
|
||||
body {
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
|
||||
margin: 20px;
|
||||
background: #111010;
|
||||
/* background:radial-gradient(ellipse at bottom center, #002a30, #000000bd), radial-gradient(ellipse at top center, #0558a8, #000000fa); */
|
||||
color: #FBFBFB;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
margin:0px;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
a {
|
||||
@ -16,15 +15,6 @@ a {
|
||||
color: #d3d3d3;
|
||||
}
|
||||
|
||||
.content-inner {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
max-width: 1140px;
|
||||
padding: 0 40px;
|
||||
padding-top: 70px;
|
||||
}
|
||||
|
||||
|
||||
h1, h2 {
|
||||
color: #FBFBFB;
|
||||
}
|
||||
@ -32,26 +22,204 @@ h2 {
|
||||
color: #55c3ec;
|
||||
}
|
||||
|
||||
/* --- Toolbar --- */
|
||||
.toolbar {
|
||||
margin-bottom: 20px;
|
||||
.content-inner {
|
||||
margin: 0 auto;
|
||||
max-width: 1220px;
|
||||
padding-top: 70px;
|
||||
width: 100%;
|
||||
}
|
||||
.inner {
|
||||
padding: 0 40px;
|
||||
margin: auto;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.toolbar button {
|
||||
margin-right: 10px;
|
||||
padding: 8px 12px;
|
||||
|
||||
/* --- Navbar & Burger Menu --- */
|
||||
|
||||
.nav-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
background: #0c0d0c29;
|
||||
z-index: 1000;
|
||||
backdrop-filter: blur(20px);
|
||||
border-bottom: 1px solid #21212157;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
max-width: 1140px;
|
||||
width: 100%;
|
||||
padding: 0 40px;
|
||||
margin: 0 auto;
|
||||
height: 70px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 22px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.nav img {
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.nav-list {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.nav-item a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: bold;
|
||||
color:#fff;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.nav-item a:hover {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: bold;
|
||||
color: #55c3ec;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.nav-list > li + li::before {
|
||||
content: " → ";
|
||||
color: #ffc700;
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
.button {
|
||||
padding: 10px 25px;
|
||||
border-radius: 40px;
|
||||
margin-left: 10px;
|
||||
font-size: 14px;
|
||||
background: linear-gradient(135deg, #26c4ff, #016074);
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
border: none;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.2s;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.toolbar button:hover {
|
||||
background-color: #45a049;
|
||||
.button:hover {
|
||||
background: linear-gradient(135deg, #72d9ff, #26657e);
|
||||
}
|
||||
|
||||
/* --- Burger Menu --- */
|
||||
.nav-burger {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
margin-left: auto;
|
||||
z-index: 1100;
|
||||
}
|
||||
|
||||
.nav-burger span {
|
||||
display: block;
|
||||
width: 28px;
|
||||
height: 4px;
|
||||
margin: 4px;
|
||||
background: #fff;
|
||||
border-radius: 2px;
|
||||
transition: 0.3s;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* --- Responsive Navbar & Burger --- */
|
||||
@media (max-width: 768px) {
|
||||
.nav-burger {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.nav-list > li::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
position: absolute;
|
||||
top: 70px;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
padding: 24px 0 12px 0;
|
||||
display: none;
|
||||
z-index: 1099;
|
||||
backdrop-filter: blur(20px);
|
||||
background-color: #000000d4
|
||||
}
|
||||
.nav-links .nav-list {
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
.nav-links .nav-item {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.nav-links .nav-item a,
|
||||
.nav-links .nav-item button {
|
||||
width: 100%;
|
||||
padding: 16px 24px;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
border: none;
|
||||
justify-content: center;
|
||||
margin: 0 20px;
|
||||
}
|
||||
|
||||
/* Show menu when burger is checked */
|
||||
.nav-toggle:checked ~ .nav-burger + .nav-links {
|
||||
display: flex;
|
||||
}
|
||||
/* Animate burger to X */
|
||||
.nav-toggle:checked ~ .nav-burger span:nth-child(1) {
|
||||
transform: translateY(12px) rotate(45deg);
|
||||
}
|
||||
.nav-toggle:checked ~ .nav-burger span:nth-child(2) {
|
||||
opacity: 0;
|
||||
}
|
||||
.nav-toggle:checked ~ .nav-burger span:nth-child(3) {
|
||||
transform: translateY(-12px) rotate(-45deg);
|
||||
}
|
||||
}
|
||||
/* --- Upload Section --- */
|
||||
.section {
|
||||
margin-bottom: 30px;
|
||||
@ -60,6 +228,8 @@ h2 {
|
||||
border: 1px solid #2f2e2e80;
|
||||
border-radius: 8px;
|
||||
padding: 0px 20px 20px 20px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.section label {
|
||||
@ -113,22 +283,6 @@ h2 {
|
||||
background-color: #d32f2f;
|
||||
}
|
||||
|
||||
/* --- Responsive Adjustments --- */
|
||||
@media (max-width: 500px) {
|
||||
body {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.toolbar button {
|
||||
margin-bottom: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.section label {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Toast Notifications --- */
|
||||
#toast-container {
|
||||
@ -165,7 +319,6 @@ h2 {
|
||||
/* --- Tags --- */
|
||||
.tag-input {
|
||||
display: flex;
|
||||
flex-wrap: wrap-reverse;
|
||||
align-content: flex-start;
|
||||
gap: 4px;
|
||||
padding: 4px;
|
||||
@ -231,6 +384,24 @@ h2 {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.photo button.validate-tag-btn {
|
||||
border-radius: 30px;
|
||||
border: none;
|
||||
background: #049b3d;
|
||||
color: #fff;
|
||||
font-size: 10px;
|
||||
cursor: pointer;
|
||||
margin-left: 4px;
|
||||
transition: all ease 0.2s;
|
||||
width: 35px;
|
||||
border: 1px solid #585858;
|
||||
}
|
||||
|
||||
.photo button.validate-tag-btn:hover {
|
||||
background: #02cb4e;
|
||||
}
|
||||
|
||||
|
||||
.suggestions li.selected {
|
||||
background-color: #007782;
|
||||
color: white;
|
||||
@ -259,149 +430,7 @@ h2 {
|
||||
width: 100%
|
||||
}
|
||||
|
||||
/* --- Top Bar & Navigation --- */
|
||||
.nav {
|
||||
height: 100%;
|
||||
max-width: 1140px;
|
||||
padding: 0 40px;
|
||||
}
|
||||
|
||||
.nav-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
background-color: #0c0d0c29;
|
||||
z-index: 1000;
|
||||
backdrop-filter: blur(20px);
|
||||
border-bottom: 1px solid #21212157;
|
||||
}
|
||||
|
||||
.nav img {
|
||||
height: 30px;
|
||||
padding: 0px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.nav > .nav-header {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.nav > .nav-header > .nav-title {
|
||||
display: inline-block;
|
||||
font-size: 22px;
|
||||
color: #fff;
|
||||
padding: 0;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.nav > .nav-btn {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav-btn label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.nav > .nav-links {
|
||||
display: inline;
|
||||
float: right;
|
||||
font-size: 14px;
|
||||
height: 100%;
|
||||
line-height: 70px;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.nav-list {
|
||||
list-style-type: disc;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.nav > .nav-links > .nav-list > .nav-item > a {
|
||||
display: inline-block;
|
||||
padding: 0px 15px 0px 15px;
|
||||
text-decoration: none;
|
||||
height: 100%;
|
||||
font-weight: 700;
|
||||
color:#fff
|
||||
}
|
||||
|
||||
.nav > .nav-links > .nav-list > .nav-item > a:hover {
|
||||
color: #55c3ec;
|
||||
}
|
||||
|
||||
.nav > .nav-links > .nav-list > .nav-item > a:active {
|
||||
color: #55c3ec;
|
||||
}
|
||||
|
||||
.nav > #nav-check {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav-list > li + li::before{
|
||||
content: " → ";
|
||||
color: #ffc700;
|
||||
}
|
||||
|
||||
.nav-cta {
|
||||
display: inline;
|
||||
float: right;
|
||||
height: 70px;
|
||||
line-height: 70px;
|
||||
}
|
||||
|
||||
.nav-cta > .arrow {
|
||||
font-size: 12px;
|
||||
display: inline;
|
||||
color: #ffc700;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.nav-bar .button {
|
||||
padding: 10px 25px;
|
||||
border-radius: 40px;
|
||||
margin: 15px 20px 15px 10px;
|
||||
font-size: 14px;
|
||||
display: inline;
|
||||
background: linear-gradient(135deg, #26c4ff, #016074);
|
||||
transition: all 0.2s ease;
|
||||
text-decoration: none;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.nav-bar .button:hover {
|
||||
background: linear-gradient(135deg, #72d9ff, #26657e);
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.nav-links > ul {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.nav-btn span {
|
||||
display: block;
|
||||
height: 4px;
|
||||
width: 28px;
|
||||
background: #fff;
|
||||
margin: 4px 0;
|
||||
border-radius: 2px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
/* --- Custom Upload Buttons --- */
|
||||
/* --- Upload Buttons --- */
|
||||
.up-btn, .footer-links a{
|
||||
display: inline-block;
|
||||
background: #00000000;
|
||||
@ -413,7 +442,6 @@ h2 {
|
||||
text-align: center;
|
||||
transition: all 0.1s ease;
|
||||
user-select: none;
|
||||
/* box-shadow: 0 4px 10px rgba(0,0,0,0.25);*/
|
||||
font-size: 14px;
|
||||
border: 1px solid #585858;
|
||||
}
|
||||
@ -430,18 +458,20 @@ h2 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 30px;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: #131313;
|
||||
background: #000000a3;
|
||||
color: #fff;
|
||||
padding: 2rem 2.5rem;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.25);
|
||||
min-width: 300px;
|
||||
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.25);
|
||||
min-width: 200px;
|
||||
max-width: 90vw;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
backdrop-filter: blur(20px);
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
@ -493,11 +523,12 @@ h2 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* Remove All Buttons */
|
||||
/* --- Remove All Buttons --- */
|
||||
#remove-all-hero, #remove-all-gallery {
|
||||
background: #2d2d2d;
|
||||
color: white;
|
||||
display: none;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
#remove-all-gallery:hover,
|
||||
@ -505,7 +536,6 @@ h2 {
|
||||
background: rgb(121, 26, 19);
|
||||
}
|
||||
|
||||
/* Responsive: stack buttons vertically on small screens */
|
||||
@media (max-width: 500px) {
|
||||
.upload-actions-row {
|
||||
flex-direction: column;
|
||||
@ -514,8 +544,7 @@ h2 {
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Site Info --- */
|
||||
|
||||
/* --- Forms --- */
|
||||
|
||||
fieldset {
|
||||
background-color: rgb(67 67 67 / 26%);
|
||||
@ -543,7 +572,7 @@ legend {
|
||||
|
||||
.input-field {
|
||||
flex: 1 1 calc(33.333% - 18px);
|
||||
min-width: 220px;
|
||||
min-width: 150px;
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
@ -575,6 +604,16 @@ label {
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.07);
|
||||
}
|
||||
|
||||
#theme-editor-form input, #theme-editor-form textarea,#theme-editor-form select {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
#theme-editor-form .fields {
|
||||
gap: 0 18px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#site-info-form input::placeholder,
|
||||
#theme-editor-form input::placeholder,
|
||||
#site-info-form textarea::placeholder,
|
||||
@ -634,7 +673,7 @@ img#thumbnail-preview {
|
||||
background: #00000000;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 18px;
|
||||
border-radius: 30px;
|
||||
padding: 7px 18px;
|
||||
font-size: 0.98em;
|
||||
margin-top: 8px;
|
||||
@ -648,20 +687,6 @@ img#thumbnail-preview {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
#site-info-form, #theme-editor-form {
|
||||
padding: 18px 8px;
|
||||
}
|
||||
.fields,
|
||||
fieldset {
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
}
|
||||
.input-field {
|
||||
min-width: 100%;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
#site-info-form button.remove-menu-item, #site-info-form button.remove-ip-paragraph, #theme-editor-form button.remove-menu-item, #theme-editor-form button.remove-ip-paragraph {
|
||||
margin-top: 0px;
|
||||
@ -693,19 +718,21 @@ img#thumbnail-preview {
|
||||
}
|
||||
|
||||
|
||||
.color-fields {
|
||||
.fields.color-fields {
|
||||
gap: 8px;
|
||||
position: relative;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
|
||||
#theme-editor button.color-btn {
|
||||
height: 100%;
|
||||
border-radius: 8px;
|
||||
#theme-editor-form button.color-btn {
|
||||
height: 40px;
|
||||
border-radius: 40px;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 4px;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
input[type="color"].color-input {
|
||||
@ -744,13 +771,13 @@ fieldset p, .section p {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
/* --- Home --- */
|
||||
/* --- Stepper --- */
|
||||
|
||||
#stepper {
|
||||
display: flex;
|
||||
gap: 18px;
|
||||
flex-wrap: nowrap;
|
||||
align-items: stretch; /* Ensures all children match tallest height */
|
||||
align-items: stretch;
|
||||
padding: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
@ -763,6 +790,11 @@ fieldset p, .section p {
|
||||
|
||||
}
|
||||
|
||||
#stepper > div::before {
|
||||
content: "→";
|
||||
color: #ffc700;
|
||||
}
|
||||
|
||||
#stepper li a, #stepper li button, #stepper > div {
|
||||
justify-content: center;
|
||||
min-width: 100px;
|
||||
@ -815,11 +847,11 @@ justify-content: center;
|
||||
}
|
||||
|
||||
#stepper li {
|
||||
flex: 1 1 auto; /* li can grow/shrink, width is flexible */
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
#stepper > div {
|
||||
flex: 0 0 auto; /* Do not grow or shrink, width is auto */
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@ -827,10 +859,16 @@ justify-content: center;
|
||||
border: none;
|
||||
background: none;
|
||||
padding: 0;
|
||||
min-width: 0; /* Prevent unwanted minimum width */
|
||||
width: auto; /* Let width fit content */
|
||||
min-width: 0;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.stepper {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* --- Footer --- */
|
||||
|
||||
#footer {
|
||||
background-color: #0c0d0c29;
|
||||
z-index: 1000;
|
||||
@ -888,3 +926,114 @@ justify-content: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* --- Global Loader & Spinner --- */
|
||||
#global-loader {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0; left: 0; width: 100vw; height: 100vh;
|
||||
z-index: 99999;
|
||||
background: rgba(0,0,0,0.4);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#global-loader.active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.loader-inner {
|
||||
opacity: 0;
|
||||
transition: opacity 0.9s cubic-bezier(.4,0,.2,1);
|
||||
background: #0c0d0c29;
|
||||
padding: 32px 48px;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 2px 24px #000;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
backdrop-filter: blur(20px);
|
||||
}
|
||||
|
||||
#global-loader.active .loader-inner {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.loader-spinner {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border: 6px solid #55c3ec;
|
||||
border-top: 6px solid #222;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
#loader-text {
|
||||
margin-top: 18px;
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
@keyframes spin { 100% { transform: rotate(360deg); } }
|
||||
|
||||
|
||||
/* --- Responsive Adjustments --- */
|
||||
@media (max-width: 768px) {
|
||||
|
||||
.nav {
|
||||
padding: 0 20px;
|
||||
}
|
||||
.inner {
|
||||
padding: 0 20px;
|
||||
}
|
||||
.section label {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
#menu-items-list > div {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#stepper {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#stepper li {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.footer-container, .footer-links {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#ip-list > div {
|
||||
flex-direction: column;
|
||||
}
|
||||
#stepper > div {
|
||||
flex-direction: column;
|
||||
}
|
||||
#stepper > div::before {
|
||||
content: "↓";
|
||||
color: #ffc700;
|
||||
}
|
||||
#stepper > div {
|
||||
/* Hide the default arrow */
|
||||
color: transparent;
|
||||
}
|
||||
#site-info-form, #theme-editor-form {
|
||||
padding: 18px 8px;
|
||||
}
|
||||
.input-field {
|
||||
min-width: 100%;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
#color-picker .input-field{
|
||||
min-width: 170px;
|
||||
}
|
||||
#theme-editor-form .fields.color-fields {
|
||||
gap: 0 8px;
|
||||
position: relative;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
}
|
86
src/webui/template/base.html
Normal file
86
src/webui/template/base.html
Normal file
@ -0,0 +1,86 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="UTF-8">
|
||||
<title>{% block title %}Lumeex{% endblock %}</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='style/style.css') }}">
|
||||
<meta name="darkreader-lock">
|
||||
</head>
|
||||
<body>
|
||||
<!-- Top bar -->
|
||||
<div class="nav-bar">
|
||||
<div class="content-inner nav">
|
||||
<div class="nav-header">
|
||||
<a href="/" class="nav-title">
|
||||
<img src="{{ url_for('static', filename='img/logo.svg') }}">
|
||||
</a>
|
||||
</div>
|
||||
<!-- Burger toggle input and label -->
|
||||
<input type="checkbox" id="nav-toggle" class="nav-toggle" hidden>
|
||||
<label for="nav-toggle" class="nav-burger">
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</label>
|
||||
<div class="nav-links">
|
||||
<ul class="nav-list">
|
||||
<li class="nav-item"><a href="/gallery-editor">Gallery</a></li>
|
||||
<li class="nav-item"><a href="/site-info">Site info</a></li>
|
||||
<li class="nav-item"><a href="/theme-editor">Theme info</a></li>
|
||||
<li class="nav-item">
|
||||
<button id="build-btn" class="button">🚀 Build!</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Toast container for notifications -->
|
||||
<div class="content-inner first-content">
|
||||
<div class="inner">
|
||||
<div id="toast-container"></div>
|
||||
<!-- Page content -->
|
||||
{% block content %}{% endblock %}
|
||||
<!-- Build success modal -->
|
||||
<div class="content-inner">
|
||||
<div id="build-success-modal" class="modal" style="display:none;">
|
||||
<div class="modal-content">
|
||||
<span id="build-success-modal-close" class="modal-close">×</span>
|
||||
<h3>✅ Build completed!</h3>
|
||||
<p>Your files are available in the output folder.</p>
|
||||
<button id="download-zip-btn" class="modal-btn">Download ZIP</button>
|
||||
<div id="zip-loader" style="display:none;">Creating ZIP...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Footer -->
|
||||
<div id="footer">
|
||||
<div class="content-inner">
|
||||
<div class="inner">
|
||||
<div class="footer-container">
|
||||
<div class="footer-credit">
|
||||
<p><a href="https//lumeex.djeex.fr"><span class="lum-first">Lum</span><span class="lum-second">eex</span> v{{ lumeex_version }}</a> — © 2025</p>
|
||||
</div>
|
||||
<div class="footer-links">
|
||||
<a class="footer-link documentation" href="https://lumeex.djeex.fr"><span class="icon"><img src="/img/favicon.svg"></span><span class="icon-text">Documentation</span></a>
|
||||
<a class="footer-link gitea" href="https://gitea.com/Djeex/lumeex"><span class="icon"><img src="/img/gitea.svg"></span><span class="icon-text">Giteex</span></a>
|
||||
<a class="footer-link github" href="https://github.com/Djeex/lumeex"><span class="icon"><img src="/img/github.svg"></span><span class="icon-text">Github</span></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Loader -->
|
||||
<div id="global-loader">
|
||||
<div class="loader-inner">
|
||||
<div class="loader-spinner"></div>
|
||||
<div id="loader-text">Uploading...</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Scripts -->
|
||||
<script src="{{ url_for('static', filename='js/build.js') }}" defer></script>
|
||||
{% block scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
@ -1,52 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="UTF-8">
|
||||
<title>Theme Editor</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='style/style.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<!-- Top bar -->
|
||||
<div class="nav-bar">
|
||||
<div class="content-inner nav">
|
||||
<input type="checkbox" id="nav-check">
|
||||
<div class="nav-header">
|
||||
<div class="nav-title">
|
||||
<a href="/"><img src="{{ url_for('static', filename='img/logo.svg') }}"></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="nav-btn">
|
||||
<label for="nav-check">
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="nav-links">
|
||||
<ul class="nav-list">
|
||||
<li class="nav-item"><a href="/gallery-editor">Gallery</a></li>
|
||||
<li class="nav-item"><a href="/site-info">Site info</a></li>
|
||||
<li class="nav-item"><a href="/theme-editor">Theme info</a></li>
|
||||
<li class="nav-item">
|
||||
<button id="build-btn" class="button">🚀 Build !</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% extends "template/base.html" %}
|
||||
|
||||
<!-- Toast container for notifications -->
|
||||
<div id="theme-editor" class="content-inner">
|
||||
<div id="toast-container"></div>
|
||||
<h1>Edit Theme</h1>
|
||||
{% block title %}Lumeex - Theme Editor{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>Edit Theme</h1>
|
||||
<!-- Show current theme -->
|
||||
<div class="theme-info">
|
||||
<strong>Current theme:</strong> <span id="current-theme"></span>
|
||||
</div>
|
||||
<form id="theme-editor-form">
|
||||
<!-- Colors Section -->
|
||||
<fieldset>
|
||||
<fieldset id="color-picker">
|
||||
<h2>Colors</h2>
|
||||
<p>Set the color values for your theme</p>
|
||||
<div class="fields">
|
||||
@ -175,11 +140,11 @@
|
||||
<p> Follow the steps to generate your static gallery</p>
|
||||
<ul id="stepper">
|
||||
<li><a href="/gallery-editor">Upload your photos</a></li>
|
||||
<div>→</div>
|
||||
<div></div>
|
||||
<li><a href="/site-info">Configure site info</a></li>
|
||||
<div>→</div>
|
||||
<div></div>
|
||||
<li><a class="step-active" href="/theme-editor">Customize your theme</a></li>
|
||||
<div>→</div>
|
||||
<div></div>
|
||||
<li><button id="stepper-build">Generate your static site!</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
@ -209,31 +174,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Build success modal -->
|
||||
<div id="build-success-modal" class="modal" style="display:none;">
|
||||
<div class="modal-content">
|
||||
<span id="build-success-modal-close" class="modal-close">×</span>
|
||||
<h3>✅ Build completed!</h3>
|
||||
<p>Your files are available in the output folder.</p>
|
||||
<button id="download-zip-btn" class="modal-btn">Download ZIP</button>
|
||||
<div id="zip-loader" style="display:none;">Creating ZIP...</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="footer">
|
||||
<div class="content-inner">
|
||||
<div class="footer-container">
|
||||
<div class="footer-credit">
|
||||
<p><a href="https//lumeex.djeex.fr"><span class="lum-first">Lum</span><span class="lum-second">eex</span> v{{ lumeex_version }}</a> — © 2025</p>
|
||||
</div>
|
||||
<div class="footer-links">
|
||||
<a class="footer-link documentation" href="https://lumeex.djeex.fr"><span class="icon"><img src="/img/favicon.svg"></span><span class="icon-text">Documentation</span></a>
|
||||
<a class="footer-link gitea" href="https://gitea.com/Djeex/lumeex"><span class="icon"><img src="/img/gitea.svg"></span><span class="icon-text">Giteex</span></a>
|
||||
<a class="footer-link github" href="https://github.com/Djeex/lumeex"><span class="icon"><img src="/img/github.svg"></span><span class="icon-text">Github</span></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="{{ url_for('static', filename='js/theme-editor.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/build.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
{% endblock %}
|
Reference in New Issue
Block a user