diff --git a/content/tweaks/color-popover.js b/content/tweaks/color-popover.js
new file mode 100644
index 0000000..734e398
--- /dev/null
+++ b/content/tweaks/color-popover.js
@@ -0,0 +1,300 @@
+(function () {
+ "use strict";
+
+ var popoverEl = null;
+ var currentSubreddit = null;
+ var currentAnchor = null;
+ var editState = { light: null, dark: null };
+ var selectedMode = "light";
+
+ var els = {};
+
+ function getHelpers() {
+ return {
+ isDarkMode: window.RedditTweaks.badgeTweak.isDarkMode,
+ getColorPrefix: window.RedditTweaks.badgeTweak.getColorPrefix,
+ };
+ }
+
+ function hexValid(val) {
+ return /^#[0-9a-f]{6}$/i.test(val);
+ }
+
+ function colorToHex(val) {
+ if (!val) return "#ffffff";
+ return val;
+ }
+
+ function createPopover() {
+ var el = document.createElement("div");
+ el.className = "rt-color-popover";
+
+ el.innerHTML = [
+ '
",
+ '',
+ ' ",
+ "
",
+ '',
+ '
",
+ '
",
+ '
",
+ "
",
+ '',
+ ' ',
+ "
",
+ '",
+ ].join("\n");
+
+ document.body.appendChild(el);
+ popoverEl = el;
+
+ els.title = el.querySelector(".rt-popover-title");
+ els.closeBtn = el.querySelector(".rt-popover-close");
+ els.modeSelect = el.querySelector(".rt-popover-mode-select");
+ els.preview = el.querySelector(".rt-popover-preview-badge");
+ els.saveBtn = el.querySelector(".rt-btn-save");
+ els.cancelBtn = el.querySelector(".rt-btn-cancel");
+ els.resetBtn = el.querySelector(".rt-btn-reset");
+
+ els.rows = {};
+ var rows = el.querySelectorAll(".rt-popover-row");
+ for (var i = 0; i < rows.length; i++) {
+ var row = rows[i];
+ var prop = row.getAttribute("data-prop");
+ els.rows[prop] = {
+ swatch: row.querySelector(".rt-color-swatch"),
+ hex: row.querySelector(".rt-color-hex"),
+ };
+ }
+
+ els.closeBtn.addEventListener("click", close);
+ els.cancelBtn.addEventListener("click", close);
+ els.saveBtn.addEventListener("click", save);
+ els.resetBtn.addEventListener("click", resetToDefault);
+
+ els.modeSelect.addEventListener("change", function () {
+ storeCurrentEdits();
+ selectedMode = els.modeSelect.value;
+ loadModeIntoInputs();
+ updatePreview();
+ });
+
+ var props = ["bg", "border", "text"];
+ for (var p = 0; p < props.length; p++) {
+ (function (prop) {
+ var row = els.rows[prop];
+ row.swatch.addEventListener("input", function () {
+ row.hex.value = row.swatch.value;
+ row.hex.classList.remove("rt-input-error");
+ updatePreview();
+ });
+ row.hex.addEventListener("input", function () {
+ if (hexValid(row.hex.value)) {
+ row.swatch.value = row.hex.value;
+ row.hex.classList.remove("rt-input-error");
+ updatePreview();
+ } else {
+ row.hex.classList.add("rt-input-error");
+ }
+ });
+ })(props[p]);
+ }
+
+ document.addEventListener("mousedown", onDocumentMouseDown, true);
+ document.addEventListener("keydown", onDocumentKeyDown, true);
+ }
+
+ function onDocumentMouseDown(e) {
+ if (!popoverEl || !popoverEl.classList.contains("rt-popover-visible")) return;
+ if (popoverEl.contains(e.target)) return;
+
+ var active = document.activeElement;
+ if (active && active.type === "color" && popoverEl.contains(active)) return;
+
+ close();
+ }
+
+ function onDocumentKeyDown(e) {
+ if (e.key === "Escape" && popoverEl && popoverEl.classList.contains("rt-popover-visible")) {
+ close();
+ }
+ }
+
+ function getDefaultsForMode(settings, mode) {
+ return {
+ bg: settings[mode + ".subredditBgColor"],
+ border: settings[mode + ".subredditBorderColor"],
+ text: settings[mode + ".subredditTextColor"],
+ };
+ }
+
+ function getColorsForMode(settings, subredditName, mode) {
+ var entry = settings.subredditColors[subredditName];
+ if (entry && entry[mode]) {
+ return { bg: entry[mode].bg, border: entry[mode].border, text: entry[mode].text };
+ }
+ return getDefaultsForMode(settings, mode);
+ }
+
+ function storeCurrentEdits() {
+ editState[selectedMode] = {
+ bg: els.rows.bg.swatch.value,
+ border: els.rows.border.swatch.value,
+ text: els.rows.text.swatch.value,
+ };
+ }
+
+ function loadModeIntoInputs() {
+ var colors = editState[selectedMode];
+ var props = ["bg", "border", "text"];
+ for (var i = 0; i < props.length; i++) {
+ var prop = props[i];
+ var val = colorToHex(colors[prop]);
+ els.rows[prop].swatch.value = val;
+ els.rows[prop].hex.value = val;
+ els.rows[prop].hex.classList.remove("rt-input-error");
+ }
+ }
+
+ function updatePreview() {
+ var bg = els.rows.bg.swatch.value;
+ var border = els.rows.border.swatch.value;
+ var text = els.rows.text.swatch.value;
+ els.preview.style.background = bg;
+ els.preview.style.borderColor = border;
+ els.preview.style.color = text;
+ }
+
+ function positionPopover(anchorEl) {
+ var rect = anchorEl.getBoundingClientRect();
+ var gap = 6;
+
+ popoverEl.style.visibility = "hidden";
+ popoverEl.classList.add("rt-popover-visible");
+
+ var popRect = popoverEl.getBoundingClientRect();
+ var top = rect.bottom + window.scrollY + gap;
+ var left = rect.left + window.scrollX;
+
+ if (rect.bottom + gap + popRect.height > window.innerHeight) {
+ top = rect.top + window.scrollY - popRect.height - gap;
+ }
+
+ var maxLeft = document.documentElement.scrollWidth - popRect.width - 8;
+ if (left > maxLeft) left = maxLeft;
+ if (left < 8) left = 8;
+
+ popoverEl.style.top = top + "px";
+ popoverEl.style.left = left + "px";
+ popoverEl.style.visibility = "";
+ }
+
+ function applyDarkModeToPopover(settings) {
+ var helpers = getHelpers();
+ if (helpers.isDarkMode(settings)) {
+ popoverEl.classList.add("rt-dark");
+ } else {
+ popoverEl.classList.remove("rt-dark");
+ }
+ }
+
+ function open(badgeEl, subredditName, settings) {
+ if (!popoverEl) createPopover();
+
+ if (currentSubreddit === subredditName && popoverEl.classList.contains("rt-popover-visible")) {
+ close();
+ return;
+ }
+
+ currentSubreddit = subredditName;
+ currentAnchor = badgeEl;
+
+ var helpers = getHelpers();
+ selectedMode = helpers.getColorPrefix(settings);
+
+ editState.light = getColorsForMode(settings, subredditName, "light");
+ editState.dark = getColorsForMode(settings, subredditName, "dark");
+
+ els.title.textContent = "r/" + subredditName;
+ els.modeSelect.value = selectedMode;
+ els.preview.textContent = "r/" + subredditName;
+
+ applyDarkModeToPopover(settings);
+ loadModeIntoInputs();
+ updatePreview();
+ positionPopover(badgeEl);
+ }
+
+ function close() {
+ if (!popoverEl) return;
+ popoverEl.classList.remove("rt-popover-visible");
+ currentSubreddit = null;
+ currentAnchor = null;
+ editState = { light: null, dark: null };
+ }
+
+ async function save() {
+ if (!currentSubreddit) return;
+
+ storeCurrentEdits();
+
+ var settings = await window.RedditTweaks.loadSettings();
+ var entry = settings.subredditColors[currentSubreddit] || {};
+
+ if (editState.light) entry.light = editState.light;
+ if (editState.dark) entry.dark = editState.dark;
+
+ settings.subredditColors[currentSubreddit] = entry;
+ await window.RedditTweaks.saveSettings(settings);
+ close();
+ }
+
+ async function resetToDefault() {
+ if (!currentSubreddit) return;
+
+ var settings = await window.RedditTweaks.loadSettings();
+ delete settings.subredditColors[currentSubreddit];
+ await window.RedditTweaks.saveSettings(settings);
+
+ editState.light = getDefaultsForMode(settings, "light");
+ editState.dark = getDefaultsForMode(settings, "dark");
+ loadModeIntoInputs();
+ updatePreview();
+ }
+
+ window.RedditTweaks = window.RedditTweaks || {};
+ window.RedditTweaks.colorPopover = {
+ open: open,
+ close: close,
+ };
+})();
diff --git a/manifest.json b/manifest.json
index 249391d..d6a072c 100644
--- a/manifest.json
+++ b/manifest.json
@@ -20,6 +20,7 @@
"js": [
"lib/settings.js",
"content/tweaks/badges.js",
+ "content/tweaks/color-popover.js",
"content/main.js"
],
"run_at": "document_idle"