(page: Page)
| 1683 | |
| 1684 | // ─── Console/Network/Dialog/Ref Wiring ──────────────────── |
| 1685 | private wirePageEvents(page: Page) { |
| 1686 | // Track tab close — remove from pages and sessions maps, switch to another tab |
| 1687 | page.on('close', () => { |
| 1688 | for (const [id, p] of this.pages) { |
| 1689 | if (p === page) { |
| 1690 | this.pages.delete(id); |
| 1691 | this.tabSessions.delete(id); |
| 1692 | console.log(`[browse] Tab closed (id=${id}, remaining=${this.pages.size})`); |
| 1693 | // If the closed tab was active, switch to another |
| 1694 | if (this.activeTabId === id) { |
| 1695 | const remaining = [...this.pages.keys()]; |
| 1696 | this.activeTabId = remaining.length > 0 ? remaining[remaining.length - 1] : 0; |
| 1697 | } |
| 1698 | break; |
| 1699 | } |
| 1700 | } |
| 1701 | this.recheckTabGuardrailsOnClose(); |
| 1702 | }); |
| 1703 | |
| 1704 | // Clear ref map on navigation — refs point to stale elements after page change |
| 1705 | // (lastSnapshot is NOT cleared — it's a text baseline for diffing) |
| 1706 | page.on('framenavigated', (frame) => { |
| 1707 | if (frame === page.mainFrame()) { |
| 1708 | // Find the TabSession for this page and clear its per-tab state |
| 1709 | for (const session of this.tabSessions.values()) { |
| 1710 | if (session.page === page) { |
| 1711 | session.onMainFrameNavigated(); |
| 1712 | break; |
| 1713 | } |
| 1714 | } |
| 1715 | } |
| 1716 | }); |
| 1717 | |
| 1718 | // ─── Dialog auto-handling (prevents browser lockup) ───── |
| 1719 | page.on('dialog', async (dialog) => { |
| 1720 | const entry: DialogEntry = { |
| 1721 | timestamp: Date.now(), |
| 1722 | type: dialog.type(), |
| 1723 | message: dialog.message(), |
| 1724 | defaultValue: dialog.defaultValue() || undefined, |
| 1725 | action: this.dialogAutoAccept ? 'accepted' : 'dismissed', |
| 1726 | response: this.dialogAutoAccept ? (this.dialogPromptText ?? undefined) : undefined, |
| 1727 | }; |
| 1728 | addDialogEntry(entry); |
| 1729 | |
| 1730 | try { |
| 1731 | if (this.dialogAutoAccept) { |
| 1732 | await dialog.accept(this.dialogPromptText ?? undefined); |
| 1733 | } else { |
| 1734 | await dialog.dismiss(); |
| 1735 | } |
| 1736 | } catch { |
| 1737 | // Dialog may have been dismissed by navigation |
| 1738 | } |
| 1739 | }); |
| 1740 | |
| 1741 | page.on('console', (msg) => { |
| 1742 | addConsoleEntry({ |
no test coverage detected