diff --git a/bun.lockb b/bun.lockb index 4ea0601..93d2a17 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/manifest.config.ts b/manifest.config.ts index 0da7622..c907e2c 100644 --- a/manifest.config.ts +++ b/manifest.config.ts @@ -25,6 +25,9 @@ export const getManifest = ({ dev }: { dev?: boolean }) => all_frames: true, }, ], + side_panel: { + default_path: 'src/entries/iframe/index.html', + }, icons: { '16': `icons/icon${dev ? '-dev' : ''}@16w.png`, '32': `icons/icon${dev ? '-dev' : ''}@32w.png`, @@ -36,6 +39,7 @@ export const getManifest = ({ dev }: { dev?: boolean }) => 'contextMenus', 'declarativeNetRequest', 'scripting', + 'sidePanel', 'storage', 'tabs', 'unlimitedStorage', diff --git a/package.json b/package.json index a2a9f4c..7336a66 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "@biomejs/biome": "1.0.0", "@samrum/vite-plugin-web-extension": "^5.1.0", "@types/chroma-js": "^2.4.0", - "@types/chrome": "^0.0.244", + "@types/chrome": "^0.0.266", "@types/opentype.js": "^1.3.4", "@types/qs": "^6.9.7", "@types/react": "^18.2.33", diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 2d79b29..5359d44 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -1,4 +1,4 @@ -import { type ReactNode, useCallback, useMemo } from 'react' +import { type ReactNode, useMemo } from 'react' import { Link } from 'react-router-dom' import { type Hex, formatGwei } from 'viem' @@ -24,15 +24,12 @@ import { useHost } from '~/hooks/useHost' import { useMine } from '~/hooks/useMine' import { useNetworkStatus } from '~/hooks/useNetworkStatus' import { usePendingBlock } from '~/hooks/usePendingBlock' -import { getMessenger } from '~/messengers' import { useAccountStore, useNetworkStore, useSessionsStore } from '~/zustand' import { useRevert } from '../hooks/useRevert' import { useSnapshot } from '../hooks/useSnapshot' import * as styles from './Header.css' -const contentMessenger = getMessenger('wallet:contentScript') - export function Header({ isNetworkOffline }: { isNetworkOffline?: boolean }) { const { type } = useAppMeta() return ( @@ -62,12 +59,6 @@ export function Header({ isNetworkOffline }: { isNetworkOffline?: boolean }) { - - - - - - )} @@ -194,31 +185,6 @@ function DappConnection() { ) } -function CollapseButton() { - const handleClose = useCallback(() => { - contentMessenger.send('toggleWallet', undefined) - }, []) - - return ( - - - - - - ) -} - function SettingsButton() { return ( @@ -232,7 +198,7 @@ function SettingsButton() { display="flex" justifyContent="center" height="full" - style={{ width: '28px' }} + style={{ width: '32px' }} > diff --git a/src/design-system/components/Text.tsx b/src/design-system/components/Text.tsx index 9314525..46700b5 100644 --- a/src/design-system/components/Text.tsx +++ b/src/design-system/components/Text.tsx @@ -185,10 +185,15 @@ export const TextTruncated = forwardRef( }, []) useLayoutEffect(() => { - setWidth( - ((wrapperRef.current as any).getBoundingClientRect() as DOMRectReadOnly) - .width, - ) + setTimeout(() => { + setWidth( + ( + ( + wrapperRef.current as any + ).getBoundingClientRect() as DOMRectReadOnly + ).width, + ) + }, 16) }, []) useResizeObserver(wrapperRef, (entry) => mounted ? setWidth(entry.contentRect.width) : undefined, diff --git a/src/entries/background/context-menu.ts b/src/entries/background/context-menu.ts index 59117bc..86b71db 100644 --- a/src/entries/background/context-menu.ts +++ b/src/entries/background/context-menu.ts @@ -1,11 +1,7 @@ -import { getMessenger } from '~/messengers' - -const inpageMessenger = getMessenger('background:inpage') - export function setupContextMenu() { - chrome.action.onClicked.addListener(() => { - inpageMessenger.send('toggleWallet', undefined) - }) + chrome.sidePanel + .setPanelBehavior({ openPanelOnActionClick: true }) + .catch((error) => console.error(error)) // TODO: Only create context menu if selected text is "openable" in Rivet. // chrome.contextMenus.create({ diff --git a/src/entries/background/index.ts b/src/entries/background/index.ts index 750d6ad..349ed70 100644 --- a/src/entries/background/index.ts +++ b/src/entries/background/index.ts @@ -7,6 +7,7 @@ import { setupExtensionId } from './extension-id' import { setupInpage } from './inpage' import { interceptJsonRpcRequests } from './intercept-requests' import { setupRpcHandler } from './rpc' +import { setupWalletSidebarHandler } from './wallet-sidebar' const contentMessenger = getMessenger('background:contentScript') const inpageMessenger = getMessenger('background:inpage') @@ -21,4 +22,5 @@ setupExtensionId() setupInpage() setupRpcHandler({ messenger: inpageMessenger }) setupRpcHandler({ messenger: walletMessenger }) +setupWalletSidebarHandler() syncStores() diff --git a/src/entries/background/rpc.ts b/src/entries/background/rpc.ts index fcd57bf..11e613d 100644 --- a/src/entries/background/rpc.ts +++ b/src/entries/background/rpc.ts @@ -75,6 +75,7 @@ export function getRpcClient({ export function setupRpcHandler({ messenger }: { messenger: Messenger }) { messenger.reply('request', async ({ request, rpcUrl }, meta) => { const isInpage = + meta.sender.tab && !meta.sender.tab?.url?.includes('extension://') && (!meta.sender.frameId || meta.sender.frameId === 0) const rpcClient = getRpcClient({ rpcUrl }) @@ -104,8 +105,6 @@ export function setupRpcHandler({ messenger }: { messenger: Messenger }) { addPendingRequest({ ...request, sender: meta.sender }) - inpageMessenger.send('toggleWallet', { open: true }) - try { const response = await new Promise((resolve, reject) => { walletMessenger.reply( @@ -137,7 +136,6 @@ export function setupRpcHandler({ messenger }: { messenger: Messenger }) { }) return response as RpcResponse } finally { - inpageMessenger.send('toggleWallet', { useStorage: true }) removePendingRequest(request.id) } } @@ -176,8 +174,6 @@ export function setupRpcHandler({ messenger }: { messenger: Messenger }) { addPendingRequest({ ...request, sender: meta.sender }) - inpageMessenger.send('toggleWallet', { open: true }) - try { const response = await new Promise((resolve) => { walletMessenger.reply( diff --git a/src/entries/background/wallet-sidebar.ts b/src/entries/background/wallet-sidebar.ts new file mode 100644 index 0000000..1383e22 --- /dev/null +++ b/src/entries/background/wallet-sidebar.ts @@ -0,0 +1,20 @@ +import { settingsStore } from '../../zustand' + +export function setupWalletSidebarHandler() { + chrome.runtime.onMessage.addListener((message, sender) => { + if (message.type === 'openWallet') { + const { bypassSignatureAuth, bypassTransactionAuth } = + settingsStore.getState() + const { method } = message.payload + if ( + method === 'eth_requestAccounts' || + (method === 'eth_sendTransaction' && !bypassTransactionAuth) || + (method === 'eth_sign' && !bypassSignatureAuth) || + (method === 'eth_signTypedData_v4' && !bypassSignatureAuth) || + (method === 'personal_sign' && !bypassSignatureAuth) + ) { + chrome.sidePanel.open({ tabId: sender.tab!.id! }) + } + } + }) +} diff --git a/src/entries/content/index.ts b/src/entries/content/index.ts index bbf2ce1..72735fb 100644 --- a/src/entries/content/index.ts +++ b/src/entries/content/index.ts @@ -1,6 +1,14 @@ +import { getMessenger } from '~/messengers' import { setupBridgeTransportRelay } from '~/messengers/transports/bridge' -import { injectWallet } from './injectWallet' - setupBridgeTransportRelay() -injectWallet() + +const backgroundMessenger = getMessenger('background:contentScript') +backgroundMessenger.send('ping', undefined) +setInterval(() => { + backgroundMessenger.send('ping', undefined) +}, 5000) + +window.addEventListener('message', ({ data }) => { + if (data.type === 'openWallet') chrome.runtime.sendMessage(data) +}) diff --git a/src/entries/content/injectWallet.ts b/src/entries/content/injectWallet.ts deleted file mode 100644 index 13eeb07..0000000 --- a/src/entries/content/injectWallet.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { getMessenger } from '~/messengers' -import { windowStorage } from '~/storage' - -const backgroundMessenger = getMessenger('background:contentScript') -const walletMessenger = getMessenger('wallet:contentScript') - -backgroundMessenger.send('ping', undefined) -setInterval(() => { - backgroundMessenger.send('ping', undefined) -}, 5000) - -export async function injectWallet() { - const extensionId: string = await backgroundMessenger.send( - 'extensionId', - undefined, - ) - - if (process.env.NODE_ENV === 'development') - windowStorage.local.setItem('open', true) - - // Inject wallet elements - const container = await injectContainer() - injectIframe({ container, extensionId }) - - const handle = injectHandle({ container }) - setupHandleListeners({ container, handle }) - setupToggleListeners({ container, handle }) -} - -///////////////////////////////////////////////////////////////////// - -async function injectContainer() { - const container = document.createElement('div') - container.id = '__dev-wallet' - container.style.width = '0px' - container.style.height = '100vh' - container.style.position = 'fixed' - container.style.top = '0' - container.style.right = '0' - container.style.border = 'none' - container.style.zIndex = '2147483646' - - if (document.body === null) - await new Promise((resolve) => { - document.addEventListener('DOMContentLoaded', () => { - resolve() - }) - }) - - document.body.appendChild(container) - return container -} - -function injectIframe({ - container, - extensionId, -}: { container: HTMLElement; extensionId: string }) { - const iframe = document.createElement('iframe') - iframe.src = `chrome-extension://${extensionId}/src/entries/iframe/index.html` - iframe.allow = 'clipboard-write' - iframe.style.width = '100%' - iframe.style.height = '100%' - iframe.style.border = 'none' - iframe.style.margin = '0px' - iframe.style.padding = '0px' - container.appendChild(iframe) - return iframe -} - -function injectHandle({ container }: { container: HTMLElement }) { - const handle = document.createElement('div') - handle.style.display = 'none' - handle.style.width = '16px' - handle.style.height = '100%' - handle.style.position = 'absolute' - handle.style.top = '80px' - handle.style.right = `${parseInt(container.style.width) - 8}px` - handle.style.cursor = 'ew-resize' - container.appendChild(handle) - return handle -} - -function setupHandleListeners({ - container, - handle, -}: { container: HTMLElement; handle: HTMLElement }) { - let isDragging = false - let startX = 0 - let startWidth = 0 - - handle.addEventListener('mousedown', (e) => { - container.style.pointerEvents = 'none' - isDragging = true - startX = e.pageX - startWidth = parseInt( - document.defaultView?.getComputedStyle(container).width ?? '0', - 10, - ) - }) - - document.addEventListener('mousemove', (e) => { - if (!isDragging) return - const width = startWidth + startX - e.pageX - if (width < 400) return - container.style.width = `${width}px` - handle.style.right = `${width - 8}px` - }) - - document.addEventListener('mouseup', () => { - container.style.pointerEvents = 'all' - isDragging = false - }) -} - -function setupToggleListeners({ - container, - handle, -}: { - container: HTMLElement - handle: HTMLElement -}) { - let open = Boolean(windowStorage.local.getItem('open')) || false - - async function listener( - args: { route?: string; open?: boolean; useStorage?: boolean } | void = {}, - ) { - if (args?.route) walletMessenger.send('pushRoute', args.route) - - if (args?.useStorage && windowStorage.local.getItem('open')) - open = Boolean(windowStorage.local.getItem('open')) - else open = args?.open ?? !open - - if (!open) { - container.style.width = '0px' - handle.style.display = 'none' - } else { - container.style.width = '400px' - handle.style.display = 'block' - handle.style.right = '392px' - } - - if (typeof args?.open === 'undefined') - windowStorage.local.setItem('open', open) - } - - backgroundMessenger.reply('toggleWallet', listener) - listener({ open }) -} diff --git a/src/messengers/schema.ts b/src/messengers/schema.ts index fed3cb2..2c3e709 100644 --- a/src/messengers/schema.ts +++ b/src/messengers/schema.ts @@ -29,14 +29,5 @@ export type Schema = { response: RpcResponse, ] toggleTheme: [payload: void, response: void] - toggleWallet: [ - payload: - | ({ route?: string } & ( - | { open: boolean; useStorage?: never } - | { open?: never; useStorage?: true } - )) - | undefined, - response: void, - ] transactionExecuted: [payload: void, response: void] } diff --git a/src/provider.ts b/src/provider.ts index 180c5a6..e19a7a6 100644 --- a/src/provider.ts +++ b/src/provider.ts @@ -65,6 +65,12 @@ export function getProvider({ removeListener: emitter.removeListener.bind(emitter), async request({ method, params }) { const id = _id++ + + window.postMessage({ + type: 'openWallet', + payload: { method }, + }) + const { result, error, ...response } = await requestMessenger.send( 'request', {