Skip to content

Add sidebar mode support#2632

Open
sdfcharles wants to merge 8 commits intostellar:masterfrom
sdfcharles:feature/sidebar-mode
Open

Add sidebar mode support#2632
sdfcharles wants to merge 8 commits intostellar:masterfrom
sdfcharles:feature/sidebar-mode

Conversation

@sdfcharles
Copy link

@sdfcharles sdfcharles commented Mar 4, 2026

This pull request introduces support for a sidebar (or side panel) feature in the browser extension, allowing users to open the extension in a sidebar by default and improving how signing windows are managed. It adds new settings, messaging, and storage logic to enable and control this sidebar behavior across both Chrome and Firefox, updates extension manifests to declare sidebar/side panel support, and refactors popup window logic to leverage the sidebar when available.

Sidebar Feature Implementation

  • Added support for sidebar/side panel in both manifest v2 and v3 by updating manifest/v2.json and manifest/v3.json to declare the sidebar/side_panel, including default panel path and icons. [1] [2]
  • Introduced new service types, message types (SidebarRegisterMessage, SidebarUnregisterMessage), and storage keys to manage sidebar registration and state. [1] [2] [3] [4] [5]
  • Implemented sidebar initialization and behavior logic in the background script, including reading and applying the "open sidebar by default" setting and interacting with Chrome and Firefox APIs. [1] [2] [3] [4] [5]

Settings and Storage Enhancements

  • Added the isOpenSidebarByDefault setting to API types, settings handlers, and storage, ensuring it is loaded, saved, and returned as part of user preferences. [1] [2] [3] [4] [5] [6] [7] [8] [9]

Popup and Signing Window Refactor

  • Refactored the logic for opening signing and authorization windows to use the sidebar/side panel when available, falling back to popups otherwise. This is handled via a new openSigningWindow function and updated message listener logic. [1] [2] [3] [4] [5] [6] [7] [8]

These changes collectively enable a more integrated and configurable sidebar experience for users and streamline how the extension handles interactive flows like signing and authorization.- Route signing flows (grant access, sign tx, sign blob, sign auth entry) through the sidebar when it is open

  • Add SIDEBAR_REGISTER/UNREGISTER handlers to track the active sidebar window
  • Add SidebarSigningListener component so the sidebar can receive and navigate to signing requests
  • Add "Sidebar mode" button in AccountHeader menu to open the sidebar manually
  • Add openSidebar() helper for Chrome (sidePanel API) and Firefox (sidebarAction)
  • Fix isFullscreenMode to exclude sidebar mode
  • Add "Open sidebar mode by default" toggle in Settings > Preferences, persisted to local storage and applied on background startup via chrome.sidePanel.setPanelBehavior

- Route signing flows (grant access, sign tx, sign blob, sign auth entry) through the sidebar when it is open
- Add SIDEBAR_REGISTER/UNREGISTER handlers to track the active sidebar window
- Add SidebarSigningListener component so the sidebar can receive and navigate to signing requests
- Add "Sidebar mode" button in AccountHeader menu to open the sidebar manually
- Add openSidebar() helper for Chrome (sidePanel API) and Firefox (sidebarAction)
- Fix isFullscreenMode to exclude sidebar mode
- Add "Open sidebar mode by default" toggle in Settings > Preferences, persisted to local storage and applied on background startup via chrome.sidePanel.setPanelBehavior

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 4, 2026 17:18
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds “sidebar mode” support to the extension so signing flows can be routed to an already-open sidebar (instead of spawning popup windows), and introduces a user preference to open the sidebar by default.

Changes:

  • Add sidebar open helper + UI entrypoint (“Sidebar mode” menu item) and sidebar-mode detection helpers.
  • Track an active sidebar window in the background and route signing requests by navigating the sidebar when available.
  • Add a persisted “Open sidebar mode by default” preference and apply Chrome side panel behavior on startup; update manifests accordingly.

Reviewed changes

