Adam 2 месяцев назад
Родитель
Сommit
262ef4fdc2

+ 1 - 1
packages/stats/app/src/i18n.ts

@@ -154,7 +154,7 @@ const en = {
   "lab.notFound": "No models matched this lab.",
   "lab.heroPrefix": "Explore {{count}} {{lab}} models used in OpenCode",
   "lab.heroIncluding": "including {{models}}",
-  "lab.heroSuffix": "Compare recent token usage, context windows, release dates, and model-specific data.",
+  "lab.heroSuffix": "Compare recent token usage, context windows, release dates, and model data.",
   "lab.tokensProcessed": "Tokens Processed",
   "lab.pending": "Pending",
   "lab.usageAfterActivity": "Usage appears after model activity lands",

+ 48 - 0
packages/stats/app/src/routes/[lab]/index.tsx

@@ -123,6 +123,7 @@ export default function StatsLab() {
               {(data) => (
                 <>
                   <LabHero lab={data()} labs={catalog()?.labs ?? []} />
+                  <LabOverview lab={data()} data={stats() ?? null} />
                   <LabUsageSection lab={data()} data={stats() ?? null} />
                   <LabModelsSection lab={data()} usage={stats()?.models ?? []} />
                 </>
@@ -229,6 +230,44 @@ function LabHeroTitleRow(props: { icon?: string; label: string }) {
   )
 }
 
+function LabOverview(props: { lab: ModelCatalogLab; data: StatsLabData | null }) {
+  const i18n = useI18n()
+  const language = useLanguage()
+  const featuredModels = createMemo(() => props.lab.models.slice(0, 3).map((model) => model.name))
+  return (
+    <section data-section="lab-overview">
+      <div data-slot="lab-overview-copy">
+        <ProviderIcon data-slot="lab-overview-watermark" aria-hidden="true" id={getProviderIconId(props.lab.id)} />
+        <p>
+          {i18n.t("lab.heroPrefix", { count: props.lab.models.length, lab: props.lab.name })}
+          <Show when={featuredModels().length > 0}>
+            {" "}
+            {i18n.t("lab.heroIncluding", { models: formatList(featuredModels(), language.tag(language.locale())) })}
+          </Show>
+          . {i18n.t("lab.heroSuffix")}
+        </p>
+      </div>
+      <LabOverviewMetric
+        label="Tokens processed"
+        value={props.data ? formatTokens(props.data.totals.tokens) : i18n.t("lab.pending")}
+      />
+      <LabOverviewMetric
+        label="Recent usage"
+        value={props.data ? formatWholePercent(props.data.tokenShare) : i18n.t("lab.pending")}
+      />
+    </section>
+  )
+}
+
+function LabOverviewMetric(props: { label: string; value: string }) {
+  return (
+    <article data-component="lab-overview-metric">
+      <span>{props.label}</span>
+      <strong>{props.value}</strong>
+    </article>
+  )
+}
+
 function LabUsageSection(props: { lab: ModelCatalogLab; data: StatsLabData | null }) {
   const i18n = useI18n()
   const [activeIndex, setActiveIndex] = createSignal<number>()
@@ -418,10 +457,19 @@ function formatCatalogDate(value: string | undefined, locale: string, unknown: s
   }).format(new Date(Date.UTC(year, month, day)))
 }
 
+function formatList(values: string[], locale = "en") {
+  if (values.length <= 1) return values[0] ?? ""
+  return new Intl.ListFormat(locale, { style: "long", type: "conjunction" }).format(values)
+}
+
 function formatPercent(value: number) {
   return `${trimNumber(value, value >= 10 ? 1 : 2)}%`
 }
 
+function formatWholePercent(value: number) {
+  return `${Math.round(value).toLocaleString("en")}%`
+}
+
 function formatTokens(value: number) {
   if (value >= 1_000_000_000_000)
     return `${trimNumber(value / 1_000_000_000_000, value >= 10_000_000_000_000 ? 0 : 1)}T`

+ 188 - 0
packages/stats/app/src/routes/index.css

@@ -3048,6 +3048,113 @@
   line-height: 1.5;
 }
 
+[data-page="stats"] [data-section="lab-overview"] {
+  position: relative;
+  box-sizing: border-box;
+  display: grid;
+  grid-template-columns: minmax(0, 720fr) repeat(2, minmax(0, 280fr));
+  min-height: 192px;
+  overflow: hidden;
+  color: var(--stats-text);
+  box-shadow:
+    inset 0 1px var(--stats-line),
+    inset 0 -1px var(--stats-line);
+}
+
+[data-page="stats"] [data-section="lab-overview"]::before,
+[data-page="stats"] [data-section="lab-overview"]::after {
+  position: absolute;
+  top: 1px;
+  bottom: 1px;
+  z-index: 2;
+  width: 1px;
+  background: var(--stats-line);
+  content: "";
+  pointer-events: none;
+}
+
+[data-page="stats"] [data-section="lab-overview"]::before {
+  left: 0;
+}
+
+[data-page="stats"] [data-section="lab-overview"]::after {
+  right: 0;
+}
+
+[data-page="stats"] [data-slot="lab-overview-copy"] {
+  position: relative;
+  box-sizing: border-box;
+  min-width: 0;
+  min-height: 192px;
+  padding: 60px 40px;
+  overflow: hidden;
+}
+
+[data-page="stats"] [data-slot="lab-overview-copy"] p {
+  position: relative;
+  z-index: 1;
+  max-width: 640px;
+  color: var(--stats-muted);
+  font-size: 16px;
+  font-weight: 400;
+  line-height: 24px;
+}
+
+[data-page="stats"] [data-slot="lab-overview-watermark"] {
+  position: absolute;
+  top: -224px;
+  left: 40px;
+  width: 640px;
+  height: 640px;
+  color: var(--stats-layer-2);
+  opacity: 0.72;
+  pointer-events: none;
+}
+
+[data-page="stats"] [data-component="lab-overview-metric"] {
+  display: grid;
+  align-content: start;
+  box-sizing: border-box;
+  min-width: 0;
+  min-height: 192px;
+  padding: 40px 60px;
+  position: relative;
+}
+
+[data-page="stats"] [data-component="lab-overview-metric"]::before {
+  position: absolute;
+  top: 1px;
+  bottom: 1px;
+  left: 0;
+  width: 1px;
+  background: var(--stats-line);
+  content: "";
+  pointer-events: none;
+}
+
+[data-page="stats"] [data-component="lab-overview-metric"] span {
+  min-width: 0;
+  overflow: hidden;
+  color: var(--stats-muted);
+  font-size: 13px;
+  font-weight: 400;
+  line-height: 24px;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+[data-page="stats"] [data-component="lab-overview-metric"] strong {
+  min-width: 0;
+  overflow: hidden;
+  color: var(--stats-text);
+  font-size: 76px;
+  font-weight: 500;
+  line-height: 88px;
+  letter-spacing: 0;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
 [data-page="stats"] [data-slot="model-weight-link"] {
   width: fit-content;
   color: var(--stats-muted);
@@ -3868,6 +3975,20 @@
     min-height: 280px;
     padding: 104px 32px 40px;
   }
+
+  [data-page="stats"] [data-slot="lab-overview-copy"] {
+    padding-right: 32px;
+    padding-left: 32px;
+  }
+
+  [data-page="stats"] [data-slot="lab-overview-watermark"] {
+    left: 32px;
+  }
+
+  [data-page="stats"] [data-component="lab-overview-metric"] {
+    padding-right: 32px;
+    padding-left: 32px;
+  }
 }
 
 @media (max-width: 58rem) {
@@ -3898,6 +4019,36 @@
     align-items: start;
   }
 
+  [data-page="stats"] [data-section="lab-overview"] {
+    grid-template-columns: repeat(2, minmax(0, 1fr));
+  }
+
+  [data-page="stats"] [data-slot="lab-overview-copy"] {
+    grid-column: 1 / -1;
+    min-height: 168px;
+    padding-top: 48px;
+    padding-bottom: 48px;
+  }
+
+  [data-page="stats"] [data-slot="lab-overview-watermark"] {
+    top: -236px;
+  }
+
+  [data-page="stats"] [data-component="lab-overview-metric"] {
+    min-height: 144px;
+    padding-top: 28px;
+    padding-bottom: 28px;
+    border-top: 1px solid var(--stats-line);
+  }
+
+  [data-page="stats"] [data-component="lab-overview-metric"]:nth-of-type(1) {
+    border-left: 0;
+  }
+
+  [data-page="stats"] [data-component="lab-overview-metric"]:nth-of-type(1)::before {
+    display: none;
+  }
+
   [data-page="stats"] [data-component="model-rank-panel"] {
     min-height: 0;
   }
@@ -4066,6 +4217,43 @@
     padding: 72px 24px 40px;
   }
 
+  [data-page="stats"] [data-section="lab-overview"] {
+    grid-template-columns: 1fr;
+  }
+
+  [data-page="stats"] [data-slot="lab-overview-copy"] {
+    min-height: 168px;
+    padding: 48px 24px;
+  }
+
+  [data-page="stats"] [data-slot="lab-overview-copy"] p {
+    font-size: 15px;
+    line-height: 23px;
+  }
+
+  [data-page="stats"] [data-slot="lab-overview-watermark"] {
+    top: -168px;
+    left: 24px;
+    width: 480px;
+    height: 480px;
+  }
+
+  [data-page="stats"] [data-component="lab-overview-metric"] {
+    min-height: 120px;
+    padding: 24px;
+    border-top: 1px solid var(--stats-line);
+    border-left: 0;
+  }
+
+  [data-page="stats"] [data-component="lab-overview-metric"]::before {
+    display: none;
+  }
+
+  [data-page="stats"] [data-component="lab-overview-metric"] strong {
+    font-size: 56px;
+    line-height: 64px;
+  }
+
   [data-page="stats"] [data-slot="lab-hero-title-row"] {
     gap: 16px;
     height: 72px;