diff --git a/public/admin.css b/public/admin.css
index 0db5a3b..161ccd4 100644
--- a/public/admin.css
+++ b/public/admin.css
@@ -166,6 +166,19 @@ label { display: grid; gap: 6px; color: var(--muted); font-size: 13px; }
.setting-switch input:checked { background: var(--brand); }
.setting-switch input:checked:before { transform: translateX(18px); }
.color-field input { margin-top: 2px; }
+.color-row {
+ display: grid;
+ grid-template-columns: 58px minmax(0, 1fr);
+ gap: 8px;
+ align-items: center;
+}
+.color-row input[type="color"] {
+ width: 58px;
+}
+.hex-input {
+ font-family: ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", monospace;
+ text-transform: lowercase;
+}
.list-tools {
padding: 10px 14px;
border-bottom: 1px solid var(--line);
diff --git a/public/admin.html b/public/admin.html
index a8fefd9..40bad70 100644
--- a/public/admin.html
+++ b/public/admin.html
@@ -99,7 +99,10 @@
diff --git a/public/admin.js b/public/admin.js
index 0471fec..0d11282 100644
--- a/public/admin.js
+++ b/public/admin.js
@@ -47,6 +47,13 @@ $("#logoutButton").addEventListener("click", async () => {
$("#settingsButton").addEventListener("click", () => $("#settingsPanel").classList.remove("hidden"));
$("#closeSettingsButton").addEventListener("click", () => $("#settingsPanel").classList.add("hidden"));
+$("#settingsForm").brand.addEventListener("input", (event) => {
+ $("#settingsForm").brandHex.value = normalizeHexColor(event.target.value) || event.target.value;
+});
+$("#settingsForm").brandHex.addEventListener("input", (event) => {
+ const color = normalizeHexColor(event.target.value);
+ if (color) $("#settingsForm").brand.value = color;
+});
window.addEventListener("focus", stopAttention);
document.addEventListener("visibilitychange", () => {
@@ -65,7 +72,7 @@ $("#settingsForm").addEventListener("submit", async (event) => {
settings.intro.cs = form.get("introCs");
settings.title.en = form.get("titleEn");
settings.intro.en = form.get("introEn");
- settings.colors.brand = form.get("brand");
+ settings.colors.brand = normalizeHexColor(form.get("brandHex")) || form.get("brand");
settings.desktop.side = form.get("desktopSide");
settings.desktop.bottomPx = Number(form.get("desktopBottom"));
settings.desktop.sideOffsetPx = Number(form.get("desktopOffset"));
@@ -309,7 +316,8 @@ function renderSite() {
form.introCs.value = s.intro.cs || "";
form.titleEn.value = s.title.en || "";
form.introEn.value = s.intro.en || "";
- form.brand.value = s.colors.brand || "#0f8f6f";
+ form.brand.value = normalizeHexColor(s.colors.brand) || "#0f8f6f";
+ form.brandHex.value = form.brand.value;
form.desktopSide.value = s.desktop.side || "right";
form.desktopBottom.value = s.desktop.bottomPx || 22;
form.desktopOffset.value = s.desktop.sideOffsetPx || 22;
@@ -829,6 +837,11 @@ function escapeHtml(value) {
})[char]);
}
+function normalizeHexColor(value) {
+ const match = String(value || "").trim().match(/^#?([0-9a-fA-F]{6})$/);
+ return match ? `#${match[1].toLowerCase()}` : "";
+}
+
function flag(countryCode) {
if (!countryCode || countryCode.length !== 2) return "";
return countryCode.toUpperCase().replace(/./g, (char) => String.fromCodePoint(127397 + char.charCodeAt()));