Add implementation plan for card layout feature
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
682
docs/superpowers/plans/2026-08-26-card-layout.md
Normal file
682
docs/superpowers/plans/2026-08-26-card-layout.md
Normal file
@@ -0,0 +1,682 @@
|
|||||||
|
# Card Layout Implementation Plan
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** Wrap each old Reddit post in a shadcn-styled card with configurable gap, page background, and independent toggle from badges.
|
||||||
|
|
||||||
|
**Architecture:** Add `rt-card` class to each `div.thing.link` via JS, apply card styling via scoped CSS. Convert internal float layout to flexbox. Shared dark mode detection in `settings.js`. Unified MutationObserver in `main.js` for both badge and card processing.
|
||||||
|
|
||||||
|
**Tech Stack:** Vanilla JS (IIFE modules on `window.RedditTweaks`), plain CSS, Firefox WebExtension manifest v2.
|
||||||
|
|
||||||
|
## Global Constraints
|
||||||
|
|
||||||
|
- old.reddit.com only — `www.reddit.com` is out of scope
|
||||||
|
- No build tools, no frameworks, no npm dependencies
|
||||||
|
- All CSS values for the card must match shadcn/ui's default neutral theme exactly (see spec `docs/superpowers/specs/2026-08-26-card-layout-design.md` for token table)
|
||||||
|
- Modules use IIFE pattern and expose API on `window.RedditTweaks`
|
||||||
|
- Imports at top of file, no inline imports
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 1: Extract shared utilities to `settings.js`
|
||||||
|
|
||||||
|
Move `isDarkMode()` and `getColorPrefix()` from `badges.js` into `settings.js` as shared infrastructure. Add new setting defaults. Update all consumers.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `lib/settings.js`
|
||||||
|
- Modify: `content/tweaks/badges.js`
|
||||||
|
- Modify: `content/tweaks/color-popover.js`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Produces: `window.RedditTweaks.isDarkMode(settings)` → `boolean`
|
||||||
|
- Produces: `window.RedditTweaks.getColorPrefix(settings)` → `"light" | "dark"`
|
||||||
|
- Produces: New DEFAULTS keys: `badgeLayout` (true), `cardLayout` (true), `cardBackground` ("reddit"), `cardGap` (16)
|
||||||
|
|
||||||
|
- [ ] **Step 1: Add dark mode functions and new defaults to `settings.js`**
|
||||||
|
|
||||||
|
Add `isDarkMode`, `getColorPrefix`, and new setting keys to `lib/settings.js`. Insert after the `resetSettings` function, before the `window.RedditTweaks` assignment:
|
||||||
|
|
||||||
|
```js
|
||||||
|
function isDarkMode(settings) {
|
||||||
|
if (settings.darkMode === "light") return false;
|
||||||
|
if (settings.darkMode === "dark") return true;
|
||||||
|
return document.body.classList.contains("res-nightmode");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getColorPrefix(settings) {
|
||||||
|
return isDarkMode(settings) ? "dark" : "light";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Add to `DEFAULTS`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
badgeLayout: true,
|
||||||
|
cardLayout: true,
|
||||||
|
cardBackground: "reddit",
|
||||||
|
cardGap: 16,
|
||||||
|
```
|
||||||
|
|
||||||
|
Add to the `window.RedditTweaks` assignment:
|
||||||
|
|
||||||
|
```js
|
||||||
|
window.RedditTweaks.isDarkMode = isDarkMode;
|
||||||
|
window.RedditTweaks.getColorPrefix = getColorPrefix;
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Update `badges.js` to use shared functions**
|
||||||
|
|
||||||
|
In `content/tweaks/badges.js`:
|
||||||
|
|
||||||
|
Remove the `isDarkMode` and `getColorPrefix` function definitions (lines 8-17).
|
||||||
|
|
||||||
|
Replace all internal calls to `isDarkMode(settings)` with `window.RedditTweaks.isDarkMode(settings)` and `getColorPrefix(settings)` with `window.RedditTweaks.getColorPrefix(settings)`.
|
||||||
|
|
||||||
|
Update the exported API — keep `isDarkMode` and `getColorPrefix` as aliases for backward compatibility during this transition, but point them at the shared versions:
|
||||||
|
|
||||||
|
```js
|
||||||
|
window.RedditTweaks.badgeTweak = {
|
||||||
|
apply: apply,
|
||||||
|
updateColors: updateColors,
|
||||||
|
isDarkMode: window.RedditTweaks.isDarkMode,
|
||||||
|
getColorPrefix: window.RedditTweaks.getColorPrefix,
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Update `color-popover.js` to use shared functions**
|
||||||
|
|
||||||
|
In `content/tweaks/color-popover.js`, change the `getHelpers()` function (lines 16-21):
|
||||||
|
|
||||||
|
```js
|
||||||
|
function getHelpers() {
|
||||||
|
return {
|
||||||
|
isDarkMode: window.RedditTweaks.isDarkMode,
|
||||||
|
getColorPrefix: window.RedditTweaks.getColorPrefix,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: Verify extension still works**
|
||||||
|
|
||||||
|
Load the extension in Firefox (`about:debugging` → Load Temporary Add-on). Navigate to `old.reddit.com`. Verify:
|
||||||
|
- Badges render with correct colors
|
||||||
|
- Dark mode detection works (if RES is installed, toggle night mode)
|
||||||
|
- Color popover opens and displays correct mode
|
||||||
|
- No console errors
|
||||||
|
|
||||||
|
- [ ] **Step 5: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add lib/settings.js content/tweaks/badges.js content/tweaks/color-popover.js
|
||||||
|
git commit -m "refactor: extract isDarkMode/getColorPrefix to settings.js as shared utilities"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 2: Badge layout toggle + FOUC CSS rename
|
||||||
|
|
||||||
|
Add `badgeLayout` toggle to the popup and gate badge processing behind it. Rename the FOUC CSS class from `rt-enabled` to `rt-badges-enabled`.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `content/styles/badges.css`
|
||||||
|
- Modify: `content/main.js`
|
||||||
|
- Modify: `popup/popup.html`
|
||||||
|
- Modify: `popup/popup.js`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: `settings.badgeLayout` (boolean, from Task 1 DEFAULTS)
|
||||||
|
- Produces: `html.rt-badges-enabled` class on `<html>` (replaces `html.rt-enabled`)
|
||||||
|
|
||||||
|
- [ ] **Step 1: Rename FOUC selectors in `badges.css`**
|
||||||
|
|
||||||
|
In `content/styles/badges.css`, change the FOUC prevention selectors (lines 1-5):
|
||||||
|
|
||||||
|
From:
|
||||||
|
```css
|
||||||
|
html.rt-enabled .thing.link .tagline .subreddit,
|
||||||
|
html.rt-enabled .thing.link .flat-list li:has(> .comments) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To:
|
||||||
|
```css
|
||||||
|
html.rt-badges-enabled .thing.link .tagline .subreddit,
|
||||||
|
html.rt-badges-enabled .thing.link .flat-list li:has(> .comments) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Update `main.js` to gate badges behind `badgeLayout`**
|
||||||
|
|
||||||
|
Replace the `init()` function in `content/main.js`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
async function init() {
|
||||||
|
const settings = await window.RedditTweaks.loadSettings();
|
||||||
|
if (!settings.enabled) return;
|
||||||
|
|
||||||
|
if (settings.badgeLayout) {
|
||||||
|
document.documentElement.classList.add("rt-badges-enabled");
|
||||||
|
window.RedditTweaks.badgeTweak.apply(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
browser.storage.onChanged.addListener(function (changes) {
|
||||||
|
if (
|
||||||
|
(changes.enabled && changes.enabled.newValue !== changes.enabled.oldValue) ||
|
||||||
|
(changes.badgeLayout && changes.badgeLayout.newValue !== changes.badgeLayout.oldValue)
|
||||||
|
) {
|
||||||
|
location.reload();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.RedditTweaks.loadSettings().then(function (updated) {
|
||||||
|
if (settings.badgeLayout) {
|
||||||
|
window.RedditTweaks.badgeTweak.updateColors(updated);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Add badge toggle to popup HTML**
|
||||||
|
|
||||||
|
In `popup/popup.html`, add a badge toggle after the existing enabled checkbox. Change the first section:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div class="section">
|
||||||
|
<label class="toggle-row">
|
||||||
|
<span>Enabled</span>
|
||||||
|
<input type="checkbox" id="enabled">
|
||||||
|
</label>
|
||||||
|
<label class="toggle-row">
|
||||||
|
<span>Badge layout</span>
|
||||||
|
<input type="checkbox" id="badgeLayout">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: Wire badge toggle in `popup.js`**
|
||||||
|
|
||||||
|
In `popup/popup.js`, add `badgeLayout` to the `populateControls` function:
|
||||||
|
|
||||||
|
```js
|
||||||
|
document.getElementById("badgeLayout").checked = settings.badgeLayout;
|
||||||
|
```
|
||||||
|
|
||||||
|
Add change listener in `init()`, next to the existing `enabled` listener:
|
||||||
|
|
||||||
|
```js
|
||||||
|
document.getElementById("badgeLayout").addEventListener("change", saveFromControls);
|
||||||
|
```
|
||||||
|
|
||||||
|
Add to `saveFromControls()` — in the settings object construction, add:
|
||||||
|
|
||||||
|
```js
|
||||||
|
badgeLayout: document.getElementById("badgeLayout").checked,
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 5: Verify badge toggle works**
|
||||||
|
|
||||||
|
Reload extension. Open popup:
|
||||||
|
- Badge layout checkbox is checked by default
|
||||||
|
- Unchecking it and reloading old.reddit.com: original subreddit/comment links are visible, no badges
|
||||||
|
- Checking it back: badges return after reload
|
||||||
|
- No console errors
|
||||||
|
|
||||||
|
- [ ] **Step 6: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add content/styles/badges.css content/main.js popup/popup.html popup/popup.js
|
||||||
|
git commit -m "feat: add badgeLayout toggle, rename FOUC class to rt-badges-enabled"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 3: Create card CSS
|
||||||
|
|
||||||
|
Create `content/styles/cards.css` with all card styling per the spec. Add it to the manifest.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `content/styles/cards.css`
|
||||||
|
- Modify: `manifest.json`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Produces: CSS rules scoped under `.rt-card`, `html.rt-card-enabled`, `html.rt-card-bg-shadcn`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create `content/styles/cards.css`**
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* --- Post list container --- */
|
||||||
|
html.rt-card-enabled #siteTable {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: var(--rt-card-gap, 1rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.rt-card-enabled #siteTable > .clearleft {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Card container --- */
|
||||||
|
.rt-card {
|
||||||
|
background: #ffffff;
|
||||||
|
color: #0a0a0a;
|
||||||
|
border: 1px solid #e5e5e5;
|
||||||
|
border-radius: 0.875rem;
|
||||||
|
padding: 1.5rem;
|
||||||
|
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||||
|
transition: border-color 0.15s;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 0.75rem;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rt-card:hover {
|
||||||
|
border-color: #d4d4d4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Card internal layout overrides --- */
|
||||||
|
.rt-card .midcol,
|
||||||
|
.rt-card .thumbnail {
|
||||||
|
float: none;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rt-card .entry {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rt-card .clearleft {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Card dark mode --- */
|
||||||
|
.rt-dark .rt-card {
|
||||||
|
background: #171717;
|
||||||
|
color: #fafafa;
|
||||||
|
border-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rt-dark .rt-card:hover {
|
||||||
|
border-color: rgba(255, 255, 255, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Page background override --- */
|
||||||
|
html.rt-card-bg-shadcn .content[role="main"] {
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.rt-card-bg-shadcn.rt-dark .content[role="main"] {
|
||||||
|
background: #262626;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Add `cards.css` to `manifest.json`**
|
||||||
|
|
||||||
|
In the `content_scripts[0].css` array, add `content/styles/cards.css` after `badges.css`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"css": ["content/styles/badges.css", "content/styles/cards.css"],
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Verify no parse errors**
|
||||||
|
|
||||||
|
Reload extension. Open `old.reddit.com`. No console CSS parse errors. Cards don't render yet (no JS adding the `rt-card` class), but the CSS is loaded.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add content/styles/cards.css manifest.json
|
||||||
|
git commit -m "feat: add card CSS with shadcn design tokens"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 4: Create card JS + unified observer
|
||||||
|
|
||||||
|
Create the card tweak module. Refactor `main.js` to use a single shared MutationObserver that dispatches to both badge and card processors. Remove the duplicate observer from `badges.js`.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `content/tweaks/cards.js`
|
||||||
|
- Modify: `content/tweaks/badges.js`
|
||||||
|
- Modify: `content/main.js`
|
||||||
|
- Modify: `manifest.json`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: `window.RedditTweaks.isDarkMode(settings)` (from Task 1)
|
||||||
|
- Consumes: `settings.cardLayout`, `settings.cardBackground`, `settings.cardGap` (from Task 1)
|
||||||
|
- Produces: `window.RedditTweaks.cardTweak.apply(settings)` — processes posts + applies card classes
|
||||||
|
- Produces: `window.RedditTweaks.cardTweak.updateSettings(settings)` — live-update gap/background
|
||||||
|
- Produces: `window.RedditTweaks.cardTweak.processNewPosts(settings)` — process unprocessed posts (called by shared observer)
|
||||||
|
- Modifies: `window.RedditTweaks.badgeTweak` — adds `processNewPosts(settings)` for shared observer, removes internal observer
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create `content/tweaks/cards.js`**
|
||||||
|
|
||||||
|
```js
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function processPost(post) {
|
||||||
|
if (post.classList.contains("rt-card")) return;
|
||||||
|
post.classList.add("rt-card");
|
||||||
|
}
|
||||||
|
|
||||||
|
function processPosts() {
|
||||||
|
var posts = document.querySelectorAll("div.thing.link:not(.rt-card)");
|
||||||
|
for (var i = 0; i < posts.length; i++) {
|
||||||
|
processPost(posts[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyDarkMode(settings) {
|
||||||
|
if (window.RedditTweaks.isDarkMode(settings)) {
|
||||||
|
document.documentElement.classList.add("rt-dark");
|
||||||
|
} else {
|
||||||
|
document.documentElement.classList.remove("rt-dark");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyBackground(settings) {
|
||||||
|
if (settings.cardBackground === "shadcn") {
|
||||||
|
document.documentElement.classList.add("rt-card-bg-shadcn");
|
||||||
|
} else {
|
||||||
|
document.documentElement.classList.remove("rt-card-bg-shadcn");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyGap(settings) {
|
||||||
|
var siteTable = document.getElementById("siteTable");
|
||||||
|
if (siteTable) {
|
||||||
|
siteTable.style.setProperty("--rt-card-gap", settings.cardGap + "px");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function apply(settings) {
|
||||||
|
document.documentElement.classList.add("rt-card-enabled");
|
||||||
|
applyDarkMode(settings);
|
||||||
|
applyBackground(settings);
|
||||||
|
applyGap(settings);
|
||||||
|
processPosts();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSettings(settings) {
|
||||||
|
applyDarkMode(settings);
|
||||||
|
applyBackground(settings);
|
||||||
|
applyGap(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.RedditTweaks = window.RedditTweaks || {};
|
||||||
|
window.RedditTweaks.cardTweak = {
|
||||||
|
apply: apply,
|
||||||
|
updateSettings: updateSettings,
|
||||||
|
processNewPosts: processPosts,
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Expose `processNewPosts` from `badges.js`, remove both internal observers**
|
||||||
|
|
||||||
|
In `content/tweaks/badges.js`:
|
||||||
|
|
||||||
|
Remove the `contentObserver` variable declaration (line 4: `let contentObserver = null;`).
|
||||||
|
|
||||||
|
Remove the `darkModeObserver` variable declaration (line 5: `let darkModeObserver = null;`).
|
||||||
|
|
||||||
|
Remove the `getObserverTarget()` function (lines 106-112).
|
||||||
|
|
||||||
|
Remove the `startContentObserver(settings)` function (lines 114-135).
|
||||||
|
|
||||||
|
Remove the `startDarkModeObserver()` function (lines 137-149).
|
||||||
|
|
||||||
|
Remove both observer calls from the `apply()` function: `startContentObserver(settings)` (line 168) and `startDarkModeObserver()` (line 169).
|
||||||
|
|
||||||
|
Add `processNewPosts` to the exported API:
|
||||||
|
|
||||||
|
```js
|
||||||
|
window.RedditTweaks.badgeTweak = {
|
||||||
|
apply: apply,
|
||||||
|
updateColors: updateColors,
|
||||||
|
isDarkMode: window.RedditTweaks.isDarkMode,
|
||||||
|
getColorPrefix: window.RedditTweaks.getColorPrefix,
|
||||||
|
processNewPosts: processPosts,
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Add unified observer to `main.js`**
|
||||||
|
|
||||||
|
In `content/main.js`, add shared observer functions before `init()`.
|
||||||
|
|
||||||
|
First, the dark mode observer (moved from `badges.js`). This watches for RES night mode toggles and dispatches to both modules:
|
||||||
|
|
||||||
|
```js
|
||||||
|
function startDarkModeObserver(settings) {
|
||||||
|
var observer = new MutationObserver(function () {
|
||||||
|
if (settings.darkMode === "auto") {
|
||||||
|
window.RedditTweaks.loadSettings().then(function (updated) {
|
||||||
|
if (settings.badgeLayout) {
|
||||||
|
window.RedditTweaks.badgeTweak.updateColors(updated);
|
||||||
|
}
|
||||||
|
if (settings.cardLayout) {
|
||||||
|
window.RedditTweaks.cardTweak.updateSettings(updated);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(document.body, {
|
||||||
|
attributes: true,
|
||||||
|
attributeFilter: ["class"],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then the content observer for new posts:
|
||||||
|
|
||||||
|
```js
|
||||||
|
function startPostObserver(settings) {
|
||||||
|
var target =
|
||||||
|
document.getElementById("siteTable") ||
|
||||||
|
document.querySelector('.content[role="main"]') ||
|
||||||
|
document.body;
|
||||||
|
|
||||||
|
var observer = new MutationObserver(function (mutations) {
|
||||||
|
var hasNewPosts = false;
|
||||||
|
for (var i = 0; i < mutations.length; i++) {
|
||||||
|
var added = mutations[i].addedNodes;
|
||||||
|
for (var j = 0; j < added.length; j++) {
|
||||||
|
var node = added[j];
|
||||||
|
if (node.nodeType !== Node.ELEMENT_NODE) continue;
|
||||||
|
if ((node.matches && node.matches("div.thing.link")) ||
|
||||||
|
(node.querySelector && node.querySelector("div.thing.link"))) {
|
||||||
|
hasNewPosts = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasNewPosts) break;
|
||||||
|
}
|
||||||
|
if (hasNewPosts) {
|
||||||
|
window.RedditTweaks.loadSettings().then(function (updated) {
|
||||||
|
if (settings.badgeLayout) {
|
||||||
|
window.RedditTweaks.badgeTweak.processNewPosts(updated);
|
||||||
|
}
|
||||||
|
if (settings.cardLayout) {
|
||||||
|
window.RedditTweaks.cardTweak.processNewPosts(updated);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(target, { childList: true, subtree: true });
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Update `init()` to call `cardTweak.apply`, start the shared observer, and observe dark mode changes for cards:
|
||||||
|
|
||||||
|
```js
|
||||||
|
async function init() {
|
||||||
|
const settings = await window.RedditTweaks.loadSettings();
|
||||||
|
if (!settings.enabled) return;
|
||||||
|
|
||||||
|
if (settings.badgeLayout) {
|
||||||
|
document.documentElement.classList.add("rt-badges-enabled");
|
||||||
|
window.RedditTweaks.badgeTweak.apply(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.cardLayout) {
|
||||||
|
window.RedditTweaks.cardTweak.apply(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.badgeLayout || settings.cardLayout) {
|
||||||
|
startPostObserver(settings);
|
||||||
|
startDarkModeObserver(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
browser.storage.onChanged.addListener(function (changes) {
|
||||||
|
if (
|
||||||
|
(changes.enabled && changes.enabled.newValue !== changes.enabled.oldValue) ||
|
||||||
|
(changes.badgeLayout && changes.badgeLayout.newValue !== changes.badgeLayout.oldValue) ||
|
||||||
|
(changes.cardLayout && changes.cardLayout.newValue !== changes.cardLayout.oldValue)
|
||||||
|
) {
|
||||||
|
location.reload();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.RedditTweaks.loadSettings().then(function (updated) {
|
||||||
|
if (settings.badgeLayout) {
|
||||||
|
window.RedditTweaks.badgeTweak.updateColors(updated);
|
||||||
|
}
|
||||||
|
if (settings.cardLayout) {
|
||||||
|
window.RedditTweaks.cardTweak.updateSettings(updated);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: Add `cards.js` to `manifest.json`**
|
||||||
|
|
||||||
|
In the `content_scripts[0].js` array, add `content/tweaks/cards.js` before `main.js`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"js": [
|
||||||
|
"lib/settings.js",
|
||||||
|
"content/tweaks/badges.js",
|
||||||
|
"content/tweaks/color-picker.js",
|
||||||
|
"content/tweaks/color-popover.js",
|
||||||
|
"content/tweaks/cards.js",
|
||||||
|
"content/main.js"
|
||||||
|
],
|
||||||
|
```
|
||||||
|
|
||||||
|
Also remove `"*://www.reddit.com/*"` from the `matches` array (out of scope per spec):
|
||||||
|
|
||||||
|
```json
|
||||||
|
"matches": ["*://old.reddit.com/*"],
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 5: Verify cards render**
|
||||||
|
|
||||||
|
Reload extension. Navigate to `old.reddit.com`:
|
||||||
|
- Each post is wrapped in a card: white background, 1px `#e5e5e5` border, 14px rounded corners, subtle shadow
|
||||||
|
- Posts are separated by 16px gaps
|
||||||
|
- Vote arrows, thumbnail, and entry are horizontally aligned (no floats)
|
||||||
|
- Hovering a card darkens the border to `#d4d4d4`
|
||||||
|
- Badges still render inside cards (if badge layout is enabled)
|
||||||
|
- No console errors
|
||||||
|
|
||||||
|
- [ ] **Step 6: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add content/tweaks/cards.js content/tweaks/badges.js content/main.js manifest.json
|
||||||
|
git commit -m "feat: add card layout tweak with unified post observer"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 5: Popup controls for card settings
|
||||||
|
|
||||||
|
Add card layout controls to the extension popup: enable toggle, background style dropdown, card spacing dropdown.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `popup/popup.html`
|
||||||
|
- Modify: `popup/popup.js`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: `settings.cardLayout`, `settings.cardBackground`, `settings.cardGap` (from Task 1 DEFAULTS)
|
||||||
|
|
||||||
|
- [ ] **Step 1: Add card layout section to popup HTML**
|
||||||
|
|
||||||
|
In `popup/popup.html`, add this section after the Dark Mode section and before the Light Mode Colors section:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div class="section">
|
||||||
|
<h2>Card Layout</h2>
|
||||||
|
<label class="toggle-row">
|
||||||
|
<span>Enable card layout</span>
|
||||||
|
<input type="checkbox" id="cardLayout">
|
||||||
|
</label>
|
||||||
|
<label for="cardBackground">Background Style</label>
|
||||||
|
<select id="cardBackground">
|
||||||
|
<option value="reddit">Reddit Default</option>
|
||||||
|
<option value="shadcn">shadcn Style</option>
|
||||||
|
</select>
|
||||||
|
<label for="cardGap">Card Spacing</label>
|
||||||
|
<select id="cardGap">
|
||||||
|
<option value="8">8px</option>
|
||||||
|
<option value="12">12px</option>
|
||||||
|
<option value="16">16px</option>
|
||||||
|
<option value="24">24px</option>
|
||||||
|
<option value="32">32px</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Wire card controls in `popup.js`**
|
||||||
|
|
||||||
|
In `populateControls(settings)`, add:
|
||||||
|
|
||||||
|
```js
|
||||||
|
document.getElementById("cardLayout").checked = settings.cardLayout;
|
||||||
|
document.getElementById("cardBackground").value = settings.cardBackground;
|
||||||
|
document.getElementById("cardGap").value = String(settings.cardGap);
|
||||||
|
```
|
||||||
|
|
||||||
|
In `saveFromControls()`, add to the settings object:
|
||||||
|
|
||||||
|
```js
|
||||||
|
cardLayout: document.getElementById("cardLayout").checked,
|
||||||
|
cardBackground: document.getElementById("cardBackground").value,
|
||||||
|
cardGap: parseInt(document.getElementById("cardGap").value, 10),
|
||||||
|
```
|
||||||
|
|
||||||
|
In `init()`, add change listeners:
|
||||||
|
|
||||||
|
```js
|
||||||
|
document.getElementById("cardLayout").addEventListener("change", saveFromControls);
|
||||||
|
document.getElementById("cardBackground").addEventListener("change", saveFromControls);
|
||||||
|
document.getElementById("cardGap").addEventListener("change", saveFromControls);
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Verify settings work end-to-end**
|
||||||
|
|
||||||
|
Reload extension. Open popup:
|
||||||
|
- Card layout checkbox is checked by default
|
||||||
|
- Background dropdown shows "Reddit Default"
|
||||||
|
- Card spacing shows "16px"
|
||||||
|
- Changing card spacing to "24px": cards on old.reddit.com update gap live (no reload)
|
||||||
|
- Changing background to "shadcn Style": page background becomes `#f5f5f5` (live)
|
||||||
|
- Unchecking card layout: page reloads, cards disappear, posts show flat list
|
||||||
|
- Settings persist after closing and reopening popup
|
||||||
|
- Settings persist after browser restart
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add popup/popup.html popup/popup.js
|
||||||
|
git commit -m "feat: add card layout controls to extension popup"
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user