import { isNavigationFailure } from 'vue-router' async function waitFor( obtain: () => T | Promise, tries = 30, delay = 100 ): Promise { for (let i = 0; i < tries; i++) { const result = await obtain() if (result !== undefined) { return result } await new Promise((resolve) => setTimeout(resolve, delay)) } return undefined } export default defineNuxtPlugin((app) => { app.hook('app:mounted', () => { const router = useRouter() router.options.scrollBehavior = (to, from, savedPosition) => { if (to.query.freeze) { return false } if (to.hash && to.path === from.path) { return false } return { top: 0 } } router.afterEach(async (to, from, failure) => { if (isNavigationFailure(failure)) { console.warn(`Failed navigation: ${JSON.stringify(failure)}`) } else if (to.hash) { const [header, element, atTop] = await Promise.all([ waitFor(() => document.querySelector('#scaffold-header') ?? undefined), waitFor(() => document.querySelector(to.hash) ?? undefined), waitFor(() => (to.path === from.path || window.scrollY < 10 ? true : undefined)), ]) if (element && atTop) { const offset = header ? header.offsetHeight : 0 const position = element.getBoundingClientRect().top window.scrollTo({ top: position - offset + window.scrollY }) } } }) }) })