Преглед изворни кода

feat(stats): unify comparison cards

Adam пре 2 месеци
родитељ
комит
f4faacbe87

+ 85 - 21
packages/stats/app/src/routes/compare-cards.tsx

@@ -1,3 +1,4 @@
+import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
 import { For, Show } from "solid-js"
 import { For, Show } from "solid-js"
 import { catalogSlug, formatCatalogLabName, type ModelCatalogEntry } from "./model-catalog"
 import { catalogSlug, formatCatalogLabName, type ModelCatalogEntry } from "./model-catalog"
 
 
@@ -13,6 +14,7 @@ export type ComparisonPair = {
   first: ComparisonModelRef
   first: ComparisonModelRef
   second: ComparisonModelRef
   second: ComparisonModelRef
   detail: string
   detail: string
+  description?: string
 }
 }
 
 
 export function modelRefFromCatalog(entry: ModelCatalogEntry): ComparisonModelRef {
 export function modelRefFromCatalog(entry: ModelCatalogEntry): ComparisonModelRef {
@@ -48,34 +50,25 @@ export function ComparisonCardsSection(props: {
   title?: string
   title?: string
   description?: string
   description?: string
   compact?: boolean
   compact?: boolean
+  variant?: "panel" | "featured"
 }) {
 }) {
+  const featured = () => props.variant === "featured"
+  const pairs = () => (featured() ? props.pairs.slice(0, 4) : props.pairs)
+
   return (
   return (
     <Show when={props.pairs.length > 0}>
     <Show when={props.pairs.length > 0}>
-      <section id="model-comparison" data-section="model-panel" data-variant={props.compact ? "compact" : undefined}>
+      <section
+        id="model-comparison"
+        data-section={featured() ? "compare-home-related" : "model-panel"}
+        data-variant={!featured() && props.compact ? "compact" : undefined}
+      >
         <p data-slot="section-title">
         <p data-slot="section-title">
           <strong>{props.title ?? "Model Comparisons"}.</strong>{" "}
           <strong>{props.title ?? "Model Comparisons"}.</strong>{" "}
           <span>{props.description ?? "Compare usage, cost, limits, and features."}</span>
           <span>{props.description ?? "Compare usage, cost, limits, and features."}</span>
         </p>
         </p>
-        <div data-component="comparison-card-grid">
-          <For each={props.pairs}>
-            {(pair) => (
-              <a data-component="comparison-card" href={comparisonHref(pair.first, pair.second)}>
-                <span>{pair.detail}</span>
-                <strong>
-                  {pair.first.name} <em>vs</em> {pair.second.name}
-                </strong>
-                <p>
-                  <b>{pair.first.labName ?? formatCatalogLabName(pair.first.lab)}</b>
-                  <i />
-                  <b>{pair.second.labName ?? formatCatalogLabName(pair.second.lab)}</b>
-                </p>
-                <Show when={pair.first.metric || pair.second.metric}>
-                  <small>
-                    {pair.first.metric ?? "Listed"} / {pair.second.metric ?? "Listed"}
-                  </small>
-                </Show>
-              </a>
-            )}
+        <div data-component={featured() ? "compare-home-card-grid" : "comparison-card-grid"}>
+          <For each={pairs()}>
+            {(pair) => (featured() ? <FeaturedComparisonCard pair={pair} /> : <ComparisonPanelCard pair={pair} />)}
           </For>
           </For>
         </div>
         </div>
       </section>
       </section>
@@ -83,6 +76,77 @@ export function ComparisonCardsSection(props: {
   )
   )
 }
 }
 
 
+function FeaturedComparisonCard(props: { pair: ComparisonPair }) {
+  return (
+    <a
+      data-component="compare-home-card"
+      href={comparisonHref(props.pair.first, props.pair.second)}
+      aria-label={`${props.pair.detail}: ${props.pair.first.name} vs ${props.pair.second.name}`}
+    >
+      <span data-slot="compare-home-card-head">
+        <span>
+          <strong>{props.pair.detail}</strong>
+          <em>{props.pair.description ?? `${props.pair.first.name} vs ${props.pair.second.name}`}</em>
+        </span>
+        <b aria-hidden="true" />
+      </span>
+      <span data-slot="compare-home-card-divider" aria-hidden="true" />
+      <span data-slot="compare-home-card-models">
+        <span>{props.pair.first.name}</span>
+        <i aria-hidden="true">·</i>
+        <span>{props.pair.second.name}</span>
+      </span>
+      <span data-slot="compare-home-card-avatars" aria-hidden="true">
+        <ComparisonLabLogo model={props.pair.first} />
+        <ComparisonLabLogo model={props.pair.second} />
+      </span>
+    </a>
+  )
+}
+
+function ComparisonPanelCard(props: { pair: ComparisonPair }) {
+  return (
+    <a data-component="comparison-card" href={comparisonHref(props.pair.first, props.pair.second)}>
+      <span>{props.pair.detail}</span>
+      <strong>
+        {props.pair.first.name} <em>vs</em> {props.pair.second.name}
+      </strong>
+      <p>
+        <b>{props.pair.first.labName ?? formatCatalogLabName(props.pair.first.lab)}</b>
+        <i />
+        <b>{props.pair.second.labName ?? formatCatalogLabName(props.pair.second.lab)}</b>
+      </p>
+      <Show when={props.pair.first.metric || props.pair.second.metric}>
+        <small>
+          {props.pair.first.metric ?? "Listed"} / {props.pair.second.metric ?? "Listed"}
+        </small>
+      </Show>
+    </a>
+  )
+}
+
+function ComparisonLabLogo(props: { model: ComparisonModelRef }) {
+  const iconId = () => providerIconId(props.model.lab)
+
+  return (
+    <span
+      data-slot="compare-home-avatar"
+      data-lab={iconId()}
+      data-size="small"
+      aria-label={props.model.labName ?? formatCatalogLabName(props.model.lab)}
+    >
+      <ProviderIcon aria-hidden="true" id={iconId()} />
+    </span>
+  )
+}
+
 function modelKey(model: ComparisonModelRef) {
 function modelKey(model: ComparisonModelRef) {
   return `${catalogSlug(model.lab)}/${catalogSlug(model.slug)}`
   return `${catalogSlug(model.lab)}/${catalogSlug(model.slug)}`
 }
 }
+
+function providerIconId(provider: string) {
+  const id = provider.toLowerCase().replace(/[^a-z0-9]+/g, "")
+  if (id === "moonshot") return "moonshotai"
+  if (id === "zhipu") return "zhipuai"
+  return id
+}

+ 7 - 46
packages/stats/app/src/routes/compare/[firstLab]/[firstModel]/[secondLab]/[secondModel].tsx

@@ -12,6 +12,7 @@ import { createAsync, query, useParams, useSearchParams } from "@solidjs/router"
 import { createEffect, createMemo, createSignal, For, onCleanup, onMount, Show } from "solid-js"
 import { createEffect, createMemo, createSignal, For, onCleanup, onMount, Show } from "solid-js"
 import { getRequestEvent } from "solid-js/web"
 import { getRequestEvent } from "solid-js/web"
 import {
 import {
+  ComparisonCardsSection,
   comparisonHref,
   comparisonHref,
   modelRefFromCatalog,
   modelRefFromCatalog,
   uniqueComparisonPairs,
   uniqueComparisonPairs,
@@ -382,7 +383,12 @@ export default function ModelComparePair() {
               </Show>
               </Show>
             </div>
             </div>
           </div>
           </div>
-          <ComparisonRelatedSection pairs={relatedPairs()} />
+          <ComparisonCardsSection
+            pairs={relatedPairs()}
+            title="Related comparisons"
+            description="Other model pairs to check."
+            variant="featured"
+          />
         </div>
         </div>
         <Footer
         <Footer
           themePreference={themePreference()}
           themePreference={themePreference()}
@@ -865,51 +871,6 @@ function ComparisonUsageBars(props: { data: ModelUsagePoint[]; column: number; l
   )
   )
 }
 }
 
 
-function ComparisonRelatedSection(props: { pairs: ComparisonPair[] }) {
-  return (
-    <Show when={props.pairs.length > 0}>
-      <section id="model-comparison" data-section="compare-home-related">
-        <p data-slot="section-title">
-          <strong>Related comparisons.</strong> <span>Other model pairs to check.</span>
-        </p>
-        <div data-component="compare-home-card-grid">
-          <For each={props.pairs.slice(0, 4)}>{(pair) => <ComparisonRelatedCard pair={pair} />}</For>
-        </div>
-      </section>
-    </Show>
-  )
-}
-
-function ComparisonRelatedCard(props: { pair: ComparisonPair }) {
-  return (
-    <a
-      data-component="compare-home-card"
-      href={comparisonHref(props.pair.first, props.pair.second)}
-      aria-label={`${props.pair.first.name} vs ${props.pair.second.name}`}
-    >
-      <span data-slot="compare-home-card-head">
-        <span>
-          <strong>{props.pair.detail}</strong>
-          <em>
-            {props.pair.first.name} vs {props.pair.second.name}
-          </em>
-        </span>
-        <b aria-hidden="true" />
-      </span>
-      <span data-slot="compare-home-card-divider" aria-hidden="true" />
-      <span data-slot="compare-home-card-models">
-        <span>{props.pair.first.name}</span>
-        <i aria-hidden="true">·</i>
-        <span>{props.pair.second.name}</span>
-      </span>
-      <span data-slot="compare-home-card-avatars" aria-hidden="true">
-        <LabLogo lab={props.pair.first.lab} label={props.pair.first.labName ?? props.pair.first.lab} size="small" />
-        <LabLogo lab={props.pair.second.lab} label={props.pair.second.labName ?? props.pair.second.lab} size="small" />
-      </span>
-    </a>
-  )
-}
-
 function ModelAvatar(props: { model: ModelCatalogEntry; size: "large" | "small" | "tiny" }) {
 function ModelAvatar(props: { model: ModelCatalogEntry; size: "large" | "small" | "tiny" }) {
   return <LabLogo lab={props.model.lab} label={props.model.name} size={props.size} />
   return <LabLogo lab={props.model.lab} label={props.model.name} size={props.size} />
 }
 }

+ 16 - 51
packages/stats/app/src/routes/compare/index.tsx

@@ -8,7 +8,13 @@ import { LocaleLinks } from "../../component/locale-links"
 import { useI18n } from "../../context/i18n"
 import { useI18n } from "../../context/i18n"
 import { useLanguage } from "../../context/language"
 import { useLanguage } from "../../context/language"
 import { localizedUrl } from "../../lib/language"
 import { localizedUrl } from "../../lib/language"
-import { comparisonHref, modelRefFromCatalog, type ComparisonModelRef } from "../compare-cards"
+import {
+  ComparisonCardsSection,
+  comparisonHref,
+  modelRefFromCatalog,
+  type ComparisonModelRef,
+  type ComparisonPair,
+} from "../compare-cards"
 import { formatCatalogLabName, getModelCatalog, type ModelCatalogEntry } from "../model-catalog"
 import { formatCatalogLabName, getModelCatalog, type ModelCatalogEntry } from "../model-catalog"
 import { setStatsPageCacheHeaders } from "../stats-cache"
 import { setStatsPageCacheHeaders } from "../stats-cache"
 import {
 import {
@@ -56,13 +62,6 @@ const categoryTemplates = [
   },
   },
 ] as const
 ] as const
 
 
-type CompareCategory = {
-  title: string
-  description: string
-  first: ComparisonModelRef
-  second: ComparisonModelRef
-  avatars: ComparisonModelRef[]
-}
 type CompareSlot = "first" | "second"
 type CompareSlot = "first" | "second"
 
 
 export default function ModelCompareIndex() {
 export default function ModelCompareIndex() {
@@ -162,16 +161,12 @@ export default function ModelCompareIndex() {
               <CompareHomeSelector models={featuredModels()} />
               <CompareHomeSelector models={featuredModels()} />
             </Show>
             </Show>
           </section>
           </section>
-          <Show when={categories().length > 0}>
-            <section id="model-comparison" data-section="compare-home-related">
-              <p data-slot="section-title">
-                <strong>Related comparisons.</strong> <span>Other model pairs to check.</span>
-              </p>
-              <div data-component="compare-home-card-grid">
-                <For each={categories()}>{(category) => <CompareHomeCard category={category} />}</For>
-              </div>
-            </section>
-          </Show>
+          <ComparisonCardsSection
+            pairs={categories()}
+            title="Related comparisons"
+            description="Other model pairs to check."
+            variant="featured"
+          />
         </div>
         </div>
         <Footer
         <Footer
           themePreference={themePreference()}
           themePreference={themePreference()}
@@ -443,35 +438,6 @@ function HeroModelStack() {
   )
   )
 }
 }
 
 
-function CompareHomeCard(props: { category: CompareCategory }) {
-  return (
-    <a
-      data-component="compare-home-card"
-      href={comparisonHref(props.category.first, props.category.second)}
-      aria-label={`${props.category.title}: ${props.category.first.name} vs ${props.category.second.name}`}
-    >
-      <span data-slot="compare-home-card-head">
-        <span>
-          <strong>{props.category.title}</strong>
-          <em>{props.category.description}</em>
-        </span>
-        <b aria-hidden="true" />
-      </span>
-      <span data-slot="compare-home-card-divider" aria-hidden="true" />
-      <span data-slot="compare-home-card-models">
-        <span>{props.category.first.name}</span>
-        <i aria-hidden="true">·</i>
-        <span>{props.category.second.name}</span>
-      </span>
-      <span data-slot="compare-home-card-avatars" aria-hidden="true">
-        <For each={props.category.avatars}>
-          {(model) => <LabLogo lab={model.lab} label={model.name} size="small" />}
-        </For>
-      </span>
-    </a>
-  )
-}
-
 function ModelAvatar(props: { model: ModelCatalogEntry; size: "large" | "small" | "tiny" }) {
 function ModelAvatar(props: { model: ModelCatalogEntry; size: "large" | "small" | "tiny" }) {
   return <LabLogo lab={props.model.lab} label={props.model.name} size={props.size} />
   return <LabLogo lab={props.model.lab} label={props.model.name} size={props.size} />
 }
 }
@@ -486,8 +452,8 @@ function LabLogo(props: { lab: string; label: string; size: "large" | "small" |
   )
   )
 }
 }
 
 
