Skip to content

feat(extension): rewrite browser extension with WXT + SolidJS#327

Merged
SuperCoolPencil merged 61 commits intomainfrom
rewrite-extension
Apr 11, 2026
Merged

feat(extension): rewrite browser extension with WXT + SolidJS#327
SuperCoolPencil merged 61 commits intomainfrom
rewrite-extension

Conversation

@SuperCoolPencil
Copy link
Copy Markdown
Member

@SuperCoolPencil SuperCoolPencil commented Apr 5, 2026

Single codebase for Chrome and Firefox, replacing duplicated
extension-chrome/ and extension-firefox/ directories.

  • WXT (wxt.dev) for cross-browser MV2/MV3 builds
  • SolidJS for reactive popup UI (replaces manual DOM manipulation)
  • TypeScript for full type safety across API contracts
  • SSE support via /events endpoint for real-time progress
  • Background service worker with typed message handling

Greptile Summary

This PR rewrites the browser extension from two separate duplicated codebases (extension-chrome/, extension-firefox/) into a single WXT + SolidJS codebase with TypeScript, SSE support, and a typed message-passing architecture between background service worker and popup UI. The scope is large but the implementation is well-structured: many issues flagged in previous review rounds have been addressed (missing store imports, SSE startup race, health-check reconnection loop, history view rendering, auth error propagation).

Confidence Score: 5/5

PR is safe to merge; all remaining findings are minor style/duplication concerns with no blocking correctness issues.

The substantial issues raised in earlier review rounds (SSE startup race, health-check reconnection loop, missing store imports, history view breakage, auth-error propagation, setAuthVerified always-true, HistoryEntry progress crash) are all resolved in the current code. The only new finding is a trivial action-button rendering duplication (P2). The CI pipeline is comprehensive with lint, type-check, unit tests, dual-browser builds, and a 10-step live integration test.

extension/entrypoints/popup/components/DownloadItem.tsx — minor duplicate action rendering at lines 174–186.

Important Files Changed

Filename Overview
extension/entrypoints/background.ts Core background service worker — SSE stream, health-check reconnection, duplicate-download persistence, and download interception are all present and correctly wired; startup SSE, health loop, and auth-error propagation issues from earlier review rounds appear resolved.
extension/entrypoints/popup/App.tsx Main popup component — store imports, SSE message handling, type guards, and settings loading are all correct in the current version; missing import and type-error issues from earlier rounds appear addressed.
extension/entrypoints/popup/components/DownloadList.tsx History view now correctly imports historyDownloads from the store and maps entries via mapHistoryEntryToDownload (including a computed progress field) before passing them to DownloadItem; previous breakage is resolved.
extension/entrypoints/popup/components/DownloadItem.tsx Download row renderer — renderActions helper is defined but the expanded non-completed view duplicates the same JSX inline rather than calling renderActions(false); minor code duplication.
extension/lib/background-logic.ts Well-tested pure utility layer covering port scan, SSE header building, pending-duplicate management, and download body construction; correctly separated from browser API calls.
extension/entrypoints/popup/components/SettingsView.tsx Settings panel — setAuthVerified always-true persistence bug from earlier rounds is fixed; token save now only writes VERIFIED: true when res?.ok is truthy (line 72).
extension/entrypoints/popup/store/index.ts SolidJS signal store with correct reconcileActiveDownloads diffing, SSE event handlers, and clean separation of active/history/settings signals.
extension/entrypoints/popup/lib/utils.ts formatETA correctly checks > 604800 before > 86400, making both branches reachable; earlier ordering concern is resolved.
extension/wxt.config.ts Clean cross-browser manifest config using WXT's per-browser manifest factory; Firefox-specific CSP and gecko ID are correctly scoped.
.github/workflows/extension.yml Comprehensive CI pipeline covering lint, type-check, unit tests, dual-browser build, and a 10-step integration test against a live Surge backend; well structured.

Sequence Diagram