Copilot reviewed 30 out of 30 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
extension/src/popup/views/tests/Swap.test.tsx Update mocked settings to include isOpenSidebarByDefault.
extension/src/popup/views/tests/SignTransaction.test.tsx Update mocked settings to include isOpenSidebarByDefault in multiple test cases.
extension/src/popup/views/tests/ManageAssets.test.tsx Update mocked settings to include isOpenSidebarByDefault.
extension/src/popup/views/tests/GrantAccess.test.tsx Update mocked settings to include isOpenSidebarByDefault.
extension/src/popup/views/tests/AddFunds.test.tsx Update mocked settings to include isOpenSidebarByDefault.
extension/src/popup/views/tests/AccountHistory.test.tsx Update mocked settings/state to include isOpenSidebarByDefault.
extension/src/popup/views/tests/AccountCreator.test.tsx Update mocked settings to include isOpenSidebarByDefault.
extension/src/popup/views/tests/Account.test.tsx Update mocked settings to include isOpenSidebarByDefault in several places.
extension/src/popup/views/Preferences/index.tsx Add “Open sidebar mode by default” toggle and persist it via saveSettings.
extension/src/popup/views/IntegrationTest.tsx Update integration test to pass isOpenSidebarByDefault to saveSettings.
extension/src/popup/helpers/navigate.ts Add openSidebar() helper (Firefox sidebarAction / Chromium sidePanel).
extension/src/popup/helpers/isSidebarMode.ts Add helper to detect sidebar mode via query param.
extension/src/popup/helpers/isFullscreenMode.ts Exclude sidebar mode from fullscreen detection.
extension/src/popup/ducks/settings.ts Add isOpenSidebarByDefault to settings state, thunk payload, and selector.
extension/src/popup/components/account/AccountHeader/index.tsx Add “Sidebar mode” menu item to open sidebar manually.
extension/src/popup/components/SidebarSigningListener/index.tsx New component to register sidebar window and handle navigation messages / window.close behavior.
extension/src/popup/Router.tsx Mount sidebar signing listener when in sidebar mode.
extension/src/constants/localStorageTypes.ts Add IS_OPEN_SIDEBAR_BY_DEFAULT_ID storage key constant.
extension/src/background/messageListener/popupMessageListener.ts Add SIDEBAR_REGISTER/UNREGISTER handling + session storage window id tracking.
extension/src/background/messageListener/handlers/saveSettings.ts Persist isOpenSidebarByDefault and apply Chrome panel behavior immediately.
extension/src/background/messageListener/handlers/loadSettings.ts Load isOpenSidebarByDefault from storage and return in settings payload.
extension/src/background/messageListener/freighterApiMessageListener.ts Route signing flows through sidebar if registered; otherwise open popup window.
extension/src/background/index.ts Add background startup initialization to apply sidebar behavior from stored preference.
extension/public/static/manifest/v3.json Add sidePanel permission and side_panel.default_path.
extension/public/static/manifest/v2.json Add sidebar_action.default_panel for Firefox MV2.
extension/public/background.ts Call initSidebarBehavior() on background startup.
@shared/constants/services.ts Add SIDEBAR_REGISTER / SIDEBAR_UNREGISTER service types.
@shared/api/types/types.ts Add isOpenSidebarByDefault to shared settings/response types.
@shared/api/types/message-request.ts Add isOpenSidebarByDefault to SaveSettingsMessage + sidebar register/unregister message types.
@shared/api/internal.ts Thread isOpenSidebarByDefault through internal saveSettings call.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +564 to +574
case SERVICE_TYPES.SIDEBAR_REGISTER: {
chrome.storage.session.set({
[SIDEBAR_WINDOW_ID_KEY]: (request as SidebarRegisterMessage).windowId,
});
return {};
}

case SERVICE_TYPES.SIDEBAR_UNREGISTER: {
chrome.storage.session.remove(SIDEBAR_WINDOW_ID_KEY);
return {};
}
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

SIDEBAR_REGISTER / SIDEBAR_UNREGISTER write to chrome.storage.session without checking availability. On platforms without storage.session, this will throw and potentially break the entire message listener. Add an availability check (and a fallback) before calling set/remove here.

