Skip to content

docs: download links name where the visit began - #7197

Merged
ukimsanov merged 3 commits into
mainfrom
docs/download-landing-source
Nov 5, 2026
Merged

ukimsanov merged 3 commits into
mainfrom
docs/download-landing-source

Conversation

@ukimsanov

Copy link
Copy Markdown
Collaborator

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.js keeps 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 own href prop from React, ignoring a changed href, 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:

Case Request
Google → Quickstart, text link ?src=docs-quickstart&ref=www.google.com&ph_id=…
Google → Quickstart, Card ?src=docs-quickstart-top&ref=www.google.com&ph_id=…
ChatGPT ad (utm_source, gclid) → Intro → Quickstart …&utm_source=chatgpt.com&ad_click=gclid&ref=chatgpt.com…
GitHub → navbar button ?src=docs-navbar&ref=github.com…
Typed address no source added
Card middle-click, text link Cmd-click tags kept, one request each

Not covered: Shift/Alt-click on a Card (left to the browser, so no tags), phones.

  • Unit tests added/updated
  • Manual testing performed
  • Documentation updated (if applicable)
  • Comments follow CONTRIBUTING.md "Comments": they say why, not what

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.
Copilot AI balanced review requested due to automatic review settings November 5, 2026 00:50
@mintlify

mintlify Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
hyperframes 🟢 Ready View Preview Nov 5, 2026, 4:19 AM

💡 Tip: Enable Automations to automatically generate PRs for you.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 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 jrusso1020 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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.

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE — 1 resolved, 0 open, 0 new · ed16eeb6 → b9387fea

— Genesis

@ukimsanov
ukimsanov added this pull request to the merge queue Nov 5, 2026
Merged via the queue into main with commit 4ba47aa Nov 5, 2026
64 checks passed
@ukimsanov
ukimsanov deleted the docs/download-landing-source branch November 5, 2026 05:01
SanHsien added a commit to SanHsien/hyperframes that referenced this pull request Nov 5, 2026
…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]>

This branch was successfully deployed

1 active deployment
staging - docs — b9387fea Deployed Nov 5, 2026 by mintlify[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants