mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-31 05:15:32 +00:00
fix(stats): improve mobile charts
This commit is contained in:
parent
6102fb2e3f
commit
fb6275e04a
2 changed files with 100 additions and 23 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue