feat(console): compare Go and Go Plus limits side by side (#51865)
This commit is contained in:
parent
f416138844
commit
b76bbb3692
3 changed files with 118 additions and 140 deletions
|
|
@ -61,12 +61,23 @@
|
|||
color: var(--go-console-muted);
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
[data-slot="tier-switch"] {
|
||||
[data-slot="legend"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
gap: 1rem;
|
||||
margin: 0;
|
||||
padding: 0 0 0.375rem;
|
||||
list-style: none;
|
||||
color: var(--go-console-secondary);
|
||||
font-weight: 530;
|
||||
white-space: nowrap;
|
||||
}
|
||||
[data-slot="tier-switch"] i {
|
||||
[data-slot="legend"] li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
[data-slot="legend"] i {
|
||||
width: 3px;
|
||||
height: 12px;
|
||||
border-radius: 1px;
|
||||
|
|
@ -90,27 +101,6 @@
|
|||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
[data-slot="tier-switch"] button {
|
||||
color: var(--go-console-secondary);
|
||||
border-radius: 4px;
|
||||
transition:
|
||||
background-color 180ms ease,
|
||||
opacity 180ms ease;
|
||||
}
|
||||
[data-slot="tier-switch"] button[aria-pressed="false"] {
|
||||
opacity: 0.4;
|
||||
font-weight: 440;
|
||||
}
|
||||
[data-slot="tier-switch"] button[aria-pressed="false"]:is(:hover, :focus-visible) {
|
||||
opacity: 0.75;
|
||||
}
|
||||
[data-slot="tier-switch"] button[aria-pressed="true"] {
|
||||
background: var(--go-console-surface);
|
||||
color: var(--go-console-text);
|
||||
}
|
||||
[data-slot="tier-switch"] button:hover {
|
||||
color: var(--go-console-text);
|
||||
}
|
||||
[data-slot="expand"][aria-expanded="true"] svg {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
|
@ -148,28 +138,48 @@
|
|||
border-start-end-radius: 6px;
|
||||
border-end-end-radius: 6px;
|
||||
}
|
||||
tbody tr > * {
|
||||
transition: background-color 120ms ease;
|
||||
}
|
||||
tbody tr:hover > * {
|
||||
background: var(--go-console-surface);
|
||||
}
|
||||
tbody tr > :first-child {
|
||||
border-start-start-radius: 6px;
|
||||
border-end-start-radius: 6px;
|
||||
}
|
||||
tbody tr > :last-child {
|
||||
border-start-end-radius: 6px;
|
||||
border-end-end-radius: 6px;
|
||||
}
|
||||
tbody th {
|
||||
padding: 0.5rem 1rem;
|
||||
text-align: start;
|
||||
font-weight: 400;
|
||||
color: var(--go-console-text);
|
||||
}
|
||||
[data-slot="model"] {
|
||||
display: inline-flex;
|
||||
[data-slot="labels"] {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 0.375rem;
|
||||
gap: 0.25rem;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
[data-slot="model"] small {
|
||||
[data-slot="labels"] :is(small, a) {
|
||||
border: 1px solid var(--go-console-border);
|
||||
padding: 0 0.25rem;
|
||||
font-size: 0.625rem;
|
||||
line-height: 1rem;
|
||||
}
|
||||
[data-slot="model"] a {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
color: var(--go-console-muted);
|
||||
[data-slot="labels"] a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
transition:
|
||||
background-color 180ms ease,
|
||||
border-color 180ms ease;
|
||||
}
|
||||
[data-slot="labels"] a:hover {
|
||||
background: var(--go-console-track);
|
||||
border-color: var(--go-console-track);
|
||||
}
|
||||
[data-slot="bars"] {
|
||||
position: relative;
|
||||
|
|
@ -178,32 +188,20 @@
|
|||
position: absolute;
|
||||
inset-block: 0;
|
||||
border-inline-start: 1px dashed var(--go-console-border);
|
||||
transition: inset-inline-start 600ms cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
[data-slot="track"] {
|
||||
position: relative;
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
height: 0.5rem;
|
||||
display: grid;
|
||||
grid-template-rows: 0.5rem 0.5rem;
|
||||
row-gap: 0.5rem;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
[data-slot="track"] > [data-tier] {
|
||||
position: relative;
|
||||
[data-slot="lane"] {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
[data-slot="lane"] > [data-tier] {
|
||||
border-radius: 2px;
|
||||
background: linear-gradient(var(--v2-yellow-500), var(--v2-yellow-600));
|
||||
transition: width 600ms cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
[data-slot="track"] > [data-tier]::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
background: linear-gradient(var(--v2-orange-500), var(--v2-orange-600));
|
||||
opacity: 0;
|
||||
transition: opacity 300ms ease;
|
||||
}
|
||||
[data-slot="track"] > [data-tier="plus"]::after {
|
||||
opacity: 1;
|
||||
}
|
||||
[data-slot="remainder"] {
|
||||
flex: 1;
|
||||
|
|
@ -215,12 +213,13 @@
|
|||
text-align: end;
|
||||
white-space: nowrap;
|
||||
}
|
||||
:is(thead th, [data-slot="number"]):last-child {
|
||||
padding-inline-start: 0.5rem;
|
||||
}
|
||||
[data-slot="number"] span {
|
||||
display: block;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
[data-slot="plan-value"] {
|
||||
animation: go-plan-value-in 300ms ease-out both;
|
||||
/* 13px digits are ~9.5px tall, so this leaves the same ~8px visual gap as the bars. */
|
||||
line-height: 1.1rem;
|
||||
}
|
||||
[data-slot="axis"] {
|
||||
position: relative;
|
||||
|
|
@ -232,7 +231,6 @@
|
|||
position: absolute;
|
||||
top: 0.25rem;
|
||||
transform: translateX(-50%);
|
||||
transition: inset-inline-start 600ms cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
&:dir(rtl) [data-slot="axis"] span {
|
||||
transform: translateX(50%);
|
||||
|
|
@ -312,28 +310,18 @@
|
|||
[data-slot="number"] {
|
||||
padding-inline: 0.5rem;
|
||||
}
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
[data-slot="tier-switch"] button,
|
||||
[data-slot="gridline"],
|
||||
[data-slot="track"] > [data-tier],
|
||||
[data-slot="track"] > [data-tier]::after,
|
||||
[data-slot="axis"] span {
|
||||
transition: none;
|
||||
/* Rows are grids here, so highlight the row itself instead of each cell. */
|
||||
tbody tr {
|
||||
border-radius: 6px;
|
||||
transition: background-color 120ms ease;
|
||||
}
|
||||
[data-slot="plan-value"] {
|
||||
animation: none;
|
||||
tbody tr:hover {
|
||||
background: var(--go-console-surface);
|
||||
}
|
||||
tbody tr > *,
|
||||
tbody tr:hover > * {
|
||||
border-radius: 0;
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes go-plan-value-in {
|
||||
from {
|
||||
opacity: 0.3;
|
||||
transform: translateY(3px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,22 +5,17 @@ import { useLanguage } from "~/context/language"
|
|||
import { goPlanModels } from "./go-models"
|
||||
|
||||
const ticks = [100, 1000, 10000]
|
||||
const max = Math.max(...goPlanModels.map((model) => model.plusRequests).filter(Number.isFinite))
|
||||
|
||||
export function GoPlanChart(props: { href: string }) {
|
||||
const i18n = useI18n()
|
||||
const language = useLanguage()
|
||||
const id = createUniqueId()
|
||||
const [expanded, setExpanded] = createSignal(false)
|
||||
const [tier, setTier] = createSignal<"go" | "go-plus">("go")
|
||||
const models = createMemo(() => goPlanModels.filter((model) => expanded() || model.featured))
|
||||
const max = createMemo(() =>
|
||||
Math.max(
|
||||
...goPlanModels.map((model) => (tier() === "go" ? model.requests : model.plusRequests)).filter(Number.isFinite),
|
||||
),
|
||||
)
|
||||
const position = (requests: number) =>
|
||||
Number.isFinite(requests)
|
||||
? 4 + Math.pow(Math.log10(Math.max(requests / 100, 1)) / Math.log10(max() / 100), 2.2) * 78
|
||||
? 4 + Math.pow(Math.log10(Math.max(requests / 100, 1)) / Math.log10(max / 100), 2.2) * 78
|
||||
: 100
|
||||
const format = createMemo(() => new Intl.NumberFormat(language.tag(language.locale())))
|
||||
const currency = createMemo(
|
||||
|
|
@ -40,24 +35,24 @@ export function GoPlanChart(props: { href: string }) {
|
|||
<h2 id={`${id}-title`}>{i18n.t("go.plans.limits")}</h2>
|
||||
<p>{i18n.t("go.plans.description")}</p>
|
||||
</div>
|
||||
<div data-slot="tier-switch" role="group" aria-label={i18n.t("go.plans.legend")}>
|
||||
<button type="button" aria-pressed={tier() === "go"} onClick={() => setTier("go")}>
|
||||
<ul data-slot="legend" aria-label={i18n.t("go.plans.legend")}>
|
||||
<li>
|
||||
<i data-tier="go" />
|
||||
Go
|
||||
</button>
|
||||
<button type="button" aria-pressed={tier() === "go-plus"} onClick={() => setTier("go-plus")}>
|
||||
</li>
|
||||
<li>
|
||||
<i data-tier="plus" />
|
||||
Go Plus
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div data-slot="scroll">
|
||||
<table id={id}>
|
||||
<colgroup>
|
||||
<col style={{ width: "26%" }} />
|
||||
<col style={{ width: "40%" }} />
|
||||
<col style={{ width: "17%" }} />
|
||||
<col style={{ width: "17%" }} />
|
||||
<col style={{ width: "44%" }} />
|
||||
<col style={{ width: "16%" }} />
|
||||
<col style={{ width: "14%" }} />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
|
|
@ -73,60 +68,55 @@ export function GoPlanChart(props: { href: string }) {
|
|||
{(model) => (
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<span data-slot="model">
|
||||
<bdi>{model.name}</bdi>
|
||||
<Show when={model.fresh}>
|
||||
<small>{i18n.t("go.graph.new")}</small>
|
||||
</Show>
|
||||
<Show when={model.limitedTime}>
|
||||
<small>{i18n.t("go.graph.limitedTime")}</small>
|
||||
</Show>
|
||||
<Show when={model.regions}>
|
||||
<a
|
||||
href="https://ai.developer.meta.com/legal/geographic-use-policy"
|
||||
title={i18n.t("go.graph.limitedRegions")}
|
||||
aria-label={`${model.name}: ${i18n.t("go.graph.limitedRegions")}`}
|
||||
>
|
||||
<svg viewBox="0 0 16 16" width="13" height="13" fill="none" aria-hidden="true">
|
||||
<circle cx="8" cy="8" r="6" stroke="currentColor" />
|
||||
<ellipse cx="8" cy="8" rx="2.5" ry="6" stroke="currentColor" />
|
||||
<path d="M2 8h12" stroke="currentColor" />
|
||||
</svg>
|
||||
</a>
|
||||
</Show>
|
||||
</span>
|
||||
<bdi>{model.name}</bdi>
|
||||
<Show when={model.fresh || model.limitedTime || model.regions}>
|
||||
<span data-slot="labels">
|
||||
<Show when={model.fresh}>
|
||||
<small>{i18n.t("go.graph.new")}</small>
|
||||
</Show>
|
||||
<Show when={model.limitedTime}>
|
||||
<small>{i18n.t("go.graph.limitedTime")}</small>
|
||||
</Show>
|
||||
<Show when={model.regions}>
|
||||
<a href="https://ai.developer.meta.com/legal/geographic-use-policy">
|
||||
{i18n.t("go.graph.limitedRegions")}
|
||||
</a>
|
||||
</Show>
|
||||
</span>
|
||||
</Show>
|
||||
</th>
|
||||
<td data-slot="bars" aria-hidden="true">
|
||||
<For each={ticks}>
|
||||
{(tick) => <span data-slot="gridline" style={{ "inset-inline-start": `${position(tick)}%` }} />}
|
||||
</For>
|
||||
<div data-slot="track">
|
||||
<span
|
||||
data-tier={tier() === "go" ? "go" : "plus"}
|
||||
style={{ width: `${position(tier() === "go" ? model.requests : model.plusRequests)}%` }}
|
||||
/>
|
||||
<span data-slot="remainder" />
|
||||
<div data-slot="lane">
|
||||
{/* The Go bar is a linear fraction of the Plus bar so the multiplier reads true on the log scale. */}
|
||||
<span
|
||||
data-tier="go"
|
||||
style={{
|
||||
width: `${
|
||||
Number.isFinite(model.plusRequests)
|
||||
? (position(model.plusRequests) * model.requests) / model.plusRequests
|
||||
: position(model.requests)
|
||||
}%`,
|
||||
}}
|
||||
/>
|
||||
<span data-slot="remainder" />
|
||||
</div>
|
||||
<div data-slot="lane">
|
||||
<span data-tier="plus" style={{ width: `${position(model.plusRequests)}%` }} />
|
||||
<span data-slot="remainder" />
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td data-slot="number">
|
||||
<Show when={tier()} keyed>
|
||||
{(selected) => (
|
||||
<span data-slot="plan-value">
|
||||
{format().format(selected === "go" ? model.requests : model.plusRequests)}
|
||||
</span>
|
||||
)}
|
||||
</Show>
|
||||
<span>{format().format(model.requests)}</span>
|
||||
<span>{format().format(model.plusRequests)}</span>
|
||||
</td>
|
||||
<td data-slot="number">
|
||||
<Show when={tier()} keyed>
|
||||
{(selected) => (
|
||||
<span data-slot="plan-value">
|
||||
{Number.isFinite(selected === "go" ? model.allowance : model.plusAllowance)
|
||||
? currency().format(selected === "go" ? model.allowance : model.plusAllowance)
|
||||
: "∞"}
|
||||
</span>
|
||||
)}
|
||||
</Show>
|
||||
<span>{Number.isFinite(model.allowance) ? currency().format(model.allowance) : "∞"}</span>
|
||||
<span>{Number.isFinite(model.plusAllowance) ? currency().format(model.plusAllowance) : "∞"}</span>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -292,8 +292,8 @@ export const dict = {
|
|||
"go.graph.scale": "Nonlinear request scale",
|
||||
"go.graph.showAll": "View all {{count}} models",
|
||||
"go.graph.showLess": "Show fewer models",
|
||||
"go.graph.limitedRegions": "limited regions",
|
||||
"go.graph.limitedTime": "limited time",
|
||||
"go.graph.limitedRegions": "Limited Regions",
|
||||
"go.graph.limitedTime": "Limited Time",
|
||||
"go.graph.tick": "{{n}}x",
|
||||
"go.graph.usageLimits": "Usage limits",
|
||||
"go.graph.aria": "Requests per 5h: {{free}} vs {{go}}",
|
||||
|
|
|
|||
Loading…
Reference in a new issue