Compare commits

...

4 Commits

Author SHA1 Message Date
dc31d21104 docs: add screenshots section to README with composed collage
Three-panel collage (popup, before, after) generated via Pillow script.
4:3 aspect ratio, 1200px max, gradient background with drop shadows.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 03:09:55 -04:00
00507fe87c refactor: decompose CSS, unify dark mode, extract shared modules
Structural cleanup based on thermo-nuclear code quality review:
- Extract shared color-picker.css and color-popover.css from badges.css (611→85 lines)
- Extract shared lib/presets.js for preset CRUD used by popup and popover
- Unify dark mode to single applyDarkModeClass on <html>, removing 4 scattered functions
- Fix stale closure in startPostObserver, data-driven reload key check
- Remove dead exports, getHelpers wrapper, duplicated colorToHex
- Replace DOM expando properties with state array in popup.js

Net: -318 lines of duplication, no file over 401 lines.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 01:39:25 -04:00
80689c6f8d docs: add chat summary for housekeeping session
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 00:45:57 -04:00
e4093935b3 fix: unwrap LICENSE to match Gitea's MIT template format
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 00:44:02 -04:00
25 changed files with 1134 additions and 1027 deletions

18
LICENSE
View File

@@ -2,20 +2,8 @@ MIT License
Copyright (c) 2026 cottongin (cottongin@cottongin.xyz) Copyright (c) 2026 cottongin (cottongin@cottongin.xyz)
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -14,6 +14,19 @@ A Firefox extension that reskins [old.reddit.com](https://old.reddit.com) with a
- **Color customization** — per-subreddit badge colors via an inline HSV picker - **Color customization** — per-subreddit badge colors via an inline HSV picker
- **Theming** — separate light/dark palettes with automatic RES night-mode detection - **Theming** — separate light/dark palettes with automatic RES night-mode detection
## Screenshots
![Extension overview](docs/reddit-extension-collage.png)
> Extension menu (left), standard old.reddit.com (top right), with extension enabled (bottom right).
> RES (Reddit Enhancement Suite) was enabled during capture.
| View | Full-size image |
|---|---|
| Extension menu | [reddit-extension-popup.png](docs/reddit-extension-popup.png) |
| Without extension | [reddit-extension-disabled.png](docs/reddit-extension-disabled.png) |
| With extension | [reddit-extension-enabled.png](docs/reddit-extension-enabled.png) |
## Installation ## Installation
1. Clone or download this repo 1. Clone or download this repo

View File

@@ -0,0 +1,42 @@
# Chat Summary — 2026-08-28 00:27
## Task
Add project housekeeping files: LICENSE, README, VERSION, .editorconfig, expanded .gitignore. Sync manifest version. Set remote and push.
## Preceded by
- Sensitive information audit (no issues found beyond expected `cottongin` in git history)
- Plan mode discussion to scope housekeeping items and branding conventions
## Changes Made
| File | Action | Details |
|------|--------|---------|
| `LICENSE` | Created | MIT license, copyright cottongin 2026 |
| `README.md` | Created | Terse/casual, Cursor/Claude notice, personal project disclaimer, features, install via about:debugging |
| `VERSION` | Created | `0.1.0` |
| `.gitignore` | Expanded | Added .DS_Store, *.xpi, web-ext-artifacts/, node_modules/, .env* |
| `.editorconfig` | Created | 2-space indent for JS/CSS/JSON/HTML, UTF-8, LF, trim whitespace |
| `manifest.json` | Modified | Version synced from 1.0.0 to 0.1.0 |
## Commit
`c0a0c15` — `chore: add LICENSE, README, VERSION, .editorconfig, expand .gitignore`
Also staged 6 previously untracked files (4 chat summaries, 2 score-ratio docs).
## Remote
Set origin to `git@code.cottongin.xyz:cottongin/reddit-tweaks-ffe.git` and pushed master.
## Branding Reference
- Full: "Reddit Tweaks Firefox Extension"
- Abbreviation: "RTFFE"
- Short: "Reddit Tweaks FFE"
- Casual: "Reddit Tweaks" / "the extension"
## Follow-up Items
- None

View File

@@ -0,0 +1,51 @@
# Thermo-Nuclear Code Quality Review — Reddit Tweaks Extension
**Date:** 2026-08-28 01:08
**Task:** Deep code quality audit of the full codebase using thermo-nuclear review criteria
## Verdict
Not approved — several structural regressions and missed simplification opportunities identified.
## Key Findings
### Critical
1. **~200 lines of CSS duplicated** between `content/styles/badges.css` (lines 409–611) and `popup/popup.css` (lines 287–489). Color picker widget and preset badge styles are copy-pasted verbatim. Fix: extract shared `color-picker.css`.
2. **`badges.css` (611 lines) conflates three concerns** — badge styling (~85 lines), color popover chrome (~230 lines), and color picker widget (~150 lines). Should be decomposed into separate files.
### High
3. **Preset management logic duplicated** across `popup.js` (`renderPopupPresets`, `addPopupPreset`, `deletePopupPreset`) and `color-popover.js` (`renderPresets`, `saveAsPreset`, `removePreset`). Nearly identical CRUD pattern. Fix: extract shared `presets.js` module.
4. **`colorToHex` identity function duplicated** in both `popup.js` and `color-popover.js`.
### Medium
5. **Dark mode class management scattered** across 4 files with inconsistent scoping — `cards.js` targets `<html>`, `badges.js` targets each container, `color-popover.js` targets itself. Code-judo: apply `rt-dark` once on `<html>` and let CSS cascade handle the rest.
6. **DOM expando properties** (`entry._colorState`, `entry._nameInput`, etc.) used as state management in `popup.js`. Should use plain objects.
7. **Stale closure** in `startPostObserver` — line 47 uses captured `settings` instead of freshly-loaded `updated`.
### Low
8. **Dead exports** (`isDarkMode`, `getColorPrefix`) on `badgeTweak` object.
9. **Verbose storage change listener** — four repetitive conditions should be data-driven.
10. **`getHelpers()` wrapper** in `color-popover.js` adds pointless indirection.
## Recommended Priority Order
1. Extract shared `color-picker.css` (~200 lines deleted)
2. Split `badges.css` into badges, popover, picker
3. Extract shared preset module
4. Unify dark mode to single `html.rt-dark` class
5. Fix stale closure bug
6. Clean up dead exports, verbose conditions, and unnecessary wrappers
## Lessons Learned
- The thermo-nuclear review skill is useful for catching CSS duplication that would otherwise grow silently — CSS files don't get the same refactoring attention as JS.
- Browser extension architecture (content scripts vs popup) makes code sharing harder since there's no module system, but shared files loaded via both manifest.json and popup.html `<script>`/`<link>` tags work fine.
- IIFE-scoped modules on a global namespace are a reasonable pattern for small extensions but make duplication easy to miss since there's no import graph to inspect.

View File

@@ -0,0 +1,80 @@
# Thermo-Nuclear Code Quality Refactor — Reddit Tweaks Extension
**Date:** 2026-08-28 01:23
**Task:** Implement all 10 findings from the thermo-nuclear code quality review
## Changes Made
### 1. CSS Deduplication — Extracted shared `color-picker.css` (NEW FILE)
- Created `content/styles/color-picker.css` (259 lines) containing color picker widget styles and preset badge styles
- This file is now shared between the content script (via manifest.json) and the popup (via `<link>` tag)
- Eliminated ~200 lines of identical CSS that was previously duplicated between `badges.css` and `popup.css`
### 2. CSS Decomposition — Extracted `color-popover.css` (NEW FILE)
- Created `content/styles/color-popover.css` (256 lines) with popover chrome, buttons, dark mode
- Trimmed `badges.css` from 611 lines to 85 lines (badges-only)
- Trimmed `popup.css` from 489 lines to 220 lines (popup-specific only)
- Updated dark mode selectors from `.rt-color-popover.rt-dark` to `.rt-dark .rt-color-popover` (ancestor form)
### 3. Shared Preset Module — Extracted `lib/presets.js` (NEW FILE)
- Created `lib/presets.js` (57 lines) with `render()`, `save()`, `delete()` functions
- Refactored `popup.js` and `color-popover.js` to use shared module
- Eliminated ~60 lines of duplicated preset rendering and CRUD logic
### 4. Dark Mode Unification
- Added `applyDarkModeClass(settings)` to `lib/settings.js`
- Removed per-container dark class toggling from `badges.js` `applyColorsToContainer()`
- Removed `applyDarkMode()` from `cards.js` (was redundant with shared function)
- Removed `applyDarkModeToPopover()` and `refreshTheme()` from `color-popover.js`
- `main.js` now calls `applyDarkModeClass()` centrally in init, dark-mode observer, and storage listener
### 5. Stale Closure Fix
- Fixed `startPostObserver` in `main.js` to use freshly-loaded `updated` settings instead of stale captured `settings`
### 6. Data-Driven Storage Listener
- Replaced 4 verbose `changes.X && changes.X.newValue !== ...` conditions with `RELOAD_KEYS.some()`
### 7. Dead Export Cleanup
- Removed `isDarkMode` and `getColorPrefix` re-exports from `badgeTweak` object in `badges.js`
### 8. Deleted `getHelpers()` Wrapper
- Removed unnecessary indirection in `color-popover.js`, replaced with direct `window.RedditTweaks.*` calls
### 9. Inlined `colorToHex`
- Removed duplicated `colorToHex` function from both `popup.js` and `color-popover.js`
- Replaced all call sites with inline `val || "#ffffff"`
### 10. DOM Expando Refactor
- Replaced `entry._colorState`, `entry._nameInput`, etc. DOM expando pattern in `popup.js`
- Introduced `subredditEntries` array of plain state objects
- `collectSubredditOverrides()` now iterates the state array instead of querying the DOM
## Files Changed
- `content/main.js` — stale closure fix, data-driven reload keys, central dark mode calls
- `content/tweaks/badges.js` — removed dark class toggle, dead exports, refreshTheme call
- `content/tweaks/cards.js` — removed applyDarkMode, use shared function
- `content/tweaks/color-popover.js` — removed getHelpers, colorToHex, applyDarkModeToPopover, refreshTheme, preset duplication
- `lib/settings.js` — added applyDarkModeClass
- `popup/popup.js` — inlined colorToHex, used shared presets, state array instead of expandos
- `popup/popup.css` — trimmed to popup-only styles
- `content/styles/badges.css` — trimmed to badge-only styles (611 → 85 lines)
- `manifest.json` — added new CSS and JS files
- `popup/popup.html` — added links to new CSS and JS files
## New Files
- `content/styles/color-picker.css` — shared picker + preset badge styles
- `content/styles/color-popover.css` — popover chrome styles
- `lib/presets.js` — shared preset CRUD module
## Net Impact
- **Before:** 3,180 lines across 12 source files
- **After:** 2,862 lines across 15 source files
- **Deleted:** ~318 lines of duplication and dead code
- No file exceeds 401 lines (previously badges.css was 611)
- Dark mode is now managed from a single source instead of 4 scattered functions
## Lessons Learned
- CSS duplication across content script and popup contexts is easy to miss because there's no import graph. Shared CSS files referenced by both manifest.json and popup.html `<link>` work well.
- Dark mode class management is a natural candidate for centralization — scattered per-element toggling creates CSS selector complexity that cascading from a single ancestor eliminates.
- DOM expando properties work but are an anti-pattern that makes code harder to reason about. A separate state array with DOM references is only marginally more code but significantly clearer.
- The `colorToHex` "conversion" function was really just null-coalescing — renaming or inlining it makes the intent obvious.

View File

@@ -0,0 +1,28 @@
# README Screenshots
## Task
Add a Screenshots section to the README featuring a composed collage hero image and links to each full-size original screenshot.
## Changes Made
### New files
- `docs/make_collage.py` -- Python/Pillow script that composes the three source screenshots into an 800x600 collage image (popup left, disabled top-right, enabled bottom-right, light gray background, 4px gaps).
- `docs/reddit-extension-collage.png` -- the generated collage output.
### Modified files
- `README.md` -- added a "Screenshots" section between "What it does" and "Installation" containing the collage image, a blockquote caption describing the layout and noting RES was active during capture, and a table linking to each full-size original.
## Source screenshots
- `docs/reddit-extension-popup.png` (645x2757) -- captured by user by opening `moz-extension://<uuid>/popup/popup.html` in a Firefox tab
- `docs/reddit-extension-disabled.png` (2794x1671) -- old.reddit.com without extension
- `docs/reddit-extension-enabled.png` (2794x1671) -- old.reddit.com with extension
## Lessons Learned
- Firefox extension popups close on focus loss. Opening the popup HTML directly via `moz-extension://` URL in a regular tab is the simplest workaround for full-page captures.
- GitHub-flavored markdown cannot crop images, does not honor `rowspan`, and strips most CSS. A pre-composed collage image is the only way to get a multi-panel layout.
- Pillow's `Image.LANCZOS` resampling produces clean downscaled screenshots even at significant reduction ratios (e.g., 2794px down to ~546px).
## Follow-up
- The `docs/make_collage.py` script can be re-run if source screenshots are updated.
- Consider adding the collage script or output to `.gitignore` if the generated image shouldn't be tracked separately from sources.

View File

@@ -0,0 +1,36 @@
# README Screenshots -- Final
## Task
Add a Screenshots section to the README with a composed collage hero image and links to full-size originals.
## Changes Made
### New files
- `docs/reddit-extension-collage.png` -- 1200x900 (4:3) composed collage of all three screenshots.
- `docs/make_collage.py` -- Pillow script that generates the collage. Configurable aspect ratio, max dimension, gradient, borders, and drop shadows.
- `docs/reddit-extension-popup.png` -- full-page capture of the extension popup (captured manually via `moz-extension://` URL in a tab).
- `docs/reddit-extension-disabled.png` -- old.reddit.com without the extension.
- `docs/reddit-extension-enabled.png` -- old.reddit.com with the extension active.
### Modified files
- `README.md` -- added Screenshots section between "What it does" and "Installation". Contains the collage hero image, a blockquote caption, and a table linking to each full-size original.
## Collage Details
Final collage configuration after iterating through multiple options:
- **Layout**: popup on left, disabled screenshot top-right, enabled screenshot bottom-right
- **Aspect ratio**: 4:3, clamped to 1200px max dimension (chosen from 5:4, 4:3, 3:2, 16:10, 16:9, 6:5, 7:6 variants)
- **Background**: 45-degree linear gradient from `#888888` to `#575757`
- **Panel borders**: `#111111`, 1px, 4px corner radius
- **Drop shadows**: offset (4,4), blur 8, rgba(0,0,0,0.43), cast bottom-right to complement the gradient angle
- **Padding**: 20px outer, 14px gaps between panels (scaled proportionally when clamped)
## Lessons Learned
- Firefox extension popups close on focus loss. Opening `moz-extension://<uuid>/popup/popup.html` in a regular tab is the simplest workaround for full-page captures.
- GitHub-flavored markdown cannot crop images, strips `rowspan`, and removes CSS. A pre-composed collage image is the only way to get a multi-panel layout in a README.
- Iterating on visual output (colors, ratios, effects) works well as a rapid generate-and-review loop with Pillow.
- Generating multiple aspect ratio variants at once and letting the user compare side-by-side is faster than guessing one at a time.
## Follow-up
- Run `python docs/make_collage.py` to regenerate the collage if source screenshots are updated.

View File

@@ -5,6 +5,7 @@
var observer = new MutationObserver(function () { var observer = new MutationObserver(function () {
window.RedditTweaks.loadSettings().then(function (updated) { window.RedditTweaks.loadSettings().then(function (updated) {
if (updated.darkMode === "auto") { if (updated.darkMode === "auto") {
window.RedditTweaks.applyDarkModeClass(updated);
if (updated.badgeLayout) { if (updated.badgeLayout) {
window.RedditTweaks.badgeTweak.updateColors(updated); window.RedditTweaks.badgeTweak.updateColors(updated);
} }
@@ -44,10 +45,10 @@
} }
if (hasNewPosts) { if (hasNewPosts) {
window.RedditTweaks.loadSettings().then(function (updated) { window.RedditTweaks.loadSettings().then(function (updated) {
if (settings.badgeLayout) { if (updated.badgeLayout) {
window.RedditTweaks.badgeTweak.processNewPosts(updated); window.RedditTweaks.badgeTweak.processNewPosts(updated);
} }
if (settings.cardLayout) { if (updated.cardLayout) {
window.RedditTweaks.cardTweak.processNewPosts(updated); window.RedditTweaks.cardTweak.processNewPosts(updated);
} }
}); });
@@ -62,6 +63,8 @@
var settings = await window.RedditTweaks.loadSettings(); var settings = await window.RedditTweaks.loadSettings();
if (!settings.enabled) return; if (!settings.enabled) return;
window.RedditTweaks.applyDarkModeClass(settings);
if (settings.badgeLayout) { if (settings.badgeLayout) {
document.documentElement.classList.add("rt-badges-enabled"); document.documentElement.classList.add("rt-badges-enabled");
window.RedditTweaks.badgeTweak.apply(settings); window.RedditTweaks.badgeTweak.apply(settings);
@@ -80,18 +83,19 @@
startDarkModeObserver(settings); startDarkModeObserver(settings);
} }
var RELOAD_KEYS = ["enabled", "badgeLayout", "fontOverride", "cardLayout"];
browser.storage.onChanged.addListener(function (changes) { browser.storage.onChanged.addListener(function (changes) {
if ( var needsReload = RELOAD_KEYS.some(function (key) {
(changes.enabled && changes.enabled.newValue !== changes.enabled.oldValue) || return changes[key] && changes[key].newValue !== changes[key].oldValue;
(changes.badgeLayout && changes.badgeLayout.newValue !== changes.badgeLayout.oldValue) || });
(changes.fontOverride && changes.fontOverride.newValue !== changes.fontOverride.oldValue) || if (needsReload) {
(changes.cardLayout && changes.cardLayout.newValue !== changes.cardLayout.oldValue)
) {
location.reload(); location.reload();
return; return;
} }
window.RedditTweaks.loadSettings().then(function (updated) { window.RedditTweaks.loadSettings().then(function (updated) {
window.RedditTweaks.applyDarkModeClass(updated);
if (updated.badgeLayout) { if (updated.badgeLayout) {
window.RedditTweaks.badgeTweak.updateColors(updated); window.RedditTweaks.badgeTweak.updateColors(updated);
} }

View File

@@ -83,529 +83,3 @@ html.rt-badges-enabled .thing.link .flat-list li:has(> .comments) {
.rt-badge-gear:hover { .rt-badge-gear:hover {
opacity: 1 !important; opacity: 1 !important;
} }
/* --- Color popover --- */
.rt-color-popover {
position: absolute;
z-index: 10000;
background: #fff;
border: 1px solid #ccc;
border-radius: 6px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);
padding: 12px;
width: 260px;
overflow-y: auto;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
font-size: 13px;
color: #1a1a1a;
display: none;
}
.rt-color-popover.rt-popover-visible {
display: block;
}
.rt-popover-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
padding-bottom: 6px;
border-bottom: 1px solid #eee;
}
.rt-popover-title {
font-weight: 600;
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 200px;
}
.rt-popover-close {
background: none;
border: none;
font-size: 16px;
cursor: pointer;
color: #999;
padding: 0 2px;
line-height: 1;
}
.rt-popover-close:hover {
color: #333;
}
.rt-popover-mode {
margin-bottom: 8px;
}
.rt-popover-mode select {
width: 100%;
padding: 3px 6px;
border: 1px solid #ccc;
border-radius: 3px;
font-size: 12px;
font-family: inherit;
}
.rt-popover-row {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 6px;
}
.rt-popover-row-label {
font-size: 12px;
flex-shrink: 0;
margin-right: 8px;
}
.rt-popover-row-inputs {
display: flex;
align-items: center;
gap: 4px;
}
.rt-popover-row .rt-color-swatch {
width: 28px;
height: 20px;
border: 1px solid #ccc;
border-radius: 3px;
cursor: pointer;
display: inline-block;
vertical-align: middle;
}
.rt-popover-row input[type="text"] {
width: 70px;
padding: 2px 4px;
border: 1px solid #ccc;
border-radius: 3px;
font-size: 12px;
font-family: monospace;
}
.rt-popover-row input[type="text"].rt-input-error {
border-color: #d9534f;
}
.rt-popover-preview {
margin: 8px 0;
padding: 6px 0;
border-top: 1px solid #eee;
border-bottom: 1px solid #eee;
text-align: center;
}
.rt-popover-footer {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 8px;
gap: 6px;
}
.rt-popover-footer-right {
display: flex;
gap: 6px;
}
.rt-btn-save {
background: #4a90d9;
color: #fff;
border: 1px solid #3a7bc8;
border-radius: 3px;
padding: 4px 12px;
font-size: 12px;
cursor: pointer;
}
.rt-btn-save:hover {
background: #3a7bc8;
}
.rt-btn-cancel {
background: #f0f0f0;
border: 1px solid #ccc;
border-radius: 3px;
padding: 4px 10px;
font-size: 12px;
cursor: pointer;
}
.rt-btn-cancel:hover {
background: #e0e0e0;
}
.rt-btn-reset {
background: none;
border: 1px solid #ccc;
border-radius: 3px;
padding: 4px 10px;
font-size: 12px;
cursor: pointer;
color: #666;
}
.rt-btn-reset:hover {
background: #f5f5f5;
color: #333;
}
/* --- Popover dark mode --- */
.rt-color-popover.rt-dark {
background: #2a2a2a;
border-color: #555;
color: #e0e0e0;
}
.rt-color-popover.rt-dark .rt-popover-header {
border-bottom-color: #444;
}
.rt-color-popover.rt-dark .rt-popover-close {
color: #888;
}
.rt-color-popover.rt-dark .rt-popover-close:hover {
color: #ccc;
}
.rt-color-popover.rt-dark .rt-popover-mode select {
background: #333;
border-color: #555;
color: #e0e0e0;
}
.rt-color-popover.rt-dark .rt-popover-row .rt-color-swatch {
border-color: #555;
}
.rt-color-popover.rt-dark .rt-popover-row input[type="text"] {
background: #333;
border-color: #555;
color: #e0e0e0;
}
.rt-color-popover.rt-dark .rt-popover-preview {
border-top-color: #444;
border-bottom-color: #444;
}
.rt-color-popover.rt-dark .rt-btn-save {
background: #4a90d9;
border-color: #3a7bc8;
}
.rt-color-popover.rt-dark .rt-btn-cancel {
background: #444;
border-color: #555;
color: #ccc;
}
.rt-color-popover.rt-dark .rt-btn-reset {
border-color: #555;
color: #999;
}
.rt-color-popover.rt-dark .rt-btn-reset:hover {
background: #333;
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;
font-size: 12px;
}
.rt-picker-tabs {
display: flex;
border-bottom: 1px solid #ccc;
margin-bottom: 8px;
}
.rt-picker-tab {
flex: 1;
padding: 4px 8px;
background: none;
border: none;
border-bottom: 2px solid transparent;
font-size: 11px;
font-family: inherit;
cursor: pointer;
color: #666;
}
.rt-picker-tab:hover {
color: #333;
}
.rt-picker-tab--active {
color: #1a1a1a;
border-bottom-color: #4a90d9;
font-weight: 600;
}
.rt-picker-panel {
max-height: 180px;
overflow-y: auto;
}
.rt-picker-grid--palette {
display: flex;
flex-direction: column;
gap: 2px;
}
.rt-picker-grid-row {
display: flex;
gap: 2px;
}
.rt-picker-swatch {
width: 16px;
height: 16px;
border-radius: 2px;
border: 1px solid rgba(0, 0, 0, 0.1);
cursor: pointer;
flex-shrink: 0;
}
.rt-picker-swatch:hover {
border-color: rgba(0, 0, 0, 0.4);
transform: scale(1.2);
z-index: 1;
position: relative;
}
.rt-picker-swatch--selected {
border: 2px solid #1a1a1a;
box-shadow: 0 0 0 1px white;
}
.rt-picker-grid--named {
display: flex;
flex-wrap: wrap;
gap: 4px;
padding: 2px 0;
}
.rt-picker-named-item {
display: flex;
flex-direction: column;
align-items: center;
cursor: pointer;
width: 40px;
}
.rt-picker-named-item .rt-picker-swatch {
width: 24px;
height: 18px;
}
.rt-picker-named-label {
font-size: 8px;
color: #888;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
line-height: 1.2;
}
.rt-picker-panel--custom {
max-height: none;
overflow: visible;
}
.rt-picker-sv-wrap {
position: relative;
margin-bottom: 6px;
}
.rt-picker-sv {
display: block;
width: 100%;
border: 1px solid #ccc;
border-radius: 3px;
cursor: crosshair;
}
.rt-picker-sv-cursor {
position: absolute;
width: 10px;
height: 10px;
border: 2px solid white;
border-radius: 50%;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
pointer-events: none;
transform: translate(-50%, -50%);
}
.rt-picker-hue {
position: relative;
height: 14px;
border: 1px solid #ccc;
border-radius: 3px;
background: linear-gradient(to right, #ff0000, #ffff00, #00ff00, #00ffff, #0000ff, #ff00ff, #ff0000);
cursor: crosshair;
}
.rt-picker-hue-indicator {
position: absolute;
top: -1px;
width: 2px;
height: calc(100% + 2px);
background: white;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
pointer-events: none;
transform: translateX(-50%);
}
/* Picker dark mode */
.rt-dark .rt-picker-tabs,
.rt-color-popover.rt-dark .rt-picker-tabs {
border-bottom-color: #555;
}
.rt-dark .rt-picker-tab,
.rt-color-popover.rt-dark .rt-picker-tab {
color: #999;
}
.rt-dark .rt-picker-tab:hover,
.rt-color-popover.rt-dark .rt-picker-tab:hover {
color: #ccc;
}
.rt-dark .rt-picker-tab--active,
.rt-color-popover.rt-dark .rt-picker-tab--active {
color: #e0e0e0;
border-bottom-color: #4a90d9;
}
.rt-dark .rt-picker-swatch,
.rt-color-popover.rt-dark .rt-picker-swatch {
border-color: rgba(255, 255, 255, 0.1);
}
.rt-dark .rt-picker-swatch:hover,
.rt-color-popover.rt-dark .rt-picker-swatch:hover {
border-color: rgba(255, 255, 255, 0.4);
}
.rt-dark .rt-picker-swatch--selected,
.rt-color-popover.rt-dark .rt-picker-swatch--selected {
border-color: #e0e0e0;
box-shadow: 0 0 0 1px #333;
}
.rt-dark .rt-picker-named-label,
.rt-color-popover.rt-dark .rt-picker-named-label {
color: #777;
}
.rt-dark .rt-picker-sv,
.rt-color-popover.rt-dark .rt-picker-sv {
border-color: #555;
}
.rt-dark .rt-picker-hue,
.rt-color-popover.rt-dark .rt-picker-hue {
border-color: #555;
}

View File

@@ -0,0 +1,259 @@
/* --- Color Picker Widget --- */
.rt-color-picker {
margin: 8px 0;
font-size: 12px;
}
.rt-picker-tabs {
display: flex;
border-bottom: 1px solid #ccc;
margin-bottom: 8px;
}
.rt-picker-tab {
flex: 1;
padding: 4px 8px;
background: none;
border: none;
border-bottom: 2px solid transparent;
font-size: 11px;
font-family: inherit;
cursor: pointer;
color: #666;
}
.rt-picker-tab:hover {
color: #333;
}
.rt-picker-tab--active {
color: #1a1a1a;
border-bottom-color: #4a90d9;
font-weight: 600;
}
.rt-picker-panel {
max-height: 180px;
overflow-y: auto;
}
.rt-picker-grid--palette {
display: flex;
flex-direction: column;
gap: 2px;
}
.rt-picker-grid-row {
display: flex;
gap: 2px;
}
.rt-picker-swatch {
width: 16px;
height: 16px;
border-radius: 2px;
border: 1px solid rgba(0, 0, 0, 0.1);
cursor: pointer;
flex-shrink: 0;
}
.rt-picker-swatch:hover {
border-color: rgba(0, 0, 0, 0.4);
transform: scale(1.2);
z-index: 1;
position: relative;
}
.rt-picker-swatch--selected {
border: 2px solid #1a1a1a;
box-shadow: 0 0 0 1px white;
}
.rt-picker-grid--named {
display: flex;
flex-wrap: wrap;
gap: 4px;
padding: 2px 0;
}
.rt-picker-named-item {
display: flex;
flex-direction: column;
align-items: center;
cursor: pointer;
width: 40px;
}
.rt-picker-named-item .rt-picker-swatch {
width: 24px;
height: 18px;
}
.rt-picker-named-label {
font-size: 8px;
color: #888;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
line-height: 1.2;
}
.rt-picker-panel--custom {
max-height: none;
overflow: visible;
}
.rt-picker-sv-wrap {
position: relative;
margin-bottom: 6px;
}
.rt-picker-sv {
display: block;
width: 100%;
border: 1px solid #ccc;
border-radius: 3px;
cursor: crosshair;
}
.rt-picker-sv-cursor {
position: absolute;
width: 10px;
height: 10px;
border: 2px solid white;
border-radius: 50%;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
pointer-events: none;
transform: translate(-50%, -50%);
}
.rt-picker-hue {
position: relative;
height: 14px;
border: 1px solid #ccc;
border-radius: 3px;
background: linear-gradient(to right, #ff0000, #ffff00, #00ff00, #00ffff, #0000ff, #ff00ff, #ff0000);
cursor: crosshair;
}
.rt-picker-hue-indicator {
position: absolute;
top: -1px;
width: 2px;
height: calc(100% + 2px);
background: white;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
pointer-events: none;
transform: translateX(-50%);
}
/* --- Preset badges --- */
.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;
}
/* --- Picker dark mode --- */
.rt-dark .rt-picker-tabs {
border-bottom-color: #555;
}
.rt-dark .rt-picker-tab {
color: #999;
}
.rt-dark .rt-picker-tab:hover {
color: #ccc;
}
.rt-dark .rt-picker-tab--active {
color: #e0e0e0;
border-bottom-color: #4a90d9;
}
.rt-dark .rt-picker-swatch {
border-color: rgba(255, 255, 255, 0.1);
}
.rt-dark .rt-picker-swatch:hover {
border-color: rgba(255, 255, 255, 0.4);
}
.rt-dark .rt-picker-swatch--selected {
border-color: #e0e0e0;
box-shadow: 0 0 0 1px #333;
}
.rt-dark .rt-picker-named-label {
color: #777;
}
.rt-dark .rt-picker-sv {
border-color: #555;
}
.rt-dark .rt-picker-hue {
border-color: #555;
}

View File

@@ -0,0 +1,256 @@
/* --- Color popover --- */
.rt-color-popover {
position: absolute;
z-index: 10000;
background: #fff;
border: 1px solid #ccc;
border-radius: 6px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);
padding: 12px;
width: 260px;
overflow-y: auto;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
font-size: 13px;
color: #1a1a1a;
display: none;
}
.rt-color-popover.rt-popover-visible {
display: block;
}
.rt-popover-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
padding-bottom: 6px;
border-bottom: 1px solid #eee;
}
.rt-popover-title {
font-weight: 600;
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 200px;
}
.rt-popover-close {
background: none;
border: none;
font-size: 16px;
cursor: pointer;
color: #999;
padding: 0 2px;
line-height: 1;
}
.rt-popover-close:hover {
color: #333;
}
.rt-popover-mode {
margin-bottom: 8px;
}
.rt-popover-mode select {
width: 100%;
padding: 3px 6px;
border: 1px solid #ccc;
border-radius: 3px;
font-size: 12px;
font-family: inherit;
}
.rt-popover-row {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 6px;
}
.rt-popover-row-label {
font-size: 12px;
flex-shrink: 0;
margin-right: 8px;
}
.rt-popover-row-inputs {
display: flex;
align-items: center;
gap: 4px;
}
.rt-popover-row .rt-color-swatch {
width: 28px;
height: 20px;
border: 1px solid #ccc;
border-radius: 3px;
cursor: pointer;
display: inline-block;
vertical-align: middle;
}
.rt-popover-row input[type="text"] {
width: 70px;
padding: 2px 4px;
border: 1px solid #ccc;
border-radius: 3px;
font-size: 12px;
font-family: monospace;
}
.rt-popover-row input[type="text"].rt-input-error {
border-color: #d9534f;
}
.rt-popover-preview {
margin: 8px 0;
padding: 6px 0;
border-top: 1px solid #eee;
border-bottom: 1px solid #eee;
text-align: center;
}
.rt-popover-footer {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 8px;
gap: 6px;
}
.rt-popover-footer-right {
display: flex;
gap: 6px;
}
.rt-btn-save {
background: #4a90d9;
color: #fff;
border: 1px solid #3a7bc8;
border-radius: 3px;
padding: 4px 12px;
font-size: 12px;
cursor: pointer;
}
.rt-btn-save:hover {
background: #3a7bc8;
}
.rt-btn-cancel {
background: #f0f0f0;
border: 1px solid #ccc;
border-radius: 3px;
padding: 4px 10px;
font-size: 12px;
cursor: pointer;
}
.rt-btn-cancel:hover {
background: #e0e0e0;
}
.rt-btn-reset {
background: none;
border: 1px solid #ccc;
border-radius: 3px;
padding: 4px 10px;
font-size: 12px;
cursor: pointer;
color: #666;
}
.rt-btn-reset:hover {
background: #f5f5f5;
color: #333;
}
/* --- Popover preset container --- */
.rt-popover-presets {
display: flex;
align-items: center;
gap: 6px;
padding: 8px 0;
border-top: 1px solid #eee;
flex-wrap: wrap;
}
/* --- Popover dark mode --- */
.rt-dark .rt-color-popover {
background: #2a2a2a;
border-color: #555;
color: #e0e0e0;
}
.rt-dark .rt-popover-header {
border-bottom-color: #444;
}
.rt-dark .rt-popover-close {
color: #888;
}
.rt-dark .rt-popover-close:hover {
color: #ccc;
}
.rt-dark .rt-popover-mode select {
background: #333;
border-color: #555;
color: #e0e0e0;
}
.rt-dark .rt-popover-row .rt-color-swatch {
border-color: #555;
}
.rt-dark .rt-popover-row input[type="text"] {
background: #333;
border-color: #555;
color: #e0e0e0;
}
.rt-dark .rt-popover-preview {
border-top-color: #444;
border-bottom-color: #444;
}
.rt-dark .rt-btn-save {
background: #4a90d9;
border-color: #3a7bc8;
}
.rt-dark .rt-btn-cancel {
background: #444;
border-color: #555;
color: #ccc;
}
.rt-dark .rt-btn-reset {
border-color: #555;
color: #999;
}
.rt-dark .rt-btn-reset:hover {
background: #333;
color: #ccc;
}
.rt-dark .rt-popover-presets {
border-top-color: #444;
}
.rt-dark .rt-preset-add {
border-color: #555;
color: #888;
}
.rt-dark .rt-preset-add:hover {
border-color: #888;
color: #ccc;
background: #333;
}

View File

@@ -6,12 +6,6 @@
function applyColorsToContainer(container, settings, subredditName) { function applyColorsToContainer(container, settings, subredditName) {
const prefix = window.RedditTweaks.getColorPrefix(settings); const prefix = window.RedditTweaks.getColorPrefix(settings);
if (window.RedditTweaks.isDarkMode(settings)) {
container.classList.add("rt-dark");
} else {
container.classList.remove("rt-dark");
}
const entry = subredditName && settings.subredditColors[subredditName]; const entry = subredditName && settings.subredditColors[subredditName];
const overrides = entry && entry[prefix]; const overrides = entry && entry[prefix];
@@ -98,10 +92,6 @@
const subredditName = container.getAttribute("data-rt-subreddit"); const subredditName = container.getAttribute("data-rt-subreddit");
applyColorsToContainer(container, settings, subredditName); applyColorsToContainer(container, settings, subredditName);
}); });
if (window.RedditTweaks.colorPopover && window.RedditTweaks.colorPopover.refreshTheme) {
window.RedditTweaks.colorPopover.refreshTheme(settings);
}
} }
function apply(settings) { function apply(settings) {
@@ -113,8 +103,6 @@
window.RedditTweaks.badgeTweak = { window.RedditTweaks.badgeTweak = {
apply: apply, apply: apply,
updateColors: updateColors, updateColors: updateColors,
isDarkMode: window.RedditTweaks.isDarkMode,
getColorPrefix: window.RedditTweaks.getColorPrefix,
processNewPosts: processPosts, processNewPosts: processPosts,
}; };
})(); })();

View File

@@ -19,14 +19,6 @@
} }
} }
function applyDarkMode(settings) {
if (window.RedditTweaks.isDarkMode(settings)) {
document.documentElement.classList.add("rt-dark");
} else {
document.documentElement.classList.remove("rt-dark");
}
}
function applyBackground(settings) { function applyBackground(settings) {
if (settings.cardBackground === "shadcn") { if (settings.cardBackground === "shadcn") {
document.documentElement.classList.add("rt-card-bg-shadcn"); document.documentElement.classList.add("rt-card-bg-shadcn");
@@ -68,7 +60,6 @@
function apply(settings) { function apply(settings) {
document.documentElement.classList.add("rt-card-enabled"); document.documentElement.classList.add("rt-card-enabled");
applyDarkMode(settings);
applyBackground(settings); applyBackground(settings);
applyDensity(settings); applyDensity(settings);
applyGap(settings); applyGap(settings);
@@ -77,7 +68,6 @@
} }
function updateSettings(settings) { function updateSettings(settings) {
applyDarkMode(settings);
applyBackground(settings); applyBackground(settings);
applyDensity(settings); applyDensity(settings);
applyGap(settings); applyGap(settings);

View File

@@ -13,22 +13,10 @@
var els = {}; var els = {};
function getHelpers() {
return {
isDarkMode: window.RedditTweaks.isDarkMode,
getColorPrefix: window.RedditTweaks.getColorPrefix,
};
}
function hexValid(val) { function hexValid(val) {
return /^#[0-9a-f]{6}$/i.test(val); return /^#[0-9a-f]{6}$/i.test(val);
} }
function colorToHex(val) {
if (!val) return "#ffffff";
return val;
}
function createPopover() { function createPopover() {
var el = document.createElement("div"); var el = document.createElement("div");
el.className = "rt-color-popover"; el.className = "rt-color-popover";
@@ -233,7 +221,7 @@
var props = ["bg", "border", "text"]; var props = ["bg", "border", "text"];
for (var i = 0; i < props.length; i++) { for (var i = 0; i < props.length; i++) {
var prop = props[i]; var prop = props[i];
var val = colorToHex(colors[prop]); var val = colors[prop] || "#ffffff";
els.rows[prop].swatch.style.backgroundColor = val; els.rows[prop].swatch.style.backgroundColor = val;
els.rows[prop].hex.value = val; els.rows[prop].hex.value = val;
els.rows[prop].hex.classList.remove("rt-input-error"); els.rows[prop].hex.classList.remove("rt-input-error");
@@ -250,32 +238,14 @@
} }
function renderPresets(presets) { function renderPresets(presets) {
els.presetList.innerHTML = ""; window.RedditTweaks.presets.render(
for (var i = 0; i < presets.length; i++) { els.presetList,
(function (index) { presets,
var p = presets[index]; applyPreset,
var badge = document.createElement("span"); function (index) {
badge.className = "rt-preset-badge"; window.RedditTweaks.presets.delete(index, renderPresets);
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) { function applyPreset(p) {
@@ -293,27 +263,15 @@
} }
} }
async function saveAsPreset() { function saveAsPreset() {
var preset = { window.RedditTweaks.presets.save(
{
bg: els.rows.bg.hex.value, bg: els.rows.bg.hex.value,
border: els.rows.border.hex.value, border: els.rows.border.hex.value,
text: els.rows.text.hex.value, text: els.rows.text.hex.value,
}; },
var settings = await window.RedditTweaks.loadSettings(); renderPresets
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) { function positionPopover(anchorEl) {
@@ -354,15 +312,6 @@
popoverEl.style.visibility = ""; 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) { function open(badgeEl, subredditName, settings) {
if (!popoverEl) createPopover(); if (!popoverEl) createPopover();
@@ -379,8 +328,7 @@
hadOverride.light = !!(existingEntry && existingEntry.light); hadOverride.light = !!(existingEntry && existingEntry.light);
hadOverride.dark = !!(existingEntry && existingEntry.dark); hadOverride.dark = !!(existingEntry && existingEntry.dark);
var helpers = getHelpers(); selectedMode = window.RedditTweaks.getColorPrefix(settings);
selectedMode = helpers.getColorPrefix(settings);
editState.light = getColorsForMode(settings, subredditName, "light"); editState.light = getColorsForMode(settings, subredditName, "light");
editState.dark = getColorsForMode(settings, subredditName, "dark"); editState.dark = getColorsForMode(settings, subredditName, "dark");
@@ -390,7 +338,6 @@
els.preview.textContent = "r/" + subredditName; els.preview.textContent = "r/" + subredditName;
closePicker(); closePicker();
applyDarkModeToPopover(settings);
loadModeIntoInputs(); loadModeIntoInputs();
updatePreview(); updatePreview();
renderPresets(settings.colorPresets || []); renderPresets(settings.colorPresets || []);
@@ -446,15 +393,9 @@
updatePreview(); updatePreview();
} }
function refreshTheme(settings) {
if (!popoverEl || !popoverEl.classList.contains("rt-popover-visible")) return;
applyDarkModeToPopover(settings);
}
window.RedditTweaks = window.RedditTweaks || {}; window.RedditTweaks = window.RedditTweaks || {};
window.RedditTweaks.colorPopover = { window.RedditTweaks.colorPopover = {
open: open, open: open,
close: close, close: close,
refreshTheme: refreshTheme,
}; };
})(); })();

186
docs/make_collage.py Normal file
View File

@@ -0,0 +1,186 @@
"""Compose a collage from three extension screenshots.
Layout (with padding and gaps):
+--padding--+---gap---+-----padding-----+
| | | |
| popup | | disabled (right) |
| (left) | +---------gap------+
| | | enabled (right) |
| | | |
+--padding--+---------+-----padding-----+
Usage: python docs/make_collage.py
"""
import math
from pathlib import Path
from PIL import Image, ImageDraw, ImageFilter
DOCS = Path(__file__).parent
PAD = 20
GAP = 14
GRAD_START = (136, 136, 136)
GRAD_END = (87, 87, 87)
GRAD_ANGLE_DEG = 45
POPUP_COL_W = 230
BORDER_COLOR = (17, 17, 17)
BORDER_W = 1
CORNER_R = 4
SHADOW_OFFSET = (4, 4)
SHADOW_BLUR = 8
SHADOW_COLOR = (0, 0, 0, 110)
def scale_and_crop(img, target_w, target_h):
"""Scale image to cover target box, then crop from top-left."""
scale = max(target_w / img.width, target_h / img.height)
new_w = round(img.width * scale)
new_h = round(img.height * scale)
img = img.resize((new_w, new_h), Image.LANCZOS)
return img.crop((0, 0, target_w, target_h))
def make_gradient(w, h):
"""Create a linear gradient background at GRAD_ANGLE_DEG degrees."""
angle = math.radians(GRAD_ANGLE_DEG)
dx, dy = math.cos(angle), math.sin(angle)
pixels = []
for y in range(h):
for x in range(w):
t = (x * dx + y * dy) / (w * dx + h * dy)
t = max(0.0, min(1.0, t))
r = round(GRAD_START[0] + (GRAD_END[0] - GRAD_START[0]) * t)
g = round(GRAD_START[1] + (GRAD_END[1] - GRAD_START[1]) * t)
b = round(GRAD_START[2] + (GRAD_END[2] - GRAD_START[2]) * t)
pixels.append((r, g, b))
img = Image.new("RGB", (w, h))
img.putdata(pixels)
return img
def round_corners(img, radius):
"""Apply rounded corners using an alpha mask."""
img = img.convert("RGBA")
mask = Image.new("L", img.size, 255)
draw = ImageDraw.Draw(mask)
draw.rectangle([0, 0, radius, radius], fill=0)
draw.rectangle([img.width - radius, 0, img.width, radius], fill=0)
draw.rectangle([0, img.height - radius, radius, img.height], fill=0)
draw.rectangle([img.width - radius, img.height - radius, img.width, img.height], fill=0)
draw.pieslice([0, 0, radius * 2, radius * 2], 180, 270, fill=255)
draw.pieslice([img.width - radius * 2, 0, img.width, radius * 2], 270, 360, fill=255)
draw.pieslice([0, img.height - radius * 2, radius * 2, img.height], 90, 180, fill=255)
draw.pieslice([img.width - radius * 2, img.height - radius * 2, img.width, img.height], 0, 90, fill=255)
img.putalpha(mask)
return img
def add_border(img, color, width, radius):
"""Draw a rounded-rect border on an RGBA image."""
draw = ImageDraw.Draw(img)
draw.rounded_rectangle(
[0, 0, img.width - 1, img.height - 1],
radius=radius, outline=color, width=width,
)
return img
def make_shadow(w, h, radius):
"""Create a drop shadow image for a panel of size w x h."""
spread = SHADOW_BLUR * 2
sw = w + spread * 2
sh = h + spread * 2
shadow = Image.new("RGBA", (sw, sh), (0, 0, 0, 0))
draw = ImageDraw.Draw(shadow)
draw.rounded_rectangle(
[spread, spread, spread + w, spread + h],
radius=radius, fill=SHADOW_COLOR,
)
shadow = shadow.filter(ImageFilter.GaussianBlur(SHADOW_BLUR))
return shadow
def paste_panel(canvas, panel_img, x, y):
"""Paste a panel onto the canvas with rounded corners, border, and drop shadow."""
w, h = panel_img.size
panel = round_corners(panel_img, CORNER_R)
panel = add_border(panel, BORDER_COLOR, BORDER_W, CORNER_R)
shadow = make_shadow(w, h, CORNER_R)
spread = SHADOW_BLUR * 2
sx = x + SHADOW_OFFSET[0] - spread
sy = y + SHADOW_OFFSET[1] - spread
canvas.paste(shadow, (sx, sy), shadow)
canvas.paste(panel, (x, y), panel)
ASPECT_RATIO = (4, 3)
MAX_DIMENSION = 1200
def main():
popup = Image.open(DOCS / "reddit-extension-popup.png")
disabled = Image.open(DOCS / "reddit-extension-disabled.png")
enabled = Image.open(DOCS / "reddit-extension-enabled.png")
popup_scale = POPUP_COL_W / popup.width
popup_natural_h = round(popup.height * popup_scale)
right_col_w = 700
right_panel_h = (popup_natural_h - GAP) // 2
content_h = popup_natural_h
content_w = POPUP_COL_W + GAP + right_col_w
shadow_extra = max(SHADOW_OFFSET[0], SHADOW_OFFSET[1]) + SHADOW_BLUR
inner_w = content_w + 2 * PAD + shadow_extra
inner_h = content_h + 2 * PAD + shadow_extra
aspect_w, aspect_h = ASPECT_RATIO
target_w = max(inner_w, round(inner_h * aspect_w / aspect_h))
target_h = max(inner_h, round(target_w * aspect_h / aspect_w))
# Clamp to MAX_DIMENSION, scaling down proportionally
if max(target_w, target_h) > MAX_DIMENSION:
scale_down = MAX_DIMENSION / max(target_w, target_h)
target_w = round(target_w * scale_down)
target_h = round(target_h * scale_down)
inner_w = round(inner_w * scale_down)
inner_h = round(inner_h * scale_down)
popup_natural_h = round(popup_natural_h * scale_down)
right_col_w = round(right_col_w * scale_down)
right_panel_h = round(right_panel_h * scale_down)
popup_col_w = round(POPUP_COL_W * scale_down)
pad = round(PAD * scale_down)
gap = round(GAP * scale_down)
else:
popup_col_w = POPUP_COL_W
pad = PAD
gap = GAP
canvas = make_gradient(target_w, target_h).convert("RGBA")
x_off = (target_w - inner_w) // 2
y_off = (target_h - inner_h) // 2
popup_resized = popup.resize((popup_col_w, popup_natural_h), Image.LANCZOS)
paste_panel(canvas, popup_resized, x_off + pad, y_off + pad)
rx = x_off + pad + popup_col_w + gap
paste_panel(canvas, scale_and_crop(disabled, right_col_w, right_panel_h), rx, y_off + pad)
paste_panel(canvas, scale_and_crop(enabled, right_col_w, right_panel_h), rx, y_off + pad + right_panel_h + gap)
out = DOCS / "reddit-extension-collage.png"
canvas.convert("RGB").save(out, optimize=True)
print(f"Saved {out} ({target_w}x{target_h})")
print(f"Popup: {popup_col_w}x{popup_natural_h} (showing 100%)")
print(f"Right panels: {right_col_w}x{right_panel_h} each")
if __name__ == "__main__":
main()

Binary file not shown.

After

Width:  |  Height:  |  Size: 417 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 912 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 709 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

57
lib/presets.js Normal file
View File

@@ -0,0 +1,57 @@
(function () {
"use strict";
function renderPresets(container, presets, onApply, onDelete) {
container.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 () {
onApply(p);
});
var del = document.createElement("span");
del.className = "rt-preset-delete";
del.textContent = "\u00d7";
del.addEventListener("click", function (e) {
e.stopPropagation();
onDelete(index);
});
badge.appendChild(del);
container.appendChild(badge);
})(i);
}
}
async function savePreset(preset, onDone) {
var settings = await window.RedditTweaks.loadSettings();
var presets = settings.colorPresets || [];
presets.push(preset);
settings.colorPresets = presets;
await window.RedditTweaks.saveSettings(settings);
onDone(presets);
}
async function deletePreset(index, onDone) {
var settings = await window.RedditTweaks.loadSettings();
var presets = settings.colorPresets || [];
presets.splice(index, 1);
settings.colorPresets = presets;
await window.RedditTweaks.saveSettings(settings);
onDone(presets);
}
window.RedditTweaks = window.RedditTweaks || {};
window.RedditTweaks.presets = {
render: renderPresets,
save: savePreset,
delete: deletePreset,
};
})();

View File

@@ -76,6 +76,14 @@
return isDarkMode(settings) ? "dark" : "light"; return isDarkMode(settings) ? "dark" : "light";
} }
function applyDarkModeClass(settings) {
if (isDarkMode(settings)) {
document.documentElement.classList.add("rt-dark");
} else {
document.documentElement.classList.remove("rt-dark");
}
}
window.RedditTweaks = window.RedditTweaks || {}; window.RedditTweaks = window.RedditTweaks || {};
window.RedditTweaks.DEFAULTS = DEFAULTS; window.RedditTweaks.DEFAULTS = DEFAULTS;
window.RedditTweaks.loadSettings = loadSettings; window.RedditTweaks.loadSettings = loadSettings;
@@ -83,4 +91,5 @@
window.RedditTweaks.resetSettings = resetSettings; window.RedditTweaks.resetSettings = resetSettings;
window.RedditTweaks.isDarkMode = isDarkMode; window.RedditTweaks.isDarkMode = isDarkMode;
window.RedditTweaks.getColorPrefix = getColorPrefix; window.RedditTweaks.getColorPrefix = getColorPrefix;
window.RedditTweaks.applyDarkModeClass = applyDarkModeClass;
})(); })();

View File

@@ -16,9 +16,10 @@
"content_scripts": [ "content_scripts": [
{ {
"matches": ["*://old.reddit.com/*", "*://www.reddit.com/*"], "matches": ["*://old.reddit.com/*", "*://www.reddit.com/*"],
"css": ["content/styles/badges.css", "content/styles/cards.css", "content/styles/fonts.css"], "css": ["content/styles/badges.css", "content/styles/color-popover.css", "content/styles/color-picker.css", "content/styles/cards.css", "content/styles/fonts.css"],
"js": [ "js": [
"lib/settings.js", "lib/settings.js",
"lib/presets.js",
"content/tweaks/badges.js", "content/tweaks/badges.js",
"content/tweaks/color-picker.js", "content/tweaks/color-picker.js",
"content/tweaks/color-popover.js", "content/tweaks/color-popover.js",

View File

@@ -210,7 +210,7 @@ fieldset input[type="text"] {
cursor: pointer; cursor: pointer;
} }
/* --- Preset badges --- */ /* --- Popup-specific preset bar --- */
.rt-preset-bar { .rt-preset-bar {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -218,272 +218,3 @@ fieldset input[type="text"] {
flex-wrap: wrap; flex-wrap: wrap;
margin-top: 4px; margin-top: 4px;
} }
.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;
}
/* --- Color Picker Widget --- */
.rt-color-picker {
margin: 8px 0;
font-size: 12px;
}
.rt-picker-tabs {
display: flex;
border-bottom: 1px solid #ccc;
margin-bottom: 8px;
}
.rt-picker-tab {
flex: 1;
padding: 4px 8px;
background: none;
border: none;
border-bottom: 2px solid transparent;
font-size: 11px;
font-family: inherit;
cursor: pointer;
color: #666;
}
.rt-picker-tab:hover {
color: #333;
}
.rt-picker-tab--active {
color: #1a1a1a;
border-bottom-color: #4a90d9;
font-weight: 600;
}
.rt-picker-panel {
max-height: 180px;
overflow-y: auto;
}
.rt-picker-grid--palette {
display: flex;
flex-direction: column;
gap: 2px;
}
.rt-picker-grid-row {
display: flex;
gap: 2px;
}
.rt-picker-swatch {
width: 16px;
height: 16px;
border-radius: 2px;
border: 1px solid rgba(0, 0, 0, 0.1);
cursor: pointer;
flex-shrink: 0;
}
.rt-picker-swatch:hover {
border-color: rgba(0, 0, 0, 0.4);
transform: scale(1.2);
z-index: 1;
position: relative;
}
.rt-picker-swatch--selected {
border: 2px solid #1a1a1a;
box-shadow: 0 0 0 1px white;
}
.rt-picker-grid--named {
display: flex;
flex-wrap: wrap;
gap: 4px;
padding: 2px 0;
}
.rt-picker-named-item {
display: flex;
flex-direction: column;
align-items: center;
cursor: pointer;
width: 40px;
}
.rt-picker-named-item .rt-picker-swatch {
width: 24px;
height: 18px;
}
.rt-picker-named-label {
font-size: 8px;
color: #888;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
line-height: 1.2;
}
.rt-picker-panel--custom {
max-height: none;
overflow: visible;
}
.rt-picker-sv-wrap {
position: relative;
margin-bottom: 6px;
}
.rt-picker-sv {
display: block;
width: 100%;
border: 1px solid #ccc;
border-radius: 3px;
cursor: crosshair;
}
.rt-picker-sv-cursor {
position: absolute;
width: 10px;
height: 10px;
border: 2px solid white;
border-radius: 50%;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
pointer-events: none;
transform: translate(-50%, -50%);
}
.rt-picker-hue {
position: relative;
height: 14px;
border: 1px solid #ccc;
border-radius: 3px;
background: linear-gradient(to right, #ff0000, #ffff00, #00ff00, #00ffff, #0000ff, #ff00ff, #ff0000);
cursor: crosshair;
}
.rt-picker-hue-indicator {
position: absolute;
top: -1px;
width: 2px;
height: calc(100% + 2px);
background: white;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
pointer-events: none;
transform: translateX(-50%);
}
/* Picker dark mode */
.rt-dark .rt-picker-tabs,
.rt-color-popover.rt-dark .rt-picker-tabs {
border-bottom-color: #555;
}
.rt-dark .rt-picker-tab,
.rt-color-popover.rt-dark .rt-picker-tab {
color: #999;
}
.rt-dark .rt-picker-tab:hover,
.rt-color-popover.rt-dark .rt-picker-tab:hover {
color: #ccc;
}
.rt-dark .rt-picker-tab--active,
.rt-color-popover.rt-dark .rt-picker-tab--active {
color: #e0e0e0;
border-bottom-color: #4a90d9;
}
.rt-dark .rt-picker-swatch,
.rt-color-popover.rt-dark .rt-picker-swatch {
border-color: rgba(255, 255, 255, 0.1);
}
.rt-dark .rt-picker-swatch:hover,
.rt-color-popover.rt-dark .rt-picker-swatch:hover {
border-color: rgba(255, 255, 255, 0.4);
}
.rt-dark .rt-picker-swatch--selected,
.rt-color-popover.rt-dark .rt-picker-swatch--selected {
border-color: #e0e0e0;
box-shadow: 0 0 0 1px #333;
}
.rt-dark .rt-picker-named-label,
.rt-color-popover.rt-dark .rt-picker-named-label {
color: #777;
}
.rt-dark .rt-picker-sv,
.rt-color-popover.rt-dark .rt-picker-sv {
border-color: #555;
}
.rt-dark .rt-picker-hue,
.rt-color-popover.rt-dark .rt-picker-hue {
border-color: #555;
}

View File

@@ -3,6 +3,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<link rel="stylesheet" href="popup.css"> <link rel="stylesheet" href="popup.css">
<link rel="stylesheet" href="../content/styles/color-picker.css">
</head> </head>
<body> <body>
<div class="popup-container"> <div class="popup-container">
@@ -118,6 +119,7 @@
</div> </div>
</div> </div>
<script src="../lib/settings.js"></script> <script src="../lib/settings.js"></script>
<script src="../lib/presets.js"></script>
<script src="../content/tweaks/color-picker.js"></script> <script src="../content/tweaks/color-picker.js"></script>
<script src="popup.js"></script> <script src="popup.js"></script>
</body> </body>

View File

@@ -4,6 +4,7 @@
var picker = null; var picker = null;
var activeSwatchEl = null; var activeSwatchEl = null;
var saveDebounceTimer = null; var saveDebounceTimer = null;
var subredditEntries = [];
var COLOR_KEYS = [ var COLOR_KEYS = [
"light.subredditBgColor", "light.subredditBgColor",
@@ -20,11 +21,6 @@
"dark.commentsTextColor", "dark.commentsTextColor",
]; ];
function colorToHex(val) {
if (!val) return "#ffffff";
return val;
}
function getColorValue(el) { function getColorValue(el) {
return el.dataset.value || "#ffffff"; return el.dataset.value || "#ffffff";
} }
@@ -78,7 +74,7 @@
COLOR_KEYS.forEach(function (key) { COLOR_KEYS.forEach(function (key) {
var el = document.getElementById(key); var el = document.getElementById(key);
if (el) setColorValue(el, colorToHex(settings[key])); if (el) setColorValue(el, settings[key] || "#ffffff");
}); });
renderSubredditOverrides(settings.subredditColors || {}); renderSubredditOverrides(settings.subredditColors || {});
@@ -87,6 +83,7 @@
function renderSubredditOverrides(overrides) { function renderSubredditOverrides(overrides) {
var list = document.getElementById("subredditList"); var list = document.getElementById("subredditList");
list.innerHTML = ""; list.innerHTML = "";
subredditEntries = [];
Object.entries(overrides).forEach(function (pair) { Object.entries(overrides).forEach(function (pair) {
addSubredditEntry(pair[0], pair[1]); addSubredditEntry(pair[0], pair[1]);
@@ -95,8 +92,8 @@
function addSubredditEntry(name, modeColors) { function addSubredditEntry(name, modeColors) {
var list = document.getElementById("subredditList"); var list = document.getElementById("subredditList");
var entry = document.createElement("div"); var el = document.createElement("div");
entry.className = "subreddit-entry"; el.className = "subreddit-entry";
var header = document.createElement("div"); var header = document.createElement("div");
header.className = "subreddit-entry-header"; header.className = "subreddit-entry-header";
@@ -106,17 +103,7 @@
nameInput.placeholder = "subreddit"; nameInput.placeholder = "subreddit";
nameInput.value = name || ""; nameInput.value = name || "";
var removeBtn = document.createElement("button");
removeBtn.className = "btn-remove";
removeBtn.textContent = "\u00d7";
removeBtn.addEventListener("click", function () {
closePicker();
entry.remove();
saveFromControls();
});
header.appendChild(nameInput); header.appendChild(nameInput);
header.appendChild(removeBtn);
var modeRow = document.createElement("div"); var modeRow = document.createElement("div");
modeRow.className = "subreddit-entry-mode"; modeRow.className = "subreddit-entry-mode";
@@ -134,39 +121,59 @@
var bgSwatch = document.createElement("div"); var bgSwatch = document.createElement("div");
bgSwatch.className = "rt-color-swatch"; bgSwatch.className = "rt-color-swatch";
setColorValue(bgSwatch, colorToHex(lightColors.bg)); setColorValue(bgSwatch, lightColors.bg || "#ffffff");
bgSwatch.title = "Background"; bgSwatch.title = "Background";
var borderSwatch = document.createElement("div"); var borderSwatch = document.createElement("div");
borderSwatch.className = "rt-color-swatch"; borderSwatch.className = "rt-color-swatch";
setColorValue(borderSwatch, colorToHex(lightColors.border)); setColorValue(borderSwatch, lightColors.border || "#ffffff");
borderSwatch.title = "Border"; borderSwatch.title = "Border";
var textSwatch = document.createElement("div"); var textSwatch = document.createElement("div");
textSwatch.className = "rt-color-swatch"; textSwatch.className = "rt-color-swatch";
setColorValue(textSwatch, colorToHex(lightColors.text)); setColorValue(textSwatch, lightColors.text || "#ffffff");
textSwatch.title = "Text"; textSwatch.title = "Text";
colorsDiv.appendChild(bgSwatch); colorsDiv.appendChild(bgSwatch);
colorsDiv.appendChild(borderSwatch); colorsDiv.appendChild(borderSwatch);
colorsDiv.appendChild(textSwatch); colorsDiv.appendChild(textSwatch);
entry._colorState = { var state = {
nameInput: nameInput,
bgSwatch: bgSwatch,
borderSwatch: borderSwatch,
textSwatch: textSwatch,
modeSelect: modeSelect,
colorState: {
light: { bg: getColorValue(bgSwatch), border: getColorValue(borderSwatch), text: getColorValue(textSwatch) }, light: { bg: getColorValue(bgSwatch), border: getColorValue(borderSwatch), text: getColorValue(textSwatch) },
dark: { bg: colorToHex(darkColors.bg), border: colorToHex(darkColors.border), text: colorToHex(darkColors.text) }, dark: { bg: darkColors.bg || "#ffffff", border: darkColors.border || "#ffffff", text: darkColors.text || "#ffffff" },
}; },
entry._hadOverride = { hadOverride: {
light: !!(modeColors && modeColors.light), light: !!(modeColors && modeColors.light),
dark: !!(modeColors && modeColors.dark), dark: !!(modeColors && modeColors.dark),
},
dirtyModes: { light: false, dark: false },
}; };
entry._dirtyModes = { light: false, dark: false }; subredditEntries.push(state);
var removeBtn = document.createElement("button");
removeBtn.className = "btn-remove";
removeBtn.textContent = "\u00d7";
removeBtn.addEventListener("click", function () {
closePicker();
var idx = subredditEntries.indexOf(state);
if (idx !== -1) subredditEntries.splice(idx, 1);
el.remove();
saveFromControls();
});
header.appendChild(removeBtn);
modeSelect.addEventListener("change", function () { modeSelect.addEventListener("change", function () {
closePicker(); closePicker();
var prev = modeSelect.value === "dark" ? "light" : "dark"; var prev = modeSelect.value === "dark" ? "light" : "dark";
entry._dirtyModes[prev] = true; state.dirtyModes[prev] = true;
entry._colorState[prev] = { bg: getColorValue(bgSwatch), border: getColorValue(borderSwatch), text: getColorValue(textSwatch) }; state.colorState[prev] = { bg: getColorValue(bgSwatch), border: getColorValue(borderSwatch), text: getColorValue(textSwatch) };
var next = entry._colorState[modeSelect.value]; var next = state.colorState[modeSelect.value];
setColorValue(bgSwatch, next.bg); setColorValue(bgSwatch, next.bg);
setColorValue(borderSwatch, next.border); setColorValue(borderSwatch, next.border);
setColorValue(textSwatch, next.text); setColorValue(textSwatch, next.text);
@@ -175,7 +182,7 @@
[bgSwatch, borderSwatch, textSwatch].forEach(function (swatch) { [bgSwatch, borderSwatch, textSwatch].forEach(function (swatch) {
swatch.addEventListener("click", function () { swatch.addEventListener("click", function () {
openPickerForSwatch(swatch, function () { openPickerForSwatch(swatch, function () {
entry._dirtyModes[modeSelect.value] = true; state.dirtyModes[modeSelect.value] = true;
}); });
}); });
}); });
@@ -184,39 +191,32 @@
saveFromControls(); saveFromControls();
}); });
entry.appendChild(header); el.appendChild(header);
entry.appendChild(modeRow); el.appendChild(modeRow);
entry.appendChild(colorsDiv); el.appendChild(colorsDiv);
entry._nameInput = nameInput; list.appendChild(el);
entry._bgSwatch = bgSwatch;
entry._borderSwatch = borderSwatch;
entry._textSwatch = textSwatch;
entry._modeSelect = modeSelect;
list.appendChild(entry);
} }
function collectSubredditOverrides() { function collectSubredditOverrides() {
var overrides = {}; var overrides = {};
var entries = document.querySelectorAll(".subreddit-entry"); subredditEntries.forEach(function (state) {
entries.forEach(function (entry) { var name = state.nameInput.value.trim().replace(/^r\//, "");
var name = entry._nameInput.value.trim().replace(/^r\//, "");
if (!name) return; if (!name) return;
var currentMode = entry._modeSelect.value; var currentMode = state.modeSelect.value;
entry._colorState[currentMode] = { state.colorState[currentMode] = {
bg: getColorValue(entry._bgSwatch), bg: getColorValue(state.bgSwatch),
border: getColorValue(entry._borderSwatch), border: getColorValue(state.borderSwatch),
text: getColorValue(entry._textSwatch), text: getColorValue(state.textSwatch),
}; };
var entryOverrides = {}; var entryOverrides = {};
if (entry._hadOverride.light || entry._dirtyModes.light) { if (state.hadOverride.light || state.dirtyModes.light) {
entryOverrides.light = entry._colorState.light; entryOverrides.light = state.colorState.light;
} }
if (entry._hadOverride.dark || entry._dirtyModes.dark) { if (state.hadOverride.dark || state.dirtyModes.dark) {
entryOverrides.dark = entry._colorState.dark; entryOverrides.dark = state.colorState.dark;
} }
if (entryOverrides.light || entryOverrides.dark) { if (entryOverrides.light || entryOverrides.dark) {
@@ -227,60 +227,31 @@
} }
function renderPopupPresets(presets) { function renderPopupPresets(presets) {
var list = document.getElementById("presetList"); window.RedditTweaks.presets.render(
list.innerHTML = ""; document.getElementById("presetList"),
for (var i = 0; i < presets.length; i++) { presets,
(function (index) { function (p) {
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 to light subreddit colors";
badge.addEventListener("click", function () {
setColorValue(document.getElementById("light.subredditBgColor"), p.bg); setColorValue(document.getElementById("light.subredditBgColor"), p.bg);
setColorValue(document.getElementById("light.subredditBorderColor"), p.border); setColorValue(document.getElementById("light.subredditBorderColor"), p.border);
setColorValue(document.getElementById("light.subredditTextColor"), p.text); setColorValue(document.getElementById("light.subredditTextColor"), p.text);
closePicker(); closePicker();
saveFromControls(); saveFromControls();
}); },
function (index) {
var del = document.createElement("span"); window.RedditTweaks.presets.delete(index, renderPopupPresets);
del.className = "rt-preset-delete";
del.textContent = "\u00d7";
del.addEventListener("click", function (e) {
e.stopPropagation();
deletePopupPreset(index);
});
badge.appendChild(del);
list.appendChild(badge);
})(i);
} }
);
} }
async function addPopupPreset() { function addPopupPreset() {
var preset = { window.RedditTweaks.presets.save(
{
bg: getColorValue(document.getElementById("light.subredditBgColor")), bg: getColorValue(document.getElementById("light.subredditBgColor")),
border: getColorValue(document.getElementById("light.subredditBorderColor")), border: getColorValue(document.getElementById("light.subredditBorderColor")),
text: getColorValue(document.getElementById("light.subredditTextColor")), text: getColorValue(document.getElementById("light.subredditTextColor")),
}; },
var settings = await window.RedditTweaks.loadSettings(); renderPopupPresets
var presets = settings.colorPresets || []; );
presets.push(preset);
settings.colorPresets = presets;
await window.RedditTweaks.saveSettings(settings);
renderPopupPresets(presets);
}
async function deletePopupPreset(index) {
var settings = await window.RedditTweaks.loadSettings();
var presets = settings.colorPresets || [];
presets.splice(index, 1);
settings.colorPresets = presets;
await window.RedditTweaks.saveSettings(settings);
renderPopupPresets(presets);
} }
async function saveFromControls() { async function saveFromControls() {