import { useEffect, useMemo, useRef, useState } from 'react'
import Tree, { prewarmTreeCanvas } from '../Tree/Tree'
import FallingLeaves from '../Tree/FallingLeaves'
const numberFormat = new Intl.NumberFormat('en-GB')
const dateFormat = new Intl.DateTimeFormat('en-GB', {
dateStyle: 'medium',
timeStyle: 'short',
})
const apiEndpoints = {
health: '/health',
uptime: '/api/uptime',
viewers: '/api/viewers',
viewerEvent: '/api/viewers/event',
viewerDelete: '/api/viewers/delete',
teams: '/api/tss/leaderboard/teams?limit=100',
homeTeams: '/api/tss/leaderboard/teams?limit=4',
teamsHealth: '/api/tss/leaderboard/teams?limit=1',
recentGames: '/api/tss/games/recent?limit=50',
game: (gameId) => `/api/tss/games/${encodeURIComponent(gameId)}`,
gameLogs: (gameId) => `/api/tss/games/${encodeURIComponent(gameId)}/logs`,
resolve: (name) => `/api/tss/teams/resolve?name=${encodeURIComponent(name)}`,
searchTeams: (name) => `/api/tss/teams/search?q=${encodeURIComponent(name)}&limit=10`,
detail: (name) => `/api/tss/teams/${encodeURIComponent(name)}`,
games: (name) => `/api/tss/teams/${encodeURIComponent(name)}/games`,
player: (uid) => `/api/tss/player/${encodeURIComponent(uid)}`,
}
const navItems = [
{ path: '/', label: 'Home' },
{ path: '/teams', label: 'Team Leaderboard' },
{ path: '/battle-logs', label: 'Battle Logs' },
{ path: '/viewers', label: 'Viewers' },
{ path: '/docs', label: 'Setup' },
]
const analyticsConsentKey = 'tssbot.analyticsConsent'
const analyticsPreferencesKey = 'tssbot.analyticsPreferences'
const analyticsPreferencesCookie = 'tssbot_analytics_preferences'
const analyticsVisitorKey = 'tssbot.analyticsVisitor'
const analyticsConsentVersion = 3
const themePreferenceKey = 'tssbot.theme'
const themePreferenceCookie = 'tssbot_theme'
const liveRefreshMs = 15000
const siteVersion = 'v1'
const turnstileSiteKey = import.meta.env.VITE_TURNSTILE_SITE_KEY || ''
const defaultAnalyticsPreferences = {
chosen: false,
analytics: false,
device: false,
display: false,
locale: false,
referrer: false,
diagnostics: false,
version: analyticsConsentVersion,
}
async function fetchJson(path, signal) {
const response = await fetch(path, {
signal,
headers: { Accept: 'application/json' },
})
const body = await response.json().catch(() => null)
if (!response.ok) {
throw new Error(body?.error || `Request failed with ${response.status}`)
}
return body
}
function parseRoute(pathname = window.location.pathname) {
if (pathname === '/') return { page: 'home', teamName: '' }
if (pathname === '/teams') return { page: 'teams', teamName: '' }
if (pathname === '/uptime') return { page: 'uptime', teamName: '' }
if (pathname === '/viewers') return { page: 'viewers', teamName: '' }
if (pathname === '/privacy') return { page: 'privacy', teamName: '' }
if (pathname === '/docs') return { page: 'docs', teamName: '' }
if (pathname.startsWith('/players/')) {
const uid = decodeURIComponent(pathname.slice('/players/'.length))
return { page: 'player', teamName: '', uid }
}
if (pathname.startsWith('/games/')) {
const gameId = decodeURIComponent(pathname.slice('/games/'.length))
return { page: 'game', teamName: '', gameId }
}
if (pathname.startsWith('/teams/')) {
const teamName = decodeURIComponent(pathname.slice('/teams/'.length))
return { page: 'team', teamName }
}
if (pathname === '/battle-logs' || pathname === '/live') return { page: 'battle-logs', teamName: '' }
return { page: 'home', teamName: '' }
}
function teamPath(name) {
return `/teams/${encodeURIComponent(name)}`
}
function gamePath(gameId) {
return `/games/${encodeURIComponent(gameId)}`
}
function formatNumber(value) {
return numberFormat.format(Number(value || 0))
}
function formatMatchSize(playerCount) {
const count = formatNumber(playerCount)
return `${count}v${count}`
}
function formatDate(timestamp) {
if (!timestamp) return 'Unknown time'
return dateFormat.format(new Date(Number(timestamp) * 1000))
}
function formatDuration(seconds) {
const total = Math.round(Number(seconds || 0))
if (!total) return ''
const m = Math.floor(total / 60)
const s = total % 60
if (!m) return `${s}s`
return `${m}m ${s}s`
}
function gameParticipants(game) {
const winner = displayTeamName(game?.winning_team)
const loser = displayTeamName(game?.losing_team)
if (winner || loser) {
return [
winner ? { name: winner, result: 'win' } : null,
loser ? { name: loser, result: 'loss' } : null,
].filter(Boolean)
}
const fallbackName = game?.team_name || ''
if (!fallbackName) return []
return [
{
name: fallbackName,
result: String(game?.result || '').toLowerCase() === 'win' ? 'win' : 'loss',
},
]
}
function displayTeamName(value) {
return String(value || '').trim()
}
function ParticipantNames({ participants }) {
if (!participants.length) {
return
Participants unknown
}
return (
{participants.map((participant) => (
{participant.name}
))}
)
}
function bestTeamName(team) {
return team?.name || ''
}
function teamDetailLooksReal(detail) {
if (!detail || typeof detail !== 'object') return false
const summary = detail.team_summary || detail.squadron_summary || null
const players = Array.isArray(detail.players) ? detail.players : []
const hasStableId = Boolean(detail.clan_id || detail.id || detail.team_id || detail.squadron_id)
const hasRoster = players.length > 0
const hasActivity = Boolean(
Number(summary?.player_count || 0) > 0 ||
Number(summary?.total_battles || 0) > 0 ||
Number(summary?.wins || 0) > 0 ||
Number(summary?.points?.total_points || summary?.total_points || 0) > 0,
)
return hasStableId || hasRoster || hasActivity
}
function canonicalTeamName(detail, fallback = '') {
return detail?.name || fallback
}
function normalizeAnalyticsPreferences(value) {
if (!value || typeof value !== 'object') return { ...defaultAnalyticsPreferences }
return {
chosen: Boolean(value.chosen) && value.version === analyticsConsentVersion,
analytics: Boolean(value.analytics),
device: Boolean(value.device),
display: Boolean(value.display),
locale: Boolean(value.locale),
referrer: Boolean(value.referrer),
diagnostics: Boolean(value.diagnostics),
version: analyticsConsentVersion,
}
}
function browserPrivacySignalEnabled() {
return Boolean(
navigator.globalPrivacyControl ||
navigator.doNotTrack === '1' ||
window.doNotTrack === '1',
)
}
function browserConnectionDetails() {
const connection =
navigator.connection || navigator.mozConnection || navigator.webkitConnection || null
if (!connection) return {}
return {
effective_type: connection.effectiveType || '',
downlink_mbps: Number.isFinite(connection.downlink) ? connection.downlink : null,
rtt_ms: Number.isFinite(connection.rtt) ? connection.rtt : null,
save_data: Boolean(connection.saveData),
}
}
function browserDiagnosticsMetadata() {
return {
cookies_enabled: navigator.cookieEnabled,
do_not_track: navigator.doNotTrack || window.doNotTrack || '',
global_privacy_control: Boolean(navigator.globalPrivacyControl),
online: navigator.onLine,
platform: navigator.platform || '',
vendor: navigator.vendor || '',
max_touch_points: navigator.maxTouchPoints || 0,
hardware_concurrency: navigator.hardwareConcurrency || null,
device_memory_gb: navigator.deviceMemory || null,
webdriver: Boolean(navigator.webdriver),
history_length: window.history.length,
visibility_state: document.visibilityState,
connection: browserConnectionDetails(),
}
}
function readCookie(name) {
try {
const match = document.cookie
.split('; ')
.find((item) => item.startsWith(`${encodeURIComponent(name)}=`))
return match ? decodeURIComponent(match.split('=').slice(1).join('=')) : ''
} catch {
return ''
}
}
function writeCookie(name, value) {
try {
const sixMonths = 60 * 60 * 24 * 180
document.cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}; Max-Age=${sixMonths}; Path=/; SameSite=Lax`
} catch {
// Some browsers block cookies; local storage and in-memory state still work.
}
}
function normalizeThemePreference(value) {
return value === 'dark' ? 'dark' : 'light'
}
function storedThemePreference() {
const bootTheme = window.__TSS_BOOT_PREFERENCES__?.theme
if (bootTheme) return normalizeThemePreference(bootTheme)
const cookieValue = readCookie(themePreferenceCookie)
if (cookieValue) return normalizeThemePreference(cookieValue)
try {
return normalizeThemePreference(window.localStorage.getItem(themePreferenceKey))
} catch {
return 'light'
}
}
function persistThemePreference(theme) {
const normalized = normalizeThemePreference(theme)
writeCookie(themePreferenceCookie, normalized)
try {
window.localStorage.setItem(themePreferenceKey, normalized)
} catch {
// Cookies still remember the theme when local storage is blocked.
}
return normalized
}
function storedAnalyticsPreferences() {
const bootPreferences = window.__TSS_BOOT_PREFERENCES__?.analyticsPreferences
if (bootPreferences) return normalizeAnalyticsPreferences(bootPreferences)
const cookieValue = readCookie(analyticsPreferencesCookie)
if (cookieValue) {
try {
return normalizeAnalyticsPreferences(JSON.parse(cookieValue))
} catch {
// Fall back through the older storage formats below.
}
}
try {
const stored = window.localStorage.getItem(analyticsPreferencesKey)
if (stored) return normalizeAnalyticsPreferences(JSON.parse(stored))
} catch {
// Fall back through the older consent flag below.
}
try {
const legacyConsent = window.localStorage.getItem(analyticsConsentKey) || ''
if (legacyConsent === 'analytics') {
return {
...defaultAnalyticsPreferences,
chosen: true,
analytics: true,
device: true,
display: true,
locale: true,
referrer: true,
diagnostics: true,
}
}
if (legacyConsent === 'declined') {
return { ...defaultAnalyticsPreferences, chosen: true, analytics: false }
}
} catch {
// Use the default prompt state.
}
return { ...defaultAnalyticsPreferences }
}
function persistAnalyticsPreferences(preferences) {
const normalized = normalizeAnalyticsPreferences(preferences)
const serialized = JSON.stringify(normalized)
writeCookie(analyticsPreferencesCookie, serialized)
try {
window.localStorage.setItem(analyticsPreferencesKey, serialized)
window.localStorage.setItem(
analyticsConsentKey,
normalized.analytics ? 'analytics' : 'declined',
)
} catch {
// Local storage can be blocked; the cookie and in-memory choice still apply.
}
return normalized
}
function stableId(storageKey) {
try {
const existing = window.localStorage.getItem(storageKey)
if (existing) return existing
const id =
window.crypto?.randomUUID?.() ||
`${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`
window.localStorage.setItem(storageKey, id)
return id
} catch {
return `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`
}
}
function storedVisitorId(storageKey) {
try {
return window.localStorage.getItem(storageKey) || ''
} catch {
return ''
}
}
function forgetVisitorId(storageKey) {
try {
window.localStorage.removeItem(storageKey)
} catch {
// Storage may be unavailable; nothing else to clear locally.
}
}
const analyticsSessionId =
window.crypto?.randomUUID?.() ||
`${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`
function browserName() {
const ua = navigator.userAgent
if (ua.includes('Edg/')) return 'Microsoft Edge'
if (ua.includes('OPR/')) return 'Opera'
if (ua.includes('Firefox/')) return 'Firefox'
if (ua.includes('Chrome/') && !ua.includes('Chromium/')) return 'Chrome'
if (ua.includes('Safari/') && ua.includes('Version/')) return 'Safari'
return 'Unknown'
}
function operatingSystem() {
const ua = navigator.userAgent
if (ua.includes('Windows NT')) return 'Windows'
if (ua.includes('Android')) return 'Android'
if (/iPhone|iPad|iPod/.test(ua)) return 'iOS'
if (ua.includes('Mac OS X')) return 'macOS'
if (ua.includes('Linux')) return 'Linux'
return 'Unknown'
}
function deviceType() {
const ua = navigator.userAgent
if (/Mobi|Android|iPhone|iPod/.test(ua)) return 'Mobile'
if (/iPad|Tablet/.test(ua)) return 'Tablet'
return 'Desktop'
}
function routeLabel(route) {
if (route.page === 'team' && route.teamName) return `Team: ${route.teamName}`
if (route.page === 'teams') return 'Team Leaderboard'
if (route.page === 'battle-logs') return 'Battle Logs'
if (route.page === 'game') return route.gameId ? `Game ${route.gameId}` : 'Game'
if (route.page === 'uptime') return 'Uptime'
if (route.page === 'viewers') return 'viewers'
if (route.page === 'privacy') return 'Privacy notice'
if (route.page === 'docs') return 'Docs'
if (route.page === 'player') return route.uid ? `Player ${route.uid}` : 'Player'
return 'Home'
}
function currentPublicOrigin() {
return window.location.origin.replace(/\/$/, '')
}
function canonicalPathForRoute(route) {
if (route.page === 'team' && route.teamName) return teamPath(route.teamName)
if (route.page === 'teams') return '/teams'
if (route.page === 'battle-logs') return '/battle-logs'
if (route.page === 'game' && route.gameId) return gamePath(route.gameId)
if (route.page === 'uptime') return '/uptime'
if (route.page === 'viewers') return '/viewers'
if (route.page === 'privacy') return '/privacy'
if (route.page === 'docs') return '/docs'
if (route.page === 'player' && route.uid) return `/players/${encodeURIComponent(route.uid)}`
return '/'
}
function seoForRoute(route, profileDetail = null) {
const teamName =
route.page === 'team'
? canonicalTeamName(profileDetail, route.teamName).trim() || route.teamName
: ''
const teamSummary = profileDetail?.team_summary || profileDetail?.squadron_summary || null
const teamPoints = Number(teamSummary?.points?.total_points || teamSummary?.total_points || 0)
const teamBattles = Number(teamSummary?.total_battles || 0)
if (route.page === 'team' && teamName) {
const stats = [
teamPoints ? `${formatNumber(teamPoints)} points` : '',
teamBattles ? `${formatNumber(teamBattles)} battles` : '',
].filter(Boolean)
return {
title: `${teamName} TSS Team Profile | Toothless' TSS Bot`,
description: stats.length
? `${teamName} TSS team profile with ${stats.join(', ')}, roster details, battle history, and recent War Thunder squadron activity.`
: `${teamName} TSS team profile with roster details, battle history, and recent War Thunder squadron activity.`,
robots: 'index, follow',
path: canonicalPathForRoute({ ...route, teamName }),
}
}
if (route.page === 'player' && route.uid) {
return {
title: `Player ${route.uid} | Toothless' TSS Bot`,
description: `TSS career stats for player ${route.uid} — battles, win rate, kills, and teams seen with.`,
robots: 'noindex, follow',
path: canonicalPathForRoute(route),
}
}
if (route.page === 'game' && route.gameId) {
return {
title: `Game ${route.gameId} | Toothless' TSS Bot`,
description: `TSS battle log details for game ${route.gameId}, including participants, map, player counts, and combat stats.`,
robots: 'noindex, follow',
path: canonicalPathForRoute(route),
}
}
const byPage = {
teams: {
title: "TSS Team Leaderboard | Toothless' TSS Bot",
description: 'Browse the live TSS team leaderboard, compare War Thunder squadron rankings, points, players, and recent team activity.',
robots: 'index, follow',
path: '/teams',
},
'battle-logs': {
title: "TSS Battle Logs | Toothless' TSS Bot",
description: 'Read recent TSS battle logs with team names, map history, player counts, battle times, and War Thunder match context.',
robots: 'index, follow',
path: '/battle-logs',
},
uptime: {
title: "TSS Bot Uptime Status | Toothless' TSS Bot",
description: 'Check Toothless TSS Bot uptime, API health, TSS data proxy status, and recent availability history.',
robots: 'index, follow',
path: '/uptime',
},
viewers: {
title: "Viewer Analytics | Toothless' TSS Bot",
description: 'Opt-in viewer analytics for Toothless TSS Bot, including active pages, broad device signals, and privacy-safe activity trends.',
robots: 'noindex, follow',
path: '/viewers',
},
privacy: {
title: "Privacy Notice | Toothless' TSS Bot",
description: 'How Toothless TSS Bot handles cookies, opt-in analytics, viewer data, retention, deletion, and privacy rights.',
robots: 'index, follow',
path: '/privacy',
},
docs: {
title: "Docs | Toothless' TSS Bot",
description: 'Documentation and command reference for Toothless TSS Bot.',
robots: 'noindex, follow',
path: '/docs',
},
}
return byPage[route.page] || {
title: "Toothless' TSS Bot | Live TSS Leaderboards and Battle Logs",
description: 'Live War Thunder TSS team leaderboards, battle logs, team profiles, uptime, and privacy-safe viewer analytics from Toothless TSS Bot.',
robots: 'index, follow',
path: '/',
}
}
function upsertMeta(selector, createAttributes, valueAttribute, value) {
let element = document.head.querySelector(selector)
if (!element) {
element = document.createElement('meta')
Object.entries(createAttributes).forEach(([key, attributeValue]) => {
element.setAttribute(key, attributeValue)
})
document.head.appendChild(element)
}
element.setAttribute(valueAttribute, value)
}
function upsertLink(rel, href) {
let element = document.head.querySelector(`link[rel="${rel}"]`)
if (!element) {
element = document.createElement('link')
element.setAttribute('rel', rel)
document.head.appendChild(element)
}
element.setAttribute('href', href)
}
function structuredDataForSeo(seo, canonicalUrl) {
const base = currentPublicOrigin()
const items = [
{
'@context': 'https://schema.org',
'@type': 'WebSite',
name: "Toothless' TSS Bot",
url: `${base}/`,
description: "Live War Thunder TSS leaderboards, battle logs, and team profiles.",
potentialAction: {
'@type': 'SearchAction',
target: `${base}/teams/{search_term_string}`,
'query-input': 'required name=search_term_string',
},
},
{
'@context': 'https://schema.org',
'@type': 'WebPage',
name: seo.title,
url: canonicalUrl,
description: seo.description,
isPartOf: {
'@type': 'WebSite',
name: "Toothless' TSS Bot",
url: `${base}/`,
},
},
]
return JSON.stringify(items)
}
function applySeo(route, profileDetail = null) {
const seo = seoForRoute(route, profileDetail)
const canonicalUrl = `${currentPublicOrigin()}${seo.path}`
document.title = seo.title
upsertMeta('meta[name="description"]', { name: 'description' }, 'content', seo.description)
upsertMeta('meta[name="robots"]', { name: 'robots' }, 'content', seo.robots)
upsertMeta('meta[property="og:title"]', { property: 'og:title' }, 'content', seo.title)
upsertMeta('meta[property="og:description"]', { property: 'og:description' }, 'content', seo.description)
upsertMeta('meta[property="og:url"]', { property: 'og:url' }, 'content', canonicalUrl)
upsertMeta('meta[name="twitter:title"]', { name: 'twitter:title' }, 'content', seo.title)
upsertMeta('meta[name="twitter:description"]', { name: 'twitter:description' }, 'content', seo.description)
upsertLink('canonical', canonicalUrl)
let structuredData = document.getElementById('site-structured-data')
if (!structuredData) {
structuredData = document.createElement('script')
structuredData.id = 'site-structured-data'
structuredData.type = 'application/ld+json'
document.head.appendChild(structuredData)
}
structuredData.textContent = structuredDataForSeo(seo, canonicalUrl)
}
async function fetchRecentTssGames(signal) {
return fetchJson(apiEndpoints.recentGames, signal)
}
function Stat({ label, value }) {
return (
)
}
function ThemeToggle({ theme, onThemeChange }) {
const isDark = theme === 'dark'
return (
onThemeChange(isDark ? 'light' : 'dark')}
title={isDark ? 'Light mode' : 'Dark mode'}
type="button"
>
{isDark ? '☾' : '☼'}
)
}
function defaultThemeTogglePosition() {
const inset = window.innerWidth >= 640 ? 32 : 20
return {
x: window.innerWidth - inset - 36,
y: inset,
}
}
function themeToggleDockPosition(navPill) {
if (!navPill) return defaultThemeTogglePosition()
const width = navPill.offsetWidth
const height = navPill.offsetHeight
const left = (window.innerWidth - width) / 2
const top = 16
return {
x: Math.round(left + width - 48),
y: Math.round(top + (height - 36) / 2),
}
}
function ThemeToggleMover({ dockPosition, homePosition, isHome, theme, onThemeChange }) {
const moverRef = useRef(null)
useEffect(() => {
const mover = moverRef.current
if (!mover) return undefined
let cancelled = false
let tween = null
async function animate() {
const [{ gsap }, { ScrollTrigger }] = await Promise.all([
import('gsap'),
import('gsap/ScrollTrigger'),
])
if (cancelled) return
gsap.registerPlugin(ScrollTrigger)
if (!isHome) {
tween = gsap.to(mover, {
x: dockPosition.x,
y: dockPosition.y,
duration: 0.56,
ease: 'power3.out',
overwrite: true,
})
return
}
tween = gsap.fromTo(
mover,
{ x: homePosition.x, y: homePosition.y },
{
x: dockPosition.x,
y: dockPosition.y,
ease: 'none',
overwrite: true,
scrollTrigger: {
end: '+=96',
scrub: 0.35,
start: 'top top',
trigger: document.documentElement,
},
},
)
}
animate()
return () => {
cancelled = true
tween?.scrollTrigger?.kill()
tween?.kill()
}
}, [dockPosition.x, dockPosition.y, homePosition.x, homePosition.y, isHome])
const initialPosition = isHome ? homePosition : dockPosition
return (
)
}
function TurnstileWidget({ siteKey, theme = 'auto', size = 'normal', action, onVerify, onExpire, onError, resetSignal = 0 }) {
const containerRef = useRef(null)
const widgetIdRef = useRef(null)
const callbacksRef = useRef({ onVerify, onExpire, onError })
useEffect(() => {
callbacksRef.current = { onVerify, onExpire, onError }
}, [onVerify, onExpire, onError])
useEffect(() => {
if (!siteKey) return undefined
const container = containerRef.current
if (!container) return undefined
let cancelled = false
let pollTimer
const renderWidget = () => {
if (cancelled) return
if (!window.turnstile || typeof window.turnstile.render !== 'function') {
pollTimer = window.setTimeout(renderWidget, 150)
return
}
const options = {
sitekey: siteKey,
theme,
size,
callback: (token) => callbacksRef.current.onVerify?.(token),
'expired-callback': () => callbacksRef.current.onExpire?.(),
'error-callback': () => callbacksRef.current.onError?.(),
}
if (action) options.action = action
widgetIdRef.current = window.turnstile.render(container, options)
}
renderWidget()
return () => {
cancelled = true
window.clearTimeout(pollTimer)
const widgetId = widgetIdRef.current
widgetIdRef.current = null
if (widgetId != null && window.turnstile?.remove) {
try { window.turnstile.remove(widgetId) } catch { /* widget already gone */ }
}
}
}, [siteKey, theme, size, action])
useEffect(() => {
if (!resetSignal) return
const widgetId = widgetIdRef.current
if (widgetId != null && window.turnstile?.reset) {
try { window.turnstile.reset(widgetId) } catch { /* widget gone or already reset */ }
}
}, [resetSignal])
if (!siteKey) return null
return
}
function SiteGate({ onVerified }) {
const [status, setStatus] = useState('idle')
const [error, setError] = useState('')
const [resetSignal, setResetSignal] = useState(0)
const submittingRef = useRef(false)
useEffect(() => {
const previousOverflow = document.body.style.overflow
document.body.style.overflow = 'hidden'
return () => {
document.body.style.overflow = previousOverflow
}
}, [])
async function handleVerify(token) {
if (submittingRef.current) return
submittingRef.current = true
setStatus('verifying')
setError('')
try {
const response = await fetch('/api/turnstile/verify', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ token }),
})
if (!response.ok) {
setError('Verification failed — please try again.')
setStatus('idle')
setResetSignal((value) => value + 1)
return
}
setStatus('verified')
onVerified()
} catch {
setError('Network error — please retry.')
setStatus('idle')
setResetSignal((value) => value + 1)
} finally {
submittingRef.current = false
}
}
return (
Verifying you are human
This quick check protects the site from automated abuse. It usually clears itself.
setError('Challenge expired — please solve it again.')}
onError={() => setError('Challenge could not load. Refresh to retry.')}
resetSignal={resetSignal}
/>
{status === 'verifying' ? (
Confirming with Cloudflare…
) : null}
{error ? {error}
: null}
)
}
function App() {
const embeddedGateState = document
.querySelector('meta[name="tss-turnstile-session"]')
?.getAttribute('content')
const initialGateState = turnstileSiteKey
? ['verified', 'required'].includes(embeddedGateState)
? embeddedGateState
: 'checking'
: 'verified'
const [gateState, setGateState] = useState(initialGateState)
useEffect(() => {
if (!turnstileSiteKey || gateState !== 'checking') return undefined
let cancelled = false
fetch('/api/turnstile/session', { headers: { Accept: 'application/json' } })
.then((response) => (response.ok ? response.json() : { verified: false }))
.then((data) => {
if (cancelled) return
setGateState(data?.verified ? 'verified' : 'required')
})
.catch(() => {
if (!cancelled) setGateState('required')
})
return () => {
cancelled = true
}
}, [gateState])
if (gateState === 'checking') {
return
}
if (gateState === 'required') {
return setGateState('verified')} />
}
return
}
function AppContent() {
const [route, setRoute] = useState(() => parseRoute())
const [leaderboard, setLeaderboard] = useState({ status: 'idle', data: null, error: null })
const [homeTeams, setHomeTeams] = useState({ status: 'idle', data: null, error: null })
const [live, setLive] = useState({ status: 'idle', data: null, error: null, updatedAt: 0 })
const [uptime, setUptime] = useState({ status: 'idle', checks: [], history: [], updatedAt: null })
const [viewers, setViewers] = useState({ status: 'idle', data: null, error: null, updatedAt: null })
const [analyticsPreferences, setAnalyticsPreferences] = useState(() => storedAnalyticsPreferences())
const [theme, setTheme] = useState(() => storedThemePreference())
const [showFloatingNav, setShowFloatingNav] = useState(() => window.scrollY > 40)
const [themeTogglePositions, setThemeTogglePositions] = useState(() => {
const position = defaultThemeTogglePosition()
return { dock: position, home: position }
})
const [teamQuery, setTeamQuery] = useState('')
const [searchHint, setSearchHint] = useState({ status: 'idle', name: '' })
const [teamSearchResults, setTeamSearchResults] = useState([])
const [profile, setProfile] = useState({
teamName: '',
detail: { status: 'idle', data: null, error: null },
games: { status: 'idle', data: null, error: null },
})
const teams = useMemo(
() => leaderboard.data?.teams || leaderboard.data?.squadrons || [],
[leaderboard.data],
)
const teamsToWatch = useMemo(
() => homeTeams.data?.teams || homeTeams.data?.squadrons || teams.slice(0, 4),
[homeTeams.data, teams],
)
const matches = live.data?.matches || []
const liveRef = useRef(live)
const navPillRef = useRef(null)
const themeRef = useRef(theme)
const themeTransitionTimerRef = useRef(null)
useEffect(() => {
liveRef.current = live
}, [live])
useEffect(() => {
themeRef.current = theme
document.documentElement.dataset.theme = theme
document.documentElement.style.colorScheme = theme
document.querySelector('meta[name="theme-color"]')?.setAttribute(
'content',
theme === 'dark' ? '#101211' : '#e82517',
)
}, [theme])
useEffect(() => () => {
window.clearTimeout(themeTransitionTimerRef.current)
document.documentElement.classList.remove('theme-transition')
}, [])
function navigate(path, { replace = false } = {}) {
if (replace) {
window.history.replaceState({}, '', path)
} else {
window.history.pushState({}, '', path)
}
setRoute(parseRoute(path))
}
useEffect(() => {
const onPopState = () => setRoute(parseRoute())
window.addEventListener('popstate', onPopState)
return () => window.removeEventListener('popstate', onPopState)
}, [])
useEffect(() => {
const onScroll = () => setShowFloatingNav(window.scrollY > 40)
onScroll()
window.addEventListener('scroll', onScroll, { passive: true })
return () => window.removeEventListener('scroll', onScroll)
}, [])
useEffect(() => {
if (route.page === 'home') return
const prewarm = () => {
renderPixelMountainsCanvas()
prewarmTreeCanvas()
}
const requestIdle = window.requestIdleCallback || ((callback) => window.setTimeout(callback, 250))
const cancelIdle = window.cancelIdleCallback || window.clearTimeout
const id = requestIdle(prewarm)
return () => cancelIdle(id)
}, [route.page])
useEffect(() => {
applySeo(
route,
route.page === 'team' && profile.detail.status === 'ready'
? profile.detail.data
: null,
)
}, [profile.detail.data, profile.detail.status, route])
useEffect(() => {
if (!analyticsPreferences.analytics) return
if (
route.page === 'team' &&
(profile.teamName !== route.teamName || profile.detail.status !== 'ready')
) {
return
}
const visitorId = stableId(analyticsVisitorKey)
let stopped = false
const deviceDetails = analyticsPreferences.device
const displayDetails = analyticsPreferences.display
const localeDetails = analyticsPreferences.locale
const referrerDetails = analyticsPreferences.referrer
const diagnosticsDetails = analyticsPreferences.diagnostics
function sendViewerEvent(eventType) {
if (stopped) return
const metadata = {
preferences: {
device: deviceDetails,
display: displayDetails,
locale: localeDetails,
referrer: referrerDetails,
diagnostics: diagnosticsDetails,
theme: themeRef.current,
},
}
if (deviceDetails) {
metadata.device = {
user_agent_length: navigator.userAgent.length,
platform: navigator.platform || '',
vendor: navigator.vendor || '',
max_touch_points: navigator.maxTouchPoints || 0,
}
}
if (displayDetails) {
metadata.display = {
color_depth: window.screen.colorDepth,
pixel_depth: window.screen.pixelDepth,
viewport: `${window.innerWidth}x${window.innerHeight}`,
device_pixel_ratio: window.devicePixelRatio || 1,
orientation: window.screen.orientation?.type || '',
}
}
if (localeDetails) {
metadata.locale = {
languages: Array.isArray(navigator.languages) ? navigator.languages.slice(0, 8) : [],
timezone_offset_minutes: new Date().getTimezoneOffset(),
}
}
if (diagnosticsDetails) {
metadata.diagnostics = browserDiagnosticsMetadata()
}
const body = {
consent: 'analytics',
event_type: eventType,
visitor_id: visitorId,
session_id: analyticsSessionId,
page_path: window.location.pathname,
page_title: routeLabel(route),
referrer: referrerDetails ? document.referrer : '',
user_agent: deviceDetails ? navigator.userAgent : 'Not shared',
browser: deviceDetails ? browserName() : 'Not shared',
os: deviceDetails ? operatingSystem() : 'Not shared',
device: deviceDetails ? deviceType() : 'Not shared',
screen: displayDetails ? `${window.screen.width}x${window.screen.height}` : '',
language: localeDetails ? navigator.language : '',
timezone: localeDetails ? Intl.DateTimeFormat().resolvedOptions().timeZone : '',
theme: themeRef.current,
metadata,
}
fetch(apiEndpoints.viewerEvent, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify(body),
keepalive: true,
}).catch(() => {})
}
sendViewerEvent('page_view')
const timer = window.setInterval(() => sendViewerEvent('heartbeat'), 30000)
const onVisibilityChange = () => {
if (document.visibilityState === 'visible') sendViewerEvent('heartbeat')
}
document.addEventListener('visibilitychange', onVisibilityChange)
return () => {
stopped = true
window.clearInterval(timer)
document.removeEventListener('visibilitychange', onVisibilityChange)
}
}, [analyticsPreferences, profile.detail.status, profile.teamName, route])
useEffect(() => {
const onKeyDown = (event) => {
if (event.key === 'Escape') {
navigate('/')
}
}
window.addEventListener('keydown', onKeyDown)
return () => window.removeEventListener('keydown', onKeyDown)
}, [])
useEffect(() => {
const query = teamQuery.trim()
if (query.length < 2) {
setSearchHint({ status: 'idle', name: '' })
setTeamSearchResults([])
return
}
const controller = new AbortController()
const timer = window.setTimeout(() => {
setSearchHint({ status: 'loading', name: '' })
fetchJson(apiEndpoints.searchTeams(query), controller.signal)
.then((data) => {
const results = (data.teams || data.results || [])
.map((team) => ({
name: bestTeamName(team),
detail: '',
aliases: [team.name].filter(Boolean),
}))
.filter((team) => team.name)
.slice(0, 10)
setTeamSearchResults(results)
setSearchHint(results.length ? { status: 'ready', name: results[0].name } : { status: 'error', name: '' })
})
.catch(() => {
if (!controller.signal.aborted) {
fetchJson(apiEndpoints.resolve(query), controller.signal)
.then((data) => {
const name = data.name || ''
setTeamSearchResults(name ? [{ name, detail: '', aliases: [name] }] : [])
setSearchHint(name ? { status: 'ready', name } : { status: 'error', name: '' })
})
.catch(() => {
if (!controller.signal.aborted) {
setTeamSearchResults([])
setSearchHint({ status: 'error', name: '' })
}
})
}
})
}, 350)
return () => {
window.clearTimeout(timer)
controller.abort()
}
}, [teamQuery])
useEffect(() => {
if (!['teams', 'team', 'battle-logs'].includes(route.page)) return
if (leaderboard.status === 'ready' || leaderboard.status === 'loading') return
const controller = new AbortController()
setLeaderboard({ status: 'loading', data: null, error: null })
fetchJson(apiEndpoints.teams, controller.signal)
.then((data) => setLeaderboard({ status: 'ready', data, error: null }))
.catch((error) => {
if (!controller.signal.aborted) {
setLeaderboard({ status: 'error', data: null, error: error.message })
}
})
return () => controller.abort()
}, [leaderboard.status, route.page])
useEffect(() => {
if (route.page !== 'home') return
if (homeTeams.status === 'ready' || homeTeams.status === 'loading') return
const controller = new AbortController()
setHomeTeams({ status: 'loading', data: null, error: null })
fetchJson(apiEndpoints.homeTeams, controller.signal)
.then((data) => setHomeTeams({ status: 'ready', data, error: null }))
.catch((error) => {
if (!controller.signal.aborted) {
setHomeTeams({ status: 'error', data: null, error: error.message })
}
})
return () => controller.abort()
}, [homeTeams.status, route.page])
useEffect(() => {
if (!['teams', 'team', 'battle-logs'].includes(route.page)) return
const controller = new AbortController()
const timer = window.setInterval(() => {
fetchJson(apiEndpoints.teams, controller.signal)
.then((data) => setLeaderboard({ status: 'ready', data, error: null }))
.catch((error) => {
if (!controller.signal.aborted) {
setLeaderboard((current) => ({ ...current, error: error.message }))
}
})
}, 60000)
return () => {
window.clearInterval(timer)
controller.abort()
}
}, [route.page])
useEffect(() => {
if (!['home', 'battle-logs'].includes(route.page)) return
const currentLive = liveRef.current
if (currentLive.status === 'ready' && Date.now() - currentLive.updatedAt < liveRefreshMs) return
const controller = new AbortController()
setLive((current) =>
current.status === 'ready'
? current
: { status: 'loading', data: null, error: null, updatedAt: current.updatedAt || 0 },
)
fetchRecentTssGames(controller.signal)
.then((data) => setLive({ status: 'ready', data, error: null, updatedAt: Date.now() }))
.catch((error) => {
if (!controller.signal.aborted) {
setLive({ status: 'error', data: null, error: error.message, updatedAt: Date.now() })
}
})
return () => controller.abort()
}, [route.page])
useEffect(() => {
if (!['home', 'battle-logs'].includes(route.page)) return
const controller = new AbortController()
const timer = window.setInterval(() => {
fetchRecentTssGames(controller.signal)
.then((data) => setLive({ status: 'ready', data, error: null, updatedAt: Date.now() }))
.catch((error) => {
if (!controller.signal.aborted) {
setLive((current) => ({ ...current, error: error.message }))
}
})
}, liveRefreshMs)
return () => {
window.clearInterval(timer)
controller.abort()
}
}, [route.page])
useEffect(() => {
if (route.page !== 'team' || !route.teamName) return
const controller = new AbortController()
setProfile({
teamName: route.teamName,
detail: { status: 'loading', data: null, error: null },
games: { status: 'loading', data: null, error: null },
})
Promise.allSettled([
fetchJson(apiEndpoints.detail(route.teamName), controller.signal),
fetchJson(apiEndpoints.games(route.teamName), controller.signal),
])
.then(([detailResult, gamesResult]) => {
if (controller.signal.aborted) return
if (detailResult.status !== 'fulfilled') {
navigate('/teams', { replace: true })
return
}
const detail = detailResult.value
if (!teamDetailLooksReal(detail)) {
navigate('/teams', { replace: true })
return
}
setProfile({
teamName: route.teamName,
detail: { status: 'ready', data: detail, error: null },
games:
gamesResult.status === 'fulfilled'
? { status: 'ready', data: gamesResult.value, error: null }
: { status: 'error', data: null, error: gamesResult.reason.message },
})
})
return () => controller.abort()
}, [route.page, route.teamName])
useEffect(() => {
if (route.page !== 'uptime') return
const controller = new AbortController()
async function loadUptime() {
setUptime((current) => ({
status: current.status === 'ready' ? 'refreshing' : 'loading',
checks: current.checks,
history: current.history,
updatedAt: current.updatedAt,
}))
fetchJson(apiEndpoints.uptime, controller.signal)
.then((data) => {
const latest = data.latest
const checks = latest
? [
{
name: 'Website',
detail: 'App shell and static assets',
ok: latest.website_ok,
label: latest.details?.website?.label || (latest.website_ok ? 'Online' : 'Issue'),
latency: latest.latency_ms,
},
{
name: 'Health endpoint',
detail: apiEndpoints.health,
ok: latest.health_ok,
label: latest.details?.health?.label || (latest.health_ok ? 'Operational' : 'Issue'),
latency: latest.latency_ms,
},
{
name: 'TSS data proxy',
detail: apiEndpoints.teamsHealth,
ok: latest.tss_ok,
label: latest.details?.tss?.label || (latest.tss_ok ? 'Operational' : 'Issue'),
latency: latest.details?.tss?.latency_ms || latest.latency_ms,
},
]
: []
setUptime({
status: 'ready',
checks,
history: (data.history || []).map((sample) => ({
timestamp: new Date(sample.checked_at).getTime(),
onlineChecks: [sample.website_ok, sample.health_ok, sample.tss_ok].filter(Boolean).length,
totalChecks: 3,
ok: sample.ok,
})),
updatedAt: latest ? new Date(latest.checked_at).getTime() : null,
configured: data.configured,
})
})
.catch((error) => {
if (controller.signal.aborted) return
setUptime({
status: 'error',
updatedAt: null,
configured: false,
history: [],
checks: [
{
name: 'Website',
detail: 'App shell and static assets',
ok: true,
label: 'Online',
latency: 0,
},
{
name: 'Health endpoint',
detail: apiEndpoints.health,
ok: false,
label: error.message,
latency: 0,
},
{
name: 'TSS data proxy',
detail: apiEndpoints.teamsHealth,
ok: false,
label: 'Uptime history unavailable',
latency: 0,
},
],
})
})
}
loadUptime()
const timer = window.setInterval(loadUptime, 60000)
return () => {
window.clearInterval(timer)
controller.abort()
}
}, [route.page])
useEffect(() => {
if (route.page !== 'viewers') return
const controller = new AbortController()
function loadViewers() {
setViewers((current) => ({
status: current.status === 'ready' ? 'refreshing' : 'loading',
data: current.data,
error: null,
updatedAt: current.updatedAt,
}))
fetchJson(apiEndpoints.viewers, controller.signal)
.then((data) => {
setViewers({
status: 'ready',
data,
error: null,
updatedAt: Date.now(),
})
})
.catch((error) => {
if (!controller.signal.aborted) {
setViewers((current) => ({ ...current, status: 'error', error: error.message }))
}
})
}
loadViewers()
const timer = window.setInterval(loadViewers, 5000)
return () => {
window.clearInterval(timer)
controller.abort()
}
}, [route.page])
useEffect(() => {
if (route.page !== 'team' || !route.teamName) return
const controller = new AbortController()
const timer = window.setInterval(() => {
Promise.allSettled([
fetchJson(apiEndpoints.detail(route.teamName), controller.signal),
fetchJson(apiEndpoints.games(route.teamName), controller.signal),
]).then(([detailResult, gamesResult]) => {
if (controller.signal.aborted) return
setProfile((current) => ({
teamName: route.teamName,
detail:
detailResult.status === 'fulfilled'
? { status: 'ready', data: detailResult.value, error: null }
: current.detail,
games:
gamesResult.status === 'fulfilled'
? { status: 'ready', data: gamesResult.value, error: null }
: current.games,
}))
})
}, 60000)
return () => {
window.clearInterval(timer)
controller.abort()
}
}, [route.page, route.teamName])
const topTeamName = bestTeamName(teams[0])
const localTeamSuggestions = useMemo(() => {
const query = teamQuery.trim().toLowerCase()
const seen = new Set()
return teams
.map((team) => {
const name = bestTeamName(team)
const aliases = [team.name].filter(Boolean)
return { name, detail: '', aliases }
})
.filter(({ name, aliases }) => {
if (!name || seen.has(name)) return false
seen.add(name)
if (!query) return true
return aliases.some((alias) => String(alias).toLowerCase().includes(query))
})
.slice(0, 10)
}, [teamQuery, teams])
const teamSuggestions = teamSearchResults.length ? teamSearchResults : localTeamSuggestions
const searchPlaceholder =
searchHint.status === 'ready'
? `Found ${searchHint.name}`
: searchHint.status === 'error'
? 'Team not found'
: topTeamName || 'Search teams'
async function handleTeamSearch(event) {
event.preventDefault()
const name = teamQuery.trim()
if (!name) return
try {
const resolved = await fetchJson(apiEndpoints.resolve(name))
const resolvedName = resolved.name
if (!resolvedName) throw new Error('Team not found')
const detail = await fetchJson(apiEndpoints.detail(resolvedName))
if (!teamDetailLooksReal(detail)) throw new Error('Team not found')
navigate(teamPath(canonicalTeamName(detail, resolvedName)))
setTeamQuery('')
} catch {
setSearchHint({ status: 'error', name: '' })
}
}
function chooseAnalyticsConsent(preferences) {
const previousVisitorId = storedVisitorId(analyticsVisitorKey)
const nextPreferences = persistAnalyticsPreferences({ ...preferences, chosen: true })
if (!nextPreferences.analytics) {
forgetVisitorId(analyticsVisitorKey)
if (previousVisitorId) {
fetch(apiEndpoints.viewerDelete, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
visitor_id: previousVisitorId,
session_id: analyticsSessionId,
}),
keepalive: true,
}).catch(() => {})
}
}
setAnalyticsPreferences(nextPreferences)
}
function chooseTheme(nextTheme) {
if (nextTheme === themeRef.current) return
window.clearTimeout(themeTransitionTimerRef.current)
document.documentElement.classList.add('theme-transition')
themeTransitionTimerRef.current = window.setTimeout(() => {
document.documentElement.classList.remove('theme-transition')
}, 460)
setTheme(persistThemePreference(nextTheme))
}
const activeNavPath =
route.page === 'team'
? '/teams'
: route.page === 'battle-logs' || route.page === 'game'
? '/battle-logs'
: route.page === 'viewers'
? '/viewers'
: window.location.pathname
const shouldShowFloatingNav = route.page !== 'home' || showFloatingNav
useEffect(() => {
let frame = 0
function updateThemeTogglePositions() {
window.cancelAnimationFrame(frame)
frame = window.requestAnimationFrame(() => {
setThemeTogglePositions({
dock: themeToggleDockPosition(navPillRef.current),
home: defaultThemeTogglePosition(),
})
})
}
updateThemeTogglePositions()
window.addEventListener('resize', updateThemeTogglePositions)
return () => {
window.cancelAnimationFrame(frame)
window.removeEventListener('resize', updateThemeTogglePositions)
}
}, [route.page])
return (
navigate('/')}
type="button"
>
{navItems.map((item) => (
navigate(item.path)}
type="button"
>
{item.label}
))}
{route.page === 'home' ? (
) : null}
{route.page === 'teams' ? (
) : null}
{route.page === 'team' ? (
) : null}
{route.page === 'battle-logs' ? (
) : null}
{route.page === 'uptime' ? : null}
{route.page === 'viewers' ? : null}
{route.page === 'privacy' ? : null}
{route.page === 'docs' ? : null}
{route.page === 'player' ? : null}
{route.page === 'game' ? : null}
)
}
function Footer({ navigate }) {
return (
)
}
function ConsentBanner({ preferences, onChoose }) {
const [isOpen, setIsOpen] = useState(() => !preferences.chosen)
const [isConfiguring, setIsConfiguring] = useState(() => Boolean(preferences.chosen))
const [draft, setDraft] = useState(() => normalizeAnalyticsPreferences(preferences))
const privacySignalEnabled = browserPrivacySignalEnabled()
useEffect(() => {
setDraft(normalizeAnalyticsPreferences(preferences))
if (!preferences.chosen) {
setIsOpen(true)
setIsConfiguring(false)
}
}, [preferences])
useEffect(() => {
if (!isOpen) return undefined
const previousOverflow = document.body.style.overflow
document.body.style.overflow = 'hidden'
return () => {
document.body.style.overflow = previousOverflow
}
}, [isOpen])
function updateDraft(key, value) {
setDraft((current) => ({ ...current, [key]: value }))
}
function savePreferences(nextPreferences) {
onChoose(normalizeAnalyticsPreferences(nextPreferences))
setIsOpen(false)
}
if (!isOpen) {
return (
{
setIsConfiguring(true)
setIsOpen(true)
}}
type="button"
>
Cookie settings
)
}
return (
Cookie and analytics settings
We use a necessary cookie to remember these choices. You can also allow
analytics for the public viewers page and choose which details are included.
{privacySignalEnabled ? (
Your browser is signalling a privacy preference, so optional analytics stay
off unless you explicitly allow them.
) : null}
{isConfiguring ? (
<>
updateDraft('analytics', value)}
/>
updateDraft('device', value)}
/>
updateDraft('display', value)}
/>
updateDraft('locale', value)}
/>
updateDraft('referrer', value)}
/>
updateDraft('diagnostics', value)}
/>
savePreferences({ ...defaultAnalyticsPreferences, chosen: true })}
type="button"
>
Decline all
savePreferences(draft)}
type="button"
>
Save choices
savePreferences({
chosen: true,
analytics: true,
device: true,
display: true,
locale: true,
referrer: true,
diagnostics: true,
version: analyticsConsentVersion,
})
}
type="button"
>
Allow all
>
) : (
setIsConfiguring(true)}
type="button"
>
Configure
savePreferences({
chosen: true,
analytics: true,
device: true,
display: true,
locale: true,
referrer: true,
diagnostics: true,
version: analyticsConsentVersion,
})
}
type="button"
>
Allow all
)}
)
}
function PreferenceToggle({ checked, description, disabled = false, label, onChange }) {
return (
onChange?.(event.target.checked)}
type="checkbox"
/>
{label}
{description}
)
}
function PrivacyPage() {
return (
Privacy notice
How we handle your data
Controller: Heidi
Contact through Discord: clippiii
Necessary cookies store your cookie, theme, and analytics choices. If you opt in to
viewer analytics, we collect page views, live viewing status, a pseudonymous
visitor ID, a session ID, and the active light/dark theme. Optional settings control whether browser/device,
screen, language/timezone, referrer, and technical diagnostics are included.
Technical diagnostics can include HTTP version, request protocol details,
content negotiation headers, browser privacy signals, network quality, touch
support, CPU/memory hints, and similar debugging fields.
Necessary cookies are used to remember your privacy and theme choices. Optional viewer
analytics are used only to power the public viewers page, understand basic site
activity, and keep abuse low.
Necessary cookies are used because they are needed to remember your consent
preferences. Optional analytics only run with your consent, and you can withdraw
that consent from the Cookie settings button at any time.
Analytics events are automatically deleted after the configured retention period,
currently documented as 30 days by default. Active viewer sessions expire after a
short inactivity window. If you decline analytics after previously allowing them,
the site asks the server to delete records linked to your current visitor and
session IDs, and removes the local visitor ID from your browser.
Viewer analytics are stored by this site and are not sold. Public viewer pages show
only consented analytics fields and never show raw IP addresses. Hosting providers
may process server logs as part of running the site.
You can ask for access, correction, deletion, restriction, objection, portability,
or withdrawal of consent by contacting Heidi on Discord at clippiii. If you are in
the UK, you can also complain to the ICO. If you are in the EU or EEA, you can
complain to your local data protection authority.
)
}
function PlayerStatCard({ label, value }) {
return (
)
}
function PlayerPage({ uid, navigate }) {
const [state, setState] = useState({ status: 'loading', data: null, error: '' })
useEffect(() => {
if (!uid) {
setState({ status: 'error', data: null, error: 'No player specified.' })
return
}
let cancelled = false
setState({ status: 'loading', data: null, error: '' })
fetchJson(apiEndpoints.player(uid))
.then((data) => {
if (!cancelled) setState({ status: 'ready', data, error: '' })
})
.catch((err) => {
if (!cancelled) setState({ status: 'error', data: null, error: err?.message || 'Failed to load player.' })
})
return () => {
cancelled = true
}
}, [uid])
const { status, data, error } = state
return (
Player
{status === 'ready' ? (data.nick || `Player ${uid}`) : `Player ${uid || ''}`}
UID {uid} · TSS career
{status === 'loading' ? Loading…
: null}
{status === 'error' ? {error}
: null}
{status === 'ready' ? (
{Array.isArray(data.teams) && data.teams.length ? (
Teams seen with
{data.teams.map((team) => {
const label = team.team_tag || team.team_name || 'Unknown'
return (
navigate(teamPath(label))}
type="button"
>
{label}
{formatNumber(team.games)} games
)
})}
) : null}
) : null}
)
}
function DocsPage() {
return (
Docs
Documentation
Coming soon.
)
}
function PrivacySection({ children, title }) {
return (
{title}
{children}
)
}
function Landing({
live,
matches,
navigate,
onTeamSearch,
searchPlaceholder,
setTeamQuery,
teamSuggestions,
teams,
teamsToWatch,
teamQuery,
}) {
const treeRef = useRef(null)
return (
BorisBot got nothin on THIS
Toothless' TSS Bot
Powered by Spectra. TSS analytics.
navigate('/teams')}
type="button"
>
Team Leaderboard
navigate('/battle-logs')}
type="button"
>
Battle Logs
Visit SREBOT
Scroll
)
}
function LandingOverview({ teams, teamsToWatch, matches, navigate }) {
const activeTeams = teamsToWatch.slice(0, 4)
const visibleTeamCount = teams.length || teamsToWatch.length
const totalPlayers = matches.reduce((sum, match) => sum + Number(match.player_count || 0), 0)
const latestMatch = matches[0]
return (
What it does
A clean window into TSS activity
Track teams, inspect recent battles, and jump from a leaderboard name to a
profile without touching the god awful TSS website.
navigate('/teams')}
title="Team discovery"
/>
navigate('/battle-logs')}
title="Battle context"
/>
navigate('/uptime')}
title="Operational status"
/>
navigate('/viewers')}
title="Viewer pulse"
/>
Latest signal
{latestMatch?.map_name || 'Waiting for the next battle'}
{latestMatch
? `${latestMatch.team_name || 'A TSS team'} played with ${formatNumber(latestMatch.player_count)} players.`
: 'Latest match data will appear here once the data proxy returns games.'}
Teams to watch
{activeTeams.length ? (
activeTeams.map((team) => {
const name = bestTeamName(team)
return (
navigate(teamPath(name))}
type="button"
>
{name}
)
})
) : (
Team data is still loading.
)}
)
}
function LandingMetric({ label, value }) {
return (
)
}
function LandingFeature({ action, description, onClick, title }) {
return (
{title}
{description}
{action}
)
}
function LandingTrustSection({ navigate }) {
return (
Brought to you by...
Built by the team behind SREBot
TSSBot is built alongside SREBot, both providing different views into different aspects of "competitive" War Thunder.
Visit SREBOT
navigate('/privacy')}
type="button"
>
Privacy notice
)
}
function RecentGamesSection({ live, matches, navigate }) {
const recentMatches = matches.slice(0, 6)
return (
Recent activity
Latest games
navigate('/battle-logs')}
type="button"
>
View Battle Logs
{recentMatches.map((match) => (
navigate(gamePath(match.session_id))}
type="button"
>
{match.map_name || 'Unknown map'}
{formatDate(match.timestamp)}
{formatNumber(match.player_count)} players
{formatNumber(match.stats?.deaths)} deaths
))}
{!recentMatches.length ? (
{live.status === 'loading' ? 'Loading latest games' : live.error || 'No games returned'}
) : null}
)
}
const cachedPixelMountainsCanvases = new Map()
function renderPixelMountainsCanvas(theme = 'light') {
const isDark = theme === 'dark'
const palette =
isDark
? ['#332614', '#4c341b', '#68431f', '#8a5824']
: ['#fcfbcf', '#fff2e6', '#fee5cd', '#fdca9b']
const cached = cachedPixelMountainsCanvases.get(theme)
if (cached) return cached
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
const WORLD_W = 1920
const WORLD_H = 900
function interpolate(points, x) {
for (let i = 0; i < points.length - 1; i++) {
const [x0, y0] = points[i]
const [x1, y1] = points[i + 1]
if (x >= x0 && x <= x1) {
const t = (x - x0) / Math.max(1, x1 - x0)
return y0 + (y1 - y0) * t
}
}
return points.at(-1)[1]
}
function drawMountain(points, color, jitter = 0) {
const width = WORLD_W
const height = WORLD_H
ctx.fillStyle = color
for (let x = 0; x < width; x++) {
const wave = jitter
? Math.sin(x * 0.08) * jitter + Math.sin(x * 0.021) * jitter * 1.8
: 0
const y = Math.round(interpolate(points, x) + wave)
ctx.fillRect(x, y, 1, height - y)
}
}
function draw() {
const width = WORLD_W
const height = WORLD_H
canvas.width = WORLD_W
canvas.height = WORLD_H
ctx.imageSmoothingEnabled = false
ctx.clearRect(0, 0, WORLD_W, WORLD_H)
drawMountain(
[
[0, height * 0.82],
[width * 0.12, height * 0.73],
[width * 0.26, height * 0.66],
[width * 0.39, height * 0.76],
[width * 0.55, height * 0.58],
[width * 0.64, height * 0.46],
[width * 0.73, height * 0.62],
[width * 0.86, height * 0.56],
[width, height * 0.64],
],
palette[0],
1.1,
)
drawMountain(
[
[0, height * 0.86],
[width * 0.15, height * 0.78],
[width * 0.31, height * 0.81],
[width * 0.43, height * 0.69],
[width * 0.52, height * 0.76],
[width * 0.62, height * 0.43],
[width * 0.69, height * 0.31],
[width * 0.78, height * 0.48],
[width, height * 0.5],
],
palette[1],
1.6,
)
drawMountain(
[
[0, height * 0.94],
[width * 0.17, height * 0.9],
[width * 0.32, height * 0.92],
[width * 0.47, height * 0.83],
[width * 0.58, height * 0.89],
[width * 0.66, height * 0.61],
[width * 0.71, height * 0.5],
[width * 0.84, height * 0.7],
[width, height * 0.68],
],
palette[2],
2.1,
)
drawMountain(
[
[0, height * 0.98],
[width * 0.17, height * 0.96],
[width * 0.34, height * 0.99],
[width * 0.48, height * 0.93],
[width * 0.62, height * 0.97],
[width * 0.72, height * 0.88],
[width * 0.86, height * 0.95],
[width, height * 0.93],
],
palette[3],
1.4,
)
}
draw()
cachedPixelMountainsCanvases.set(theme, canvas)
return canvas
}
function PixelMountains() {
const [activeTheme, setActiveTheme] = useState(() =>
document.documentElement.dataset.theme === 'dark' ? 'dark' : 'light',
)
const [previousTheme, setPreviousTheme] = useState(null)
const [skyTransition, setSkyTransition] = useState('')
const [skyPaths, setSkyPaths] = useState({})
const activeCanvasRef = useRef(null)
const previousCanvasRef = useRef(null)
const skyRef = useRef(null)
const fadeTimerRef = useRef(null)
const skyTimerRef = useRef(null)
function drawCanvas(canvas, theme) {
if (!canvas || !theme) return
const source = renderPixelMountainsCanvas(theme)
const ctx = canvas.getContext('2d')
canvas.width = source.width
canvas.height = source.height
ctx.imageSmoothingEnabled = false
ctx.drawImage(source, 0, 0)
}
useEffect(() => {
drawCanvas(activeCanvasRef.current, activeTheme)
}, [activeTheme])
useEffect(() => {
drawCanvas(previousCanvasRef.current, previousTheme)
}, [previousTheme])
useEffect(() => {
function syncTheme() {
const theme = document.documentElement.dataset.theme === 'dark' ? 'dark' : 'light'
setActiveTheme((current) => {
if (theme === current) return current
window.clearTimeout(fadeTimerRef.current)
window.clearTimeout(skyTimerRef.current)
setPreviousTheme(current)
setSkyTransition(`${current}-to-${theme}`)
fadeTimerRef.current = window.setTimeout(() => setPreviousTheme(null), 440)
skyTimerRef.current = window.setTimeout(() => setSkyTransition(''), 900)
return theme
})
}
syncTheme()
const observer = new MutationObserver(syncTheme)
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] })
return () => {
observer.disconnect()
window.clearTimeout(fadeTimerRef.current)
window.clearTimeout(skyTimerRef.current)
}
}, [])
useEffect(() => {
function quadraticPath(start, control, end) {
return `path("M ${start.x.toFixed(1)} ${start.y.toFixed(1)} Q ${control.x.toFixed(1)} ${control.y.toFixed(1)} ${end.x.toFixed(1)} ${end.y.toFixed(1)}")`
}
function updateSkyPaths() {
const rect = skyRef.current?.getBoundingClientRect()
const width = rect?.width || window.innerWidth || 1920
const height = rect?.height || window.innerHeight || 900
const display = { x: width * 0.78, y: height * 0.18 }
const westSet = { x: width * 0.08, y: height * 0.67 }
const eastRise = { x: width * 0.94, y: height * 0.67 }
const westControl = { x: width * 0.46, y: height * -0.05 }
const eastControl = { x: width * 0.98, y: height * -0.03 }
setSkyPaths({
'--celestial-exit-path': quadraticPath(display, westControl, westSet),
'--celestial-enter-path': quadraticPath(eastRise, eastControl, display),
})
}
updateSkyPaths()
window.addEventListener('resize', updateSkyPaths)
return () => window.removeEventListener('resize', updateSkyPaths)
}, [])
return (
{previousTheme ? (
) : null}
)
}
function TeamsPage({ leaderboard, navigate, teams }) {
return (
Team Leaderboard
{leaderboard.status === 'loading'
? 'Loading leaderboard'
: leaderboard.error || `${teams.length} teams returned`}
{teams.map((team, index) => {
const name = bestTeamName(team)
return (
navigate(teamPath(name))}
type="button"
>
#{index + 1}
{name}
{formatNumber(team.player_count)} players
{formatNumber(team.total_battles)} battles
{Number(team.win_rate || 0).toFixed(1)}% WR
{formatNumber(team.points?.total_points || team.total_kills)}
)
})}
{!teams.length ? (
{leaderboard.status === 'loading' ? 'Loading leaderboard' : 'No teams returned'}
) : null}
)
}
function TeamProfilePage({ navigate, profile, requestedTeam, teams }) {
const detail = profile.detail.data
const summary = detail?.team_summary || detail?.squadron_summary
const players = detail?.players || []
const games = profile.games.data?.games || []
const leaderboardTeam = teams.find((team) => bestTeamName(team) === requestedTeam)
const displayName = detail?.name || bestTeamName(leaderboardTeam) || requestedTeam
return (
navigate('/teams')}
type="button"
>
Back to leaderboard
Team profile
{displayName}
{profile.detail.error || ''}
)
}
const SCOREBOARD_GRID = 'grid-cols-[minmax(220px,1fr)_repeat(6,minmax(52px,72px))]'
// Battle-log lines are prefixed +/-/ for winner/loser/neither (matching
// the Discord diff format), so colour each line by its acting team.
function battleLineColor(line) {
if (line.startsWith('+')) return 'text-win'
if (line.startsWith('-')) return 'text-loss'
return 'text-text-soft'
}
function formatLogTime(ms) {
const totalSeconds = Math.floor(Number(ms || 0) / 1000)
const minutes = String(Math.floor(totalSeconds / 60)).padStart(2, '0')
const seconds = String(totalSeconds % 60).padStart(2, '0')
return `${minutes}:${seconds}`
}
function deadVehicleKey(uid, cdk) {
return `${String(uid || '').trim()}:${String(cdk || '').trim()}`
}
function deadVehicleKeysFromEventLog(eventLog) {
const keys = new Set()
const kills = Array.isArray(eventLog?.kills) ? eventLog.kills : []
kills.forEach((kill) => {
const uid = kill?.offended_uid
const cdk = kill?.offended_unit
if (uid !== undefined && uid !== null && cdk) {
keys.add(deadVehicleKey(uid, cdk))
}
})
return keys
}
function logLookups(participants) {
const players = new Map()
;(participants || []).forEach((participant) => {
const result = String(participant.result || '').toLowerCase() === 'win' ? 'win' : 'loss'
;(participant.players || []).forEach((player) => {
const vehicles = new Map()
;(player.vehicles || []).forEach((vehicle) => {
vehicles.set(String(vehicle.cdk || ''), vehicle.name || vehicle.cdk || 'Unknown')
})
players.set(String(player.uid), {
name: player.nick || player.uid,
team: participant.team_name || '',
result,
className: result === 'win' ? 'text-win' : 'text-loss',
vehicles,
})
})
})
return players
}
function logPlayer(players, uid) {
return players.get(String(uid)) || {
name: uid === undefined || uid === null ? 'Unknown' : `Player#${uid}`,
team: '',
result: '',
className: 'text-text-soft',
vehicles: new Map(),
}
}
function logVehicle(player, cdk) {
if (!cdk) return 'Unknown'
return player.vehicles.get(String(cdk)) || String(cdk)
}
function structuredBattleEvents(eventLog) {
const kills = Array.isArray(eventLog?.kills) ? eventLog.kills : []
const damage = Array.isArray(eventLog?.damage) ? eventLog.damage : []
return [
...kills.map((event) => ({ ...event, kind: 'kill' })),
...damage.map((event) => ({ ...event, kind: 'damage' })),
].sort((a, b) => Number(a.time || 0) - Number(b.time || 0))
}
function GamePage({ gameId, navigate }) {
const [gameState, setGameState] = useState({ status: 'loading', data: null, error: null })
const [logs, setLogs] = useState({ chat_log: [], battle_log: [], event_log: { kills: [], damage: [], chat: [] } })
useEffect(() => {
if (!gameId) return
const controller = new AbortController()
setGameState({ status: 'loading', data: null, error: null })
setLogs({ chat_log: [], battle_log: [], event_log: { kills: [], damage: [], chat: [] } })
fetchJson(apiEndpoints.game(gameId), controller.signal)
.then((data) => {
if (!controller.signal.aborted) {
setGameState({ status: 'ready', data, error: null })
}
})
.catch((error) => {
if (!controller.signal.aborted) {
setGameState({ status: 'error', data: null, error: error.message })
}
})
fetchJson(apiEndpoints.gameLogs(gameId), controller.signal)
.then((data) => {
if (!controller.signal.aborted) {
setLogs({
chat_log: Array.isArray(data?.chat_log) ? data.chat_log : [],
battle_log: Array.isArray(data?.battle_log) ? data.battle_log : [],
event_log: data?.event_log || { kills: [], damage: [], chat: [] },
})
}
})
.catch(() => {
// Logs are non-critical; leave them empty on failure.
})
return () => controller.abort()
}, [gameId])
const game = gameState.data?.game
const participants = useMemo(() => gameState.data?.participants || [], [gameState.data?.participants])
const deadVehicleKeys = useMemo(() => deadVehicleKeysFromEventLog(logs.event_log), [logs.event_log])
const playersByUid = useMemo(() => logLookups(participants), [participants])
const battleEvents = useMemo(() => structuredBattleEvents(logs.event_log), [logs.event_log])
const chatEvents = Array.isArray(logs.event_log?.chat) ? logs.event_log.chat : []
const participantNames = participants.length
? participants.map((participant) => ({
name: participant.team_name,
result: String(participant.result || '').toLowerCase() === 'win' ? 'win' : 'loss',
}))
: gameParticipants(game)
const subtitle = [game?.mission_mode, game?.tournament_name].filter(Boolean).join(' · ')
const duration = formatDuration(game?.duration)
return (
navigate('/battle-logs')}
type="button"
>
Back to battle logs
Game
{game?.session_id || gameId}
{game?.map_name || 'Battle log'}
{game?.draw ? (
Draw
) : null}
{subtitle ?
{subtitle}
: null}
{game ? formatDate(game.timestamp) : ''}
{duration ? ` · ${duration}` : ''}
{gameState.status === 'error' ? (
{gameState.error}
) : null}
{participants.length ? (
Team / player
Air
Ground
Assists
Deaths
Caps
Score
) : null}
{participants.map((participant) => {
const won = String(participant.result || '').toLowerCase() === 'win'
const accent = won ? 'border-l-win' : 'border-l-loss'
const nameColor = won ? 'text-win' : 'text-loss'
return (
navigate(teamPath(participant.team_name))}
type="button"
>
{participant.team_name}
{won ? 'Win' : 'Loss'} · {formatNumber(participant.player_count)} players
{formatNumber(participant.stats?.air_kills)}
{formatNumber(participant.stats?.ground_kills)}
{formatNumber(participant.stats?.assists)}
{formatNumber(participant.stats?.deaths)}
{formatNumber(participant.stats?.captures)}
{formatNumber(participant.stats?.score)}
{(participant.players || []).map((player) => (
navigate(`/players/${encodeURIComponent(player.uid)}`)}
type="button"
>
{player.nick || player.uid}
{(player.vehicles || []).length ? (
player.vehicles.map((vehicle) => {
const isDead = deadVehicleKeys.has(deadVehicleKey(player.uid, vehicle.cdk))
return (
{ event.currentTarget.style.display = 'none' }}
src={`/vehicle-icons/${vehicle.icon}`}
/>
{vehicle.name}
)
})
) : (
{player.uid}
)}
{formatNumber(player.stats?.air_kills)}
{formatNumber(player.stats?.ground_kills)}
{formatNumber(player.stats?.assists)}
{formatNumber(player.stats?.deaths)}
{formatNumber(player.stats?.captures)}
{formatNumber(player.stats?.score)}
))}
)
})}
{!participants.length ? (
{gameState.status === 'loading' ? 'Loading game' : 'No participants returned'}
) : null}
{battleEvents.length || logs.battle_log.length ? (
Battle Log
{battleEvents.length ? (
battleEvents.map((event, i) => (
))
) : (
logs.battle_log.map((line, i) => (
{line}
))
)}
) : null}
{chatEvents.length || logs.chat_log.length ? (
Chat Log
{chatEvents.length ? (
chatEvents.map((event, i) => (
))
) : (
logs.chat_log.map((line, i) => (
{line}
))
)}
) : null}
)
}
function BattleEventLine({ event, players }) {
const offender = logPlayer(players, event.offender_uid)
const victim = logPlayer(players, event.offended_uid)
const ts = formatLogTime(event.time)
const victimLabel = `${victim.name} (${logVehicle(victim, event.offended_unit)})`
if (event.kind === 'kill' && (event.crashed || event.offender_uid === undefined || event.offender_uid === null)) {
return (
[{ts}]
[{victim.team}] {victimLabel}
crashed
)
}
const offenderLabel = `${offender.name} (${logVehicle(offender, event.offender_unit)})`
const action = event.kind === 'kill' ? 'destroyed' : `damaged ${event.afire ? '(FIRE) ' : ''}`
return (
[{ts}]
[{offender.team}] {offenderLabel}
{action}
{victimLabel}
)
}
function ChatEventLine({ event, players }) {
const player = logPlayer(players, event.uid)
return (
[{formatLogTime(event.time)}] [{event.type || 'ALL'}] [{player.team}] `{player.name}`: {event.message || ''}
)
}
function RosterTable({ players, status }) {
const sortedPlayers = [...players].sort((a, b) => {
return (b.total_kills || 0) - (a.total_kills || 0) || String(a.nick || '').localeCompare(b.nick || '')
})
return (
Roster
{formatNumber(sortedPlayers.length)} players
{sortedPlayers.map((player) => (
{player.nick || player.uid}
{player.uid} · {formatNumber(player.points || player.sqb_points)} pts
{formatNumber(player.total_battles)} battles
{formatNumber(player.total_kills)} kills
{Number(player.win_rate || 0).toFixed(1)}% WR
{Number(player.kdr || 0).toFixed(1)} KDR
{formatNumber(player.assists)} assists
))}
{!sortedPlayers.length ? (
{status === 'loading' ? 'Loading roster' : 'No roster rows returned'}
) : null}
)
}
function BattleResults({ games, navigate, status }) {
return (
Battle results
{formatNumber(games.length)} battles returned
{games.map((game) => (
navigate(gamePath(game.session_id))}
type="button"
>
{game.map_name || 'Unknown map'}
{formatDate(game.timestamp)} · {game.session_id}
{formatNumber(game.player_count)} players
{formatNumber(game.stats?.assists)} assists
{formatNumber(game.stats?.deaths)} deaths
))}
{!games.length ? (
{status === 'loading' ? 'Loading battle results' : 'No battle results returned'}
) : null}
)
}
function BattleLogsPage({ live, matches, navigate }) {
return (
Battle Logs
{live.status === 'loading' ? 'Loading battles' : live.error || `${matches.length} battles returned`}
{matches.map((match) => (
navigate(gamePath(match.session_id))}
type="button"
>
{match.map_name || 'Unknown map'}
{formatDate(match.timestamp)} · {match.session_id}
{formatMatchSize(match.player_count)}
))}
{!matches.length ? (
{live.status === 'loading' ? 'Loading battles' : 'No battles returned'}
) : null}
)
}
function relativeSeconds(timestamp) {
if (!timestamp) return 'unknown'
const seconds = Math.max(0, Math.round((Date.now() - new Date(timestamp).getTime()) / 1000))
if (seconds < 60) return `${seconds}s ago`
return `${Math.round(seconds / 60)}m ago`
}
const shortDateFormat = new Intl.DateTimeFormat('en-GB', {
day: '2-digit',
month: 'short',
})
const shortTimeFormat = new Intl.DateTimeFormat('en-GB', {
hour: '2-digit',
minute: '2-digit',
})
const countryNames = {
AR: 'Argentina',
AU: 'Australia',
BR: 'Brazil',
CA: 'Canada',
CL: 'Chile',
CN: 'China',
DE: 'Germany',
ES: 'Spain',
FR: 'France',
GB: 'United Kingdom',
ID: 'Indonesia',
IN: 'India',
IT: 'Italy',
JP: 'Japan',
KR: 'South Korea',
MX: 'Mexico',
NL: 'Netherlands',
PL: 'Poland',
RU: 'Russia',
SE: 'Sweden',
SG: 'Singapore',
US: 'United States',
ZA: 'South Africa',
}
function repairMojibake(value) {
const text = String(value || '')
if (!/[ÃÂâ]/.test(text) || typeof TextDecoder === 'undefined') return text
try {
const bytes = Uint8Array.from(text, (character) => character.charCodeAt(0))
return new TextDecoder('utf-8', { fatal: true }).decode(bytes)
} catch {
return text
}
}
function filledLast30Days(rows) {
const byDate = new Map(rows.map((row) => [row.date, row]))
return Array.from({ length: 30 }, (_, index) => {
const date = new Date()
date.setDate(date.getDate() - (29 - index))
const key = date.toISOString().slice(0, 10)
return byDate.get(key) || {
date: key,
events: 0,
visitors: 0,
page_views: 0,
clients: 0,
locations: 0,
}
})
}
function filledLast24Hours(rows) {
const byDate = new Map(rows.map((row) => [row.date, row]))
return Array.from({ length: 24 }, (_, index) => {
const date = new Date()
date.setMinutes(0, 0, 0)
date.setHours(date.getHours() - (23 - index))
const key = date.toISOString().slice(0, 13) + ':00:00.000Z'
return byDate.get(key) || {
date: key,
events: 0,
visitors: 0,
page_views: 0,
clients: 0,
locations: 0,
client_labels: [],
location_labels: [],
}
})
}
function MiniLineChart({
accent = 'text-fury-cyan',
bucketLabel = 'Daily count',
data,
label,
metric,
pointFormat = shortDateFormat,
stroke = '#e82517',
}) {
const [hoveredPoint, setHoveredPoint] = useState(null)
const width = 320
const height = 112
const padding = 12
const maxValue = Math.max(1, ...data.map((item) => Number(item[metric] || 0)))
const midValue = Math.round(maxValue / 2)
const points = data.map((item, index) => {
const x = padding + (index / Math.max(1, data.length - 1)) * (width - padding * 2)
const y = height - padding - (Number(item[metric] || 0) / maxValue) * (height - padding * 2)
return { ...item, x, y, value: Number(item[metric] || 0) }
})
const pathData = points
.map((point, index) => `${index === 0 ? 'M' : 'L'} ${point.x.toFixed(2)} ${point.y.toFixed(2)}`)
.join(' ')
const latest = points.at(-1)?.value || 0
return (
{formatNumber(maxValue)}
{formatNumber(midValue)}
0
{points.map((point) => (
setHoveredPoint(null)}
onFocus={() => setHoveredPoint(point)}
onMouseEnter={() => setHoveredPoint(point)}
onMouseLeave={() => setHoveredPoint(null)}
r="4"
tabIndex="0"
/>
))}
{hoveredPoint ? (
{pointFormat.format(new Date(hoveredPoint.date))}: {formatNumber(hoveredPoint.value)} {label.toLowerCase()}
{formatNumber(hoveredPoint.visitors || 0)} visitors
{metric === 'clients' && hoveredPoint.client_labels?.length ? (
{hoveredPoint.client_labels
.map((client) => `${client.label}: ${formatNumber(client.visitors)} visitors`)
.join(', ')}
) : null}
{metric === 'locations' ? (
{hoveredPoint.location_labels?.length
? hoveredPoint.location_labels
.map((location) => `${location.label} (${formatNumber(location.visitors)})`)
.join(', ')
: 'No location labels stored for this day'}
) : null}
) : null}
)
}
function AnalyticsPeriodSection({
activity,
bucketLabel,
description,
pointFormat,
title,
totals,
}) {
return (
{formatNumber(totals.visitors)} visitors
{formatNumber(totals.sessions)} sessions
{formatNumber(totals.pageViews)} page views
{formatNumber(totals.events)} events
)
}
function ViewersPage({ viewers }) {
const [analyticsWindow, setAnalyticsWindow] = useState('24h')
const data = viewers.data || {}
const active = data.active || []
const activePages = data.active_pages || []
const activity24h = filledLast24Hours(data.activity_24h || [])
const activity30d = filledLast30Days(data.activity_30d || [])
const totals = data.totals || {}
const generatedAt = data.generated_at ? dateFormat.format(new Date(data.generated_at)) : 'Waiting for data'
const periods = {
'24h': {
label: '24hr',
title: 'Last 24 hours',
description: 'Consented analytics grouped by hour',
bucketLabel: 'Hourly count',
pointFormat: shortTimeFormat,
activity: activity24h,
topPages: data.top_pages || [],
clients: data.clients || [],
themes: data.themes || [],
countries: data.countries_24h || [],
locations: data.locations_24h || [],
totals: {
events: totals.events_24h,
visitors: totals.visitors_24h,
sessions: totals.sessions_24h,
pageViews: totals.page_views_24h,
},
},
'30d': {
label: '30d',
title: 'Last 30 days',
description: 'Retained consented analytics over the current privacy window',
bucketLabel: 'Daily count',
pointFormat: shortDateFormat,
activity: activity30d,
topPages: data.top_pages_30d || data.top_pages || [],
clients: data.clients_30d || [],
themes: data.themes_30d || data.themes || [],
countries: data.countries || [],
locations: data.locations || [],
totals: {
events: totals.events_30d,
visitors: totals.visitors_30d,
sessions: totals.sessions_30d,
pageViews: totals.page_views_30d,
},
},
}
const periodData = periods[analyticsWindow]
return (
Public analytics
viewers
Live consented browser sessions and page activity. Last refreshed {generatedAt}.
{Object.entries(periods).map(([key, period]) => (
setAnalyticsWindow(key)}
type="button"
>
{period.label}
))}
{viewers.status === 'loading' ? 'Loading' : `${formatNumber(totals.active_now)} active now`}
Currently viewing
Pages with active heartbeats within {formatNumber(data.active_window_seconds || 75)} seconds
{formatNumber(active.length)} active visitors
{activePages.map((page) => (
{page.page_title || page.page_path}
{page.page_path}
{(page.clients || []).map((client) => (
{client.label} ({formatNumber(client.count)})
))}
{(page.countries || []).map((country) => (
{country}
))}
{(page.themes || []).map((theme) => (
{theme.theme} theme ({formatNumber(theme.count)})
))}
{formatNumber(page.viewers)} viewing
{formatNumber(page.visitors)} visitors
Seen {relativeSeconds(page.last_seen_at)}
))}
{!activePages.length ? (
{viewers.error || 'No pages are actively being viewed right now'}
) : null}
Active visitors
Visitors currently contributing to the active page counts
{active.map((viewer) => (
{viewer.page_title || viewer.page_path}
{viewer.page_path}
{viewer.browser} on {viewer.os}
{viewer.device} · {viewer.screen || 'unknown screen'} · {viewer.theme || 'light'} theme · {viewer.country || viewer.timezone || 'unknown location'}
{viewer.language || 'unknown language'}
Seen {relativeSeconds(viewer.last_seen_at)}
{formatNumber(viewer.sessions || 1)} {(viewer.sessions || 1) === 1 ? 'tab' : 'tabs'} · #{viewer.visitor}
))}
{!active.length ? (
{viewers.error || 'No consented viewers are active right now'}
) : null}
Top pages
Page views over {periodData.title.toLowerCase()}
{periodData.topPages.map((page) => (
{page.page_title || page.page_path}
{page.page_path}
{formatNumber(page.views)}
))}
{!periodData.topPages.length ?
No page views recorded yet
: null}
Clients
Browsers, devices, and operating systems seen over {periodData.title.toLowerCase()}
{periodData.clients.map((client) => (
{client.browser} on {client.os}
{client.device}
{formatNumber(client.visitors || 0)} visitors
{formatNumber(client.events)} events
))}
{!periodData.clients.length ?
No client data recorded yet
: null}
Themes
Light and dark mode usage over {periodData.title.toLowerCase()}
{periodData.themes.map((theme) => {
const totalEvents = periodData.themes.reduce((sum, item) => sum + Number(item.events || 0), 0) || 1
const percent = Math.round((Number(theme.events || 0) / totalEvents) * 100)
return (
{theme.theme} mode
{percent}%
{formatNumber(theme.visitors || 0)} visitors · {formatNumber(theme.events || 0)} events
)
})}
{!periodData.themes.length ?
No theme data recorded yet
: null}
Locations
Cities and locations seen over {periodData.title.toLowerCase()}
Location signals
Cloudflare edge geolocation from {periodData.title.toLowerCase()}
Analytics are opt-in, retained for {formatNumber(data.privacy?.retention_days || 30)} days,
and public output excludes raw IP addresses and precise location.
)
}
function LocationSignalTable({ countries, locations }) {
const rows = locations.length
? locations.map((location) => {
const city = repairMojibake(location.city)
const region = repairMojibake(location.region)
const timezone = repairMojibake(location.timezone)
return {
key: `${location.country}-${location.region}-${location.city}-${location.latitude}-${location.longitude}-${location.timezone}`,
place: city || region || timezone || 'Unknown city',
region: region || 'Not shared',
country: countryNames[location.country] || location.country || 'Not shared',
visitors: location.visitors,
events: location.events,
}
})
: countries.map((country) => ({
key: country.country,
place: countryNames[country.country] || country.country || 'Unknown country',
region: 'Country signal',
country: countryNames[country.country] || country.country || 'Not shared',
visitors: country.visitors,
events: country.events,
}))
return (
City/location
Region
Country
Visitors
Events
{rows.map((row) => (
{row.place}
{row.region}
{row.country}
{formatNumber(row.visitors)}
{formatNumber(row.events)}
))}
{!rows.length ?
No location data recorded yet
: null}
)
}
function LocationSignalMap({ countries, locations }) {
const mapRef = useRef(null)
const markersRef = useRef(null)
const leafletRef = useRef(null)
const [mapReady, setMapReady] = useState(false)
const countryMarkerColor = '#e82517'
const maxMarkerVisitors = Math.max(
1,
...countries.map((country) => Number(country.visitors || 0)),
...locations.map((location) => Number(location.visitors || 0)),
)
const countryMarkers = countries
.map((country) => {
const lat = Number(country.latitude)
const lon = Number(country.longitude)
if (!Number.isFinite(lat) || !Number.isFinite(lon)) return null
return {
...country,
lat,
lon,
label: countryNames[country.country] || country.country,
}
})
.filter(Boolean)
const cityMarkers = locations
.map((location) => {
const lat = Number(location.latitude)
const lon = Number(location.longitude)
if (!Number.isFinite(lat) || !Number.isFinite(lon)) return null
const place = [repairMojibake(location.city), repairMojibake(location.region), countryNames[location.country] || location.country]
.filter(Boolean)
.join(', ')
return {
...location,
lat,
lon,
label: place || location.country || repairMojibake(location.timezone) || 'Unknown location',
}
})
.filter(Boolean)
const topLocations = cityMarkers.length
? cityMarkers.slice(0, 8).map((location) => ({
key: `${location.country}-${location.region}-${location.city}-${location.lat}-${location.lon}`,
label: location.label,
detail: 'Cloudflare edge location',
visitors: location.visitors,
events: location.events,
}))
: countries.slice(0, 8).map((country) => ({
key: country.country,
label: countryNames[country.country] || country.country,
detail: 'country signal',
visitors: country.visitors,
events: country.events,
}))
useEffect(() => {
if (!mapRef.current || markersRef.current) return undefined
let cancelled = false
let map = null
async function initializeMap() {
const [{ default: L }] = await Promise.all([
import('leaflet'),
import('leaflet/dist/leaflet.css'),
])
if (cancelled || !mapRef.current) return
leafletRef.current = L
map = L.map(mapRef.current, {
center: [25, 0],
maxBounds: [[-85, -220], [85, 220]],
minZoom: 1,
scrollWheelZoom: true,
worldCopyJump: true,
zoom: 1,
})
L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', {
attribution: '© OpenStreetMap contributors © CARTO',
maxZoom: 8,
}).addTo(map)
markersRef.current = {
layer: L.layerGroup().addTo(map),
map,
}
setMapReady(true)
}
initializeMap()
return () => {
cancelled = true
markersRef.current = null
leafletRef.current = null
map?.remove()
}
}, [])
useEffect(() => {
const L = leafletRef.current
if (!markersRef.current || !L) return
const { layer } = markersRef.current
layer.clearLayers()
const scaleMarkerRadius = (visitors, minRadius, maxRadius) => {
const count = Math.max(0, Number(visitors || 0))
const normalized = Math.log1p(count) / Math.log1p(maxMarkerVisitors)
return minRadius + normalized * (maxRadius - minRadius)
}
const mapMarkers = cityMarkers.length ? cityMarkers : countryMarkers
mapMarkers.forEach((location) => {
const isCityMarker = cityMarkers.length > 0
const radius = isCityMarker
? scaleMarkerRadius(location.visitors, 5, 14)
: scaleMarkerRadius(location.visitors, 8, 24)
L.circleMarker([location.lat, location.lon], {
color: countryMarkerColor,
fillColor: countryMarkerColor,
fillOpacity: 0.4,
opacity: 0,
radius,
stroke: false,
})
.bindTooltip(`${location.label}: ${formatNumber(location.visitors)} visitors`, {
direction: 'top',
opacity: 0.95,
})
.addTo(layer)
})
}, [cityMarkers, countryMarkerColor, countryMarkers, mapReady, maxMarkerVisitors])
return (
{topLocations.map((location) => (
{location.label}
{location.detail}
{formatNumber(location.visitors)}
))}
{!countries.length && !locations.length ? (
No Cloudflare location signals have been shared yet.
) : null}
)
}
function UptimePage({ uptime }) {
const [hoveredSnapshot, setHoveredSnapshot] = useState(null)
const checks = uptime.checks
const history = uptime.history
const operationalCount = checks.filter((check) => check.ok).length
const allOperational = checks.length > 0 && operationalCount === checks.length
const updatedAt = uptime.updatedAt ? dateFormat.format(new Date(uptime.updatedAt)) : 'Not checked yet'
const onlineSamples = history.filter((sample) => sample.ok).length
const availability = history.length ? (onlineSamples / history.length) * 100 : 0
return (
Website uptime
{allOperational ? 'All systems operational' : 'Status check'}
Last server snapshot {updatedAt}. The page refreshes once a minute.
{uptime.status === 'loading' ? 'Checking' : `${operationalCount}/${checks.length || 3} online`}
Availability timeline
Last {formatNumber(history.length)} persisted server snapshots
{history.length ? `${availability.toFixed(1)}% uptime` : 'Waiting for first sample'}
{history.map((sample, index) => {
const height = `${Math.max(12, (sample.onlineChecks / sample.totalChecks) * 100)}%`
const left = history.length > 1 ? (index / (history.length - 1)) * 100 : 50
return (
setHoveredSnapshot(null)}
onFocus={() => setHoveredSnapshot({ ...sample, left })}
onMouseEnter={() => setHoveredSnapshot({ ...sample, left })}
onMouseLeave={() => setHoveredSnapshot(null)}
style={{ height }}
tabIndex="0"
/>
)
})}
{hoveredSnapshot ? (
{dateFormat.format(new Date(hoveredSnapshot.timestamp))}
{hoveredSnapshot.ok ? 'Uptime' : 'Downtime or degraded'} · {formatNumber(hoveredSnapshot.onlineChecks)}/{formatNumber(hoveredSnapshot.totalChecks)} online
) : null}
{!history.length ? (
Checking website status
) : null}
Uptime
Downtime or degraded
{checks.map((check) => (
{check.name}
{check.detail}
{check.ok ? 'Online' : 'Issue'}
{check.label}
{formatNumber(check.latency)} ms response window
))}
{!checks.length ? (
Checking website status
) : null}
)
}
export default App