From fb6275e04a6456f300d9033a2824feb142e283c0 Mon Sep 17 00:00:00 2001
From: Adam <2363879+adamdotdevin@users.noreply.github.com>
Date: Fri, 29 May 2026 07:27:30 -0500
Subject: [PATCH] fix(stats): improve mobile charts
---
packages/stats/app/src/routes/index.css | 72 ++++++++++++++++++++-----
packages/stats/app/src/routes/index.tsx | 51 ++++++++++++++----
2 files changed, 100 insertions(+), 23 deletions(-)
diff --git a/packages/stats/app/src/routes/index.css b/packages/stats/app/src/routes/index.css
index 17fbda83ab..5aea25fbb5 100644
--- a/packages/stats/app/src/routes/index.css
+++ b/packages/stats/app/src/routes/index.css
@@ -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;
diff --git a/packages/stats/app/src/routes/index.tsx b/packages/stats/app/src/routes/index.tsx
index 5ee4f5f2ec..b2ad4a82a6 100644
--- a/packages/stats/app/src/routes/index.tsx
+++ b/packages/stats/app/src/routes/index.tsx
@@ -864,11 +864,17 @@ function MarketShare(props: {
)}
@@ -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 (
-
-
+
+
)
@@ -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 (