Repository navigation
docs: download links name where the visit began - #7197
Conversation
Docs download links carry the visit's source (ref, utm_*, ad-click kind), like the homepage's links. Mintlify's Card download button now carries them and the PostHog id it was dropping.
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Automations to automatically generate PRs for you. |
There was a problem hiding this comment.
🟢 Approval recommended
No active review issues block the download-attribution changes.
No open issues 🎉
What changed in this PR
Extends docs download attribution so the download site receives the visit’s original source alongside the existing PostHog visitor ID.
Changes:
- Preserves first-page campaign tags, ad-click type, and referring hostname for the tab.
- Adds source parameters at click time without overwriting source tags already on the link.
- Handles standard, middle, and Cmd/Ctrl clicks directly so Mintlify Cards retain attribution and the PostHog ID.
| File | Description |
|---|---|
docs/download-attribution.js |
Stores landing-source attribution and updates download-click handling, including Mintlify Cards. |
Review effort: Balanced
jrusso1020
left a comment
There was a problem hiding this comment.
Reviewed at ed16eeb67a02c56eafddc64f4dd2cdef33492b63. COMMENT only.
The query-input path is bounded to the five campaign keys, an ad-click kind and referring hostname (docs/download-attribution.js:69–92). URLSearchParams encodes values rather than interpolating URL text, and the destination check fixes HTTPS, host, empty port and /studio/download (:106–118). A value containing &ph_id=…&redirect=… remains one campaign value and cannot change the destination or create those parameters.
Privacy/identity: ph_id in this UUID-validated handoff is the SDK's stable pseudonymous analytics identifier, not a credential or an email. The existing UUID shape check, loaded-script check and opt-out/DNT/GPC rules still gate its addition; the change extends that existing handoff to Card navigation. The actual download consumer revalidates UUID shape, uses it only for guest site-event attribution, preserves the server-derived/signed-in identity, and does not copy it onto the installer URL (hyperframes-internal/packages/demo-next/src/app/studio/download/route.ts:74–88, lib/auth/analyticsPerson.ts:18). URL/access-log exposure remains inherent in this handoff; this is not an authentication mechanism. Source campaign tags are still forwarded when no analytics ID is allowed, as the implementation intends.
Nit — re-filter restored state: docs/download-attribution.js:124–126 replays every key from hf_docs_landing, whereas the initial URL collector is allowlisted. A deliberately pre-populated storage value can inject extra query keys (including ph_id) even while the normal ID path is opted out. No remote URL-only path to write that storage was found; it requires existing same-origin storage control, and the fixed target/download server do not turn it into an open redirect or authorization change. Cheap hardening: replay only CAMPAIGN/ref/ad_click and validate ad_click again, keeping ph_id owned exclusively by its consent/UUID path.
Validation: seven native Chromium scenarios exercised encoding/unknown-key exclusion, host-only referrer, first-landing persistence, storage hardening boundary, UUID versus email, opt-out and noreferrer behavior. Original anchor hrefs remain untagged in the intercepted navigation path; new tabs use noopener. These used the actual script with synthetic docs markup and intercepted window opening, not the full Mintlify React Card runtime. Shift/Alt Card behavior is explicitly excluded by the author and remains unverified here; no new Important issue found. Prior Copilot review read; no duplicate inline findings.
Need: yes · Size: ok · Reuse: existing download handoff and URLSearchParams · Simpler: already small; filter persisted keys at restore · Structure: fine
Risk: low — fixed-destination attribution metadata with an existing pseudonymous ID handoff.
Verdict: COMMENT
Reasoning: No verified blocking regression in the new query/click path. The storage-restore allowlist is nonblocking hardening; no approval stamp is submitted.
— Rames (hf-review)
There was a problem hiding this comment.
Verdict: REQUEST CHANGES · 1 blocker · 0 important · 1 nit · ed16eeb6
The new click takeover stops Mintlify's own navbar Download CTA event (docs.navitem.cta_click / cta_click, ~250/day), and a saved insight is built on it. Details and a narrower fix ↓ inline. The rest checks out: the param names and NOT_A_SOURCE match what /studio/download parses (downloadAttribution.ts landingSource), a tag the link already carries keeps its value, and the typed-address case returns before any takeover.
Scope
Read docs/download-attribution.js end to end at head. Cross-checked against hyperframes-internal packages/demo-next/src/lib/downloadAttribution.ts. Read Mintlify's production bundle on hyperframes.heygen.com, where the Topbar CTA is a Next <Link onClick={() => track("docs.navitem.cta_click")}>. Event counts come from the Hyperframes PostHog project (356858). CI green; Copilot found nothing.
— Genesis
| var newTab = event.button === 1 || event.metaKey || event.ctrlKey || link.target === "_blank"; | ||
| if ((event.button === 0 || event.button === 1) && !event.shiftKey && !event.altKey) { | ||
| event.preventDefault(); | ||
| event.stopPropagation(); |
There was a problem hiding this comment.
blocker: this stopPropagation() (document, capture phase) runs for every download link, so React never sees the click. That is how the Card stops opening twice, but it also mutes Mintlify's navbar CTA tracking. In the live bundle the Topbar button is <Link target="_blank" onClick={() => track("docs.navitem.cta_click", {name, url, type: "button"})}>, and the mobile NavItem passes analytics: {event: "docs.navitem.cta_click"}. With PostHog loaded nearly every click has a ph_id to add, so nearly every navbar click is taken over and docs.navitem.cta_click + legacy cta_click (about 250/day each over the last 10 days) drop to roughly 0. Insight 13318166, "Docs pages where people click Download Studio (last completed week)", is built on cta_click and would go silently flat.
Only the Card needs the takeover, and its anchor is identifiable: data-client-link + display: contents. The navbar, footer and in-text links have neither. Suggest keeping the href swap (the pre-PR path) for everything else:
if (link.hasAttribute("data-client-link") && (event.button === 0 || event.button === 1) && !event.shiftKey && !event.altKey) {Please re-run the navbar case to confirm cta_click still fires and only one tab opens.
nit: the takeover also turns a Cmd/Ctrl-click (background tab) into a foreground window.open. Scoping it to the Card limits that to the Card only.
There was a problem hiding this comment.
Fixed in b9387fe, as suggested: only an anchor with data-client-link (the Card) is taken over; the navbar, footer and in-text links keep the href swap and the browser follows them. Re-ran in Chrome against mint dev: the navbar click reaches the page's own click handlers (a window-level listener fires), opens one tab, and requests ?src=docs-navbar&ref=github.com&ph_id=…; a Cmd-click on a text link follows the tagged href; the Card still opens once with its tags. Also applied the storage nit: a restored source only replays utm_*, ref and a known ad_click kind.
There was a problem hiding this comment.
✅ Fixed in b9387fe: only the Card (data-client-link) is taken over now. The navbar keeps the href swap, so the Next <Link> onClick (docs.navitem.cta_click) runs, and the browser follows the swapped href once dispatch finishes.
…s still run Stopping every download click hid the navbar CTA's docs.navitem.cta_click. Only Mintlify's Card (data-client-link) ignores a changed href; every other link is followed by the browser from the tagged href, as before. A source restored from storage carries only its own names.
…eview Adopts heygen-com#7199 (delivery-PCM duration normalization), heygen-com#7194 (browser install retries while Windows holds a file), the Studio marquee and timeline range-selection series, waapi initial-state, lint media and CLI check/contrast fixes, and skills docs. Not adopted with ledger reasons: heygen-com#7203, heygen-com#7207, heygen-com#7202, heygen-com#7197; limitDeliveryPeaks and the neverStyledStarts cache are left out because the fork lacks their prerequisites. Watermark advances to aa34cff / PR heygen-com#7190. Co-authored-by: WaterrrForever <[email protected]> Co-authored-by: Miguel Ángel <[email protected]> Co-authored-by: kevin <[email protected]> Co-authored-by: Jason <[email protected]> Co-authored-by: cooleryu <[email protected]> Co-authored-by: Vincent Yip <[email protected]> Co-authored-by: Vance Ingalls <[email protected]> Co-Authored-By: Claude Opus 5.5 <[email protected]>
What
Docs download links now say where the visit began (
ref,utm_*, the kind of ad click id), the same way the homepage's download links already do. Mintlify's<Card>download button (top of Quickstart) now carries them too, plus the PostHog id (ph_id) it was silently dropping.Why
A download clicked from the docs reached the download page with no idea how the visitor found HyperFrames: the click's Referer is the docs themselves, or nothing at all, since the navbar button is
rel="noreferrer". The homepage already passes this along; the docs didn't.Related work
Refs #6468 (docs download links carry the PostHog id).
How
docs/download-attribution.jskeeps the first docs page's source for the tab (sessionStorage) and adds it to download links at click time. A tag the link already names keeps its value; only the referring site's host is sent, never its path. Mintlify's<Card>opens its ownhrefprop from React, ignoring a changedhref, so a plain, middle or Cmd/Ctrl click on a download link is followed by the script itself; Shift/Alt clicks are left to the browser.Test plan
Real Chrome against
mint dev, with the download request captured:?src=docs-quickstart&ref=www.google.com&ph_id=…?src=docs-quickstart-top&ref=www.google.com&ph_id=…utm_source,gclid) → Intro → Quickstart…&utm_source=chatgpt.com&ad_click=gclid&ref=chatgpt.com…?src=docs-navbar&ref=github.com…Not covered: Shift/Alt-click on a Card (left to the browser, so no tags), phones.