Skip to content

Commit 84b98d4

Browse files
committed
docs: integrate tracking for download and navigation events, add TIANJI analytics script, and create TrackedButton/TrackedLink components for enhanced user interaction tracking
1 parent deb9901 commit 84b98d4

9 files changed

Lines changed: 152 additions & 15 deletions

File tree

website/app/download-browser/DownloadRedirect.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { useEffect, useState } from 'react';
44
import { LucideIcon } from '@/components/ui/LucideIcon';
5+
import { trackEvent } from '@/lib/analytics';
56
import styles from './download.module.css';
67

78
interface PlatformInfo {
@@ -60,6 +61,11 @@ export function DownloadRedirect() {
6061
setTargetUrl(target);
6162
setStatus('ready');
6263

64+
trackEvent('download_redirect', {
65+
platform: key,
66+
version: cfg.version,
67+
});
68+
6369
// Redirect on the next tick so the "preparing your download" message
6470
// is rendered before navigation. `replace` keeps the back button sane.
6571
window.setTimeout(() => {

website/app/layout.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import type { Metadata } from 'next';
2+
import Script from 'next/script';
23
import './globals.css';
34
import { SITE_URL } from '@/lib/version';
5+
import { TIANJI_WEBSITE_ID } from '@/lib/analytics';
46

57
export const metadata: Metadata = {
68
metadataBase: new URL(SITE_URL),
@@ -54,6 +56,11 @@ export default function RootLayout({ children }: { children: React.ReactNode })
5456
return (
5557
<html lang="en">
5658
<body>{children}</body>
59+
<Script
60+
src="https://app.tianji.dev/tracker.js"
61+
data-website-id={TIANJI_WEBSITE_ID}
62+
strategy="afterInteractive"
63+
/>
5764
</html>
5865
);
5966
}

website/components/DownloadCTA.tsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import {
33
CHROMIUM_VERSION,
44
GITHUB_URL,
55
} from '@/lib/version';
6-
import { Button } from './ui/Button';
6+
import { TrackedButton } from './ui/TrackedButton';
7+
import { TrackedLink } from './ui/TrackedLink';
78
import { LucideIcon } from './ui/LucideIcon';
89
import styles from './DownloadCTA.module.css';
910

@@ -14,20 +15,27 @@ export function DownloadCTA() {
1415
<p className={styles.sub}>
1516
v{PRODUCT_VERSION} · Built on Chromium {CHROMIUM_VERSION}
1617
</p>
17-
<Button href="/download" variant="primary">
18+
<TrackedButton
19+
href="/download"
20+
variant="primary"
21+
event="download_click"
22+
eventPayload={{ source: 'cta' }}
23+
>
1824
<LucideIcon name="download" size={16} aria-hidden />
1925
Download
20-
</Button>
26+
</TrackedButton>
2127
<p className={styles.hint}>
2228
macOS (Apple Silicon) available · Linux and Windows coming soon ·{' '}
23-
<a
29+
<TrackedLink
2430
href={GITHUB_URL}
2531
target="_blank"
2632
rel="noopener noreferrer"
2733
className={styles.hintLink}
34+
event="github_click"
35+
eventPayload={{ source: 'cta_hint' }}
2836
>
2937
★ Star on GitHub for updates
30-
</a>
38+
</TrackedLink>
3139
</p>
3240
</section>
3341
);

website/components/Hero.tsx

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {
33
CHROMIUM_VERSION,
44
GITHUB_URL,
55
} from '@/lib/version';
6-
import { Button } from './ui/Button';
6+
import { TrackedButton } from './ui/TrackedButton';
77
import { LucideIcon } from './ui/LucideIcon';
88
import { HeroAnimation } from './ui/HeroAnimation';
99
import styles from './Hero.module.css';
@@ -15,14 +15,25 @@ export function Hero() {
1515
<h1 className={styles.h1}>An opinionated browser, built on Chromium.</h1>
1616
<p className={styles.subtitle}>Vertical tabs, soft corners, content first.</p>
1717
<div className={styles.ctas}>
18-
<Button href="/download" variant="primary">
18+
<TrackedButton
19+
href="/download"
20+
variant="primary"
21+
event="download_click"
22+
eventPayload={{ source: 'hero' }}
23+
>
1924
<LucideIcon name="download" size={16} aria-hidden />
2025
Download
21-
</Button>
22-
<Button href={GITHUB_URL} variant="ghost" external>
26+
</TrackedButton>
27+
<TrackedButton
28+
href={GITHUB_URL}
29+
variant="ghost"
30+
external
31+
event="github_click"
32+
eventPayload={{ source: 'hero' }}
33+
>
2334
<LucideIcon name="star" size={16} aria-hidden />
2435
Star on GitHub
25-
</Button>
36+
</TrackedButton>
2637
</div>
2738
<p className={styles.versionHint}>
2839
Latest: v{PRODUCT_VERSION} · Chromium {CHROMIUM_VERSION}

website/components/TopNav.tsx

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
'use client';
2+
13
/* eslint-disable @next/next/no-img-element */
24
import Link from 'next/link';
35
import { GITHUB_URL } from '@/lib/version';
46
import { Button } from './ui/Button';
57
import { LucideIcon } from './ui/LucideIcon';
68
import { GitHubIcon } from './ui/BrandIcons';
9+
import { trackEvent } from '@/lib/analytics';
710
import styles from './TopNav.module.css';
811

912
export function TopNav() {
@@ -32,10 +35,18 @@ export function TopNav() {
3235
<span className={styles.brandWord}>Dao</span>
3336
</a>
3437
<div className={styles.links}>
35-
<Link href="/#features" className={styles.link}>
38+
<Link
39+
href="/#features"
40+
className={styles.link}
41+
onClick={() => trackEvent('nav_link_click', { target: 'features' })}
42+
>
3643
<span className={styles.linkText}>Features</span>
3744
</Link>
38-
<Link href="/agent" className={styles.link}>
45+
<Link
46+
href="/agent"
47+
className={styles.link}
48+
onClick={() => trackEvent('nav_link_click', { target: 'agent' })}
49+
>
3950
<span className={styles.linkText}>AI Agent</span>
4051
</Link>
4152
<a
@@ -44,10 +55,15 @@ export function TopNav() {
4455
rel="noopener noreferrer"
4556
className={styles.link}
4657
aria-label="GitHub repository"
58+
onClick={() => trackEvent('github_click', { source: 'nav' })}
4759
>
4860
<GitHubIcon size={18} aria-hidden />
4961
</a>
50-
<Button href="/download" variant="primary">
62+
<Button
63+
href="/download"
64+
variant="primary"
65+
onClick={() => trackEvent('download_click', { source: 'nav' })}
66+
>
5167
<LucideIcon name="download" size={16} aria-hidden />
5268
Download
5369
</Button>

website/components/ui/BrowserFrameMockup.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ function ArticlePage({
271271
<article className={styles.article}>
272272
<div className={styles.articleEyebrow}>ESSAY · 8 MIN READ</div>
273273
<h1 className={styles.articleTitle}>Why we still build browsers</h1>
274-
<div className={styles.articleByline}>by Jamie Lin · April 2026</div>
274+
<div className={styles.articleByline}>by Lyon Chen · April 2026</div>
275275
<p className={styles.articleBody}>
276276
The browser has been with us for thirty years, and somehow it has never
277277
stopped feeling unfinished. Tabs sprawl. Sidebars clutter. Search bars
@@ -402,7 +402,7 @@ function AgentMockup() {
402402
<article className={styles.article}>
403403
<div className={styles.articleEyebrow}>ESSAY · 8 MIN READ</div>
404404
<h1 className={styles.articleTitle}>Why we still build browsers</h1>
405-
<div className={styles.articleByline}>by Jamie Lin · April 2026</div>
405+
<div className={styles.articleByline}>by Lyon Chen · April 2026</div>
406406
<p className={styles.articleBody}>
407407
The browser has been with us for thirty years, and somehow it has never
408408
stopped feeling unfinished. Tabs sprawl. Sidebars clutter. Search bars
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
'use client';
2+
3+
import { Button, type ButtonProps } from './Button';
4+
import { trackEvent, type AnalyticsEvent } from '@/lib/analytics';
5+
6+
type EventPayload = Record<string, string | number | boolean>;
7+
8+
interface TrackedButtonProps extends ButtonProps {
9+
event: AnalyticsEvent;
10+
eventPayload?: EventPayload;
11+
}
12+
13+
export function TrackedButton({
14+
event,
15+
eventPayload,
16+
onClick,
17+
...rest
18+
}: TrackedButtonProps) {
19+
return (
20+
<Button
21+
{...rest}
22+
onClick={(e) => {
23+
trackEvent(event, eventPayload);
24+
onClick?.(e);
25+
}}
26+
/>
27+
);
28+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
'use client';
2+
3+
import type { AnchorHTMLAttributes } from 'react';
4+
import { trackEvent, type AnalyticsEvent } from '@/lib/analytics';
5+
6+
type EventPayload = Record<string, string | number | boolean>;
7+
8+
interface TrackedLinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
9+
event: AnalyticsEvent;
10+
eventPayload?: EventPayload;
11+
}
12+
13+
export function TrackedLink({
14+
event,
15+
eventPayload,
16+
onClick,
17+
children,
18+
...rest
19+
}: TrackedLinkProps) {
20+
return (
21+
<a
22+
{...rest}
23+
onClick={(e) => {
24+
trackEvent(event, eventPayload);
25+
onClick?.(e);
26+
}}
27+
>
28+
{children}
29+
</a>
30+
);
31+
}

website/lib/analytics.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
export const TIANJI_WEBSITE_ID = 'cmphf1o19dv4k78rcxz79qz3x';
2+
3+
export type AnalyticsEvent =
4+
| 'download_click'
5+
| 'download_redirect'
6+
| 'github_click'
7+
| 'nav_link_click';
8+
9+
type EventPayload = Record<string, string | number | boolean>;
10+
11+
interface TianjiTracker {
12+
track: (name: string, payload?: EventPayload) => void;
13+
}
14+
15+
declare global {
16+
interface Window {
17+
tianji?: TianjiTracker;
18+
}
19+
}
20+
21+
export function trackEvent(name: AnalyticsEvent, payload?: EventPayload): void {
22+
if (typeof window === 'undefined') return;
23+
const tracker = window.tianji;
24+
if (!tracker || typeof tracker.track !== 'function') return;
25+
try {
26+
tracker.track(name, payload);
27+
} catch {
28+
// Swallow tracker errors — analytics must never break the UI.
29+
}
30+
}

0 commit comments

Comments
 (0)