deploy: 7a372a7003
This commit is contained in:
@@ -1 +1 @@
|
||||
{ "commit_hash": "afda24a38af79400cca45cdb7e83e5f54bf89253" }
|
||||
{ "commit_hash": "7a372a7003cacfad93e13082e73049dd659868ec" }
|
||||
|
||||
79
scripts/template-generator.py
Normal file
79
scripts/template-generator.py
Normal file
@@ -0,0 +1,79 @@
|
||||
#!/bin/python3
|
||||
|
||||
import os
|
||||
import json
|
||||
import shutil
|
||||
import logging
|
||||
import argparse
|
||||
|
||||
args_parser = argparse.ArgumentParser(description="Generator Template Generator")
|
||||
args_parser.add_argument("config", type=str, help="config json path", nargs="?")
|
||||
args = args_parser.parse_args()
|
||||
|
||||
if args.config:
|
||||
config = None
|
||||
with open(args.config, "r") as f:
|
||||
config = json.loads(f.read())
|
||||
|
||||
name = config["name"]
|
||||
desc = config["desc"]
|
||||
title = config["title"]
|
||||
repo_name = config["repo_name"]
|
||||
|
||||
else:
|
||||
name = input("Generator name (like fortune, quote): ")
|
||||
desc = input("Generator desc: ")
|
||||
title = input("Generator title: ")
|
||||
repo_name = input("Github repo name: ")
|
||||
|
||||
folder_path = f"{name}_generator"
|
||||
|
||||
if os.path.exists(folder_path):
|
||||
logging.error(f"{folder_path} already exists. Please choose another name.")
|
||||
exit(1)
|
||||
|
||||
os.mkdir(folder_path)
|
||||
os.mkdir(f"{folder_path}/css")
|
||||
os.mkdir(f"{folder_path}/js")
|
||||
os.mkdir(f"{folder_path}/images")
|
||||
os.mkdir(f"{folder_path}/json")
|
||||
|
||||
|
||||
def write_file(src_path, dst_path, **kwargs):
|
||||
content = None
|
||||
with open(f"scripts/template/{src_path}", "r") as f:
|
||||
content = f.read()
|
||||
|
||||
for key, val in kwargs.items():
|
||||
assert (
|
||||
content.find("{{ %s }}" % key) != -1
|
||||
), f"The key '{key}' does not appear in scripts/template/{src_path}"
|
||||
content = content.replace("{{ %s }}" % key, val)
|
||||
|
||||
with open(dst_path, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
|
||||
write_file("css/styles.css", f"{folder_path}/css/styles.css")
|
||||
write_file("js/main.js", f"{folder_path}/js/{name}.js", name=name)
|
||||
write_file("js/matrix.js", f"{folder_path}/js/matrix.js")
|
||||
write_file("js/scripts.js", f"{folder_path}/js/scripts.js")
|
||||
write_file("js/theme.js", f"{folder_path}/js/theme.js")
|
||||
write_file(
|
||||
"js/service-worker.js",
|
||||
f"{folder_path}/js/service-worker.js",
|
||||
name=name,
|
||||
repo_name=repo_name,
|
||||
folder_path=folder_path,
|
||||
)
|
||||
write_file(
|
||||
"manifest.json",
|
||||
f"{folder_path}/manifest.json",
|
||||
title=title,
|
||||
desc=desc,
|
||||
repo_name=repo_name,
|
||||
folder_path=folder_path,
|
||||
)
|
||||
write_file("json/themes.json", f"{folder_path}/json/themes.json")
|
||||
write_file("index.html", f"{folder_path}/index.html", name=name, desc=desc, title=title)
|
||||
shutil.copytree("scripts/template/images", f"{folder_path}/images", dirs_exist_ok=True)
|
||||
135
scripts/template/css/styles.css
Normal file
135
scripts/template/css/styles.css
Normal file
@@ -0,0 +1,135 @@
|
||||
:root {
|
||||
--button-color: #73a3eb;
|
||||
--button-hover-color: #459aef;
|
||||
--toggle-theme-button-color: #000000;
|
||||
--copy-result-button-color: #000000;
|
||||
--bg-color: #ffffff;
|
||||
--title-color: #000000cc;
|
||||
}
|
||||
|
||||
* {
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.container {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 80%;
|
||||
max-width: 800px;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
text-align: center;
|
||||
transform: translate(-50%, -50%);
|
||||
background-color: var(--bg-color);
|
||||
border-radius: 40px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
|
||||
button {
|
||||
background-color: var(--button-color);
|
||||
color: var(--bg-color);
|
||||
z-index: 2;
|
||||
font-size: 20px;
|
||||
border: none;
|
||||
padding: 20px 20px;
|
||||
border-radius: 30px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: var(--button-hover-color);
|
||||
}
|
||||
|
||||
#Matrix {
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
#toggle-theme-button {
|
||||
margin-top: 15px;
|
||||
font-size: 2.4rem;
|
||||
color: var(--toggle-theme-button-color);
|
||||
cursor: pointer;
|
||||
opacity: 85%;
|
||||
}
|
||||
|
||||
#copy-result-button {
|
||||
margin-top: 20px;
|
||||
font-size: 1.5rem;
|
||||
color: var(--copy-result-button-color);
|
||||
}
|
||||
|
||||
#themeModal {
|
||||
.modal-content {
|
||||
background-color: var(--bg-color) !important;
|
||||
color: var(--title-color) !important;
|
||||
}
|
||||
|
||||
.modal-header,
|
||||
.modal-footer {
|
||||
background-color: var(--bg-color) !important;
|
||||
color: var(--bg-color) !important;
|
||||
}
|
||||
|
||||
.modal-title,
|
||||
.btn-close {
|
||||
color: var(--title-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
#themeItem {
|
||||
background-color: var(--bg-color);
|
||||
color: var(--title-color);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: var(--bg-color);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--button-color);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--button-hover-color);
|
||||
}
|
||||
|
||||
.color-preview-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 3px;
|
||||
border-radius: 25px;
|
||||
}
|
||||
|
||||
.color-preview {
|
||||
display: flex; /* Use flex to align dots in a row */
|
||||
}
|
||||
|
||||
.color-dot {
|
||||
display: inline-block;
|
||||
width: 12px; /* Dot size */
|
||||
height: 12px; /* Dot size */
|
||||
border-radius: 50%; /* Circular shape */
|
||||
margin-left: 5px; /* Spacing between dots */
|
||||
}
|
||||
|
||||
.color-preview .color-dot:first-child {
|
||||
margin-left: 0; /* No margin on the left for the first dot */
|
||||
}
|
||||
BIN
scripts/template/images/logo-180x180.png
Normal file
BIN
scripts/template/images/logo-180x180.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
BIN
scripts/template/images/logo-192x192.png
Normal file
BIN
scripts/template/images/logo-192x192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 65 KiB |
BIN
scripts/template/images/logo-270x270.png
Normal file
BIN
scripts/template/images/logo-270x270.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 120 KiB |
BIN
scripts/template/images/logo-512x512.png
Normal file
BIN
scripts/template/images/logo-512x512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 336 KiB |
BIN
scripts/template/images/logo.png
Normal file
BIN
scripts/template/images/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 354 KiB |
111
scripts/template/index.html
Normal file
111
scripts/template/index.html
Normal file
@@ -0,0 +1,111 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="description" content="{{ desc }}"/>
|
||||
<title>{{ title }}</title>
|
||||
<link rel="icon" href="./images/logo.png" />
|
||||
<link rel="manifest" href="./manifest.json" />
|
||||
<!-- bootstrap -->
|
||||
<link
|
||||
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
|
||||
rel="stylesheet"
|
||||
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
<script
|
||||
src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"
|
||||
></script>
|
||||
|
||||
<link
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<script
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/html-to-image/1.11.11/html-to-image.min.js"
|
||||
integrity="sha512-7tWCgq9tTYS/QkGVyKrtLpqAoMV9XIUxoou+sPUypsaZx56cYR/qio84fPK9EvJJtKvJEwt7vkn6je5UVzGevw=="
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
></script>
|
||||
<link rel="stylesheet" href="./css/styles.css" />
|
||||
<script>
|
||||
if ("serviceWorker" in navigator) {
|
||||
navigator.serviceWorker.register("./js/service-worker.js");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<div id="init-page">
|
||||
</div>
|
||||
|
||||
<div id="result-page" style="display: none;">
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<i
|
||||
class="col-2 fas fa-palette"
|
||||
id="toggle-theme-button"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#themeModal"
|
||||
></i>
|
||||
<button class="col-4 offset-2 bi bi-files" id="btn" onclick="get{{ name }}()">
|
||||
Generate
|
||||
</button>
|
||||
<i
|
||||
class="col-2 offset-2 fas fa-clone d-none"
|
||||
id="copy-result-button"
|
||||
onclick="copyResultImageToClipboard()"
|
||||
></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Theme Modal -->
|
||||
<div
|
||||
class="modal fade"
|
||||
id="themeModal"
|
||||
tabindex="-1"
|
||||
aria-labelledby="themeModalLabel"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="themeModalLabel">Choose Theme</h5>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-close"
|
||||
data-bs-dismiss="modal"
|
||||
aria-label="Close"
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body" style="max-height: 70vh; overflow-y: auto">
|
||||
<ul class="list-group" id="themeList">
|
||||
<!-- Theme items will be dynamically populated here -->
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<canvas id="Matrix"></canvas>
|
||||
<script src="./js/scripts.js"></script>
|
||||
<script src="./js/{{ name }}.js"></script>
|
||||
<script src="./js/matrix.js"></script>
|
||||
<script src="./js/theme.js"></script>
|
||||
|
||||
<script
|
||||
src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"
|
||||
integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r"
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
<script
|
||||
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js"
|
||||
integrity="sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy"
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
</body>
|
||||
</html>
|
||||
15
scripts/template/js/main.js
Normal file
15
scripts/template/js/main.js
Normal file
@@ -0,0 +1,15 @@
|
||||
function InitPage() {
|
||||
$("#init-page").show();
|
||||
$("#result-page").hide();
|
||||
}
|
||||
|
||||
function Appear() {
|
||||
$("#init-page").hide();
|
||||
$("#result-page").show();
|
||||
}
|
||||
|
||||
function get{{ name }}() {
|
||||
Update();
|
||||
}
|
||||
|
||||
InitPage()
|
||||
51
scripts/template/js/matrix.js
Normal file
51
scripts/template/js/matrix.js
Normal file
@@ -0,0 +1,51 @@
|
||||
const canvas = document.getElementById("Matrix");
|
||||
const context = canvas.getContext("2d");
|
||||
|
||||
canvas.height = globalThis.innerHeight + 100;
|
||||
canvas.width = globalThis.innerWidth + 5;
|
||||
|
||||
const chars =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./*-+#$%^@!~?><:;[]{}=_αβΓγΔδεζηΘθικΛλμΞξΠπρΣσςτυΦφχΨψΩω×≦≧≠∞≒≡~∩∠∪∟⊿∫∮∵∴$¥〒¢£℃€℉╩◢ⅩⅨⅧⅦⅥⅤⅣⅢⅡⅠあいうえおがぎぐげござじずぜぞだぢつでづどにぬのばひぴぶへぺぼみゃょァゐゎè";
|
||||
|
||||
const fontSize = 16;
|
||||
const columns = canvas.width / fontSize;
|
||||
|
||||
const charArr = [];
|
||||
for (let i = 0; i < columns; i++) {
|
||||
charArr[i] = 1;
|
||||
}
|
||||
|
||||
let frame = 0;
|
||||
let str;
|
||||
|
||||
context.fillStyle = "rgba(0, 0, 0, 1)";
|
||||
context.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
function Update() {
|
||||
context.fillStyle = "rgba(0, 0, 0, 0.05)";
|
||||
context.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
if (frame == 0) {
|
||||
const a = parseInt(Math.random() * 255);
|
||||
str = `rgba(${a}, ${Math.abs(a - 127)}, ${Math.abs(a - 255)}, 0.9)`;
|
||||
}
|
||||
context.fillStyle = str;
|
||||
context.font = fontSize + "px monospace";
|
||||
|
||||
for (let i = 0; i < columns; i++) {
|
||||
const text = chars[Math.floor(Math.random() * chars.length)];
|
||||
context.fillText(text, i * fontSize, charArr[i] * fontSize);
|
||||
if (charArr[i] * fontSize > canvas.height && Math.random() > 0.90) {
|
||||
charArr[i] = 0;
|
||||
}
|
||||
charArr[i]++;
|
||||
}
|
||||
frame++;
|
||||
|
||||
if (frame <= 40 * (Math.floor(Math.random() * 10) + 3)) {
|
||||
requestAnimationFrame(Update); // 40 frames a cycle
|
||||
} else {
|
||||
frame = 0;
|
||||
Appear();
|
||||
}
|
||||
}
|
||||
45
scripts/template/js/scripts.js
Normal file
45
scripts/template/js/scripts.js
Normal file
@@ -0,0 +1,45 @@
|
||||
function copyResultImageToClipboard() {
|
||||
try {
|
||||
const root = document.documentElement;
|
||||
const backgroundColor = getComputedStyle(root).getPropertyValue('--bg-color');
|
||||
|
||||
htmlToImage.toBlob($("#result-page")[0], {
|
||||
skipFonts: true,
|
||||
preferredFontFormat: "woff2",
|
||||
backgroundColor: backgroundColor, // Set background color dynamically
|
||||
}).then((blob) => {
|
||||
navigator.clipboard.write([new ClipboardItem({ [blob.type]: blob })]);
|
||||
showCopiedNotice();
|
||||
$title.parent().remove();
|
||||
}).catch((error) => {
|
||||
console.error("Error converting result page to image:", error);
|
||||
$title.parent().remove();
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error copying result image to clipboard:", error);
|
||||
}
|
||||
}
|
||||
|
||||
function showCopiedNotice() {
|
||||
const notice = $("<div>", {
|
||||
text: "Copied to clipboard!",
|
||||
css: {
|
||||
position: "fixed",
|
||||
bottom: "20px",
|
||||
right: "20px",
|
||||
padding: "10px 20px",
|
||||
backgroundColor: "rgba(0, 0, 0, 0.7)",
|
||||
color: "#fff",
|
||||
borderRadius: "5px",
|
||||
zIndex: 1000,
|
||||
},
|
||||
});
|
||||
|
||||
$("body").append(notice);
|
||||
|
||||
setTimeout(() => {
|
||||
notice.fadeOut(300, () => {
|
||||
notice.remove();
|
||||
});
|
||||
}, 3000);
|
||||
}
|
||||
102
scripts/template/js/service-worker.js
Normal file
102
scripts/template/js/service-worker.js
Normal file
@@ -0,0 +1,102 @@
|
||||
const pre_cache_file_version = "pre-v1.0.0";
|
||||
const auto_cache_file_version = "auto-v1.0.0";
|
||||
|
||||
const ASSETS = [
|
||||
"/{{ repo_name }}/{{ folder_path }}/images/logo-192x192.png",
|
||||
"/{{ repo_name }}/{{ folder_path }}/images/logo-512x512.png",
|
||||
"/{{ repo_name }}/{{ folder_path }}/images/logo-180x180.png",
|
||||
"/{{ repo_name }}/{{ folder_path }}/images/logo-270x270.png",
|
||||
"/{{ repo_name }}/{{ folder_path }}/images/logo.jpg",
|
||||
|
||||
"https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css",
|
||||
"https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js",
|
||||
];
|
||||
|
||||
const NEED_UPDATE = [
|
||||
"/{{ repo_name }}/{{ folder_path }}/",
|
||||
"/{{ repo_name }}/{{ folder_path }}/index.html",
|
||||
"/{{ repo_name }}/{{ folder_path }}/css/styles.css",
|
||||
"/{{ repo_name }}/{{ folder_path }}/js/{{ name }}.js",
|
||||
"/{{ repo_name }}/{{ folder_path }}/js/matrix.js",
|
||||
"/{{ repo_name }}/{{ folder_path }}/json/theme.json",
|
||||
"/{{ repo_name }}/{{ folder_path }}/json/manifest.json",
|
||||
];
|
||||
|
||||
const limit_cache_size = (name, size) => {
|
||||
caches.open(name).then((cache) => {
|
||||
cache.keys().then((keys) => {
|
||||
if (keys.length > size) {
|
||||
cache.delete(keys[0]).then(() => {
|
||||
limit_cache_size(name, size);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const is_in_array = (str, array) => {
|
||||
let path = "";
|
||||
|
||||
// Check the request's domain is the same as the current domain.
|
||||
if (str.indexOf(self.origin) === 0) {
|
||||
path = str.substring(self.origin.length); // Remove https://lifeadventurer.github.io
|
||||
} else {
|
||||
path = str; // outside request
|
||||
}
|
||||
|
||||
return array.indexOf(path) > -1;
|
||||
};
|
||||
|
||||
// install
|
||||
self.addEventListener("install", (event) => {
|
||||
self.skipWaiting();
|
||||
|
||||
//pre-cache files
|
||||
event.waitUntil(
|
||||
caches.open(pre_cache_file_version).then((cache) => {
|
||||
cache.addAll(ASSETS);
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
// activate
|
||||
self.addEventListener("activate", (event) => {
|
||||
event.waitUntil(
|
||||
caches.keys().then((keys) => {
|
||||
return Promise.all(keys.map((key) => {
|
||||
if (
|
||||
pre_cache_file_version.indexOf(key) === -1 &&
|
||||
auto_cache_file_version.indexOf(key) === -1
|
||||
) {
|
||||
return caches.delete(key);
|
||||
}
|
||||
}));
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
// fetch event
|
||||
self.addEventListener("fetch", (event) => {
|
||||
if (is_in_array(event.request.url, ASSETS)) {
|
||||
// cache only strategy
|
||||
|
||||
event.respondWith(
|
||||
caches.match(event.request.url),
|
||||
);
|
||||
} else if (is_in_array(event.request.url, NEED_UPDATE)) {
|
||||
event.respondWith(
|
||||
fetch(event.request.url).then(async (response) => {
|
||||
if (response.ok) {
|
||||
const cache = await caches.open(auto_cache_file_version);
|
||||
cache.put(event.request.url, response.clone());
|
||||
return response;
|
||||
}
|
||||
|
||||
throw new Error("Network response was not ok.");
|
||||
}).catch(async (_error) => {
|
||||
const cache = await caches.open(auto_cache_file_version);
|
||||
return cache.match(event.request.url);
|
||||
}),
|
||||
);
|
||||
}
|
||||
});
|
||||
91
scripts/template/js/theme.js
Normal file
91
scripts/template/js/theme.js
Normal file
@@ -0,0 +1,91 @@
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const themeListContainer = document.querySelector("#themeList");
|
||||
const root = document.documentElement;
|
||||
|
||||
// Apply the saved theme if it exists
|
||||
applySavedTheme();
|
||||
|
||||
async function fetchThemes() {
|
||||
try {
|
||||
const response = await fetch("./json/themes.json");
|
||||
const themes = await response.json();
|
||||
populateThemeList(themes["themes"]);
|
||||
} catch (error) {
|
||||
console.error("Error fetching themes:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Populate theme list in modal
|
||||
function populateThemeList(themes) {
|
||||
themeListContainer.innerHTML = "";
|
||||
themes.forEach((theme) => {
|
||||
const themeItem = document.createElement("div");
|
||||
themeItem.className =
|
||||
"theme-item list-group-item d-flex justify-content-between align-items-center";
|
||||
themeItem.style.cursor = "pointer";
|
||||
themeItem.id = "themeItem";
|
||||
|
||||
// Add theme name
|
||||
const themeName = document.createElement("span");
|
||||
themeName.textContent = theme.name;
|
||||
themeItem.appendChild(themeName);
|
||||
|
||||
const colorPreivewContainer = document.createElement("div");
|
||||
colorPreivewContainer.className = "color-preview-container";
|
||||
|
||||
const propertyKeys = Object.keys(theme.properties);
|
||||
colorPreivewContainer.style.backgroundColor =
|
||||
theme.properties[propertyKeys[5]];
|
||||
|
||||
// Add color dots for visual preview
|
||||
const colorPreview = document.createElement("div");
|
||||
colorPreview.className = "color-preview";
|
||||
|
||||
Object.values(theme.properties).slice(0, 3).forEach((color) => {
|
||||
const colorDot = document.createElement("span");
|
||||
colorDot.style.backgroundColor = color;
|
||||
colorDot.className = "color-dot";
|
||||
colorPreview.appendChild(colorDot);
|
||||
});
|
||||
|
||||
colorPreivewContainer.appendChild(colorPreview);
|
||||
themeItem.appendChild(colorPreivewContainer);
|
||||
|
||||
// Apply theme on click
|
||||
themeItem.addEventListener("click", () => {
|
||||
applyTheme(theme.properties);
|
||||
saveThemeToLocalStorage(theme.name);
|
||||
});
|
||||
|
||||
themeListContainer.appendChild(themeItem);
|
||||
});
|
||||
}
|
||||
|
||||
// Apply theme by setting CSS variables
|
||||
function applyTheme(properties) {
|
||||
Object.entries(properties).forEach(([key, value]) => {
|
||||
root.style.setProperty(`--${key}`, value);
|
||||
});
|
||||
}
|
||||
|
||||
function saveThemeToLocalStorage(themeName) {
|
||||
localStorage.setItem("selectedTheme", themeName);
|
||||
}
|
||||
|
||||
function applySavedTheme() {
|
||||
const savedThemeName = localStorage.getItem("selectedTheme");
|
||||
if (savedThemeName) {
|
||||
fetch("./json/themes.json")
|
||||
.then((response) => response.json())
|
||||
.then((themes) => {
|
||||
const theme = themes.themes.find((t) => t.name === savedThemeName);
|
||||
if (theme) {
|
||||
applyTheme(theme.properties);
|
||||
}
|
||||
})
|
||||
.catch((error) => console.error("Error fetching themes:", error));
|
||||
}
|
||||
}
|
||||
|
||||
fetchThemes();
|
||||
});
|
||||
26
scripts/template/json/themes.json
Normal file
26
scripts/template/json/themes.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"themes": [
|
||||
{
|
||||
"name": "Classic Light",
|
||||
"properties": {
|
||||
"title-color": "#000000cc",
|
||||
"bg-color": "#ffffff",
|
||||
"button-color": "#73a3eb",
|
||||
"button-hover-color": "#459aef",
|
||||
"toggle-theme-button-color": "#000000",
|
||||
"copy-result-button-color": "#000000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Classic Dark",
|
||||
"properties": {
|
||||
"title-color": "#cdcdcd",
|
||||
"bg-color": "#1e1d24",
|
||||
"button-color": "#5d99f4",
|
||||
"button-hover-color": "#9ac6f1",
|
||||
"toggle-theme-button-color": "#ffffff",
|
||||
"copy-result-button-color": "#ffffff"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
32
scripts/template/manifest.json
Normal file
32
scripts/template/manifest.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"short_name": "{{ title }}",
|
||||
"name": "{{ title }}",
|
||||
"description": "{{ desc }}",
|
||||
"background_color": "#1a1b1e",
|
||||
"theme_color": "#1a1b1e",
|
||||
"icons": [
|
||||
{
|
||||
"src": "./images/logo-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "./images/logo-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "./images/logo-180x180.png",
|
||||
"sizes": "180x180",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "./images/logo-270x270.png",
|
||||
"sizes": "270x270",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"start_url": "/{{ repo_name }}/{{ folder_path }}/index.html",
|
||||
"display": "standalone",
|
||||
"orientation": "portrait"
|
||||
}
|
||||
Reference in New Issue
Block a user