A three-way exit for unsaved work — beforeunload armed only while dirty, in-app navigation held through an injected navigate handler, and a save / discard / stay dialog instead of the browser's bare prompt.
npx shadcn@latest add @zyeon/unsaved-changes-guardZyeon UI limits how often it can be asked, and Unsaved Changes Guard hasn't had its turn. Its source will show here once it has.