sequenceDiagram
    participant B as Browser Downloads API
    participant BG as Background (SW)
    participant Surge as Surge HTTP API
    participant SSE as /events SSE Stream
    participant Popup as Popup UI (SolidJS)

    B->>BG: onCreated (download intercepted)
    BG->>B: downloads.cancel + erase
    BG->>Surge: GET /health
    BG->>Surge: GET /list (duplicate check)
    alt Duplicate URL
        BG->>BG: queueDuplicateDownload()
        BG->>Popup: sendMessage(promptDuplicate)
        Popup->>BG: sendMessage(confirmDuplicate / skipDuplicate)
    end
    BG->>Surge: POST /download {url, filename, headers, path}

    note over BG,SSE: SSE stream lifecycle
    BG->>SSE: GET /events (persistent)
    SSE-->>BG: event: progress / started / complete / error
    BG->>Popup: sendMessage(sseEvent)
    Popup->>Popup: handleSseEvent() → signal update

    note over BG,Popup: Periodic sync fallback (60s)
    BG->>Surge: GET /list + GET /history
    BG->>Popup: sendMessage(syncUpdate)
Loading

Comments Outside Diff (1)

  1. .github/workflows/extension-integration.yml, line 232-238 (link)

    P1 Integration test step 7 fails when server correctly returns 404

    curl -sf treats HTTP 4xx as an error exit (code 22). With set -euo pipefail at the top of the script, when /download?id=nonexistent correctly returns 404, curl exits non-zero and the entire script terminates before echo "PASS". The test always fails in CI against a correctly behaving backend.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: .github/workflows/extension-integration.yml
    Line: 232-238
    
    Comment:
    **Integration test step 7 fails when server correctly returns 404**
    
    `curl -sf` treats HTTP 4xx as an error exit (code 22). With `set -euo pipefail` at the top of the script, when `/download?id=nonexistent` correctly returns 404, `curl` exits non-zero and the entire script terminates before `echo "PASS"`. The test always fails in CI against a correctly behaving backend.
    
    
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: extension/entrypoints/popup/components/DownloadItem.tsx
Line: 174-186

Comment:
**Duplicate action button rendering**

`renderActions` (defined at line 113) is only called for the compact completed view. The expanded non-completed view at lines 174–186 re-implements the exact same JSX — same container class, same `onClick`, same `For`/`button` structure — without calling `renderActions(false)`. Any future change to button markup needs to be applied in two places.

```suggestion
        {renderActions(false)}
```

