Sets up web/ Vue 3 SPA skeleton for issue #8, synthesizing all 15 gotchas from avocet's Vue port testbed. Key fixes baked in before any component work: - App.vue root uses .app-root class (not id="app") — gotcha #1 - overflow-x: clip on html (not hidden) — gotcha #3 - UnoCSS presetAttributify with prefixedOnly: true — gotcha #4 - peregrine.css alias map for theme variable names — gotcha #5 - useHaptics guards navigator.vibrate — gotcha #9 - Pinia setup store pattern documented — gotcha #10 - test-setup.ts stubs matchMedia, vibrate, ResizeObserver — gotcha #12 - min-height: 100dvh throughout — gotcha #13 Includes: - All 7 Peregrine views as stubs (ready to port from Streamlit) - AppNav with all routes - useApi (fetch + SSE), useMotion, useHaptics, useEasterEgg composables - Konami hacker mode easter egg + confetti + cursor trail - docs/vue-spa-migration.md: full migration guide + implementation order - Build verified clean (0 errors) - .gitleaks.toml: allowlist web/package-lock.json (sha512 integrity hashes)
35 lines
1 KiB
TypeScript
35 lines
1 KiB
TypeScript
// jsdom does not implement window.matchMedia — stub it so useMotion and other
|
|
// composables that check prefers-reduced-motion can import without throwing.
|
|
// Gotcha #12.
|
|
if (typeof window !== 'undefined' && !window.matchMedia) {
|
|
Object.defineProperty(window, 'matchMedia', {
|
|
writable: true,
|
|
value: (query: string) => ({
|
|
matches: false,
|
|
media: query,
|
|
onchange: null,
|
|
addListener: () => {},
|
|
removeListener: () => {},
|
|
addEventListener: () => {},
|
|
removeEventListener: () => {},
|
|
dispatchEvent: () => false,
|
|
}),
|
|
})
|
|
}
|
|
|
|
// navigator.vibrate not in jsdom — stub so useHaptics doesn't throw. Gotcha #9.
|
|
if (typeof window !== 'undefined' && !('vibrate' in window.navigator)) {
|
|
Object.defineProperty(window.navigator, 'vibrate', {
|
|
writable: true,
|
|
value: () => false,
|
|
})
|
|
}
|
|
|
|
// ResizeObserver not in jsdom — stub if any component uses it.
|
|
if (typeof window !== 'undefined' && !window.ResizeObserver) {
|
|
window.ResizeObserver = class {
|
|
observe() {}
|
|
unobserve() {}
|
|
disconnect() {}
|
|
}
|
|
}
|