2.0 - WebUI builder ("Cielight" merge) #9
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 920 KiB  | 
@@ -1,36 +1,26 @@
 | 
				
			|||||||
# Please change this by your settings.
 | 
					 | 
				
			||||||
info:
 | 
					info:
 | 
				
			||||||
  title:
 | 
					  title: ''
 | 
				
			||||||
  subtitle:
 | 
					  subtitle: ''
 | 
				
			||||||
  description:
 | 
					  description: ''
 | 
				
			||||||
  canonical:
 | 
					  canonical: ''
 | 
				
			||||||
  keywords:
 | 
					  keywords: []
 | 
				
			||||||
  author:
 | 
					  author: ''
 | 
				
			||||||
 | 
					 | 
				
			||||||
social:
 | 
					social:
 | 
				
			||||||
  instagram_url:
 | 
					  instagram_url: ''
 | 
				
			||||||
  thumbnail:
 | 
					  thumbnail: ''
 | 
				
			||||||
  
 | 
					 | 
				
			||||||
menu:
 | 
					menu:
 | 
				
			||||||
  items:
 | 
					  items:
 | 
				
			||||||
  - label: Home
 | 
					  - label: Home
 | 
				
			||||||
    href: /
 | 
					    href: /
 | 
				
			||||||
 | 
					 | 
				
			||||||
footer:
 | 
					footer:
 | 
				
			||||||
  copyright: Copyright © 2025
 | 
					  copyright: Copyright © 2025
 | 
				
			||||||
  legal_link: '/legals/'
 | 
					 | 
				
			||||||
  legal_label: Legal notice
 | 
					  legal_label: Legal notice
 | 
				
			||||||
 | 
					 | 
				
			||||||
# Build parameters
 | 
					 | 
				
			||||||
build:
 | 
					build:
 | 
				
			||||||
  theme: modern # choose a theme in config/theme folder
 | 
					  theme: modern
 | 
				
			||||||
  convert_images: true # true to enable image conversion
 | 
					  convert_images: true
 | 
				
			||||||
  resize_images: true # true to enable image resizing
 | 
					  resize_images: true
 | 
				
			||||||
 | 
					 | 
				
			||||||
# Change this by your legals
 | 
					 | 
				
			||||||
legals:
 | 
					legals:
 | 
				
			||||||
  hoster_name:
 | 
					  hoster_name: ''
 | 
				
			||||||
  hoster_adress:
 | 
					  hoster_adress: ''
 | 
				
			||||||
  hoster_contact:
 | 
					  hoster_contact: ''
 | 
				
			||||||
  intellectual_property:
 | 
					  intellectual_property: []
 | 
				
			||||||
    - paragraph: ""
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -181,6 +181,22 @@ def upload_thumbnail():
 | 
				
			|||||||
        yaml.safe_dump(data, f, sort_keys=False, allow_unicode=True)
 | 
					        yaml.safe_dump(data, f, sort_keys=False, allow_unicode=True)
 | 
				
			||||||
    return jsonify({"status": "ok", "filename": filename})
 | 
					    return jsonify({"status": "ok", "filename": filename})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@app.route("/api/thumbnail/remove", methods=["POST"])
 | 
				
			||||||
 | 
					def remove_thumbnail():
 | 
				
			||||||
 | 
					    PHOTOS_DIR = app.config["PHOTOS_DIR"]
 | 
				
			||||||
 | 
					    thumbnail_path = PHOTOS_DIR / "thumbnail.png"
 | 
				
			||||||
 | 
					    # Remove thumbnail file if exists
 | 
				
			||||||
 | 
					    if thumbnail_path.exists():
 | 
				
			||||||
 | 
					        thumbnail_path.unlink()
 | 
				
			||||||
 | 
					    # Update site.yaml to remove thumbnail key
 | 
				
			||||||
 | 
					    with open(SITE_YAML, "r") as f:
 | 
				
			||||||
 | 
					        data = yaml.safe_load(f)
 | 
				
			||||||
 | 
					    if "social" in data and "thumbnail" in data["social"]:
 | 
				
			||||||
 | 
					        data["social"]["thumbnail"] = ""
 | 
				
			||||||
 | 
					    with open(SITE_YAML, "w") as f:
 | 
				
			||||||
 | 
					        yaml.safe_dump(data, f, sort_keys=False, allow_unicode=True)
 | 
				
			||||||
 | 
					    return jsonify({"status": "ok"})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# --- Run server ---
 | 
					# --- Run server ---
 | 
				
			||||||
