export function deepEqual(obj1: any, obj2: any) { if (obj1 === obj2) return true; if (typeof obj1 !== "object" || obj1 === null || typeof obj2 !== "object" || obj2 === null) { return false; } const keys1 = Object.keys(obj1); const keys2 = Object.keys(obj2); if (keys1.length !== keys2.length) return false; for (let key of keys1) { if (!keys2.includes(key)) return false; if (!deepEqual(obj1[key], obj2[key])) return false; } return true; } export function useBeep() { const audioContext = ref(new (window.AudioContext)()); const playBeep = (frequency = 440, duration = 0.1) => { const oscillator = audioContext.value.createOscillator(); const gainNode = audioContext.value.createGain(); oscillator.connect(gainNode); gainNode.connect(audioContext.value.destination); gainNode.gain.setValueAtTime(0, audioContext.value.currentTime); gainNode.gain.linearRampToValueAtTime(1, audioContext.value.currentTime + 0.01); gainNode.gain.linearRampToValueAtTime(0, audioContext.value.currentTime + duration); oscillator.frequency.value = frequency; oscillator.start(audioContext.value.currentTime); oscillator.stop(audioContext.value.currentTime + duration); }; return {playBeep}; } export function toggleDark(event: MouseEvent) { const isAppearanceTransition = document.startViewTransition && !window.matchMedia('(prefers-reduced-motion: reduce)').matches if (!isAppearanceTransition) { useColorMode().preference = useColorMode().value == 'dark' ? 'light' : 'dark' return } const x = event.clientX const y = event.clientY const endRadius = Math.hypot(Math.max(x, innerWidth - x), Math.max(y, innerHeight - y),) // @ts-expect-error: Transition API const transition = document.startViewTransition(async () => { useColorMode().preference = useColorMode().value == 'dark' ? 'light' : 'dark' await nextTick() }) transition.ready .then(() => { const clipPath = [`circle(0px at ${x}px ${y}px)`, `circle(${endRadius}px at ${x}px ${y}px)`,] document.documentElement.animate({ clipPath: useColorMode().value == 'dark' ? [...clipPath].reverse() : clipPath, }, { duration: 400, easing: 'ease-out', pseudoElement: useColorMode().value == 'dark' ? '::view-transition-old(root)' : '::view-transition-new(root)', },) }) }