fix(stats): improve mobile charts

This commit is contained in:
Adam 2026-05-29 07:27:30 -05:00
parent 6102fb2e3f
commit fb6275e04a
No known key found for this signature in database
GPG key ID: 9CB48779AF150E75
2 changed files with 100 additions and 23 deletions

View file

@ -1719,6 +1719,7 @@
[data-page="stats"] [data-slot="leader-copy"] strong {
color: var(--stats-text);
font-weight: 600;
line-height: 1.3;
}
[data-page="stats"] [data-slot="leader-copy"] span {
@ -1799,6 +1800,14 @@
font-weight: 600;
}
[data-page="stats"] [data-slot="market-axis-label"] {
display: grid;
}
[data-page="stats"] [data-slot="market-date-mobile"] {
display: none;
}
[data-page="stats"] [data-slot="market-bars"] {
flex: 1;
min-height: 0;
@ -1977,23 +1986,21 @@
}
[data-page="stats"] [data-component="metric-bar"] {
display: flex;
align-items: center;
gap: 3px;
position: relative;
display: block;
flex: 1;
min-width: 0;
height: 5px;
background: var(--stats-layer-2);
font-style: normal;
}
[data-page="stats"] [data-component="metric-bar"] b,
[data-page="stats"] [data-component="metric-bar"] em {
display: block;
height: 5px;
}
[data-page="stats"] [data-component="metric-bar"] b {
flex-basis: 0;
position: absolute;
inset: 0 auto 0 0;
display: block;
width: var(--metric-bar-fill);
height: 5px;
background: var(--stats-text);
}
@ -2002,12 +2009,10 @@
}
[data-page="stats"] [data-component="metric-bar"] em {
flex: 1;
min-width: 8px;
background: var(--stats-layer-2);
display: none;
}
[data-page="stats"] [data-component="metric-bar"][data-active="true"] em {
[data-page="stats"] [data-component="metric-bar"][data-active="true"] {
background: var(--stats-line-strong);
}
@ -2311,6 +2316,13 @@
grid-template-rows: none;
}
[data-page="stats"] [data-component="token-tooltip"] {
position: static;
order: -1;
width: 100%;
margin-bottom: 8px;
}
[data-page="stats"] [data-slot="market-footer"] {
align-items: flex-start;
flex-direction: column;
@ -2376,6 +2388,11 @@
height: 400px;
}
[data-page="stats"] [data-component="market-share"] {
grid-template-rows: 40px minmax(0, 1fr);
height: 400px;
}
[data-page="stats"] [data-slot="top-models-axis"] > div {
position: relative;
display: flex;
@ -2385,6 +2402,14 @@
font-weight: 600;
}
[data-page="stats"] [data-slot="market-labels"] button {
position: relative;
align-items: center;
justify-content: center;
height: 40px;
line-height: 1.2;
}
[data-page="stats"] [data-slot="axis-label"] {
position: absolute;
left: 50%;
@ -2394,16 +2419,35 @@
transform-origin: left center;
}
[data-page="stats"] [data-slot="market-axis-label"] {
position: absolute;
left: 50%;
width: max-content;
max-width: 72px;
transform: rotate(-90deg) translateX(-50%);
transform-origin: left center;
}
[data-page="stats"] [data-slot="top-models-axis"] > div[data-mobile-hidden="true"] [data-slot="axis-label"],
[data-page="stats"] [data-slot="axis-total"],
[data-page="stats"] [data-slot="axis-date-full"] {
display: none;
}
[data-page="stats"] [data-slot="market-labels"] button[data-mobile-hidden="true"] [data-slot="market-axis-label"],
[data-page="stats"] [data-slot="market-total"],
[data-page="stats"] [data-slot="market-date-full"] {
display: none;
}
[data-page="stats"] [data-slot="axis-date-mobile"] {
display: block;
}
[data-page="stats"] [data-slot="market-date-mobile"] {
display: block;
}
[data-page="stats"] [data-section="top-models"] [data-component="chart-tooltip"] {
position: fixed;
top: auto;

View file

@ -864,11 +864,17 @@ function MarketShare(props: {
<button
type="button"
data-active={props.inspecting && props.activeIndex === index() ? "true" : undefined}
data-mobile-hidden={isMarketMobileLabelHidden(index(), props.data.length) ? "true" : undefined}
onClick={() => props.onActiveIndexChange(index())}
onPointerEnter={() => props.onActiveIndexChange(index())}
>
<span>{formatTrillions(day.total)}</span>
<span>{day.date}</span>
<span data-slot="market-axis-label">
<span data-slot="market-total">{formatTrillions(day.total)}</span>
<span data-slot="market-date">
<span data-slot="market-date-full">{day.date}</span>
<span data-slot="market-date-mobile">{formatMarketMobileDate(day.date)}</span>
</span>
</span>
</button>
)}
</For>
@ -964,21 +970,43 @@ function getMarketSegmentColor(author: string, color: string, activeAuthor: stri
return "var(--stats-bar-idle)"
}
function isMarketMobileLabelHidden(index: number, count: number) {
return count > 7 && index % 2 === 1
}
function formatMarketMobileDate(label: string) {
return marketDateParts(label).start
}
function formatTrillions(value: number) {
return `${value.toFixed(value >= 10 ? 0 : 1)}T`
}
function formatMarketDate(day: MarketDay | undefined) {
if (!day) return "No data"
return `${day.date} ${new Date().getFullYear()}`
return formatMarketDateLabel(day.date)
}
function formatMarketRange(data: MarketDay[]) {
const first = data[0]?.date
const last = data[data.length - 1]?.date
if (!first || !last) return "No data"
const start = marketDateParts(first).start
const end = marketDateParts(last).end
if (start === end) return formatMarketDateLabel(start)
return `${start} ${new Date().getFullYear()}${end} ${new Date().getFullYear()}`
}
function formatMarketDateLabel(label: string) {
const parts = marketDateParts(label)
const year = new Date().getFullYear()
return `${first} ${year}${last} ${year}`
if (parts.start === parts.end) return `${parts.start} ${year}`
return `${parts.start} ${year}${parts.end} ${year}`
}
function marketDateParts(label: string) {
const [start, end] = label.split(" - ")
return { start: start ?? label, end: end ?? start ?? label }
}
function TokenCostSection(props: { data: StatsHomeData["tokenCost"] }) {
@ -1019,7 +1047,7 @@ function TokenCostChart(props: {
activeIndex: number
onActiveIndexChange: (index: number) => void
}) {
const max = createMemo(() => Math.max(1, ...props.data.map((item) => item.total)))
const max = createMemo(() => Math.max(0, ...props.data.map((item) => item.total)) || 1)
const active = createMemo(() => props.data[props.activeIndex] ?? props.data[0])
return (
@ -1066,9 +1094,14 @@ function formatDollars(value: number) {
}
function MetricBar(props: { value: number; max: number; active: boolean }) {
const fill = createMemo(() => Math.min(1, Math.max(props.value / props.max, props.value > 0 ? 0.03 : 0)))
return (
<i data-component="metric-bar" data-active={props.active ? "true" : undefined}>
<b style={{ "flex-grow": Math.max(props.value / Math.max(props.max, 1), 0.05) }} />
<i
data-component="metric-bar"
data-active={props.active ? "true" : undefined}
style={{ "--metric-bar-fill": `${fill() * 100}%` } as JSX.CSSProperties}
>
<b />
<em />
</i>
)
@ -1115,8 +1148,8 @@ function SessionCostChart(props: {
activeIndex: number
onActiveIndexChange: (index: number) => void
}) {
const maxCost = createMemo(() => Math.max(1, ...props.data.map((item) => item.cost)))
const maxTokens = createMemo(() => Math.max(1, ...props.data.map((item) => item.tokens)))
const maxCost = createMemo(() => Math.max(0, ...props.data.map((item) => item.cost)) || 1)
const maxTokens = createMemo(() => Math.max(0, ...props.data.map((item) => item.tokens)) || 1)
const active = createMemo(() => props.data[props.activeIndex] ?? props.data[0])
return (