Copilot uses AI. Check for mistakes.
Comment on lines +72 to +93
const openSigningWindow = async (hashRoute: string) => {
const sidebarWindowId = await getSidebarWindowId();
if (sidebarWindowId !== null) {
browser.runtime.sendMessage({ type: SIDEBAR_NAVIGATE, route: hashRoute });
try {
if ((browser as any).sidebarAction) {
// Firefox
await (browser as any).sidebarAction.open();
} else {
// Chrome and other Chromium browsers
await chrome.sidePanel.open({ windowId: sidebarWindowId });
}
} catch (_) {
// ignore if unavailable
}
return null;
}
return browser.windows.create({
url: chrome.runtime.getURL(`/index.html#${hashRoute}`),
...WINDOW_SETTINGS,
});
};
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

openSigningWindow returns null when routing through the sidebar. In that case there’s no windows.onRemoved signal, so if the user collapses/closes the sidebar without explicitly approving/rejecting, the returned Promise in the submit* flows can hang indefinitely. Consider adding an explicit decline path for sidebar close (e.g., via a Port disconnect, a dedicated message from the sidebar on unload tied to the active uuid, or a timeout-based fallback).

Copilot uses AI. Check for mistakes.
Comment on lines +24 to +29
const win = await chrome.windows.getCurrent();
await chrome.sidePanel.setOptions({
path: "index.html?mode=sidebar",
enabled: true,
});
await chrome.sidePanel.open({ windowId: win.id! });
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

openSidebar() assumes chrome.sidePanel is available and uses win.id!. On Chromium versions without the sidePanel API (or if win.id is undefined), this will throw and the menu click will cause an unhandled rejection. Please guard for chrome.sidePanel/chrome.windows availability and handle a missing windowId gracefully (e.g., fall back to opening a tab/window or show an error).

Suggested change
const win = await chrome.windows.getCurrent();
await chrome.sidePanel.setOptions({
path: "index.html?mode=sidebar",
enabled: true,
});
await chrome.sidePanel.open({ windowId: win.id! });
const anyChrome = typeof chrome !== "undefined" ? (chrome as any) : undefined;
if (anyChrome && anyChrome.sidePanel && anyChrome.windows) {
const win = await anyChrome.windows.getCurrent();
const windowId =
win && typeof win.id === "number"
? win.id
: undefined;
await anyChrome.sidePanel.setOptions({
path: "index.html?mode=sidebar",
enabled: true,
});
if (windowId !== undefined) {
await anyChrome.sidePanel.open({ windowId });
return;
}
}
// Fallback: open sidebar UI in a new tab if sidePanel API or windowId is unavailable
await openTab(browser.runtime.getURL("index.html?mode=sidebar"));

