Conversation
- 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>
There was a problem hiding this comment.
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.
| 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 {}; | ||
| } |
There was a problem hiding this comment.
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.
| 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, | ||
| }); | ||
| }; |
There was a problem hiding this comment.
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).
| const win = await chrome.windows.getCurrent(); | ||
| await chrome.sidePanel.setOptions({ | ||
| path: "index.html?mode=sidebar", | ||
| enabled: true, | ||
| }); | ||
| await chrome.sidePanel.open({ windowId: win.id! }); |
There was a problem hiding this comment.
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).
| 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")); |
| const register = async () => { | ||
| const win = await chrome.windows.getCurrent(); | ||
| chrome.runtime.sendMessage({ | ||
| type: SERVICE_TYPES.SIDEBAR_REGISTER, | ||
| windowId: win.id, | ||
| }); |
There was a problem hiding this comment.
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).
| isDataSharingAllowed: false, | ||
| isMemoValidationEnabled: false, | ||
| isHideDustEnabled: true, | ||
| isOpenSidebarByDefault: false, |
There was a problem hiding this comment.
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.
| isOpenSidebarByDefault: false, | |
| isOpenSidebarByDefault: false, |
| networkDetails: TESTNET_NETWORK_DETAILS, | ||
| networksList: DEFAULT_NETWORKS, | ||
| isHideDustEnabled: true, | ||
| isOpenSidebarByDefault: false, |
There was a problem hiding this comment.
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.
| isOpenSidebarByDefault: false, | |
| isOpenSidebarByDefault: false, |
| isDataSharingAllowed: false, | ||
| isMemoValidationEnabled: false, | ||
| isHideDustEnabled: true, | ||
| isOpenSidebarByDefault: false, |
There was a problem hiding this comment.
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.
| isOpenSidebarByDefault: false, | |
| isOpenSidebarByDefault: false, |
| const SIDEBAR_WINDOW_ID_KEY = "sidebarWindowId"; | ||
|
|
||
| export const getSidebarWindowId = async (): Promise<number | null> => { |
There was a problem hiding this comment.
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.
| 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; | |
| } |
There was a problem hiding this comment.
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.
| 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; |
There was a problem hiding this comment.
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.
| 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; |
| 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); |
There was a problem hiding this comment.
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.
| 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); | |
| } | |
| })(); |
| const popup = await openSigningWindow(`/sign-transaction?${encodedBlob}`); | ||
|
|
||
| return new Promise((resolve) => { | ||
| if (!popup) { | ||
| if (popup === undefined) { | ||
| resolve({ |
There was a problem hiding this comment.
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).
| {!/Arc\//.test(navigator.userAgent) && ( | ||
| <div | ||
| className="AccountHeader__options__item" | ||
| onClick={() => openSidebar()} | ||
| > |
There was a problem hiding this comment.
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.
| } catch (_) { | ||
| // ignore if unavailable | ||
| } | ||
| return null; |
There was a problem hiding this comment.
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.
| } 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. | |
| } |
| chrome.sidePanel.setPanelBehavior({ | ||
| openPanelOnActionClick: isOpenSidebarByDefault, | ||
| }); |
There was a problem hiding this comment.
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).
| 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); | |
| } |
extension/src/background/index.ts
Outdated
| ((await localStore.getItem(IS_OPEN_SIDEBAR_BY_DEFAULT_ID)) as boolean) ?? | ||
| false; | ||
| if (chrome.sidePanel?.setPanelBehavior) { | ||
| chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: !!val }); |
There was a problem hiding this comment.
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.
| chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: !!val }); | |
| try { | |
| await chrome.sidePanel.setPanelBehavior({ | |
| openPanelOnActionClick: !!val, | |
| }); | |
| } catch (error) { | |
| console.error("Failed to set side panel behavior", error); | |
| } |
| {!/Arc\//.test(navigator.userAgent) && ( | ||
| <div className="Preferences--section"> |
There was a problem hiding this comment.
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.
| const SIDEBAR_WINDOW_ID_KEY = "sidebarWindowId"; | ||
|
|
||
| export const getSidebarWindowId = async (): Promise<number | null> => { | ||
| const result = await chrome.storage.session.get(SIDEBAR_WINDOW_ID_KEY); |
There was a problem hiding this comment.
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({ |
There was a problem hiding this comment.
if it's possible, it'd be great to:
- use
browser.runtime.sendMessageinstead - move this calls to the
backgroundscript. We generally try to make all interactions with thebrowseritself happen in thebackgroundrather than the UI layer (orpopupin this repo). This separates concerns so the UI can mostly focus on presentational efforts and thebackgroundcan handlebrowserstorage/interaction
| import { SERVICE_TYPES } from "@shared/constants/services"; | ||
| import { ROUTES } from "popup/constants/routes"; | ||
|
|
||
| const SIDEBAR_NAVIGATE = "SIDEBAR_NAVIGATE"; |
There was a problem hiding this comment.
I notice that this const is defined twice (it's also defined in freighterApiMessageListener). You can consolidate these
|
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 |
…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>
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
manifest/v2.jsonandmanifest/v3.jsonto declare the sidebar/side_panel, including default panel path and icons. [1] [2]SidebarRegisterMessage,SidebarUnregisterMessage), and storage keys to manage sidebar registration and state. [1] [2] [3] [4] [5]Settings and Storage Enhancements
isOpenSidebarByDefaultsetting 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
openSigningWindowfunction 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