1import { render } from 'solid-js/web';2import { PasskeyToast } from './PasskeyToast';345import styles from './passkey-toast.css?inline';6import { ACCENT_COLORS } from '../../shared/types';78let currentDispose: (() => void) | null = null;9let currentRequestId: string | null = null;10let toastContainer: HTMLElement | null = null;11let shadowRoot: ShadowRoot | null = null;1213function ensureContainer(): ShadowRoot {14 if (!toastContainer) {15 toastContainer = document.createElement('div');16 toastContainer.id = 'peach-passkey-toast-root';17 shadowRoot = toastContainer.attachShadow({ mode: 'closed' });18 document.body.appendChild(toastContainer);1920 const style = document.createElement('style');21 style.textContent = styles;22 shadowRoot.appendChild(style);23 }24 return shadowRoot!;25}2627function applyAccentToShadow(): void {28 if (!shadowRoot) return;29 chrome.runtime.sendMessage({ type: 'GET_SETTINGS' }).then((resp: unknown) => {30 const name = (resp as { ok?: boolean; data?: { accentColor?: string } } | null)?.data?.accentColor;31 if (!name || !shadowRoot) return;32 const color = ACCENT_COLORS.find(c => c.name === name);33 if (!color) return;34 let el = shadowRoot.querySelector<HTMLStyleElement>('#peach-accent-var');35 if (!el) {36 el = document.createElement('style');37 el.id = 'peach-accent-var';38 shadowRoot.appendChild(el);39 }40 el.textContent = `* { --peach-accent: ${color.hex}; }`;41 }).catch(() => {});42}4344export function showPasskeyToast(payload: {45 site: string;46 displayName: string;47 username: string;48 requestId: string;49 toastToken: string;50}) {51 52 if (currentDispose) {53 dismissPasskeyToast(currentRequestId ?? undefined);54 }5556 const root = ensureContainer();57 applyAccentToShadow();58 const wrapper = document.createElement('div');59 root.appendChild(wrapper);6061 let dispose: (() => void) | null = null;6263 dispose = render(() => (64 <PasskeyToast65 {...payload}66 onSignIn={() => {67 chrome.runtime.sendMessage({68 type: 'PASSKEY_AUTH_RESPONSE',69 payload: { requestId: payload.requestId, approved: true },70 });71 cleanup();72 }}73 onDeny={() => {74 chrome.runtime.sendMessage({75 type: 'PASSKEY_AUTH_RESPONSE',76 payload: { requestId: payload.requestId, approved: false },77 });78 cleanup();79 }}80 />81 ), wrapper);8283 currentDispose = dispose;84 currentRequestId = payload.requestId;8586 function cleanup() {87 if (dispose) {88 dispose();89 dispose = null;90 }91 wrapper.remove();92 currentDispose = null;93 currentRequestId = null;94 95 window.postMessage({ source: 'peach-passkey-toast', type: 'toast-closed', token: payload.toastToken }, '*');96 }97}9899export function dismissPasskeyToast(denyRequestId?: string) {100 if (denyRequestId) {101 chrome.runtime.sendMessage({102 type: 'PASSKEY_AUTH_RESPONSE',103 payload: { requestId: denyRequestId, approved: false },104 });105 }106 if (currentDispose) {107 currentDispose();108 currentDispose = null;109 currentRequestId = null;110 }111}112113chrome.runtime.onMessage.addListener((msg, _sender, sendResponse) => {114 if (msg.type === 'SHOW_PASSKEY_TOAST') {115 showPasskeyToast(msg.payload);116 sendResponse({ ok: true });117 }118 if (msg.type === 'DISMISS_PASSKEY_TOAST') {119 dismissPasskeyToast();120 sendResponse({ ok: true });121 }122});