Centralize repo and feed logic while polishing setup and themes#32
Conversation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Swap all shadcn/ui Zinc (#09090B / #FAFAFA / #F4F4F5) color tokens to Stone (#1C1917 / #FAFAF9 / #F5F5F4) for a warmer cream/charcoal theme in both light and dark modes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Change .header-stats from column to row layout with a centered dot separator, and update .header-refresh-btn to a bordered button style with explicit 32x32 dimensions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove muted background/padding from .filters container and change active tab from popped-out white card to filled dark pill style using --ring color. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add border, border-radius, margin, and overflow:hidden to .repo-group for a card appearance. Remove sticky positioning from .repo-group-header. Tighten activity-item padding and remove border on last child. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace per-type colored backgrounds with a unified zinc pill badge using a colored dot via ::before pseudo-element. Add missing .activity-description style for truncated description text. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use var(--background) instead of var(--muted) for .list-header to blend with the main content area in the Stone theme. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Update icon test assertions for Lucide stroke-based SVGs (viewBox, fill→stroke, polygon/circle element types) - Replace old Octicon warning icon in error-handler.js with Lucide - Add missing width/height to onboarding info-icon SVG - Use var(--background) instead of literal white for filter active state - Remove redundant dark-mode filter override All 779 tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add Google Fonts Inter import to popup.html - Category icons: replace solid colored squares with zinc cards with colored left border accents (matching dot badge pattern) - Welcome feature items: larger icon containers, muted bg, hover - Tip items on completion: icon containers, muted bg, hover, tighter - Completion heading: uppercase label style for "What's next?" - Step headings: 20px/700 weight for more presence - Overall: more consistent card treatment across all onboarding steps Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… lift Major visual overhaul of onboarding flow: - Remove borders everywhere, use muted bg fills instead (modern look) - Larger border-radius (12px) on all card surfaces - Bigger headings (22px/700), tighter letter-spacing (-0.04em) - Icon containers with box-shadow instead of borders - Hover lift effect (translateY -1px + shadow) on all interactive cards - Cubic-bezier easing on all transitions for smoother feel - Category icons back to solid color backgrounds (40x40, 10px radius) - Tips use 36x36 icon containers with shadow - Buttons get shadow + hover lift treatment - Progress bar thinner (2px), smoother animation - More whitespace throughout (24px padding, bigger gaps) - Add Inter font import to popup.html Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- onboarding-view: overflow hidden, flex column layout - onboarding-content: flex-grow to fill space, vertically centered - onboarding-nav: margin-top auto to anchor at bottom - Token step: grid layout for input group, tighter padding - Security note: smaller text (11px), less margin - Progress section: less bottom margin - Headings: tighter margins (6px/12px) - Repo suggestions: slightly smaller padding - All steps now fit within 600px without scrolling Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dark mode options fixes: - Tone down --link-color from #FAFAFA to #D4D4D8 in dark mode - Replace all hardcoded 'color: white' with 'color: var(--background)' for step numbers, badges, primary buttons, chips, action buttons - Elements no longer blast bright white against dark backgrounds Popup token step fixes: - Token input + Copy button now sit on one row (was stacked 4 rows) - Hidden redundant hint text (placeholder covers it) - Tightened margins on instructions, input group, security note - Thinner stroke-width (1.75) on footer and toolbar icons Add GitHub OAuth client ID (Ov23li7Mi1dWEgmZTOzB) to config — public client IDs are safe to expose per OAuth spec (Device Flow doesn't use a client secret). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…gradient variable
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Unread indicator: rgba(211,162,75) → var(--accent-color) - Error messages: hardcoded #FEF2F2/#991B1B/#FECACA → var(--error-*) - Success border: hardcoded #BBF7D0 → var(--success-border) - Activity toggle: hardcoded #10b981 → var(--primary) - Removed redundant body.dark-mode overrides for error/success states Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…solution CSS custom properties with var() references on :root resolve against :root's own values, and the computed result is inherited — ignoring body's theme overrides. Moving aliases like --text-primary: var(--foreground) to body ensures they resolve against the active theme's values. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
isConfiguredClientId was checking `clientId !== OAUTH_CONFIG.CLIENT_ID` which blocked the built-in default from being used. The intent was to detect a missing ID, not a default one — so drop that condition. Updated the test to confirm the default client ID flows through to the fetch call rather than rejecting early.
After the device code arrives, try navigator.clipboard.writeText so the user can just paste it on the GitHub tab without having to read and type it manually. Falls back to the old "Enter X on GitHub" message if the clipboard API isn't available.
The old top-level setInterval was leaking if the page ever got torn down and had no way to react instantly when snooze state changed. Replaced it with setupSnoozedReposAutoRefresh() that wires up a storage.onChanged listener for immediate updates alongside the 5-min interval tick, and cleans everything up on beforeunload.
The theme init was buried in chained .then() calls which made the sequencing hard to follow. Pulled it into an async function so the flow reads top-to-bottom: load theme → save if missing → apply → load color theme → update icon.
Headings and descriptions were a bit generic. Updated welcome screen,
token step, and repos step text to be more specific about what the
extension does. Changed button labels from "Next/Get Started" to
"Continue/Open Feed" to better reflect what each step transitions to.
Updated onboarding tests to match renamed status strings
("sign-in" -> "connection").
The test was skipped with a TODO about Jest 30 timer handling. Switched from advanceTimersByTime to advanceTimersByTimeAsync so async callbacks inside the timer fire correctly, which is what Jest 30 requires.
cleanupCache() was only called during periodic maintenance, so entries could linger between renders and skew the deduplication logic. Calling it at the top of render() ensures the cache reflects actual current state before generating HTML.
Switching moduleResolution from "node" to "bundler" better matches how the extension's ES modules are actually resolved at runtime. Also bumps the lockfile version to match the 1.0.3 package.json.
The Inter import added earlier loads from fonts.googleapis.com, but the CSP only allowed 'self' for styles and fonts. Added both domains so the browser doesn't block the stylesheet and font file requests.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Code Review
This pull request introduces a comprehensive refactoring of the extension's architecture and a major UI overhaul. Key improvements include the centralization of state management, repository resolution, and feed policies into shared modules, alongside the implementation of a new theme system and Lucide-style icons. The update also enhances the onboarding experience and significantly expands the test suite. Review feedback correctly identified a critical bug in the settings persistence logic that could overwrite user data with defaults and highlighted the need for additional permissions to ensure reliable clipboard functionality in Manifest V3.
| * @returns {Promise<void>} | ||
| */ | ||
| export async function updateSettings(updates) { | ||
| await setWatchedRepos(updates.watchedRepos); | ||
| await setSyncItem('lastCheck', updates.lastCheck); | ||
| await setSyncItem('filters', updates.filters); | ||
| await setSyncItem('notifications', updates.notifications); | ||
| await setSyncItem('mutedRepos', updates.mutedRepos); | ||
| await setSyncItem('snoozedRepos', updates.snoozedRepos); | ||
| await setSyncItem('checkInterval', updates.checkInterval); | ||
| await setSyncItem('theme', updates.theme); | ||
| if ('watchedRepos' in updates) { | ||
| await setWatchedRepos(updates.watchedRepos); | ||
| } | ||
|
|
||
| if ('lastCheck' in updates) { | ||
| await setSyncItem('lastCheck', updates.lastCheck); | ||
| } | ||
|
|
||
| const syncSettings = pickSyncSettings(updates); | ||
| const syncKeys = Object.keys(syncSettings); | ||
|
|
||
| for (const key of syncKeys) { | ||
| await setSyncItem(key, syncSettings[key]); |
There was a problem hiding this comment.
The current implementation of updateSettings has a critical bug: it uses pickSyncSettings(updates), which internally calls normalizeSettings. This causes any keys missing from the updates object to be filled with default values, effectively overwriting the user's existing settings with defaults whenever a single setting is updated.
export async function updateSettings(updates) {
const syncUpdates = {};
if ('watchedRepos' in updates) {
await setWatchedRepos(updates.watchedRepos);
}
if ('lastCheck' in updates) {
syncUpdates.lastCheck = updates.lastCheck;
}
SETTINGS_SYNC_KEYS.forEach(key => {
if (key in updates) {
syncUpdates[key] = updates[key];
}
});
if (Object.keys(syncUpdates).length > 0) {
await new Promise((resolve, reject) => {
if (typeof chrome === 'undefined' || !chrome.storage) {
resolve();
return;
}
chrome.storage.sync.set(syncUpdates, () => {
if (chrome.runtime.lastError) {
reject(new Error(chrome.runtime.lastError.message));
} else {
resolve();
}
});
});
}
}References
- When fixing specific bugs, defer larger, unrelated refactorings to a follow-up task to keep the pull request focused.
| const copied = navigator.clipboard?.writeText | ||
| ? await navigator.clipboard.writeText(userCode).then(() => true).catch(() => false) | ||
| : false; |
There was a problem hiding this comment.
Using navigator.clipboard.writeText within an asynchronous callback (like the device auth response) is unreliable in Chrome extensions without the clipboardWrite permission. In Manifest V3, clipboard access typically requires a recent user gesture; since this call happens after a network request, the gesture may have expired. It is recommended to add "clipboardWrite" to the permissions array in manifest.json to ensure this feature works consistently.
jonmartin721
left a comment
There was a problem hiding this comment.
Reviewed the current head commit 2722682.
The blocking settings persistence regression is fixed: partial updateSettings() calls no longer overwrite unrelated preferences with defaults, and there is now a regression test covering that behavior.
I do not have any remaining blocking code-review findings on the current diff.
Residual note: the PR is still marked as draft, so it still needs to be switched to ready for review before merge.
Summary
This branch pulls a few related cleanup tracks into one pass so the extension behaves more consistently day to day.
Why
A lot of this code had started to drift into similar-but-not-quite-the-same implementations. Repo input handling lived in multiple places, settings defaults were scattered, and feed actions like marking read or clearing archive were split between UI code and background listeners. The result worked, but it made follow-up changes harder and raised the odds of onboarding, popup, and settings getting out of sync.
This pass tightens that up while also finishing the theme work so the extension looks and feels like one product instead of a few separate surfaces.
Testing
npm test -- --runInBandnpm run lintnpm run typecheckNotes