**Rule Used:** What: Eliminate duplicate logic, functions, or cod... ([source](https://app.greptile.com/review/custom-context?memory=f0a796a9-684f-4dfb-a5cf-8c99c16b63a1))

How can I resolve this? If you propose a fix, please make it concise.

Reviews (21): Last reviewed commit: "feat: implement background SSE retry log..." | Re-trigger Greptile

Comment thread extension/entrypoints/popup/App.tsx Fixed
Comment thread extension/entrypoints/popup/App.tsx Fixed
Comment thread extension/entrypoints/background.ts Fixed
Comment thread extension/entrypoints/background.ts Fixed
Comment thread extension/entrypoints/background.ts Fixed
Comment thread extension/entrypoints/background.ts Fixed
Comment thread extension/entrypoints/background.ts Fixed
Comment thread extension/entrypoints/popup/App.tsx Outdated
Comment thread extension/entrypoints/popup/components/DownloadList.tsx Outdated
Comment thread extension/entrypoints/background.ts Outdated
Comment thread extension/entrypoints/popup/lib/utils.ts Outdated
Comment thread extension/entrypoints/popup/lib/api.ts Outdated
Comment thread extension/entrypoints/popup/App.tsx Outdated
Comment thread extension/entrypoints/popup/store/index.ts Outdated
Comment thread extension/entrypoints/popup/App.tsx Fixed
Comment thread extension/entrypoints/popup/components/DownloadList.tsx Fixed
Comment thread extension/entrypoints/popup/App.tsx
Comment thread extension/entrypoints/popup/components/SettingsView.tsx Outdated
Comment thread extension/entrypoints/popup/App.tsx Outdated
Comment thread extension/entrypoints/background.ts Outdated
Comment thread extension/entrypoints/background.ts Fixed
@SuperCoolPencil
Copy link
Copy Markdown
Member Author

@greptile review

Comment thread extension/entrypoints/background.ts Outdated
Comment thread extension/entrypoints/popup/components/DownloadList.tsx Outdated
Comment thread extension/entrypoints/popup/components/DuplicateModal.tsx
Comment thread extension/entrypoints/background.ts Outdated
Comment thread extension/entrypoints/background.ts
@SuperCoolPencil SuperCoolPencil linked an issue Apr 6, 2026 that may be closed by this pull request
4 tasks
Comment thread extension/entrypoints/background.ts
SuperCoolPencil and others added 17 commits April 6, 2026 19:01
Single codebase for Chrome and Firefox, replacing duplicated
extension-chrome/ and extension-firefox/ directories.

- WXT (wxt.dev) for cross-browser MV2/MV3 builds
- SolidJS for reactive popup UI (replaces manual DOM manipulation)
- TypeScript for full type safety across API contracts
- SSE support via /events endpoint for real-time progress
- Background service worker with typed message handling

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Build both Chrome and Firefox extensions from the single extension/
directory using WXT instead of zipping the old separate directories.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
The extension packaging script now requires npm to build with WXT.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Add eslint.config.js, lint/check/test scripts to package.json, and a new
GitHub workflow (.github/workflows/extension.yml) that runs lint and
type-check on PRs/pushes touching extension/**.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Re-add _healthCheckTimer/_syncIntervalTimer declarations that were
accidentally removed, and restore serverUrl import in SettingsModal.
Also remove unused API_BASE constant.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Embed the ViewSwitch (active/history) directly inside the download list,
making it part of the scrollable content area instead of the section
header. Fix broken .toggle-row:hover and .toggle CSS rules.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
…ing, comments

- Fix getBaseUrl() reconnect deadlock by always probing cached URL instead of gating on stale isConnected flag
- Fix integration test step 7 curl to properly expect 404 status code
- Combine EXIT traps to prevent Surge server process leak
- Reduce popup polling from 1s to 15s to avoid redundant requests alongside SSE
- Replace section comments in store with meaningful why/context comments

Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Prefix unused _removePendingDuplicate to silence ESLint warning
- Add type assertions to all browser.runtime.sendMessage returns
- Fix browser.downloads.onCreated and storage.onChanged type incompatibilities
- Cast browser.runtime.getURL to allow empty path argument
- Cast vite-plugin-solid plugins as any to resolve duplicate Vite types
- Normalize HistoryEntry to DownloadStatus shape with computed progress/speed/eta
- Add handleViewChange to eagerly fetch history on tab switch
- Extract sortDownloads() to avoid double evaluation per render
- Fix indentation of confirmDuplicate case in handleMessage

Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Add npx wxt prepare to CI lint and type-check jobs since .wxt/ is gitignored
  and tsc cannot find the generated tsconfig without it
- Wrap items in createMemo for reactive updates in DownloadList
- Initialize pendingDuplicateCounter from persisted IDs after rehydration
- Fix unreachable >1 week branch in formatETA

Co-Authored-By: Claude Opus 4.6 <[email protected]>
…ads immediately and implement exponential backoff for SSE stream retries
Comment thread extension/entrypoints/popup/App.tsx
Comment thread extension/entrypoints/popup/App.tsx Fixed
SuperCoolPencil and others added 2 commits April 11, 2026 05:28
…tion or class'

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Comment thread extension/entrypoints/background.ts
@SuperCoolPencil SuperCoolPencil merged commit 781b3a7 into main Apr 11, 2026
21 checks passed
@SuperCoolPencil SuperCoolPencil deleted the rewrite-extension branch April 11, 2026 00:48
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.

[Enhancement] Cluttered extension layout

1 participant