diff --git a/packages/console/app/src/i18n/en.ts b/packages/console/app/src/i18n/en.ts index 3f39fcd9d..9bf8ad43d 100644 --- a/packages/console/app/src/i18n/en.ts +++ b/packages/console/app/src/i18n/en.ts @@ -246,6 +246,8 @@ export const dict = { "zen.privacy.exceptionsLink": "following exceptions", "go.title": "OpenCode Go | Low cost coding models for everyone", + "go.meta.description": + "Go is a $10/month subscription with generous 5-hour request limits for GLM-5, Kimi K2.5, and MiniMax M2.5.", "go.hero.title": "Low cost coding models for everyone", "go.hero.body": "Go brings agentic coding to programmers around the world. Offering generous limits and reliable access to the most capable open-source models, so you can build with powerful agents without worrying about cost or availability.", diff --git a/packages/console/app/src/routes/go/index.css b/packages/console/app/src/routes/go/index.css index 34be47f0d..99c61a70b 100644 --- a/packages/console/app/src/routes/go/index.css +++ b/packages/console/app/src/routes/go/index.css @@ -21,6 +21,13 @@ } } +@keyframes go-graph-bar { + to { + opacity: 1; + transform: scaleX(1); + } +} + [data-page="go"] { --color-background: hsl(0, 20%, 99%); --color-background-weak: hsl(0, 8%, 97%); @@ -424,13 +431,78 @@ body { [data-component="limit-graph"] { margin: 0 auto; - max-width: calc(100% - (var(--padding) * 2)); + width: calc(100% - 120px); + max-width: calc(100% - 120px); border: none; background: transparent; - padding: 18px 18px 56px; + padding: 58px var(--padding) 56px; + + @media (max-width: 48rem) { + width: 100%; + max-width: 100%; + } [data-slot="plot"] { position: relative; + overflow: visible; + width: 100%; + margin: 0 auto; + margin-left: -40px; + } + + [data-slot="ylabels"] { + position: absolute; + inset: 0; + pointer-events: none; + } + + [data-slot="ylabels"] [data-ylabel] { + position: absolute; + left: var(--x); + top: var(--y); + transform: translate(-100%, -50%); + color: var(--color-text-strong); + font-size: 16px; + font-weight: 700; + line-height: 1; + white-space: nowrap; + } + + [data-slot="pills"] { + position: absolute; + inset: 0; + pointer-events: none; + + [data-item] { + position: absolute; + left: var(--x); + top: var(--y); + transform: translate(12px, -50%); + display: inline-flex; + align-items: center; + gap: 8px; + border: none; + background: transparent; + height: 20px; + padding: 0 8px; + border-radius: 2px; + max-width: calc(100% - 12px); + font-size: 13px; + line-height: 20px; + box-sizing: border-box; + opacity: 0; + } + + [data-name] { + color: var(--color-text); + white-space: nowrap; + } + + [data-value] { + color: var(--color-text-strong); + font-weight: 600; + white-space: nowrap; + } } [data-slot="plot-labels"] { @@ -451,8 +523,7 @@ body { svg { width: 100%; - height: auto; - aspect-ratio: 720 / 220; + height: 220px; display: block; } @@ -479,13 +550,44 @@ body { font-weight: 600; } + [data-row], + [data-val] { + opacity: 0; + } + + &[data-visible] [data-row], + &[data-visible] [data-val] { + opacity: 1; + transition: opacity 240ms ease; + transition-delay: var(--d, 0ms); + } + [data-stub] { stroke: var(--color-border); - stroke-width: 2; + stroke-width: 1; stroke-linecap: round; opacity: 0.55; } + [data-bar] { + transform-box: fill-box; + transform-origin: left center; + opacity: 0; + transform: scaleX(0.02); + fill: var(--color-go-2); + stroke: none; + } + + [data-bar][data-kind="free"] { + fill: var(--color-text-strong); + } + + [data-val] { + fill: var(--color-text-strong); + font-size: 13px; + font-weight: 650; + } + [data-range] { stroke: var(--color-text-strong); stroke-width: 2; @@ -542,6 +644,17 @@ body { animation-delay: var(--d, 0ms); } + &[data-visible] [data-bar] { + animation: go-graph-bar 560ms cubic-bezier(0.2, 0.7, 0.2, 1) forwards; + animation-delay: var(--d, 0ms); + } + + &[data-visible] [data-slot="pills"] [data-item] { + opacity: 1; + transition: opacity 240ms ease; + transition-delay: var(--d, 0ms); + } + @media (prefers-reduced-motion: reduce) { [data-animate="line"] { stroke-dashoffset: 0; @@ -552,34 +665,49 @@ body { transform: none; animation: none; } + [data-bar] { + opacity: 1; + transform: none; + animation: none; + } + [data-row], + [data-val] { + opacity: 1; + transition: none; + } + + [data-slot="pills"] [data-item] { + opacity: 1; + transition: none; + } } figcaption { margin-top: 34px; display: flex; - flex-direction: column; - gap: 10px; + justify-content: center; font-size: 13px; + text-align: center; } [data-slot="caption-row"] { display: flex; width: 100%; + justify-content: center; } [data-slot="caption-left"] { - display: grid; + display: flex; width: 100%; - grid-template-columns: var(--start, 16.9%) minmax(0, 1fr); - grid-template-rows: auto auto; - align-items: center; - column-gap: 0; - row-gap: 0; min-width: 0; + justify-content: center; } [data-slot="caption-meta"] { - display: contents; + display: flex; + flex-direction: row; + gap: 16px; + align-items: baseline; } [data-slot="caption-label"] { @@ -587,8 +715,6 @@ body { font-weight: 650; white-space: nowrap; line-height: 1; - grid-column: 1; - grid-row: 1; } [data-slot="caption-link"] { @@ -596,73 +722,6 @@ body { text-decoration-thickness: 1px; width: fit-content; line-height: 1; - grid-column: 1; - grid-row: 2; - align-self: start; - } - - [data-slot="legend"] { - display: flex; - width: 100%; - flex-wrap: nowrap; - gap: 10px; - min-width: 0; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - padding-bottom: 8px; - margin-left: -12px; - grid-column: 2; - grid-row: 1; - align-self: center; - - [data-item] { - display: inline-flex; - flex: 0 0 auto; - align-items: center; - gap: 8px; - border: 1px solid var(--color-border-weak); - background: var(--color-background); - padding: 6px 10px; - border-radius: 999px; - max-width: 100%; - } - - [data-dot] { - width: 10px; - height: 10px; - border-radius: 999px; - display: inline-block; - border: 1px solid var(--color-text-strong); - background: var(--color-background); - flex: 0 0 auto; - } - - [data-dot][data-kind="go"] { - background: var(--color-background-interactive); - } - - [data-dot][data-kind="go"][data-model="glm"] { - background: var(--color-go-1); - } - - [data-dot][data-kind="go"][data-model="kimi"] { - background: var(--color-go-2); - } - - [data-dot][data-kind="go"][data-model="minimax"] { - background: var(--color-go-3); - } - - [data-name] { - color: var(--color-text); - white-space: nowrap; - } - - [data-value] { - color: var(--color-text-strong); - font-weight: 600; - white-space: nowrap; - } } [data-slot="caption-note"] { @@ -671,35 +730,8 @@ body { } @media (max-width: 56.25rem) { - [data-slot="caption-left"] { - grid-template-columns: var(--start, 16.9%) minmax(0, 1fr); - grid-template-rows: auto auto; - align-items: start; - } - - [data-slot="legend"] { - grid-column: 2; - grid-row: 1; - } - [data-slot="caption-meta"] { - display: flex; - gap: 24px; - align-items: baseline; - grid-column: 2; - grid-row: 2; - margin-top: 12px; - } - - [data-slot="caption-label"] { - grid-column: auto; - grid-row: auto; - } - - [data-slot="caption-link"] { - grid-column: auto; - grid-row: auto; - align-self: baseline; + gap: 14px; } } } diff --git a/packages/console/app/src/routes/go/index.tsx b/packages/console/app/src/routes/go/index.tsx index 9e4f0eefd..6ae5e476c 100644 --- a/packages/console/app/src/routes/go/index.tsx +++ b/packages/console/app/src/routes/go/index.tsx @@ -10,6 +10,7 @@ import { Faq } from "~/component/faq" import { Legal } from "~/component/legal" import { Footer } from "~/component/footer" import { Header } from "~/component/header" +import { config } from "~/config" import { getLastSeenWorkspaceID } from "../workspace/common" import { IconMiniMax, IconZai } from "~/component/icon" import { useI18n } from "~/context/i18n" @@ -49,24 +50,50 @@ function LimitsGraph(props: { href: string }) { { id: "kimi", name: "Kimi K2.5", req: 1850, d: "240ms" }, { id: "minimax", name: "MiniMax M2.5", req: 20000, d: "360ms" }, ] - const ratio = (n: number) => n / free const w = 720 const h = 220 - const left = 88 - const right = 24 - const top = 22 - const bottom = 46 + const left = 40 + const right = 60 + const top = 18 + const bottom = 44 const plot = w - left - right + + const ratio = (n: number) => n / free const rmax = Math.max(1, ...models.map((m) => ratio(m.req))) const log = (n: number) => Math.log10(Math.max(n, 1)) - const x = (r: number) => left + (log(r) / log(rmax)) * plot + const base = 24 + const p = 2.2 + const x = (r: number) => left + base + Math.pow(log(r) / log(rmax), p) * (plot - base) const start = (x(1) / w) * 100 - const yFree = 74 - const yGo = 134 const ticks = [1, 2, 5, 10, 25, 50, 100].filter((t) => t <= rmax) - const y = (n: number) => `${(n / h) * 100}%` + const labels = (() => { + const set = new Set() + let last = -Infinity + for (const t of ticks) { + if (t === 1) { + set.add(t) + last = x(t) + continue + } + const pos = x(t) + if (pos - last < 44) continue + set.add(t) + last = pos + } + return set + })() + const bh = 8 + const gap = 16 + const step = bh + gap + const sep = bh + 40 + const fy = top + 22 + const gy = (i: number) => fy + sep + step * i + const my = models.length < 2 ? gy(0) : (gy(0) + gy(models.length - 1)) / 2 + const px = (n: number) => `${(n / w) * 100}%` + const py = (n: number) => `${(n / h) * 100}%` + const lx = px(left - 16) return (
-
- + + +
@@ -134,22 +190,6 @@ function LimitsGraph(props: { href: string }) { {i18n.t("go.graph.usageLimits")}
-
- - - {i18n.t("go.graph.free")} - {free.toLocaleString()} - - - {(m) => ( - - - {m.name} - {m.req.toLocaleString()} - - )} - -
@@ -165,9 +205,17 @@ export default function Home() {
{/**/} {i18n.t("go.title")} + - - + + + + + + + + +