avocet/web/src/composables/useMotion.ts
pyr0ball 7edcb089a9 feat(avocet): useApi, useMotion, useHaptics, useEasterEgg (Konami/hacker mode)
- useApiFetch: typed fetch wrapper with network/http error discrimination
- useMotion: reactive localStorage override for rich-animation toggle, respects OS prefers-reduced-motion
- useHaptics: label/discard/skip/undo vibration patterns, gated on rich mode
- useKonamiCode + useHackerMode: 10-key Konami sequence → hacker theme, persisted in localStorage
- test-setup.ts: jsdom matchMedia stub so useMotion imports cleanly in Vitest
- smoke.test.ts: import smoke tests for all 4 composables (12 tests, all passing)
2026-03-03 15:58:43 -08:00

28 lines
775 B
TypeScript

import { computed, ref } from 'vue'
const STORAGE_KEY = 'cf-avocet-rich-motion'
// OS-level prefers-reduced-motion — checked once at module load
const OS_REDUCED = typeof window !== 'undefined'
? window.matchMedia('(prefers-reduced-motion: reduce)').matches
: false
// Reactive ref so toggling localStorage triggers re-reads in the same session
const _richOverride = ref(
typeof window !== 'undefined'
? localStorage.getItem(STORAGE_KEY)
: null
)
export function useMotion() {
const rich = computed(() =>
!OS_REDUCED && _richOverride.value !== 'false'
)
function setRich(enabled: boolean) {
localStorage.setItem(STORAGE_KEY, enabled ? 'true' : 'false')
_richOverride.value = enabled ? 'true' : 'false'
}
return { rich, setRich }
}