mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-30 14:58:08 +00:00
The year of commits was GitHub's stats/commit_activity for public, non-fork repositories: one call each, some 2,100, more than the deploy token's hourly allowance, so a push never refreshed it and the private two-thirds of the estate were never in it. The forge now mirrors every repository of every org the estate publishes under (universe estate.json), and cloud serves what its index counts at GET /v1/git/activity. sync-oss reads that once a build: - the year is our commits, by SHA, across every repository, private ones counted and never named; a carried project's upstream history is in estate.commits and heads nothing - the days, the newest commits (linked to GitHub where GitHub publishes the repository, else the forge), each org's commits and every forge link come from the same read - each group's orgs are rewritten from the estate's list - GitHub still answers what only it knows: public names, stars, forks, languages, homepages, contributors The forge card shows the whole estate when the snapshot carries it, and /oss/<org> its org as the forge holds it. Until cloud serves the read, or when it answers a partial year, the committed snapshot stands.
181 lines
8.7 KiB
TypeScript
181 lines
8.7 KiB
TypeScript
'use client'
|
|
|
|
import { useMemo, useState } from 'react'
|
|
import Link from 'next/link'
|
|
import { ArrowLeft, Search } from 'lucide-react'
|
|
import { useI18n } from '@hanzo/i18n/react'
|
|
import { Button, Input, Text, View, XStack, YStack } from '@hanzo/ui'
|
|
import { Counts } from '@hanzo/ui/marketing'
|
|
import { Github } from '@/components/brand'
|
|
import { Page, PageHero, Section } from '@/components/marketing/page-kit'
|
|
import { Caption, Mono } from '@/components/home/plate'
|
|
import { github, named, type Estate } from '@/lib/oss'
|
|
|
|
/** One public repository of an org, as the sync read it: GitHub's figures, and our commits in it this year as the forge counted them. */
|
|
export type Row = { name: string; description?: string; language?: string; stars: number; forks: number; pushed?: string; archived?: boolean; commits?: number; forge?: string }
|
|
/**
|
|
* An org as GitHub describes it, its public repositories newest push first, and
|
|
* the org as the forge holds it (`estate`): every repository, private ones counted.
|
|
*/
|
|
export type Held = { name?: string; description?: string; forge?: string; estate?: Omit<Estate, 'orgs' | 'contributors' | 'active'> & { last?: string }; repos: Row[] }
|
|
|
|
const fmt = (n: number) => n.toLocaleString('en-US')
|
|
const day = (iso: string) => new Date(iso).toLocaleDateString('en-US', { timeZone: 'UTC', month: 'short', day: 'numeric', year: 'numeric' })
|
|
/** How many rows a page draws before the reader asks for the rest. */
|
|
const FIRST = 60
|
|
|
|
function Figure({ id, value, title }: { id: string; value: number; title: string }) {
|
|
return (
|
|
<YStack gap={4} p={16} rounded="var(--radius-xl)" borderWidth={1} borderColor="var(--border)" bg="$panel" minW={0}>
|
|
<Text fontFamily="$mono" fontSize="$7" lineHeight="$7" fontWeight="600" color="var(--foreground)" data-figure={id} data-i18n-ltr>
|
|
{fmt(value)}
|
|
</Text>
|
|
<Caption pt={0}>{title}</Caption>
|
|
</YStack>
|
|
)
|
|
}
|
|
|
|
function Line({ login, row }: { login: string; row: Row }) {
|
|
const { t } = useI18n()
|
|
const full = `${login}/${row.name}`
|
|
return (
|
|
<YStack render="li" gap={6} py={14} borderTopWidth={1} borderColor="var(--border)" data-repo={full}>
|
|
<XStack items="baseline" justify="space-between" gap={12} flexWrap="wrap">
|
|
<Text render={<a href={github(full)} target="_blank" rel="noopener noreferrer" />} fontFamily="$mono" fontSize="$3" fontWeight="600" color="var(--foreground)" data-i18n-ltr $platform-web={{ textDecoration: 'none', wordBreak: 'break-all' }}>
|
|
{full}
|
|
</Text>
|
|
<XStack items="center" gap={12}>
|
|
{row.language ? <Mono color="var(--muted-foreground)">{row.language}</Mono> : null}
|
|
<Counts stars={row.stars} forks={row.forks} size="sm" />
|
|
</XStack>
|
|
</XStack>
|
|
{row.description ? (
|
|
<Text render="p" display="block" fontSize="$2" lineHeight="$3" color="var(--muted-foreground)">
|
|
{row.description}
|
|
</Text>
|
|
) : null}
|
|
<XStack items="center" gap={12} flexWrap="wrap">
|
|
{row.pushed ? (
|
|
<Mono color="var(--muted-foreground)" data-i18n-ltr>
|
|
<time dateTime={row.pushed}>{t('Pushed {date}', { date: day(row.pushed) })}</time>
|
|
</Mono>
|
|
) : null}
|
|
{row.archived ? <Mono color="var(--muted-foreground)">{t('Archived')}</Mono> : null}
|
|
{row.commits ? (
|
|
<Mono color="var(--muted-foreground)" data-commits={row.commits}>
|
|
{row.commits === 1 ? t('1 commit this year') : t('{n} commits this year', { n: fmt(row.commits) })}
|
|
</Mono>
|
|
) : null}
|
|
{row.forge ? (
|
|
<Text render={<a href={row.forge} target="_blank" rel="noopener noreferrer" />} fontFamily="$mono" fontSize="$1" color="var(--muted-foreground)" hoverStyle={{ color: 'var(--foreground)' }} $platform-web={{ textDecoration: 'none' }}>
|
|
{t('Forge')}
|
|
</Text>
|
|
) : null}
|
|
</XStack>
|
|
</YStack>
|
|
)
|
|
}
|
|
|
|
export function OrgView({ login, held, family, read }: { login: string; held: Held; family: string; read: string }) {
|
|
const { t } = useI18n()
|
|
const [query, setQuery] = useState('')
|
|
const [all, setAll] = useState(false)
|
|
const found = useMemo(() => {
|
|
const q = query.trim().toLowerCase()
|
|
return q ? held.repos.filter((r) => `${r.name} ${r.description ?? ''} ${r.language ?? ''}`.toLowerCase().includes(q)) : held.repos
|
|
}, [held.repos, query])
|
|
const shown = all || query ? found : found.slice(0, FIRST)
|
|
const stars = held.repos.reduce((n, r) => n + r.stars, 0)
|
|
const forks = held.repos.reduce((n, r) => n + r.forks, 0)
|
|
const at = new Date(read).toLocaleString('en-US', { timeZone: 'UTC', month: 'short', day: 'numeric', year: 'numeric' })
|
|
|
|
return (
|
|
<Page>
|
|
<PageHero
|
|
eyebrow={t('Open source · {family}', { family })}
|
|
title={named(login, held.name)}
|
|
lede={held.description ?? t('The public repositories of {org} on GitHub, each with its stars, forks, language and last push.', { org: login })}
|
|
>
|
|
{/* The hero lays its actions out in one row that wraps (components/marketing/fold); a row of our own inside it would not give way on a phone. */}
|
|
<>
|
|
<Button size="sm" asChild>
|
|
<a href={github(login)} target="_blank" rel="noopener noreferrer">
|
|
<Github width={14} height={14} />
|
|
{t('{org} on GitHub', { org: login })}
|
|
</a>
|
|
</Button>
|
|
{held.forge ? (
|
|
<Button size="sm" variant="outline" asChild>
|
|
<a href={held.forge} target="_blank" rel="noopener noreferrer">
|
|
{t('On Hanzo Forge')}
|
|
</a>
|
|
</Button>
|
|
) : null}
|
|
<Button size="sm" variant="ghost" asChild>
|
|
<Link href="/open-source">
|
|
<ArrowLeft size={14} data-i18n-mirror="" />
|
|
{t('Every organization')}
|
|
</Link>
|
|
</Button>
|
|
</>
|
|
</PageHero>
|
|
|
|
<Section title={t('Counted from GitHub')}>
|
|
<View display="grid" gridTemplateColumns="minmax(0, 1fr)" gap={12} $sm={{ gridTemplateColumns: 'repeat(3, minmax(0, 1fr))' }}>
|
|
<Figure id="repos" value={held.repos.length} title={t('public repositories')} />
|
|
<Figure id="stars" value={stars} title={t('stars')} />
|
|
<Figure id="forks" value={forks} title={t('forks')} />
|
|
</View>
|
|
<Caption>{t('Forks left out · read from GitHub {at}', { at })}</Caption>
|
|
</Section>
|
|
|
|
{held.estate ? (
|
|
<Section title={t('Counted on Hanzo Forge')}>
|
|
<View display="grid" gridTemplateColumns="minmax(0, 1fr)" gap={12} $sm={{ gridTemplateColumns: 'repeat(2, minmax(0, 1fr))' }} $lg={{ gridTemplateColumns: 'repeat(4, minmax(0, 1fr))' }}>
|
|
<Figure id="ours" value={held.estate.ours} title={t('of our commits in the past year')} />
|
|
<Figure id="estate-repos" value={held.estate.repos} title={t('repositories, {n} of them private', { n: fmt(held.estate.private) })} />
|
|
<Figure id="active30" value={held.estate.active30} title={t('repositories with a commit in 30 days')} />
|
|
<Figure id="merges" value={held.estate.merges} title={t('merges in the past year')} />
|
|
</View>
|
|
<Caption>{t('Every repository of {org}, mirrored to the forge: private ones are counted, never named, and each commit is counted once.', { org: login })}</Caption>
|
|
</Section>
|
|
) : null}
|
|
|
|
<Section title={t('Repositories')}>
|
|
<YStack gap={12}>
|
|
<XStack items="center" gap={8} px={10} height={40} rounded={8} borderWidth={1} borderColor="var(--border)" maxW={420} $platform-web={{ borderRadius: '8px' }}>
|
|
<Search size={14} color="var(--muted-foreground)" aria-hidden />
|
|
<Input
|
|
type="search"
|
|
value={query}
|
|
onChange={(e) => setQuery(e.target.value)}
|
|
placeholder={t('Search {n} repositories', { n: fmt(held.repos.length) })}
|
|
aria-label={t('Search the repositories of {org}', { org: login })}
|
|
flex={1}
|
|
minW={0}
|
|
borderWidth={0}
|
|
bg="transparent"
|
|
/>
|
|
</XStack>
|
|
{shown.length ? (
|
|
<YStack render="ul" $platform-web={{ listStyle: 'none', margin: 0, padding: 0 }} data-slot="repos">
|
|
{shown.map((row) => (
|
|
<Line key={row.name} login={login} row={row} />
|
|
))}
|
|
</YStack>
|
|
) : (
|
|
<Caption>{t('No repository of {org} matches that.', { org: login })}</Caption>
|
|
)}
|
|
{!query && !all && found.length > FIRST ? (
|
|
<XStack>
|
|
<Button size="sm" variant="outline" onPress={() => setAll(true)}>
|
|
{t('Show all {n}', { n: fmt(found.length) })}
|
|
</Button>
|
|
</XStack>
|
|
) : null}
|
|
</YStack>
|
|
</Section>
|
|
</Page>
|
|
)
|
|
}
|