fix(panel): warn before a web tab drops unsaved work

Merged
DA
6d ago
+310-27

Fixes a P1 from the post-merge review of the web app shell: closing, reloading or navigating a web tab threw away unsaved work (an automation draft, say) without asking. The panel's leave guard only watched the panel's own ways out.

What changed

  • Panel: useLeaveGuard takes an optional onStanding callback. It gets the ask each time a page stands one, and undefined once the standing ask comes down. App passes that on to a new optional AppPlatform.unsavedWork hook. The desktop platform leaves the hook out, so the desktop behaves exactly as before, Electron's window close included.
  • Web (apps/web/src/app/page-leave.ts, new):
    • Adds the beforeunload listener only while an ask stands and removes it when the ask comes down, because a listener that is always on keeps the page out of the back-forward cache.
    • Its leave(go) sends the tab's own same-tab navigations through the panel's question first. Once the developer says Leave, the browser doesn't ask a second time.
    • When the back-forward cache brings the page back, the hold applies again.
  • The sign-in redirect (cookieDoor in web-host.ts) and the refused-popup connect fallback (connect-tab.ts) now go through leave. The connect-tab.ts edit is small: one leave field on ConnectBrowser and the two fallback lines wrapped in it.

Tests (each fails without the fix)

  • apps/web/tests/page-leave.test.ts: the browser holds back a close or reload only while work is unsaved, and stops once the ask comes down. A way out asks first, and once the developer says Leave the browser doesn't hold it back. A page restored from the back-forward cache holds back again.
  • apps/web/tests/connect-tab.test.ts: when the popup is refused and the page holds unsaved work, the connect asks before sending the tab to the provider, and only records its return address once told to leave.
  • packages/panel/src/desktop/automations-page.test.ts: the shell hears the ask only while an edit is unsaved. Calling it shows the page's own "Leave with unsaved changes?" dialog, and the shell hears nothing again once the edit is saved.

Known limit

If the developer presses Cancel on a fresh sign-in (signed out → provider), the account source stays in "signing in" until they cancel that themselves. A signed-out panel shows no editor, so this is unlikely to come up. The signInAgain link flow is unaffected.

Made with Luke