Files
hanzo.ai/components/oss/Activity.tsx
zeekay 24e67d965e landing: what we ship is counted, and drawn from the page's own parts
The commits section read numbers that were typed into scripts/sync-oss.ts
(5,420 repositories, 214 orgs, 784 pushes a day, 245,417 commits, each
contributor's count), a day's count scaled by a made-up factor, and a star
count that began at 1,420. The forge's database holds 1,150 repositories
and 46 orgs; none of that was read. Its panels were tinted rgba over a
stylesheet, DualStarButton showed its guess before GitHub answered, and
BulkStarModal said "successfully starred" over a forge PUT.

scripts/sync-oss.ts (was .mjs) now counts GitHub's public activity for the
orgs we publish: commits a day, the year, the week, active repositories and
the newest commits, written at each build with the moment it was read. A
figure the snapshot does not hold is not drawn. The grid, bars and
repository card are @hanzo/ui/marketing (8.29.19); the showcase stylesheet
and DayActivityModal are deleted. The org and repo pages lose their typed
"velocity" banners.

Design lint is zero in components/oss, home/OpenSource and ForgeBetaBanner.
e2e/gates/activity.spec.ts holds each figure to the snapshot, the cards to
one fill and edge in both schemes, a card's name, line and foot to their own
rows, and the phone to no sideways scroll.

Claude-Session: https://claude.ai/code/session_017vrVus6PPBgP5YRzKV1iqW
2026-10-26 09:57:01 -07:00

107 lines
5.3 KiB
TypeScript

'use client'
import { useI18n } from '@hanzo/i18n/react'
import { Text, View, XStack, YStack } from '@hanzo/ui'
import { Heatmap, More, Sparkline } from '@hanzo/ui/marketing'
import activity from '@/lib/data/oss-activity.json'
import stats from '@/lib/data/oss-stats.json'
import { Card, Caption, Mono } from '@/components/home/plate'
import { Onward } from '@/components/home/label'
import { figures, weekly, type Figure as Counted } from '@/lib/activity'
/**
* What the public repositories shipped, counted and not typed.
*
* Every figure and every cell is read by scripts/sync-oss.ts off GitHub's public
* API for the repositories we publish (forks left out, because a fork's history
* is the upstream's), and written to lib/data/oss-activity.json and
* oss-stats.json at each build, and `stats.read` is the moment it was read. A figure that file does not hold is not drawn,
* and with no activity at all the section is not drawn: there is no placeholder
* number and no fallback. The grid and the bars are @hanzo/ui's (`Heatmap`,
* `Sparkline`): this file hands them the data and the words.
*/
const daily = activity.daily as Record<string, number>
const active = activity.active as Record<string, number>
const fmt = (n: number) => n.toLocaleString('en-US')
const label = (k: string) => new Date(`${k}T00:00:00Z`).toLocaleDateString('en-US', { timeZone: 'UTC', month: 'short', day: 'numeric', year: 'numeric' })
const hour = (iso: string) => new Date(iso).toLocaleString('en-US', { timeZone: 'UTC', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', hour12: false })
function Figure({ id, title, value, caption, children }: { id: string; title: string; value: string; caption?: string; children?: React.ReactNode }) {
return (
<Card title={title}>
<Text fontFamily="$mono" fontSize="2rem" lineHeight="2.25rem" fontWeight="600" color="var(--foreground)" $platform-web={{ fontVariantNumeric: 'tabular-nums' }} data-figure={id}>
{value}
</Text>
{children}
{caption ? <Caption>{caption}</Caption> : null}
</Card>
)
}
function Latest() {
const { t } = useI18n()
const rows = activity.latest as { repo: string; at: string; sha: string; message: string }[]
if (!rows.length) return null
return (
<Card title={t('Latest commits')}>
<YStack render="ul" $platform-web={{ listStyle: 'none', margin: 0, padding: 0 }} data-slot="latest">
{rows.map((r) => (
<XStack key={`${r.repo}@${r.sha}`} render="li" items="baseline" gap={16} py={10} borderTopWidth={1} borderColor="var(--border)" flexWrap="wrap" $md={{ flexWrap: 'nowrap' }}>
<Mono color="var(--muted-foreground)" shrink={0} width={200} $platform-web={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
{r.repo}
</Mono>
<Text render={<a href={`https://github.com/${r.repo}/commit/${r.sha}`} target="_blank" rel="noopener noreferrer" />} flex={1} minW={200} fontSize="$2" lineHeight="$2" color="var(--foreground)" $platform-web={{ textDecoration: 'none' }}>
{r.message}
</Text>
<Mono color="var(--muted-foreground)" shrink={0} data-i18n-ltr>
<time dateTime={r.at}>{hour(r.at)} UTC</time>
</Mono>
</XStack>
))}
</YStack>
</Card>
)
}
/** The panel: the counted figures the snapshot holds, the year as a grid, and the newest commits. Not drawn without activity. */
export default function Activity() {
const { t } = useI18n()
if (!Object.keys(daily).length) return null
const titles = { year: t('Commits, past year'), week: t('Commits, past 7 days'), repos: t('Public repositories'), active: t('Active this month') } as const
const notes = (f: Counted) => (f.id === 'repos' && f.of != null ? t('{n} organizations', { n: f.of }) : f.id === 'active' ? t('repositories with a commit in 30 days') : undefined)
const shown = figures(stats)
return (
<YStack gap={16} data-slot="activity">
{shown.length ? (
<View display="grid" gridTemplateColumns="repeat(2, minmax(0, 1fr))" gap={12} $md={{ gridTemplateColumns: `repeat(${shown.length}, minmax(0, 1fr))`, gap: 16 }}>
{shown.map((f) => (
<Figure key={f.id} id={f.id} title={titles[f.id]} value={fmt(f.value)} caption={notes(f)}>
{f.id === 'year' ? <Sparkline values={weekly(daily, activity.from, activity.to)} label={t('Commits a week over the past year')} height={28} /> : null}
</Figure>
))}
</View>
) : null}
<Card title={t('Commits a day')}>
<Heatmap
days={daily}
from={activity.from}
to={activity.to}
locale="en-US"
label={t('Commits a day over the past year across our public repositories: {n} in all.', { n: fmt(stats.commits.year) })}
line={(day, n) => t('{date} ยท {n} commits across {repos} repositories', { date: label(day), n: fmt(n), repos: fmt(active[day] ?? 0) })}
rest={t('Commits a day, from {from} to {to}', { from: label(activity.from), to: label(activity.to) })}
legend={{ less: t('Less'), more: t('More') }}
/>
</Card>
<Latest />
<XStack justify="flex-end">
<More href="https://github.com/hanzoai">
<Onward>{t('Read the source')}</Onward>
</More>
</XStack>
</YStack>
)
}