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}) {50 51 if (currentDispose) {52 dismissPasskeyToast(currentRequestId ?? undefined);53 }5455 const root = ensureContainer();56 applyAccentToShadow();57 const wrapper = document.createElement('div');58 root.appendChild(wrapper);5960 let dispose: (() => void) | null = null;6162 dispose = render(() => (63 <PasskeyToast64 {...payload}65 onSignIn={() => {66 chrome.runtime.sendMessage({67 type: 'PASSKEY_AUTH_RESPONSE',68 payload: { requestId: payload.requestId, approved: true },69 });70 cleanup();71 }}72 onDeny={() => {73 chrome.runtime.sendMessage({74 type: 'PASSKEY_AUTH_RESPONSE',75 payload: { requestId: payload.requestId, approved: false },76 });77 cleanup();78 }}79 />80 ), wrapper);8182 currentDispose = dispose;83 currentRequestId = payload.requestId;8485 function cleanup() {86 if (dispose) {87 dispose();88 dispose = null;89 }90 wrapper.remove();91 currentDispose = null;92 currentRequestId = null;93 94 window.postMessage({ source: 'peach-passkey-toast', type: 'toast-closed' }, '*');95 }96}9798export function dismissPasskeyToast(denyRequestId?: string) {99 if (denyRequestId) {100 chrome.runtime.sendMessage({101 type: 'PASSKEY_AUTH_RESPONSE',102 payload: { requestId: denyRequestId, approved: false },103 });104 }105 if (currentDispose) {106 currentDispose();107 currentDispose = null;108 currentRequestId = null;109 }110}111112chrome.runtime.onMessage.addListener((msg, _sender, sendResponse) => {113 if (msg.type === 'SHOW_PASSKEY_TOAST') {114 showPasskeyToast(msg.payload);115 sendResponse({ ok: true });116 }117 if (msg.type === 'DISMISS_PASSKEY_TOAST') {118 dismissPasskeyToast();119 sendResponse({ ok: true });120 }121});