-function buildComparisonCategories(models: ModelCatalogEntry[]): CompareCategory[] {
-  return categoryTemplates.reduce<{ keys: Set<string>; categories: CompareCategory[] }>(
+function buildComparisonCategories(models: ModelCatalogEntry[]): ComparisonPair[] {
+  return categoryTemplates.reduce<{ keys: Set<string>; categories: ComparisonPair[] }>(
     (result, template, index) => {
     (result, template, index) => {
       const candidates = categoryCandidates(template.kind, models)
       const candidates = categoryCandidates(template.kind, models)
       const pair = categoryPair(candidates, models, index, result.keys)
       const pair = categoryPair(candidates, models, index, result.keys)
@@ -496,11 +462,10 @@ function buildComparisonCategories(models: ModelCatalogEntry[]): CompareCategory
       const first = modelRefFromCatalog(pair.first)
       const first = modelRefFromCatalog(pair.first)
       const second = modelRefFromCatalog(pair.second)
       const second = modelRefFromCatalog(pair.second)
       result.categories.push({
       result.categories.push({
-        title: template.title,
+        detail: template.title,
         description: template.description,
         description: template.description,
         first,
         first,
         second,
         second,
-        avatars: [first, second],
       })
       })
       return result
       return result
     },
     },

+ 1 - 0
packages/stats/app/src/routes/index.tsx

@@ -194,6 +194,7 @@ export default function StatsHome() {
                   pairs={homeComparisonPairs(stats().leaderboard["All Users"]["2M"])}
                   pairs={homeComparisonPairs(stats().leaderboard["All Users"]["2M"])}
                   title="Model Comparisons"
                   title="Model Comparisons"
                   description="Popular model pairs from the leaderboard."
                   description="Popular model pairs from the leaderboard."
+                  variant="featured"
                 />
                 />
               </>
               </>
             )}
             )}