From 33bbcf0db119089c37bc317afafdcd95fbf58ee2 Mon Sep 17 00:00:00 2001 From: Lee Edwards Date: Wed, 12 Aug 2026 12:01:46 -0700 Subject: [PATCH] Stop a resize from re-counting a deep-link arrival MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enabling analytics on deep links in #128 made every window resize fire a second commandSent event. xterm clears its buffer on resize, so resizeListener reruns the deep link to redraw the output — that is the same visit, not a new arrival. Caught it on production: one load of /portfolio/chargelab/ had already logged two events. It would have inflated exactly the numbers the tracking was added to produce, and worst on mobile, where showing and hiding the address bar fires resize. The replay path now passes trackAnalytics: false, matching the history replay immediately below it, which calls term.command directly for the same reason. Co-Authored-By: Claude Opus 5 --- js/terminal-ext.js | 11 ++++++++--- tests/terminal-ext.test.js | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/js/terminal-ext.js b/js/terminal-ext.js index bedb5379..3ecac058 100644 --- a/js/terminal-ext.js +++ b/js/terminal-ext.js @@ -352,7 +352,7 @@ const extend = (term) => { if (typeof preloadASCIIArt === "function") { window.scheduleIdleTask(() => preloadASCIIArt(), 1500); } - term.runDeepLink(); + term.runDeepLink({ replay: true }); for (const c of term.history) { term.prompt("\r\n", ` ${c}\r\n`); term.command(c); @@ -408,7 +408,12 @@ const extend = (term) => { }; // Runs the deep-link command parsed from the URL hash on page load. - term.runDeepLink = () => { + // + // `replay` is set by the resize listener, which reruns this to redraw a + // buffer xterm cleared. That is the same visit, not a new arrival, so it must + // not be counted again — the history replay right below it calls term.command + // directly rather than executeCommandLine for exactly this reason. + term.runDeepLink = ({ replay = false } = {}) => { if (term.deepLink != "") { term.executeCommandLine(term.deepLink, { addToHistory: false, @@ -417,7 +422,7 @@ const extend = (term) => { // Deep links are how visitors now reach a specific company or person, // so these are the arrivals worth counting. Fragments never fire a // pageview of their own, so without this they would be invisible. - trackAnalytics: true, + trackAnalytics: !replay, }).catch((error) => { console.error("Deep link failed", error); }); diff --git a/tests/terminal-ext.test.js b/tests/terminal-ext.test.js index 12982f64..3162b37f 100644 --- a/tests/terminal-ext.test.js +++ b/tests/terminal-ext.test.js @@ -126,6 +126,25 @@ describe("terminal-ext", () => { }); }); + it("does not re-count a deep link when a resize replays it", () => { + // xterm clears its buffer on resize, so resizeListener reruns the deep link + // to redraw the output. That is the same visit — counting it again inflates + // every deep-link arrival by one per resize, and mobile browsers fire + // resize just from showing and hiding the address bar. + const { extend } = loadTerminalExt(); + const term = createTerm(); + + extend(term); + term.deepLink = "whois lee"; + term.executeCommandLine = vi.fn(() => Promise.resolve()); + term.runDeepLink({ replay: true }); + + expect(term.executeCommandLine).toHaveBeenCalledWith( + "whois lee", + expect.objectContaining({ trackAnalytics: false }) + ); + }); + it.each([ ["#jobs", "jobs"], ["#whois-root", "whois root"],