Skip to content

Centralize repo and feed logic while polishing setup and themes#32

Merged
jonmartin721 merged 42 commits into
mainfrom
release-split
Apr 15, 2026
Merged

Centralize repo and feed logic while polishing setup and themes#32
jonmartin721 merged 42 commits into
mainfrom
release-split

Conversation

@jonmartin721

Copy link
Copy Markdown
Owner

Summary

This branch pulls a few related cleanup tracks into one pass so the extension behaves more consistently day to day.

  • moves watched-repo parsing, validation, and normalization into shared helpers used by onboarding and settings
  • adds a shared settings schema plus feed mutation/policy helpers so popup, options, and background logic stop re-implementing the same state rules
  • expands theming with shared color-theme tokens, applies those themes across popup/options/onboarding, and refreshes the onboarding copy and icon treatment
  • rounds out the popup and settings flows with archive clearing, snooze refresh cleanup, better repo input handling, and more focused tests around the new bootstrap and interaction paths
  • refreshes the README so setup, permissions, repo limits, and day-to-day usage match the current extension behavior

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 -- --runInBand
  • npm run lint
  • npm run typecheck

Notes

  • This is a fairly wide branch, but the changes cluster around shared state/theme plumbing plus the UI surfaces that consume it.
  • README screenshots were not regenerated in this branch, so the copy is fresher than the image set in a few spots.

jonmartin721 and others added 30 commits April 13, 2026 22:28
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>
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.
@sentry

sentry Bot commented Apr 14, 2026

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread shared/storage-helpers.js
Comment on lines 420 to +435
* @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]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

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
  1. When fixing specific bugs, defer larger, unrelated refactorings to a follow-up task to keep the pull request focused.

Comment on lines +158 to +160
const copied = navigator.clipboard?.writeText
? await navigator.clipboard.writeText(userCode).then(() => true).catch(() => false)
: false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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
jonmartin721 marked this pull request as ready for review April 15, 2026 21:25

@jonmartin721 jonmartin721 left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@jonmartin721
jonmartin721 merged commit d578a1f into main Apr 15, 2026
5 checks passed
@jonmartin721
jonmartin721 deleted the release-split branch April 15, 2026 21:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant