feat: add color presets + fix badge hover effect

- Add global color presets: save/apply/delete sets of {bg, border, text}
  colors as reusable presets, shown as mini badges in their own colors.
- Popover: preset badges under preview with + to save, click to apply,
  × on hover to delete.
- Popup: dedicated Color Presets section with same functionality,
  applying to light subreddit badge colors.
- Storage: colorPresets array added to settings defaults.
- Fix badge hover: change brightness(0.9) to brightness(1.1) so hover
  brightens (activates) instead of dimming.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-26 18:00:54 -04:00
parent fb0c03d46a
commit 48db2fc70e
9 changed files with 2053 additions and 23 deletions

View File

@@ -25,7 +25,7 @@ html.rt-enabled .thing.link .flat-list li:has(> .comments) {
}
.rt-badge:hover {
filter: brightness(0.9);
filter: brightness(1.1);
}
/* --- Subreddit badge: relative for gear positioning --- */
@@ -316,6 +316,96 @@ html.rt-enabled .thing.link .flat-list li:has(> .comments) {
color: #ccc;
}
/* --- Preset badges --- */
.rt-popover-presets {
display: flex;
align-items: center;
gap: 6px;
padding: 8px 0;
border-top: 1px solid #eee;
flex-wrap: wrap;
}
.rt-preset-list {
display: flex;
gap: 4px;
flex-wrap: wrap;
}
.rt-preset-badge {
display: inline-block;
position: relative;
border: 1px solid;
border-radius: 3px;
padding: 2px 6px;
font-size: 10px;
font-weight: bold;
cursor: pointer;
line-height: 1.4;
user-select: none;
}
.rt-preset-badge:hover {
filter: brightness(1.1);
}
.rt-preset-delete {
display: none;
position: absolute;
top: -6px;
right: -6px;
width: 14px;
height: 14px;
background: #d9534f;
color: white;
border-radius: 50%;
font-size: 10px;
line-height: 14px;
text-align: center;
cursor: pointer;
}
.rt-preset-badge:hover .rt-preset-delete {
display: block;
}
.rt-preset-add {
width: 22px;
height: 22px;
border: 1px dashed #ccc;
border-radius: 3px;
background: none;
color: #999;
font-size: 14px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
padding: 0;
}
.rt-preset-add:hover {
border-color: #999;
color: #666;
background: #f5f5f5;
}
.rt-color-popover.rt-dark .rt-popover-presets {
border-top-color: #444;
}
.rt-color-popover.rt-dark .rt-preset-add {
border-color: #555;
color: #888;
}
.rt-color-popover.rt-dark .rt-preset-add:hover {
border-color: #888;
color: #ccc;
background: #333;
}
/* --- Color Picker Widget --- */
.rt-color-picker {
margin: 8px 0;

View File

@@ -105,6 +105,21 @@
var previewSection = el.querySelector(".rt-popover-preview");
el.insertBefore(picker.el, previewSection);
var presetsDiv = document.createElement("div");
presetsDiv.className = "rt-popover-presets";
var presetList = document.createElement("div");
presetList.className = "rt-preset-list";
var presetAddBtn = document.createElement("button");
presetAddBtn.className = "rt-preset-add";
presetAddBtn.type = "button";
presetAddBtn.textContent = "+";
presetAddBtn.title = "Save current colors as preset";
presetAddBtn.addEventListener("click", saveAsPreset);
presetsDiv.appendChild(presetList);
presetsDiv.appendChild(presetAddBtn);
el.querySelector(".rt-popover-footer").insertAdjacentElement("beforebegin", presetsDiv);
els.presetList = presetList;
els.closeBtn.addEventListener("click", close);
els.cancelBtn.addEventListener("click", close);
els.saveBtn.addEventListener("click", save);
@@ -234,6 +249,73 @@
els.preview.style.color = text;
}
function renderPresets(presets) {
els.presetList.innerHTML = "";
for (var i = 0; i < presets.length; i++) {
(function (index) {
var p = presets[index];
var badge = document.createElement("span");
badge.className = "rt-preset-badge";
badge.style.background = p.bg;
badge.style.borderColor = p.border;
badge.style.color = p.text;
badge.textContent = "Aa";
badge.title = "Click to apply";
badge.addEventListener("click", function () {
applyPreset(p);
});
var del = document.createElement("span");
del.className = "rt-preset-delete";
del.textContent = "\u00d7";
del.addEventListener("click", function (e) {
e.stopPropagation();
removePreset(index);
});
badge.appendChild(del);
els.presetList.appendChild(badge);
})(i);
}
}
function applyPreset(p) {
markModeDirty(selectedMode);
var props = ["bg", "border", "text"];
for (var i = 0; i < props.length; i++) {
var prop = props[i];
els.rows[prop].swatch.style.backgroundColor = p[prop];
els.rows[prop].hex.value = p[prop];
els.rows[prop].hex.classList.remove("rt-input-error");
}
updatePreview();
if (picker && picker.isOpen() && activeProp) {
picker.setColor(els.rows[activeProp].hex.value);
}
}
async function saveAsPreset() {
var preset = {
bg: els.rows.bg.hex.value,
border: els.rows.border.hex.value,
text: els.rows.text.hex.value,
};
var settings = await window.RedditTweaks.loadSettings();
var presets = settings.colorPresets || [];
presets.push(preset);
settings.colorPresets = presets;
await window.RedditTweaks.saveSettings(settings);
renderPresets(presets);
}
async function removePreset(index) {
var settings = await window.RedditTweaks.loadSettings();
var presets = settings.colorPresets || [];
presets.splice(index, 1);
settings.colorPresets = presets;
await window.RedditTweaks.saveSettings(settings);
renderPresets(presets);
}
function positionPopover(anchorEl) {
var rect = anchorEl.getBoundingClientRect();
var gap = 6;
@@ -311,6 +393,7 @@
applyDarkModeToPopover(settings);
loadModeIntoInputs();
updatePreview();
renderPresets(settings.colorPresets || []);
positionPopover(badgeEl);
}