Repository navigation
feat(otter): Chrome extensions in the browser preview - #15
Merged
Merged
Conversation
The preview's tabs can now run Chrome extensions from the Chrome Web Store, such as 1Password, as Otter Mail's browser does. Electron runs an extension's worker, pages and content scripts; this adds what it lacks: the Web Store's install button (electron-chrome-web-store, MIT), the toolbar's buttons and popups, and chrome.action, the rest of chrome.tabs, chrome.windows, contextMenus, notifications, webNavigation and friends, answered by the main process through a session preload. Every persistent preview partition (one per environment and browser profile) loads the same extensions and keeps its own extension data, as Chrome profiles do. Incognito tabs get none. The preview's toolbar takes Otter Mail's look: pills for navigation, the address, the extensions and the tools. Settings › Browser Extensions lists, switches, inspects and removes them. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Given a User-Agent that names Electron, Google serves a lite sign-in page (flowName=WebLiteSignIn) that never asks for passkeys, such as 1Password's, until you go looking for them. Requests to accounts.google.com now drop the Electron token, so Google serves Chrome's page (GlifWebSignIn), which offers passkeys as it loads. Only those requests change: rewriting the session's whole User-Agent, Electron token removed, still makes Cloudflare Turnstile fail with 600010 (pingdotgg#5002), checked again on Electron 44. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Laurin-Notemann
added a commit
that referenced
this pull request
Oct 4, 2026
* feat(otter): Chrome extensions in the browser preview (#15) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Laurin-Notemann
added a commit
that referenced
this pull request
Oct 5, 2026
* feat(otter): Chrome extensions in the browser preview (#15) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Laurin-Notemann
added a commit
that referenced
this pull request
Oct 5, 2026
* feat(otter): Chrome extensions in the browser preview (#15) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Laurin-Notemann
added a commit
that referenced
this pull request
Oct 5, 2026
* feat(otter): Chrome extensions in the browser preview (#15) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Laurin-Notemann
added a commit
that referenced
this pull request
Oct 6, 2026
* feat(otter): Chrome extensions in the browser preview (#15) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Laurin-Notemann
added a commit
that referenced
this pull request
Oct 7, 2026
* feat(otter): Chrome extensions in the browser preview (#15) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Laurin-Notemann
added a commit
that referenced
this pull request
Oct 8, 2026
* feat(otter): Chrome extensions in the browser preview (#15) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The browser preview couldn't run Chrome extensions, so password managers like 1Password weren't available when signing in to sites in a thread's browser. This brings over Otter Mail's extension support and gives the preview's toolbar Otter Mail's look, keeping everything the preview already does (agent control, annotate, screenshots and recording, picture-in-picture, profiles, cookie import).
How it works
electron-chrome-web-store(MIT), behind a native "Add …?" confirmation that says what the extension can read. Extensions update on their own.preview-extensions-preload.ts) adds them to the extension'schromeandbrowserobjects:chrome.action, the rest ofchrome.tabs,chrome.windows,contextMenus,notifications,webNavigation,commands,permissions, and simpledownloadsandprivacy.preview/extensions/PreviewExtensions.tsanswers those calls in the main process; it's a port of Otter Mail's MIT layer.contextIsolation=falsefor the element picker, and therecontextBridgethrows. The preload puts the Web Store's API on the page's window instead, and installs its own APIs directly on extension pages that load in a tab.flowName=WebLiteSignIn) that never asks for passkeys until you click "Try another way". Requests toaccounts.google.comnow drop theElectron/xtoken, so Google serves Chrome's page (GlifWebSignIn). That page asks for passkeys as it loads (navigator.credentials.get,mediation: "conditional"), and 1Password offers "Use passkey" there. Only those requests change: rewriting the session's whole User-Agent still breaks Cloudflare Turnstile with error 600010 ([Bug]: Cloudflare Turnstile challenge loops indefinitely in the built-in browser preview pingdotgg/t3code#5002). I re-checked this on Electron 44: the stripped session failed on dash.cloudflare.com, while the Google-only rewrite passed it and still got Google's full page with the passkey request.Upstream files change only where the feature hooks in:
getPreviewConfigenables a partition's extensions,DesktopWindowadds the menu items,preload.tsexposes the bridge,PreviewChromeRowis restyled, the overlay carries the tab's webContents id, and Settings gets the new page.Screenshots
Before: the current release's row of ghost buttons with a borderless address field.
After: the toolbar's right end, with 1Password pinned, the Extensions menu, and the tools pill

After: Settings › Browser Extensions

Verified
In a dev build: 1Password installed from the Chrome Web Store (in the Web Store window from Settings) and pinned, and it runs in the preview's toolbar.
tscfor desktop, web and contracts. Lint on every changed file. Tests: 83 web files (preview, settings, sidebar, preview state) and 12 desktop files (IPC, windows, browser session) pass. The third-party license check passes with the new notice.User agent, in a throwaway Electron 44 with each setup side by side:
WebLiteSignInand made no passkey request.GlifWebSignInand a conditional passkey request, but Turnstile error 600010 on dash.cloudflare.com.GlifWebSignIn, the conditional passkey request, no Turnstile error. An email went on to Google's normal next step, with no "browser may not be secure".Not verified yet: "Add to Otter Code" from a Web Store page inside a preview tab, which relies on the context-isolation shim.
Work by Claude Opus 5.5 in Claude Code.
🤖 Generated with Claude Code