if __name__ == "__main__":
 | 
					if __name__ == "__main__":
 | 
				
			||||||
    logging.info("Starting WebUI at http://127.0.0.1:5000")
 | 
					    logging.info("Starting WebUI at http://127.0.0.1:5000")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,19 @@
 | 
				
			|||||||
 | 
					function showToast(message, type = "success", duration = 3000) {
 | 
				
			||||||
 | 
					  const container = document.getElementById("toast-container");
 | 
				
			||||||
 | 
					  if (!container) return;
 | 
				
			||||||
 | 
					  const toast = document.createElement("div");
 | 
				
			||||||
 | 
					  toast.className = `toast ${type}`;
 | 
				
			||||||
 | 
					  toast.textContent = message;
 | 
				
			||||||
 | 
					  container.appendChild(toast);
 | 
				
			||||||
 | 
					  requestAnimationFrame(() => toast.classList.add("show"));
 | 
				
			||||||
 | 
					  setTimeout(() => {
 | 
				
			||||||
 | 
					    toast.classList.remove("show");
 | 
				
			||||||
 | 
					    toast.addEventListener("transitionend", () => toast.remove());
 | 
				
			||||||
 | 
					  }, duration);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
document.addEventListener("DOMContentLoaded", () => {
 | 
					document.addEventListener("DOMContentLoaded", () => {
 | 
				
			||||||
  const form = document.getElementById("site-info-form");
 | 
					  const form = document.getElementById("site-info-form");
 | 
				
			||||||
  const status = document.getElementById("site-info-status");
 | 
					 | 
				
			||||||
  const menuList = document.getElementById("menu-items-list");
 | 
					  const menuList = document.getElementById("menu-items-list");
 | 
				
			||||||
  const addMenuBtn = document.getElementById("add-menu-item");
 | 
					  const addMenuBtn = document.getElementById("add-menu-item");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -45,7 +58,7 @@ document.addEventListener("DOMContentLoaded", () => {
 | 
				
			|||||||
      div.style.gap = "8px";
 | 
					      div.style.gap = "8px";
 | 
				
			||||||
      div.style.marginBottom = "6px";
 | 
					      div.style.marginBottom = "6px";
 | 
				
			||||||
      div.innerHTML = `
 | 
					      div.innerHTML = `
 | 
				
			||||||
        <input type="text" placeholder="Paragraph" value="${item.paragraph || ""}" style="flex:1;" data-idx="${idx}">
 | 
					        <textarea placeholder="Paragraph" style="flex:1;" data-idx="${idx}">${item.paragraph || ""}</textarea>
 | 
				
			||||||
        <button type="button" class="remove-ip-paragraph" data-idx="${idx}">🗑</button>
 | 
					        <button type="button" class="remove-ip-paragraph" data-idx="${idx}">🗑</button>
 | 
				
			||||||
      `;
 | 
					      `;
 | 
				
			||||||
      ipList.appendChild(div);
 | 
					      ipList.appendChild(div);
 | 
				
			||||||
@@ -53,9 +66,9 @@ document.addEventListener("DOMContentLoaded", () => {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  function updateIpParagraphsFromInputs() {
 | 
					  function updateIpParagraphsFromInputs() {
 | 
				
			||||||
    const inputs = ipList.querySelectorAll("input");
 | 
					    const textareas = ipList.querySelectorAll("textarea");
 | 
				
			||||||
    ipParagraphs = Array.from(inputs).map(input => ({
 | 
					    ipParagraphs = Array.from(textareas).map(textarea => ({
 | 
				
			||||||
      paragraph: input.value.trim()
 | 
					      paragraph: textarea.value.trim()
 | 
				
			||||||
    })).filter(item => item.paragraph !== "");
 | 
					    })).filter(item => item.paragraph !== "");
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -66,10 +79,36 @@ document.addEventListener("DOMContentLoaded", () => {
 | 
				
			|||||||
  // --- Theme select ---
 | 
					  // --- Theme select ---
 | 
				
			||||||
  const themeSelect = document.getElementById("theme-select");
 | 
					  const themeSelect = document.getElementById("theme-select");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // --- Thumbnail upload ---
 | 
					  // --- Thumbnail upload & modal logic ---
 | 
				
			||||||
  const thumbnailInput = form?.elements["social.thumbnail"];
 | 
					  const thumbnailInput = form?.elements["social.thumbnail"];
 | 
				
			||||||
  const thumbnailUpload = document.getElementById("thumbnail-upload");
 | 
					  const thumbnailUpload = document.getElementById("thumbnail-upload");
 | 
				
			||||||
 | 
					  const chooseThumbnailBtn = document.getElementById("choose-thumbnail-btn");
 | 
				
			||||||
  const thumbnailPreview = document.getElementById("thumbnail-preview");
 | 
					  const thumbnailPreview = document.getElementById("thumbnail-preview");
 | 
				
			||||||
 | 
					  const removeThumbnailBtn = document.getElementById("remove-thumbnail-btn");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Modal elements
 | 
				
			||||||
 | 
					  const deleteModal = document.getElementById("delete-modal");
 | 
				
			||||||
 | 
					  const deleteModalClose = document.getElementById("delete-modal-close");
 | 
				
			||||||
 | 
					  const deleteModalConfirm = document.getElementById("delete-modal-confirm");
 | 
				
			||||||
 | 
					  const deleteModalCancel = document.getElementById("delete-modal-cancel");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Show/hide remove button, preview, and choose button
 | 
				
			||||||
 | 
					  function updateThumbnailPreview(src) {
 | 
				
			||||||
 | 
					    if (thumbnailPreview) {
 | 
				
			||||||
 | 
					      thumbnailPreview.src = src || "";
 | 
				
			||||||
 | 
					      thumbnailPreview.style.display = src ? "block" : "none";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (removeThumbnailBtn) {
 | 
				
			||||||
 | 
					      removeThumbnailBtn.style.display = src ? "inline-block" : "none";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (chooseThumbnailBtn) {
 | 
				
			||||||
 | 
					      chooseThumbnailBtn.style.display = src ? "none" : "inline-block";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (chooseThumbnailBtn && thumbnailUpload) {
 | 
				
			||||||
 | 
					    chooseThumbnailBtn.addEventListener("click", () => thumbnailUpload.click());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (thumbnailUpload) {
 | 
					  if (thumbnailUpload) {
 | 
				
			||||||
    thumbnailUpload.addEventListener("change", async (e) => {
 | 
					    thumbnailUpload.addEventListener("change", async (e) => {
 | 
				
			||||||
@@ -81,19 +120,45 @@ document.addEventListener("DOMContentLoaded", () => {
 | 
				
			|||||||
      const result = await res.json();
 | 
					      const result = await res.json();
 | 
				
			||||||
      if (result.status === "ok") {
 | 
					      if (result.status === "ok") {
 | 
				
			||||||
        if (thumbnailInput) thumbnailInput.value = result.filename;
 | 
					        if (thumbnailInput) thumbnailInput.value = result.filename;
 | 
				
			||||||
        if (thumbnailPreview) {
 | 
					        updateThumbnailPreview(`/photos/${result.filename}`);
 | 
				
			||||||
          thumbnailPreview.src = `/photos/${result.filename}`;
 | 
					        showToast("Thumbnail uploaded!", "success");
 | 
				
			||||||
          thumbnailPreview.style.display = "block";
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        status.textContent = "✅ Thumbnail uploaded!";
 | 
					 | 
				
			||||||
        setTimeout(() => status.textContent = "", 2000);
 | 
					 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        status.textContent = "❌ Error uploading thumbnail";
 | 
					        showToast("Error uploading thumbnail", "error");
 | 
				
			||||||
        setTimeout(() => status.textContent = "", 2000);
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Attach modal logic to remove button
 | 
				
			||||||
 | 
					  if (removeThumbnailBtn) {
 | 
				
			||||||
 | 
					    removeThumbnailBtn.addEventListener("click", () => {
 | 
				
			||||||
 | 
					      deleteModal.style.display = "flex";
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Modal logic
 | 
				
			||||||
 | 
					  if (deleteModal && deleteModalClose && deleteModalConfirm && deleteModalCancel) {
 | 
				
			||||||
 | 
					    deleteModalClose.onclick = deleteModalCancel.onclick = () => {
 | 
				
			||||||
 | 
					      deleteModal.style.display = "none";
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    window.onclick = function(event) {
 | 
				
			||||||
 | 
					      if (event.target === deleteModal) {
 | 
				
			||||||
 | 
					        deleteModal.style.display = "none";
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    deleteModalConfirm.onclick = async () => {
 | 
				
			||||||
 | 
					      const res = await fetch("/api/thumbnail/remove", { method: "POST" });
 | 
				
			||||||
 | 
					      const result = await res.json();
 | 
				
			||||||
 | 
					      if (result.status === "ok") {
 | 
				
			||||||
 | 
					        if (thumbnailInput) thumbnailInput.value = "";
 | 
				
			||||||
 | 
					        updateThumbnailPreview("");
 | 
				
			||||||
 | 
					        showToast("Thumbnail removed!", "success");
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        showToast("Error removing thumbnail", "error");
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      deleteModal.style.display = "none";
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Fetch theme list and populate select
 | 
					  // Fetch theme list and populate select
 | 
				
			||||||
  if (themeSelect) {
 | 
					  if (themeSelect) {
 | 
				
			||||||
    fetch("/api/themes")
 | 
					    fetch("/api/themes")
 | 
				
			||||||
@@ -134,10 +199,7 @@ document.addEventListener("DOMContentLoaded", () => {
 | 
				
			|||||||
        form.elements["info.author"].value = data.info?.author || "";
 | 
					        form.elements["info.author"].value = data.info?.author || "";
 | 
				
			||||||
        form.elements["social.instagram_url"].value = data.social?.instagram_url || "";
 | 
					        form.elements["social.instagram_url"].value = data.social?.instagram_url || "";
 | 
				
			||||||
        if (thumbnailInput) thumbnailInput.value = data.social?.thumbnail || "";
 | 
					        if (thumbnailInput) thumbnailInput.value = data.social?.thumbnail || "";
 | 
				
			||||||
        if (thumbnailPreview && data.social?.thumbnail) {
 | 
					        updateThumbnailPreview(data.social?.thumbnail ? `/photos/${data.social.thumbnail}` : "");
 | 
				
			||||||
          thumbnailPreview.src = `/photos/${data.social.thumbnail}`;
 | 
					 | 
				
			||||||
          thumbnailPreview.style.display = "block";
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        form.elements["footer.copyright"].value = data.footer?.copyright || "";
 | 
					        form.elements["footer.copyright"].value = data.footer?.copyright || "";
 | 
				
			||||||
        form.elements["footer.legal_label"].value = data.footer?.legal_label || "";
 | 
					        form.elements["footer.legal_label"].value = data.footer?.legal_label || "";
 | 
				
			||||||
        if (themeSelect) {
 | 
					        if (themeSelect) {
 | 
				
			||||||
@@ -207,7 +269,6 @@ document.addEventListener("DOMContentLoaded", () => {
 | 
				
			|||||||
      updateMenuItemsFromInputs();
 | 
					      updateMenuItemsFromInputs();
 | 
				
			||||||
      updateIpParagraphsFromInputs();
 | 
					      updateIpParagraphsFromInputs();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // --- Build object with checkboxes and theme select ---
 | 
					 | 
				
			||||||
      const build = {
 | 
					      const build = {
 | 
				
			||||||
        theme: themeSelect ? themeSelect.value : "",
 | 
					        theme: themeSelect ? themeSelect.value : "",
 | 
				
			||||||
        convert_images: !!(convertImagesCheckbox && convertImagesCheckbox.checked),
 | 
					        convert_images: !!(convertImagesCheckbox && convertImagesCheckbox.checked),
 | 
				
			||||||
@@ -248,8 +309,11 @@ document.addEventListener("DOMContentLoaded", () => {
 | 
				
			|||||||
        body: JSON.stringify(payload)
 | 
					        body: JSON.stringify(payload)
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      const result = await res.json();
 | 
					      const result = await res.json();
 | 
				
			||||||
      status.textContent = result.status === "ok" ? "✅ Saved!" : "❌ Error saving";
 | 
					      if (result.status === "ok") {
 | 
				
			||||||
      setTimeout(() => status.textContent = "", 2000);
 | 
					        showToast("Site info saved!", "success");
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        showToast("Error saving site info", "error");
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
@@ -31,9 +31,9 @@
 | 
				
			|||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <div class="nav-links">
 | 
					      <div class="nav-links">
 | 
				
			||||||
        <ul class="nav-list">
 | 
					        <ul class="nav-list">
 | 
				
			||||||
          <li class="nav-item appear2"><a href="#">Site info</a>
 | 
					          <li class="nav-item appear2"><a href="#">Site info</a></li>
 | 
				
			||||||
          <li class="nav-item appear2"><a href="#">Theme info</a>
 | 
					          <li class="nav-item appear2"><a href="#">Theme info</a></li>
 | 
				
			||||||
          <li class="nav-item appear2"><a href="#">Gallery</a>
 | 
					          <li class="nav-item appear2"><a href="#">Gallery</a></li>
 | 
				
			||||||
        </ul>
 | 
					        </ul>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
@@ -43,8 +43,10 @@
 | 
				
			|||||||
    <div id="toast-container"></div>
 | 
					    <div id="toast-container"></div>
 | 
				
			||||||
    <h1>Edit Site Info</h1>
 | 
					    <h1>Edit Site Info</h1>
 | 
				
			||||||
    <form id="site-info-form">
 | 
					    <form id="site-info-form">
 | 
				
			||||||
 | 
					      <!-- Info Section -->
 | 
				
			||||||
      <fieldset>
 | 
					      <fieldset>
 | 
				
			||||||
        <legend>Info</legend>
 | 
					        <legend>Info</legend>
 | 
				
			||||||
 | 
					        <div class="fields">
 | 
				
			||||||
          <div class="input-field">
 | 
					          <div class="input-field">
 | 
				
			||||||
            <label>Title:</label>
 | 
					            <label>Title:</label>
 | 
				
			||||||
            <input type="text" name="info.title">
 | 
					            <input type="text" name="info.title">
 | 
				
			||||||
@@ -55,7 +57,7 @@
 | 
				
			|||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          <div class="input-field">
 | 
					          <div class="input-field">
 | 
				
			||||||
            <label>Description:</label>
 | 
					            <label>Description:</label>
 | 
				
			||||||
            <textarea name="info.description"></textarea>
 | 
					            <input type="text" name="info.description"></input>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          <div class="input-field">
 | 
					          <div class="input-field">
 | 
				
			||||||
            <label>Canonical URL:</label>
 | 
					            <label>Canonical URL:</label>
 | 
				
			||||||
@@ -69,57 +71,51 @@
 | 
				
			|||||||
            <label>Author:</label>
 | 
					            <label>Author:</label>
 | 
				
			||||||
            <input type="text" name="info.author">
 | 
					            <input type="text" name="info.author">
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
      </fieldset>
 | 
					      </fieldset>
 | 
				
			||||||
 | 
					      <!-- Social Section -->
 | 
				
			||||||
      <fieldset>
 | 
					      <fieldset>
 | 
				
			||||||
        <legend>Social</legend>
 | 
					        <legend>Social</legend>
 | 
				
			||||||
 | 
					        <div class="fields">
 | 
				
			||||||
          <div class="input-field">
 | 
					          <div class="input-field">
 | 
				
			||||||
            <label>Instagram URL:</label>
 | 
					            <label>Instagram URL:</label>
 | 
				
			||||||
            <input type="text" name="social.instagram_url">
 | 
					            <input type="text" name="social.instagram_url">
 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
          <div class="input-field">
 | 
					 | 
				
			||||||
            <label>Thumbnail: </label>
 | 
					 | 
				
			||||||
            <input type="text" name="social.thumbnail" readonly>
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
          <div class="input-field">
 | 
					 | 
				
			||||||
            <label>Upload Thumbnail:</label>
 | 
					            <label>Upload Thumbnail:</label>
 | 
				
			||||||
              <input type="file" id="thumbnail-upload" accept="image/png,image/jpeg,image/webp">
 | 
					            <input type="file" id="thumbnail-upload" accept="image/png,image/jpeg,image/webp" style="display:none;">
 | 
				
			||||||
 | 
					            <button type="button" id="choose-thumbnail-btn" class="up-btn">Choose a photo</button>
 | 
				
			||||||
            <img id="thumbnail-preview" src="" alt="Thumbnail preview" style="max-width:100px;display:none;">
 | 
					            <img id="thumbnail-preview" src="" alt="Thumbnail preview" style="max-width:100px;display:none;">
 | 
				
			||||||
 | 
					            <button type="button" id="remove-thumbnail-btn" class="up-btn danger" style="display:none;">Remove</button>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </fieldset>
 | 
					      </fieldset>
 | 
				
			||||||
 | 
					      <!-- Menu Section -->
 | 
				
			||||||
      <fieldset>
 | 
					      <fieldset>
 | 
				
			||||||
        <legend>Menu</legend>
 | 
					        <legend>Menu</legend>
 | 
				
			||||||
        <div id="menu-items-list">
 | 
					        <div class="fields">
 | 
				
			||||||
        </div>
 | 
					          <div class="input-field" style="flex: 1 1 100%;">
 | 
				
			||||||
 | 
					            <div id="menu-items-list"></div>
 | 
				
			||||||
            <button type="button" id="add-menu-item">+ Add menu item</button>
 | 
					            <button type="button" id="add-menu-item">+ Add menu item</button>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
      </fieldset>
 | 
					      </fieldset>
 | 
				
			||||||
 | 
					      <!-- Footer Section -->
 | 
				
			||||||
      <fieldset>
 | 
					      <fieldset>
 | 
				
			||||||
        <legend>Footer</legend>
 | 
					        <legend>Footer</legend>
 | 
				
			||||||
 | 
					        <div class="fields">
 | 
				
			||||||
          <div class="input-field">
 | 
					          <div class="input-field">
 | 
				
			||||||
            <label>Copyright:</label>
 | 
					            <label>Copyright:</label>
 | 
				
			||||||
          <input type="text" name="footer.copyright"></label>
 | 
					            <input type="text" name="footer.copyright">
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          <div class="input-field">
 | 
					          <div class="input-field">
 | 
				
			||||||
            <label>Legal Label:</label>
 | 
					            <label>Legal Label:</label>
 | 
				
			||||||
          <input type="text" name="footer.legal_label"></label>
 | 
					            <input type="text" name="footer.legal_label">
 | 
				
			||||||
        </div>
 | 
					          </div>
 | 
				
			||||||
      </fieldset>
 | 
					 | 
				
			||||||
      <fieldset>
 | 
					 | 
				
			||||||
        <legend>Build</legend>
 | 
					 | 
				
			||||||
        <div class="input-field">
 | 
					 | 
				
			||||||
          <label>Theme:</label>
 | 
					 | 
				
			||||||
          <select name="build.theme" id="theme-select"></select>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <div class="input-field">
 | 
					 | 
				
			||||||
          <label>Convert images</label>
 | 
					 | 
				
			||||||
          <input type="checkbox" name="build.convert_images" id="convert-images-checkbox">
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <div class="input-field">
 | 
					 | 
				
			||||||
          <label>Resize images</label>
 | 
					 | 
				
			||||||
          <input type="checkbox" name="build.resize_images" id="resize-images-checkbox">
 | 
					 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </fieldset>
 | 
					      </fieldset>
 | 
				
			||||||
 | 
					      <!-- Legals Section -->
 | 
				
			||||||
      <fieldset>
 | 
					      <fieldset>
 | 
				
			||||||
        <legend>Legals</legend>
 | 
					        <legend>Legals</legend>
 | 
				
			||||||
 | 
					        <div class="fields">
 | 
				
			||||||
          <div class="input-field">
 | 
					          <div class="input-field">
 | 
				
			||||||
            <label>Hoster Name:</label>
 | 
					            <label>Hoster Name:</label>
 | 
				
			||||||
            <input type="text" name="legals.hoster_name">
 | 
					            <input type="text" name="legals.hoster_name">
 | 
				
			||||||
@@ -132,18 +128,46 @@
 | 
				
			|||||||
            <label>Hoster Contact:</label>
 | 
					            <label>Hoster Contact:</label>
 | 
				
			||||||
            <input type="text" name="legals.hoster_contact">
 | 
					            <input type="text" name="legals.hoster_contact">
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        <div class="input-field">
 | 
					          <div class="input-field" style="flex: 1 1 100%;">
 | 
				
			||||||
            <label>Intellectual Property:</label>
 | 
					            <label>Intellectual Property:</label>
 | 
				
			||||||
            <div id="ip-list"></div>
 | 
					            <div id="ip-list"></div>
 | 
				
			||||||
            <button type="button" id="add-ip-paragraph">+ Add paragraph</button>
 | 
					            <button type="button" id="add-ip-paragraph">+ Add paragraph</button>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </fieldset>
 | 
					      </fieldset>
 | 
				
			||||||
 | 
					      <!-- Build Section -->
 | 
				
			||||||
 | 
					      <fieldset>
 | 
				
			||||||
 | 
					        <legend>Build</legend>
 | 
				
			||||||
 | 
					        <div class="fields">
 | 
				
			||||||
 | 
					          <div class="input-field">
 | 
				
			||||||
 | 
					            <label>Theme:</label>
 | 
				
			||||||
 | 
					            <select name="build.theme" id="theme-select"></select>
 | 
				
			||||||
 | 
					            <label>
 | 
				
			||||||
 | 
					              <input type="checkbox" name="build.convert_images" id="convert-images-checkbox">
 | 
				
			||||||
 | 
					              Convert images
 | 
				
			||||||
 | 
					            </label>
 | 
				
			||||||
 | 
					            <label>
 | 
				
			||||||
 | 
					              <input type="checkbox" name="build.resize_images" id="resize-images-checkbox">
 | 
				
			||||||
 | 
					              Resize images
 | 
				
			||||||
 | 
					            </label>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </fieldset>
 | 
				
			||||||
      <button type="submit">Save</button>
 | 
					      <button type="submit">Save</button>
 | 
				
			||||||
    
 | 
					    </form>
 | 
				
			||||||
 | 
					  </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 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>
 | 
					  </div>
 | 
				
			||||||
  
 | 
					 | 
				
			||||||
<script src="{{ url_for('static', filename='js/site-info.js')}}"></script>
 | 
					<script src="{{ url_for('static', filename='js/site-info.js')}}"></script>
 | 
				
			||||||
</body>
 | 
					</body>
 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||
@@ -468,44 +468,145 @@ h1, h2 {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/* --- Site Info --- */
 | 
					/* --- Site Info --- */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#site-info-form input, #site-info-form textarea {
 | 
					/* --- Site Info Form --- */
 | 
				
			||||||
    display: block;
 | 
					#site-info-form {
 | 
				
			||||||
    margin-top: 5px;
 | 
					  max-width: 950px;
 | 
				
			||||||
    border: none;
 | 
					 | 
				
			||||||
    backdrop-filter: blur(20px);
 | 
					 | 
				
			||||||
    background: #00293054;
 | 
					 | 
				
			||||||
    border-radius: 5px;
 | 
					 | 
				
			||||||
    outline: none;
 | 
					 | 
				
			||||||
    font-size: 14px;
 | 
					 | 
				
			||||||
    font-weight: 400;
 | 
					 | 
				
			||||||
    color: #fff;
 | 
					 | 
				
			||||||
    border-radius: 5px;
 | 
					 | 
				
			||||||
    border: 1px solid #aaa;
 | 
					 | 
				
			||||||
    padding: 0 15px;
 | 
					 | 
				
			||||||
    height: 42px;
 | 
					 | 
				
			||||||
    margin: 8px 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#site-info-form label {
 | 
					 | 
				
			||||||
  font-size: 12px;
 | 
					 | 
				
			||||||
  font-weight: 500;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#site-info-form .input-field {
 | 
					 | 
				
			||||||
    display: flex;
 | 
					 | 
				
			||||||
    width: calc(100% / 3 - 15px);
 | 
					 | 
				
			||||||
    flex-direction: column;
 | 
					 | 
				
			||||||
    margin: 4px 0;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#site-info-form fieldset {
 | 
					#site-info-form fieldset {
 | 
				
			||||||
    display: flex;
 | 
					 | 
				
			||||||
    align-items: center;
 | 
					 | 
				
			||||||
    justify-content: space-between;
 | 
					 | 
				
			||||||
    flex-wrap: wrap;
 | 
					 | 
				
			||||||
  border: none;
 | 
					  border: none;
 | 
				
			||||||
 | 
					  margin-bottom: 28px;
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					  background: rgba(0, 41, 48, 0.45);
 | 
				
			||||||
 | 
					  border-radius: 18px;
 | 
				
			||||||
 | 
					  box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.25);
 | 
				
			||||||
 | 
					  padding: 32px 28px;
 | 
				
			||||||
 | 
					  margin: 32px auto;
 | 
				
			||||||
 | 
					  backdrop-filter: blur(12px);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
h3 {
 | 
					#site-info-form legend {
 | 
				
			||||||
  display: block;
 | 
					  font-size: 1.2em;
 | 
				
			||||||
 | 
					  font-weight: 700;
 | 
				
			||||||
 | 
					  color: #26c4ff;
 | 
				
			||||||
 | 
					  margin-bottom: 12px;
 | 
				
			||||||
 | 
					  letter-spacing: 1px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#site-info-form .fields {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-wrap: wrap;
 | 
				
			||||||
 | 
					  gap: 18px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#site-info-form .input-field {
 | 
				
			||||||
 | 
					  flex: 1 1 calc(33.333% - 18px);
 | 
				
			||||||
 | 
					  min-width: 220px;
 | 
				
			||||||
 | 
					  max-width: 100%;
 | 
				
			||||||
 | 
					  box-sizing: border-box;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					  margin-bottom: 10px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#site-info-form label {
 | 
				
			||||||
 | 
					  font-size: 13px;
 | 
				
			||||||
 | 
					  font-weight: 600;
 | 
				
			||||||
 | 
					  color: #b8eaff;
 | 
				
			||||||
 | 
					  margin-bottom: 6px;
 | 
				
			||||||
 | 
					  letter-spacing: 0.5px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#site-info-form input,
 | 
				
			||||||
 | 
					#site-info-form textarea,
 | 
				
			||||||
 | 
					#site-info-form select {
 | 
				
			||||||
 | 
					  background: rgba(4, 44, 60, 0.55);
 | 
				
			||||||
 | 
					  color: #fff;
 | 
				
			||||||
 | 
					  border: 1px solid #26c4ff33;
 | 
				
			||||||
 | 
					  border-radius: 8px;
 | 
				
			||||||
 | 
					  font-size: 15px;
 | 
				
			||||||
 | 
					  font-weight: 400;
 | 
				
			||||||
 | 
					  padding: 10px 14px;
 | 
				
			||||||
 | 
					  margin-bottom: 4px;
 | 
				
			||||||
 | 
					  outline: none;
 | 
				
			||||||
 | 
					  transition: border-color 0.2s, background 0.2s;
 | 
				
			||||||
 | 
					  box-shadow: 0 2px 8px rgba(0,0,0,0.07);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#site-info-form input:focus,
 | 
				
			||||||
 | 
					#site-info-form textarea:focus,
 | 
				
			||||||
 | 
					#site-info-form select:focus {
 | 
				
			||||||
 | 
					  border-color: #26c4ff;
 | 
				
			||||||
 | 
					  background: rgba(38, 196, 255, 0.09);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#site-info-form textarea {
 | 
				
			||||||
 | 
					  min-height: 60px;
 | 
				
			||||||
 | 
					  resize: vertical;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#site-info-form input[type="file"] {
 | 
				
			||||||
 | 
					  background: none;
 | 
				
			||||||
 | 
					  color: #fff;
 | 
				
			||||||
 | 
					  border: none;
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					  margin-top: 2px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#site-info-form img#thumbnail-preview {
 | 
				
			||||||
 | 
					  margin-top: 8px;
 | 
				
			||||||
 | 
					  border-radius: 8px;
 | 
				
			||||||
 | 
					  box-shadow: 0 2px 8px rgba(38,196,255,0.12);
 | 
				
			||||||
 | 
					  border: 1px solid #26c4ff33;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#site-info-form button[type="submit"] {
 | 
				
			||||||
 | 
					  background: linear-gradient(135deg, #26c4ff, #016074);
 | 
				
			||||||
 | 
					  color: #fff;
 | 
				
			||||||
 | 
					  font-weight: 700;
 | 
				
			||||||
 | 
					  border: none;
 | 
				
			||||||
 | 
					  border-radius: 30px;
 | 
				
			||||||
 | 
					  padding: 12px 32px;
 | 
				
			||||||
 | 
					  font-size: 1.1em;
 | 
				
			||||||
 | 
					  margin-top: 18px;
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  box-shadow: 0 4px 16px rgba(38,196,255,0.15);
 | 
				
			||||||
 | 
					  transition: background 0.2s;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#site-info-form button[type="submit"]:hover {
 | 
				
			||||||
 | 
					  background: linear-gradient(135deg, #72d9ff, #26657e);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#site-info-form button[type="button"] {
 | 
				
			||||||
 | 
					  background: #074053;
 | 
				
			||||||
 | 
					  color: #fff;
 | 
				
			||||||
 | 
					  border: none;
 | 
				
			||||||
 | 
					  border-radius: 18px;
 | 
				
			||||||
 | 
					  padding: 7px 18px;
 | 
				
			||||||
 | 
					  font-size: 0.98em;
 | 
				
			||||||
 | 
					  margin-top: 8px;
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  box-shadow: 0 2px 8px rgba(38,196,255,0.09);
 | 
				
			||||||
 | 
					  transition: background 0.2s;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#site-info-form button[type="button"]:hover {
 | 
				
			||||||
 | 
					  background: #26c4ff;
 | 
				
			||||||
 | 
					  color: #002a30;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@media (max-width: 900px) {
 | 
				
			||||||
 | 
					  #site-info-form {
 | 
				
			||||||
 | 
					    padding: 18px 8px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  #site-info-form .fields,
 | 
				
			||||||
 | 
					  #site-info-form fieldset {
 | 
				
			||||||
 | 
					    flex-direction: column;
 | 
				
			||||||
 | 
					    gap: 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  #site-info-form .input-field {
 | 
				
			||||||
 | 
					    min-width: 100%;
 | 
				
			||||||
 | 
					    margin-bottom: 12px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user