Copilot uses AI. Check for mistakes.
Comment on lines +12 to +17
const register = async () => {
const win = await chrome.windows.getCurrent();
chrome.runtime.sendMessage({
type: SERVICE_TYPES.SIDEBAR_REGISTER,
windowId: win.id,
});
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

chrome.windows.getCurrent() can return an object with id undefined. Since SidebarRegisterMessage.windowId is typed as number, sending win.id without checking can break sidebar routing (and potentially store undefined). Add a guard before sending the register message (and consider logging/handling when id is missing).

Copilot uses AI. Check for mistakes.
isDataSharingAllowed: false,
isMemoValidationEnabled: false,
isHideDustEnabled: true,
isOpenSidebarByDefault: false,
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

This inserted isOpenSidebarByDefault property is mis-indented compared to the surrounding object literal. If CI runs ESLint/Prettier checks, this may fail formatting/lint validation—please reformat this block (and the other new occurrences in this file) to match the existing style.

Suggested change
isOpenSidebarByDefault: false,
isOpenSidebarByDefault: false,

Copilot uses AI. Check for mistakes.
networkDetails: TESTNET_NETWORK_DETAILS,
networksList: DEFAULT_NETWORKS,
isHideDustEnabled: true,
isOpenSidebarByDefault: false,
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

This isOpenSidebarByDefault line is mis-indented relative to the surrounding settings object. Please run formatting (or adjust indentation) to keep the object literal consistent and avoid potential lint/format check failures.

Suggested change
isOpenSidebarByDefault: false,
isOpenSidebarByDefault: false,

Copilot uses AI. Check for mistakes.
isDataSharingAllowed: false,
isMemoValidationEnabled: false,
isHideDustEnabled: true,
isOpenSidebarByDefault: false,
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

This newly added isOpenSidebarByDefault field is not aligned/indented like the other properties in the mocked settings response. Please reformat this object literal (and the other new insertions in the file) to match existing formatting to avoid lint/Prettier issues.

Suggested change
isOpenSidebarByDefault: false,
isOpenSidebarByDefault: false,

Copilot uses AI. Check for mistakes.
Comment on lines +96 to +98
const SIDEBAR_WINDOW_ID_KEY = "sidebarWindowId";

export const getSidebarWindowId = async (): Promise<number | null> => {
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

chrome.storage.session is used unguarded for sidebar window tracking. This API is not available in MV2/Firefox (and can be undefined in some Chromium versions), which would throw at runtime when getSidebarWindowId() is called. Consider guarding for chrome.storage.session existence and falling back to an in-memory variable (or another supported storage) when unavailable.

Suggested change
const SIDEBAR_WINDOW_ID_KEY = "sidebarWindowId";
export const getSidebarWindowId = async (): Promise<number | null> => {
const SIDEBAR_WINDOW_ID_KEY = "sidebarWindowId";
let inMemorySidebarWindowId: number | null = null;
export const getSidebarWindowId = async (): Promise<number | null> => {
// Guard against environments where chrome.storage.session is unavailable
if (
!chrome.storage ||
!("session" in chrome.storage) ||
!chrome.storage.session ||
typeof chrome.storage.session.get !== "function"
) {
return inMemorySidebarWindowId;
}

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 30 out of 30 changed files in this pull request and generated 8 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +98 to +100
export const getSidebarWindowId = async (): Promise<number | null> => {
const result = await chrome.storage.session.get(SIDEBAR_WINDOW_ID_KEY);
return result[SIDEBAR_WINDOW_ID_KEY] ?? null;
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

chrome.storage.session is used directly to track the sidebar window id. This API isn’t available in all target environments (notably Firefox MV2), and will throw when getSidebarWindowId() is called. Use the existing storage abstraction (browser.storage.session via dataStorageAccess(browserSessionStorage)), and add a fallback (e.g., in-memory variable or browser.storage.local) when session storage isn’t supported.

Suggested change
export const getSidebarWindowId = async (): Promise<number | null> => {
const result = await chrome.storage.session.get(SIDEBAR_WINDOW_ID_KEY);
return result[SIDEBAR_WINDOW_ID_KEY] ?? null;
// Fallback storage for environments without session storage support
let inMemorySidebarWindowId: number | null = null;
export const getSidebarWindowId = async (): Promise<number | null> => {
try {
// Prefer WebExtension `browser.storage.session` when available,
// fall back to `chrome.storage.session` otherwise.
const storageSession =
(typeof browser !== "undefined" &&
(browser as any).storage &&
(browser as any).storage.session) ||
(typeof chrome !== "undefined" &&
(chrome as any).storage &&
(chrome as any).storage.session);
if (storageSession && typeof storageSession.get === "function") {
const result = await storageSession.get(SIDEBAR_WINDOW_ID_KEY);
const value = (result as any)[SIDEBAR_WINDOW_ID_KEY];
inMemorySidebarWindowId =
typeof value === "number" ? value : inMemorySidebarWindowId;
return inMemorySidebarWindowId;
}
} catch {
// Ignore errors and fall back to in-memory value below.
}
return inMemorySidebarWindowId;

Copilot uses AI. Check for mistakes.
Comment on lines +565 to +572
chrome.storage.session.set({
[SIDEBAR_WINDOW_ID_KEY]: (request as SidebarRegisterMessage).windowId,
});
return {};
}

case SERVICE_TYPES.SIDEBAR_UNREGISTER: {
chrome.storage.session.remove(SIDEBAR_WINDOW_ID_KEY);
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

The register/unregister handlers write to chrome.storage.session without awaiting/handling failures. Beyond the cross-browser availability issue, a rejected promise here can become an unhandled rejection and leave a stale sidebarWindowId behind. Please await these calls (or catch/log) and consider clearing/overwriting the stored id on failure.

Suggested change
chrome.storage.session.set({
[SIDEBAR_WINDOW_ID_KEY]: (request as SidebarRegisterMessage).windowId,
});
return {};
}
case SERVICE_TYPES.SIDEBAR_UNREGISTER: {
chrome.storage.session.remove(SIDEBAR_WINDOW_ID_KEY);
(async () => {
try {
await chrome.storage.session.set({
[SIDEBAR_WINDOW_ID_KEY]: (request as SidebarRegisterMessage).windowId,
});
} catch (e) {
console.error("Failed to register sidebar window ID in session storage", e);
}
})();
return {};
}
case SERVICE_TYPES.SIDEBAR_UNREGISTER: {
(async () => {
try {
await chrome.storage.session.remove(SIDEBAR_WINDOW_ID_KEY);
} catch (e) {
console.error("Failed to unregister sidebar window ID from session storage", e);
}
})();

Copilot uses AI. Check for mistakes.
Comment on lines +371 to 375
const popup = await openSigningWindow(`/sign-transaction?${encodedBlob}`);

return new Promise((resolve) => {
if (!popup) {
if (popup === undefined) {
resolve({
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

When openSigningWindow returns null (sidebar flow), this Promise sets up no equivalent of the windows.onRemoved decline path. If the user closes/collapses the sidebar without explicitly approving/rejecting, the dApp request can remain pending indefinitely (queue cleanup only removes entries; it doesn’t resolve the Promise). Please add a decline/timeout path for the sidebar case (e.g., listen for SIDEBAR_UNREGISTER for the relevant uuid, or have the UI send an explicit decline on unload).

Copilot uses AI. Check for mistakes.
Comment on lines +182 to +186
{!/Arc\//.test(navigator.userAgent) && (
<div
className="AccountHeader__options__item"
onClick={() => openSidebar()}
>
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

UA sniffing for Arc (!/Arc\//.test(navigator.userAgent)) is brittle and can mis-detect capability over time. Since openSidebar already feature-detects browser.sidebarAction / chrome.sidePanel, consider using the same capability detection to decide whether to show the Sidebar option instead of relying on the user agent string.

Copilot uses AI. Check for mistakes.
Comment on lines +84 to +87
} catch (_) {
// ignore if unavailable
}
return null;
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

openSigningWindow returns null whenever a sidebarWindowId is present, even if opening the sidebar/side panel fails (the error is swallowed). In that failure case, signing requests can be dropped with no fallback UI. Consider: if the open attempt throws, clear the stored sidebarWindowId and fall back to creating a popup window instead of returning null.

Suggested change
} catch (_) {
// ignore if unavailable
}
return null;
// Sidebar/side panel successfully opened; no popup window is needed.
return null;
} catch (_) {
// If opening the sidebar/side panel fails, fall back to creating a popup window.
}

Copilot uses AI. Check for mistakes.
Comment on lines +43 to +45
chrome.sidePanel.setPanelBehavior({
openPanelOnActionClick: isOpenSidebarByDefault,
});
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

chrome.sidePanel.setPanelBehavior(...) returns a promise; calling it without await/error handling can cause unhandled rejections in the background and make the setting appear applied when it wasn’t. Please await it (or .catch) and consider handling errors (e.g., if the API is unavailable).

Suggested change
chrome.sidePanel.setPanelBehavior({
openPanelOnActionClick: isOpenSidebarByDefault,
});
try {
await chrome.sidePanel.setPanelBehavior({
openPanelOnActionClick: isOpenSidebarByDefault,
});
} catch (error) {
// Swallow or log the error to avoid unhandled promise rejections
// and to prevent the setting from appearing applied when it was not.
console.error("Failed to set side panel behavior", error);
}

Copilot uses AI. Check for mistakes.
((await localStore.getItem(IS_OPEN_SIDEBAR_BY_DEFAULT_ID)) as boolean) ??
false;
if (chrome.sidePanel?.setPanelBehavior) {
chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: !!val });
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

Same as in saveSettings: chrome.sidePanel.setPanelBehavior(...) is invoked without awaiting/handling errors. Please await/catch here to avoid unhandled rejections during background startup and to ensure the stored preference is actually applied.

Suggested change
chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: !!val });
try {
await chrome.sidePanel.setPanelBehavior({
openPanelOnActionClick: !!val,
});
} catch (error) {
console.error("Failed to set side panel behavior", error);
}

Copilot uses AI. Check for mistakes.
Comment on lines +185 to +186
{!/Arc\//.test(navigator.userAgent) && (
<div className="Preferences--section">
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

UA sniffing for Arc (!/Arc\//.test(navigator.userAgent)) is brittle and can mis-detect sidebar capability. Consider feature-detecting browser.sidebarAction / chrome.sidePanel (or a shared helper) to decide whether to render this toggle, instead of relying on user agent parsing.

Copilot uses AI. Check for mistakes.
const SIDEBAR_WINDOW_ID_KEY = "sidebarWindowId";

export const getSidebarWindowId = async (): Promise<number | null> => {
const result = await chrome.storage.session.get(SIDEBAR_WINDOW_ID_KEY);
Copy link
Contributor

Choose a reason for hiding this comment

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

You'll see this elsewhere in the code, but generally, whenever you call a method on chrome, you should use the library browser instead. The browser library makes sure whatever method you're calling (here, it's .storage) will work whether you're in Chrome, Firefox, or any other browser the library supports

useEffect(() => {
const register = async () => {
const win = await chrome.windows.getCurrent();
chrome.runtime.sendMessage({
Copy link
Contributor

Choose a reason for hiding this comment

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

if it's possible, it'd be great to:

  1. use browser.runtime.sendMessage instead
  2. move this calls to the background script. We generally try to make all interactions with the browser itself happen in the background rather than the UI layer (or popup in this repo). This separates concerns so the UI can mostly focus on presentational efforts and the background can handle browser storage/interaction

import { SERVICE_TYPES } from "@shared/constants/services";
import { ROUTES } from "popup/constants/routes";

const SIDEBAR_NAVIGATE = "SIDEBAR_NAVIGATE";
Copy link
Contributor

Choose a reason for hiding this comment

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

I notice that this const is defined twice (it's also defined in freighterApiMessageListener). You can consolidate these

@piyalbasu
Copy link
Contributor

I left some comments here. In addition, you can have GH Copilot implement all of its suggestions - they all look reasonable.

1 issue we need to fix: I can no longer get Freighter signing windows to popup when I'm NOT in sidebar mode. See below:

Screen.Recording.2026-03-23.at.8.03.18.PM.mov

sdfcharles and others added 5 commits March 25, 2026 12:26
…timeouts, feature detection, and popup close behavior

- Fix setAllowedStatus bypassing openSigningWindow, causing signing windows to not appear when sidebar mode is disabled
- Replace chrome.storage.session with in-memory variable for cross-browser (Firefox) compatibility
- Add error handling for setPanelBehavior() calls
- Add 5-minute timeout to reject hanging Promises when sidebar closes mid-signing
- Replace Arc browser UA sniffing with feature detection (typeof globalThis.chrome?.sidePanel?.open)
- Fix popup not closing when opening sidebar mode
- Add missing isOpenSidebarByDefault to SignTransaction test mocks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace unreliable beforeunload-based SIDEBAR_UNREGISTER with a long-lived
port connection. Background now clears sidebarWindowId via onDisconnect when
the sidebar closes, preventing stale IDs from routing signing requests through
a closed sidebar instead of opening a popup.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The test.slow() call was removed during the 5.38.0 refactoring, reducing
the timeout from 45s to 15s. This causes intermittent CI failures when
network stubs take longer to respond under sequential execution
(IS_INTEGRATION_MODE=true with workers=1).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…route()

Chrome extension service workers make fetch requests outside the page context,
so page.route() does not intercept them. Switching to context.route() ensures
the account history stubs are applied at the browser context level, which covers
both page and service worker requests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Playwright's context.route()/page.route() do not reliably intercept fetch
requests from Chrome extension popup pages in CI headless mode. Instead,
use page.addInitScript() to patch window.fetch directly in the extension
page's JavaScript environment. This approach injects code before any page
scripts run and is guaranteed to intercept the account-history fetch calls
regardless of the network interception mechanism.

Keep context.route() as a network-level fallback for environments where
route interception does work.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

3 participants