feat(data): another redesigned lab section

This commit is contained in:
Adam 2026-07-01 14:06:34 -05:00
parent 91ca75c4dc
commit 85e8ac3b36
No known key found for this signature in database
GPG key ID: 9CB48779AF150E75
2 changed files with 519 additions and 84 deletions

View file

@ -42,6 +42,14 @@ const getLabData = query(async (lab: string) => {
return runStatsEffect(getStatsLabData(lab))
}, "getStatsLabData")
type LabModelTooltipState = {
model: ModelCatalogEntry
placement: "left" | "right"
usage: LabUsageModelEntry | undefined
x: number
y: number
}
export default function StatsLab() {
const i18n = useI18n()
const language = useLanguage()
@ -478,52 +486,168 @@ function LabUsageSection(props: { lab: ModelCatalogLab; data: StatsLabData | nul
function LabModelsSection(props: { lab: ModelCatalogLab; usage: LabUsageModelEntry[] }) {
const i18n = useI18n()
const [activeTooltip, setActiveTooltip] = createSignal<LabModelTooltipState>()
const usageBySlug = createMemo(() => new Map(props.usage.map((item) => [item.slug, item])))
return (
<section id="models" data-section="model-panel">
<SectionHeading
href="#models"
title={i18n.t("lab.modelsTitle", { lab: props.lab.name })}
description={i18n.t("lab.recentUsageAndLimits")}
/>
<div data-component="lab-model-grid">
<For each={props.lab.models}>
{(model) => <LabModelCard model={model} usage={usageBySlug().get(model.slug)} />}
</For>
<section id="models" data-section="model-panel" data-variant="lab-models">
<div data-slot="lab-model-heading">
<SectionHeading href="#models" title={i18n.t("nav.models")} description={i18n.t("lab.recentUsageAndLimits")} />
<button data-slot="lab-model-compare" type="button" aria-label="Compare models" hidden>
<span data-slot="lab-model-compare-icon" aria-hidden="true" />
<span>Compare</span>
</button>
</div>
<div data-slot="lab-model-pattern" aria-hidden="true" />
<div data-component="lab-model-table" role="table" aria-label={i18n.t("lab.modelsTitle", { lab: props.lab.name })}>
<div data-slot="lab-model-table-track">
<div data-slot="lab-model-table-head" role="row">
<span data-column="model" role="columnheader">
{i18n.t("nav.models")}
</span>
<span data-column="usage" role="columnheader">
{i18n.t("lab.usage")}
</span>
<span data-column="share" role="columnheader">
{i18n.t("lab.share")}
</span>
<span data-column="context" role="columnheader">
{i18n.t("model.context")}
</span>
<span data-column="output" role="columnheader">
{i18n.t("model.output")}
</span>
<span data-column="release" role="columnheader">
{i18n.t("model.release")}
</span>
</div>
<For each={props.lab.models}>
{(model) => (
<LabModelRow
model={model}
usage={usageBySlug().get(model.slug)}
onTooltipChange={setActiveTooltip}
/>
)}
</For>
</div>
</div>
<Show when={activeTooltip()} keyed>
{(state) => <LabModelTooltip state={state} />}
</Show>
</section>
)
}
function LabModelCard(props: { model: ModelCatalogEntry; usage: LabUsageModelEntry | undefined }) {
function LabModelRow(props: {
model: ModelCatalogEntry
onTooltipChange: (state: LabModelTooltipState | undefined) => void
usage: LabUsageModelEntry | undefined
}) {
const i18n = useI18n()
const language = useLanguage()
const showTooltip = (x: number, y: number) => {
const viewportWidth = typeof window === "undefined" ? 0 : window.innerWidth
const viewportHeight = typeof window === "undefined" ? 0 : window.innerHeight
props.onTooltipChange({
model: props.model,
placement: viewportWidth > 0 && x > viewportWidth - 280 ? "left" : "right",
usage: props.usage,
x,
y: viewportHeight > 0 ? Math.min(Math.max(y, 96), viewportHeight - 128) : y,
})
}
const showPointerTooltip: JSX.EventHandler<HTMLAnchorElement, PointerEvent> = (event) => {
if (event.pointerType === "touch") return
showTooltip(event.clientX, event.clientY)
}
const showFocusTooltip: JSX.EventHandler<HTMLAnchorElement, FocusEvent> = (event) => {
const rect = event.currentTarget.getBoundingClientRect()
showTooltip(rect.left + rect.width * 0.58, rect.top + rect.height / 2)
}
return (
<a data-component="lab-model-card" href={language.route(`${import.meta.env.BASE_URL}${props.model.id}`)}>
<strong>{props.model.name}</strong>
<div data-slot="lab-model-card-meta">
<a
data-component="lab-model-row"
href={language.route(`${import.meta.env.BASE_URL}${props.model.id}`)}
role="row"
aria-label={props.model.name}
onBlur={() => props.onTooltipChange(undefined)}
onFocus={showFocusTooltip}
onPointerEnter={showPointerTooltip}
onPointerLeave={(event) => {
if (event.pointerType === "touch") return
props.onTooltipChange(undefined)
}}
onPointerMove={showPointerTooltip}
>
<span data-slot="lab-model-cell" data-column="model" role="cell">
<span data-slot="lab-model-avatar" aria-hidden="true">
<ProviderIcon id={getProviderIconId(props.model.lab)} />
</span>
<strong>{props.model.name}</strong>
</span>
<span data-slot="lab-model-cell" data-column="usage" role="cell">
{props.usage ? formatTokens(props.usage.tokens) : "-"}
</span>
<span data-slot="lab-model-cell" data-column="share" role="cell">
{props.usage ? formatPercent(props.usage.share) : "-"}
</span>
<span data-slot="lab-model-cell" data-column="context" role="cell">
{formatCatalogLimit(props.model.limit?.context, i18n.t("home.unknown"))}
</span>
<span data-slot="lab-model-cell" data-column="output" role="cell">
{formatCatalogLimit(props.model.limit?.output, i18n.t("home.unknown"))}
</span>
<span data-slot="lab-model-cell" data-column="release" role="cell">
{formatCatalogDate(props.model.releaseDate, language.tag(language.locale()), i18n.t("home.unknown"))}
</span>
</a>
)
}
function LabModelTooltip(props: { state: LabModelTooltipState }) {
const i18n = useI18n()
return (
<div
data-component="lab-model-tooltip"
data-placement={props.state.placement}
style={
{
"--lab-model-tooltip-x": `${props.state.x}px`,
"--lab-model-tooltip-y": `${props.state.y}px`,
} as JSX.CSSProperties
}
>
<div data-slot="lab-model-tooltip-summary">
<div data-slot="lab-model-tooltip-head">
<span data-slot="lab-model-tooltip-avatar" aria-hidden="true">
<ProviderIcon id={getProviderIconId(props.state.model.lab)} />
</span>
<strong>{props.state.model.name}</strong>
</div>
<p>
<b>{i18n.t("lab.usage")}</b>
<em>{props.usage ? formatTokens(props.usage.tokens) : "—"}</em>
</p>
<p>
<b>{i18n.t("lab.share")}</b>
<em>{props.usage ? formatPercent(props.usage.share) : "—"}</em>
</p>
<p>
<b>{i18n.t("model.context")}</b>
<em>{formatCatalogLimit(props.model.limit?.context, i18n.t("home.unknown"))}</em>
</p>
<p>
<b>{i18n.t("model.output")}</b>
<em>{formatCatalogLimit(props.model.limit?.output, i18n.t("home.unknown"))}</em>
</p>
<p>
<b>{i18n.t("model.release")}</b>
<em>{formatCatalogDate(props.model.releaseDate, language.tag(language.locale()), i18n.t("home.unknown"))}</em>
Recent OpenCode Go usage, share, context, and output limits.
</p>
</div>
</a>
<div data-slot="tooltip-divider" />
<div data-slot="lab-model-tooltip-metrics">
<p>
<span>{i18n.t("lab.usage")}</span>
<b>{props.state.usage ? formatTokens(props.state.usage.tokens) : "-"}</b>
</p>
<p>
<span>{i18n.t("lab.share")}</span>
<b>{props.state.usage ? formatPercent(props.state.usage.share) : "-"}</b>
</p>
<p>
<span>{i18n.t("model.context")}</span>
<b>{formatCatalogLimit(props.state.model.limit?.context, i18n.t("home.unknown"))}</b>
</p>
<p>
<span>{i18n.t("model.output")}</span>
<b>{formatCatalogLimit(props.state.model.limit?.output, i18n.t("home.unknown"))}</b>
</p>
</div>
</div>
)
}

View file

@ -3299,68 +3299,360 @@
color: #b82d35;
}
[data-page="stats"] [data-component="lab-model-grid"] {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 8px;
[data-page="stats"] [data-section="model-panel"][data-variant="lab-models"] {
overflow: hidden;
}
[data-page="stats"] [data-component="lab-model-card"] {
[data-page="stats"] [data-slot="lab-model-heading"] {
display: flex;
align-items: center;
justify-content: space-between;
gap: 40px;
min-height: 42px;
margin-bottom: 40px;
}
[data-page="stats"] [data-slot="lab-model-heading"] [data-slot="section-title"] {
min-width: 0;
margin: 0;
}
[data-page="stats"] [data-slot="lab-model-compare"] {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
gap: 8px;
height: 32px;
padding: 0 12px 0 8px;
border: 0;
border-radius: 0;
color: var(--stats-text);
background: var(--stats-bg);
box-shadow:
0 0 0 0.5px var(--stats-line-strong),
0 1px 1.5px #0000001a;
font: inherit;
font-size: 13px;
font-weight: 500;
line-height: 14px;
white-space: nowrap;
cursor: pointer;
}
[data-page="stats"] [data-slot="lab-model-compare"][hidden] {
display: none;
}
[data-page="stats"] [data-slot="lab-model-compare"]::before {
position: absolute;
top: 0;
right: 0;
left: 0;
height: 16px;
pointer-events: none;
content: "";
background: linear-gradient(180deg, #ffffff00 0%, #ffffff16 100%);
}
[data-page="stats"] [data-slot="lab-model-compare"] span {
position: relative;
z-index: 1;
}
[data-page="stats"] [data-slot="lab-model-compare-icon"] {
width: 16px;
height: 16px;
color: var(--stats-muted);
background:
linear-gradient(currentColor 0 0) 3px 3px / 3px 3px no-repeat,
linear-gradient(currentColor 0 0) 10px 3px / 3px 3px no-repeat,
linear-gradient(currentColor 0 0) 3px 10px / 3px 3px no-repeat,
linear-gradient(currentColor 0 0) 10px 10px / 3px 3px no-repeat;
}
[data-page="stats"] [data-slot="lab-model-compare"]:hover,
[data-page="stats"] [data-slot="lab-model-compare"]:focus-visible {
color: var(--stats-text);
outline: none;
box-shadow:
0 0 0 0.5px var(--stats-text),
0 1px 1.5px #0000001a;
}
[data-page="stats"] [data-slot="lab-model-pattern"] {
height: 16px;
margin-bottom: 40px;
overflow: hidden;
background: color-mix(in srgb, var(--stats-text) 10%, transparent);
mask-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 6 6' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 0H2V2H0V0Z' fill='black'/%3E%3C/svg%3E");
mask-position: center top;
mask-repeat: repeat;
mask-size: 6px 6px;
-webkit-mask-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 6 6' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 0H2V2H0V0Z' fill='black'/%3E%3C/svg%3E");
-webkit-mask-position: center top;
-webkit-mask-repeat: repeat;
-webkit-mask-size: 6px 6px;
}
[data-page="stats"] [data-component="lab-model-table"] {
--lab-model-row-bleed: 16px;
box-sizing: border-box;
width: calc(100% + 32px);
margin-inline: -16px;
padding-inline: 16px;
overflow-x: auto;
overflow-y: hidden;
scrollbar-width: none;
}
[data-page="stats"] [data-component="lab-model-table"]::-webkit-scrollbar {
display: none;
}
[data-page="stats"] [data-slot="lab-model-table-track"] {
--lab-model-column-template: 240px repeat(4, 200px) minmax(160px, 1fr);
--lab-model-table-width: 1200px;
position: relative;
display: grid;
align-content: space-between;
gap: 28px;
min-height: 220px;
padding: 16px;
overflow: hidden;
border: 1px solid var(--stats-line);
width: 100%;
min-width: var(--lab-model-table-width);
}
[data-page="stats"] [data-slot="lab-model-table-track"]::before {
position: absolute;
inset: 0;
z-index: 0;
pointer-events: none;
content: "";
background:
linear-gradient(var(--stats-line), var(--stats-line)) 240px 0 / 1px 100% no-repeat,
linear-gradient(var(--stats-line), var(--stats-line)) 440px 0 / 1px 100% no-repeat,
linear-gradient(var(--stats-line), var(--stats-line)) 640px 0 / 1px 100% no-repeat,
linear-gradient(var(--stats-line), var(--stats-line)) 840px 0 / 1px 100% no-repeat,
linear-gradient(var(--stats-line), var(--stats-line)) 1040px 0 / 1px 100% no-repeat;
}
[data-page="stats"] [data-slot="lab-model-table-head"],
[data-page="stats"] [data-component="lab-model-row"] {
position: relative;
z-index: 1;
display: grid;
grid-template-columns: var(--lab-model-column-template);
}
[data-page="stats"] [data-slot="lab-model-table-head"] {
align-items: start;
height: 20px;
margin-bottom: 20px;
color: var(--stats-muted);
font-size: 13px;
font-weight: 500;
line-height: 20px;
text-transform: uppercase;
}
[data-page="stats"] [data-slot="lab-model-table-head"] span,
[data-page="stats"] [data-slot="lab-model-cell"] {
min-width: 0;
}
[data-page="stats"] [data-slot="lab-model-table-head"] span:not([data-column="model"]) {
padding-left: 40px;
}
[data-page="stats"] [data-component="lab-model-row"] {
align-items: center;
height: 64px;
color: var(--stats-text);
text-decoration: none;
isolation: isolate;
}
[data-page="stats"] [data-component="lab-model-row"]::before {
position: absolute;
top: 8px;
right: calc(var(--lab-model-row-bleed) * -1);
bottom: 8px;
left: calc(var(--lab-model-row-bleed) * -1);
z-index: 0;
content: "";
background: transparent;
pointer-events: none;
}
[data-page="stats"] [data-component="lab-model-row"]:hover,
[data-page="stats"] [data-component="lab-model-row"]:focus-visible {
color: var(--stats-text);
outline: none;
text-decoration: none;
}
[data-page="stats"] [data-component="lab-model-row"]:hover::before,
[data-page="stats"] [data-component="lab-model-row"]:focus-visible::before {
background: var(--stats-layer);
}
[data-page="stats"] [data-component="lab-model-card"]:hover,
[data-page="stats"] [data-component="lab-model-card"]:focus-visible {
[data-page="stats"] [data-slot="lab-model-cell"] {
position: relative;
z-index: 1;
border-color: var(--stats-text);
outline: none;
}
[data-page="stats"] [data-component="lab-model-card"] > strong {
align-self: start;
min-width: 0;
color: var(--stats-text);
font-size: 24px;
font-weight: 500;
line-height: 1.05;
letter-spacing: 0;
overflow-wrap: anywhere;
}
[data-page="stats"] [data-slot="lab-model-card-meta"] {
display: grid;
gap: 8px;
}
[data-page="stats"] [data-slot="lab-model-card-meta"] p {
display: flex;
justify-content: space-between;
gap: 12px;
margin: 0;
padding-top: 8px;
border-top: 1px solid var(--stats-line);
color: var(--stats-muted);
font-size: 11px;
font-weight: 500;
line-height: 1.3;
}
[data-page="stats"] [data-slot="lab-model-card-meta"] b,
[data-page="stats"] [data-slot="lab-model-card-meta"] em {
font: inherit;
}
[data-page="stats"] [data-slot="lab-model-card-meta"] em {
align-items: center;
height: 24px;
color: var(--stats-text);
font-size: 16px;
font-weight: 400;
line-height: 24px;
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
[data-page="stats"] [data-slot="lab-model-cell"][data-column="model"] {
gap: 16px;
}
[data-page="stats"] [data-slot="lab-model-cell"]:not([data-column="model"]):not([data-column="release"]) {
justify-content: flex-end;
padding-right: 40px;
text-align: right;
}
[data-page="stats"] [data-slot="lab-model-cell"][data-column="release"] {
padding-left: 40px;
}
[data-page="stats"] [data-slot="lab-model-avatar"] {
display: grid;
place-items: center;
flex: 0 0 auto;
width: 20px;
height: 20px;
color: #ffffff;
background: var(--stats-accent-text);
box-shadow: inset 0 0 0 1px #0000001a;
}
[data-page="stats"] [data-slot="lab-model-avatar"] svg {
width: 15px;
height: 15px;
}
[data-page="stats"] [data-slot="lab-model-cell"] strong {
min-width: 0;
overflow: hidden;
color: var(--stats-text);
font-size: inherit;
font-weight: 500;
line-height: inherit;
text-overflow: ellipsis;
}
[data-page="stats"] [data-component="lab-model-tooltip"] {
position: fixed;
top: var(--lab-model-tooltip-y);
left: var(--lab-model-tooltip-x);
z-index: 40;
width: 240px;
color: var(--stats-text);
background: var(--stats-bg);
border: 1px solid var(--stats-line);
box-shadow: 0 12px 24px #0000001f;
pointer-events: none;
transform: translate(12px, -50%);
}
[data-page="stats"] [data-component="lab-model-tooltip"][data-placement="left"] {
transform: translate(calc(-100% - 12px), -50%);
}
[data-page="stats"] [data-slot="lab-model-tooltip-summary"] {
display: grid;
gap: 6px;
min-width: 0;
padding: 12px;
}
[data-page="stats"] [data-slot="lab-model-tooltip-head"] {
display: flex;
align-items: center;
gap: 8px;
min-width: 0;
}
[data-page="stats"] [data-slot="lab-model-tooltip-avatar"] {
display: grid;
place-items: center;
flex: 0 0 auto;
width: 16px;
height: 16px;
color: #ffffff;
background: var(--stats-accent-text);
box-shadow: inset 0 0 0 1px #0000001a;
}
[data-page="stats"] [data-slot="lab-model-tooltip-avatar"] svg {
width: 12px;
height: 12px;
}
[data-page="stats"] [data-slot="lab-model-tooltip-head"] strong {
min-width: 0;
overflow: hidden;
color: var(--stats-text);
font-size: 12px;
font-weight: 500;
line-height: 17px;
text-overflow: ellipsis;
white-space: nowrap;
}
[data-page="stats"] [data-slot="lab-model-tooltip-summary"] > p {
margin: 0;
color: var(--stats-muted);
font-size: 12px;
font-weight: 400;
line-height: 17px;
}
[data-page="stats"] [data-component="lab-model-tooltip"] [data-slot="tooltip-divider"] {
height: 0.5px;
background: var(--stats-line);
}
[data-page="stats"] [data-slot="lab-model-tooltip-metrics"] {
display: grid;
padding: 12px;
}
[data-page="stats"] [data-slot="lab-model-tooltip-metrics"] p {
display: grid;
grid-template-columns: minmax(0, 100px) minmax(0, 1fr);
align-items: center;
column-gap: 16px;
min-width: 0;
height: 17px;
margin: 0;
padding: 0;
color: var(--stats-muted);
font-size: 12px;
font-weight: 400;
line-height: 17px;
}
[data-page="stats"] [data-slot="lab-model-tooltip-metrics"] span,
[data-page="stats"] [data-slot="lab-model-tooltip-metrics"] b {
min-width: 0;
overflow: hidden;
font: inherit;
text-overflow: ellipsis;
white-space: nowrap;
}
[data-page="stats"] [data-slot="lab-model-tooltip-metrics"] b {
color: var(--stats-text);
font-weight: 500;
font-variant-numeric: tabular-nums;
text-align: right;
}
@ -4527,6 +4819,25 @@
font-size: 13px;
}
[data-page="stats"] [data-slot="lab-model-heading"] {
align-items: flex-start;
flex-direction: column;
gap: 16px;
margin-bottom: 32px;
}
[data-page="stats"] [data-slot="lab-model-pattern"] {
margin-bottom: 32px;
}
[data-page="stats"] [data-component="lab-model-table"] {
--lab-model-row-bleed: 24px;
box-sizing: border-box;
width: calc(100% + 48px);
margin-inline: -24px;
padding-inline: 24px;
}
[data-page="stats"] [data-component="model-metric-grid"],
[data-page="stats"] [data-component="model-metric-grid"][data-variant="dense"],
[data-page="stats"] [data-component="lab-model-grid"] {