SPB Git forge
15commits 1branches 0releases
29.7 MBsize
maindefault branch
10 days agolast push
TypeScript 36.3% Python 31.8% Go 18% JavaScript 9.8% Shell 1.9% SQL 1.4% CSS 0.5%
5.0 KB · 107 lines tsx
Raw Blame History
1'use client';23import { useState } from 'react';4import { AnimatedNumber } from '@/components/ui/AnimatedNumber';5import { Sparkline } from '@/components/ui/primitives';6import { fmt, fmtDelta, fmtInt } from '@/lib/format';7import { useDegraded, useLive } from '@/lib/live';8import { levelById, pressureColor, trendArrow } from '@/lib/pressure';9import { Time } from '@/lib/time';10import type { GlobalPressure } from '@/lib/types';11import { ExplainPanel } from './ExplainPanel';1213/** The instrument. SSR renders `initial`; afterwards the live slice drives it. Click → Explain (spec §44). */14export function Gauge({ initial }: { initial: GlobalPressure | null }) {15  const live = useLive((s) => s.global);16  const updates = useLive((s) => s.updates);17  const { degraded, frozenSince } = useDegraded();18  const [open, setOpen] = useState(false);19  const g = live ?? initial;2021  if (!g) {22    return (23      <div className="py-10">24        <p className="label">Global Internet Pressure</p>25        <p className="num mt-2 text-[96px] leading-none text-ink-3">—</p>26        <p className="mt-3 text-sm text-warn">The pressure API is unreachable. This is our failure, not an Internet event.</p>27      </div>28    );29  }3031  const color = pressureColor(g.level);32  const level = levelById(g.level);33  const dim = degraded ? 'opacity-50 saturate-50' : '';3435  return (36    <div className="relative">37      {/* faint radial glow tinted by the current level — the only gradient on the site */}38      <div aria-hidden="true" className="pointer-events-none absolute inset-x-0 -top-10 h-[360px] max-w-[560px] opacity-[0.22]" style={{ background: `radial-gradient(ellipse 60% 55% at 30% 45%, ${color}, transparent 70%)` }} />39      <p className="label relative">Global Internet Pressure</p>40      <button41        type="button"42        onClick={() => setOpen(true)}43        className={`group relative mt-1 flex flex-wrap items-baseline gap-x-5 gap-y-1 text-left ${dim}`}44        aria-label={`Global Internet Pressure ${fmt(g.pressure)}, ${level?.label ?? g.level}. Open explanation.`}45        aria-haspopup="dialog"46      >47        <AnimatedNumber48          value={g.pressure}49          digits={1}50          duration={updates > 0 ? 700 : 0}51          className="text-[112px] font-semibold leading-[0.9] tracking-[-0.045em] text-ink sm:text-[160px] lg:text-[184px]"52          aria-live="polite"53          aria-atomic="true"54        />55        <span className="flex flex-col gap-1">56          <span className="text-[22px] font-medium uppercase tracking-[0.18em] sm:text-[28px]" style={{ color }}>57            {level?.short ?? g.level}58          </span>59          <span className="num text-[18px] text-ink sm:text-[22px]">60            {trendArrow(g.trend, g.delta_1h)} {fmtDelta(g.delta_1h)} <span className="text-ink-3">/ 1h</span>61          </span>62        </span>63        <span className="absolute -bottom-5 left-0 text-[10.5px] uppercase tracking-[0.12em] text-ink-3 opacity-0 transition-opacity group-hover:opacity-100 group-focus-visible:opacity-100">Click to explain →</span>64      </button>6566      <div className={`relative mt-7 grid gap-x-8 gap-y-2 text-[12.5px] text-ink-2 sm:grid-cols-[auto_1fr] ${dim}`}>67        <div className="flex flex-wrap gap-x-6 gap-y-1">68          <span>69            <span className="label mr-2">velocity</span>70            <span className="num text-ink">{fmtDelta(g.velocity_per_h)}/h</span>71            <span className="num text-ink-3"> · {fmtDelta(g.acceleration_per_h2)}/h²</span>72          </span>73          <span>74            <span className="label mr-2">Δ24h</span>75            <span className="num text-ink">{fmtDelta(g.delta_24h)}</span>76          </span>77          <span>78            <span className="label mr-2">volatility</span>79            <span className="num text-ink">{fmt(g.volatility_1h)}</span>80          </span>81        </div>82        <div className="num sm:text-right">83          {fmtInt(g.coverage.probes_active)}84          <span className="text-ink-3">/{fmtInt(g.coverage.probes_total)}</span> probes · {fmtInt(g.coverage.probe_regions)} regions · {fmtInt(g.coverage.targets)} targets · {fmtInt(g.coverage.bgp_collectors)} collectors · confidence{' '}85          <span className="text-ink">{Math.round(g.confidence * 100)} %</span>86        </div>87      </div>8889      <div className={`relative mt-4 flex items-end gap-4 ${dim}`}>90        <Sparkline values={g.sparkline_1h} width={320} height={44} color={color} className="w-full max-w-[420px]" />91        <span className="label whitespace-nowrap">1 h</span>92      </div>9394      {degraded && (95        <p className="relative mt-3 text-[12px] text-warn">96          Frozen since <Time ts={frozenSince ?? g.ts} className="num" /> — instrument degraded, not an Internet event.97        </p>98      )}99      <p className="relative mt-2 text-[11px] text-ink-3">100        Engine <Time ts={g.ts} style="time" className="num" /> · baseline {fmt(g.coverage.baseline_days)} d · {fmtInt(g.coverage.measurements_5m)} measurements / 5 min101      </p>102103      {open && <ExplainPanel global={g} onClose={() => setOpen(false)} />}104    </div>105  );106}107