diff --git a/bun.lock b/bun.lock index e8db819e1..8beb9ae6f 100644 --- a/bun.lock +++ b/bun.lock @@ -15,6 +15,7 @@ "@actions/artifact": "5.0.1", "@tsconfig/bun": "catalog:", "@types/mime-types": "3.0.1", + "@typescript/native-preview": "catalog:", "glob": "13.0.5", "husky": "9.1.7", "prettier": "3.6.2", diff --git a/package.json b/package.json index f2c4dac99..ba84d1052 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "@actions/artifact": "5.0.1", "@tsconfig/bun": "catalog:", "@types/mime-types": "3.0.1", + "@typescript/native-preview": "catalog:", "glob": "13.0.5", "husky": "9.1.7", "prettier": "3.6.2", diff --git a/packages/console/app/script/generate-sitemap.ts b/packages/console/app/script/generate-sitemap.ts index bdce205b9..89bca6bac 100755 --- a/packages/console/app/script/generate-sitemap.ts +++ b/packages/console/app/script/generate-sitemap.ts @@ -26,6 +26,7 @@ async function getMainRoutes(): Promise { { path: "/enterprise", priority: 0.8, changefreq: "weekly" }, { path: "/brand", priority: 0.6, changefreq: "monthly" }, { path: "/zen", priority: 0.8, changefreq: "weekly" }, + { path: "/go", priority: 0.8, changefreq: "weekly" }, ] for (const item of staticRoutes) { diff --git a/packages/console/app/src/asset/go-ornate-dark.svg b/packages/console/app/src/asset/go-ornate-dark.svg new file mode 100644 index 000000000..9b617c677 --- /dev/null +++ b/packages/console/app/src/asset/go-ornate-dark.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/console/app/src/asset/go-ornate-light.svg b/packages/console/app/src/asset/go-ornate-light.svg new file mode 100644 index 000000000..79991973d --- /dev/null +++ b/packages/console/app/src/asset/go-ornate-light.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/console/app/src/component/header.tsx b/packages/console/app/src/component/header.tsx index 6ab5ce2f5..24d5a897c 100644 --- a/packages/console/app/src/component/header.tsx +++ b/packages/console/app/src/component/header.tsx @@ -36,7 +36,7 @@ const fetchSvgContent = async (svgPath: string): Promise => { } } -export function Header(props: { zen?: boolean; hideGetStarted?: boolean }) { +export function Header(props: { zen?: boolean; go?: boolean; hideGetStarted?: boolean }) { const navigate = useNavigate() const i18n = useI18n() const language = useLanguage() @@ -161,19 +161,24 @@ export function Header(props: { zen?: boolean; hideGetStarted?: boolean }) {
  • {i18n.t("nav.docs")}
  • + +
  • + {i18n.t("nav.zen")} +
  • +
    + +
  • + {i18n.t("nav.go")} +
  • +
  • {i18n.t("nav.enterprise")}
  • -
  • - - - {i18n.t("nav.login")} - - - {i18n.t("nav.zen")} - - -
  • + +
  • + {i18n.t("nav.login")} +
  • +
  • @@ -257,19 +262,24 @@ export function Header(props: { zen?: boolean; hideGetStarted?: boolean }) {
  • {i18n.t("nav.docs")}
  • + +
  • + {i18n.t("nav.zen")} +
  • +
    + +
  • + {i18n.t("nav.go")} +
  • +
  • {i18n.t("nav.enterprise")}
  • -
  • - - - {i18n.t("nav.login")} - - - {i18n.t("nav.zen")} - - -
  • + +
  • + {i18n.t("nav.login")} +
  • +
  • diff --git a/packages/console/app/src/i18n/en.ts b/packages/console/app/src/i18n/en.ts index 07a4ddb4c..82ec78bda 100644 --- a/packages/console/app/src/i18n/en.ts +++ b/packages/console/app/src/i18n/en.ts @@ -6,6 +6,7 @@ export const dict = { "nav.x": "X", "nav.enterprise": "Enterprise", "nav.zen": "Zen", + "nav.go": "Go", "nav.login": "Login", "nav.free": "Free", "nav.home": "Home", @@ -54,6 +55,7 @@ export const dict = { "common.cancel": "Cancel", "common.creating": "Creating...", "common.create": "Create", + "common.contactUs": "Contact us", "common.videoUnsupported": "Your browser does not support the video tag.", "common.figure": "Fig {{n}}.", @@ -243,6 +245,105 @@ export const dict = { "All Zen models are hosted in the US. Providers follow a zero-retention policy and do not use your data for model training, with the", "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.", + + "go.cta.start": "Subscribe to Go", + "go.cta.template": "{{text}} {{price}}", + "go.cta.text": "Subscribe to Go", + "go.cta.price": "$10/month", + "go.pricing.body": "Use with any agent. Top up credit if needed. Cancel any time.", + "go.graph.free": "Free", + "go.graph.freePill": "Big Pickle and promotional", + "go.graph.go": "Go", + "go.graph.label": "Requests per 5 hour", + "go.graph.usageLimits": "Usage limits", + "go.graph.tick": "{{n}}x", + "go.graph.aria": "Requests per 5h: {{free}} vs {{go}}", + + "go.testimonials.brand.zen": "Zen", + "go.testimonials.brand.go": "Go", + "go.testimonials.handle": "@OpenCode", + "go.testimonials.dax.name": "Dax Raad", + "go.testimonials.dax.title": "ex-CEO, Terminal Products", + "go.testimonials.dax.quoteAfter": "has been life changing, it's truly a no-brainer.", + "go.testimonials.jay.name": "Jay V", + "go.testimonials.jay.title": "ex-Founder, SEED, PM, Melt, Pop, Dapt, Cadmus, and ViewPoint", + "go.testimonials.jay.quoteBefore": "4 out of 5 people on our team love using", + "go.testimonials.jay.quoteAfter": ".", + "go.testimonials.adam.name": "Adam Elmore", + "go.testimonials.adam.title": "ex-Hero, AWS", + "go.testimonials.adam.quoteBefore": "I can't recommend", + "go.testimonials.adam.quoteAfter": "enough. Seriously, it's really good.", + "go.testimonials.david.name": "David Hill", + "go.testimonials.david.title": "ex-Head of Design, Laravel", + "go.testimonials.david.quoteBefore": "With", + "go.testimonials.david.quoteAfter": "I know all the models are tested and perfect for coding agents.", + "go.testimonials.frank.name": "Frank Wang", + "go.testimonials.frank.title": "ex-Intern, Nvidia (4 times)", + "go.testimonials.frank.quote": "I wish I was still at Nvidia.", + "go.problem.title": "What problem is Go solving?", + "go.problem.body": + "We're focused on bringing the OpenCode experience to as many people as possible. OpenCode Go is a low cost ($10/month) subscription designed to bring agentic coding to programmers around the world. It provides generous limits and reliable access to the most capable open source models.", + "go.problem.subtitle": " ", + "go.problem.item1": "Low cost subscription pricing", + "go.problem.item2": "Generous limits and reliable access", + "go.problem.item3": "Built for as many programmers as possible", + "go.problem.item4": "Includes GLM-5, Kimi K2.5, and MiniMax M2.5", + "go.how.title": "How Go works", + "go.how.body": "Go is a $10/month subscription you can use with OpenCode or any agent.", + "go.how.step1.title": "Create an account", + "go.how.step1.beforeLink": "follow the", + "go.how.step1.link": "setup instructions", + "go.how.step2.title": "Subscribe to Go", + "go.how.step2.link": "$10/month", + "go.how.step2.afterLink": "with generous limits", + "go.how.step3.title": "Start coding", + "go.how.step3.body": "with reliable access to open-source models", + "go.privacy.title": "Your privacy is important to us", + "go.privacy.body": + "The plan is designed primarily for international users, with models hosted in the US, EU, and Singapore for stable global access.", + "go.privacy.contactAfter": "if you have any questions.", + "go.privacy.beforeExceptions": + "Go models are hosted in the US. Providers follow a zero-retention policy and do not use your data for model training, with the", + "go.privacy.exceptionsLink": "following exceptions", + "go.faq.q1": "What is OpenCode Go?", + "go.faq.a1": + "Go is a low-cost subscription that gives you reliable access to capable open-source models for agentic coding.", + "go.faq.q2": "What models does Go include?", + "go.faq.a2": "Go includes GLM-5, Kimi K2.5, and MiniMax M2.5, with generous limits and reliable access.", + "go.faq.q3": "Is Go the same as Zen?", + "go.faq.a3": + "No. Zen is pay-as-you-go, while Go is a $10/month subscription with generous limits and reliable access to open-source models GLM-5, Kimi K2.5, and MiniMax M2.5.", + "go.faq.q4": "How much does Go cost?", + "go.faq.a4.p1.beforePricing": "Go costs", + "go.faq.a4.p1.pricingLink": "$10/month", + "go.faq.a4.p1.afterPricing": "with generous limits.", + "go.faq.a4.p2.beforeAccount": "You can manage your subscription in your", + "go.faq.a4.p2.accountLink": "account", + "go.faq.a4.p3": "Cancel any time.", + "go.faq.q5": "What about data and privacy?", + "go.faq.a5.body": + "The plan is designed primarily for international users, with models hosted in the US, EU, and Singapore for stable global access.", + "go.faq.a5.contactAfter": "if you have any questions.", + "go.faq.a5.beforeExceptions": + "Go models are hosted in the US. Providers follow a zero-retention policy and do not use your data for model training, with the", + "go.faq.a5.exceptionsLink": "following exceptions", + "go.faq.q6": "Can I top up credit?", + "go.faq.a6": "If you need more usage, you can top up credit in your account.", + "go.faq.q7": "Can I cancel?", + "go.faq.a7": "Yes, you can cancel any time.", + "go.faq.q8": "Can I use Go with other coding agents?", + "go.faq.a8": "Yes, you can use Go with any agent. Follow the setup instructions in your preferred coding agent.", + + "go.faq.q9": "What is the difference between free models and Go?", + "go.faq.a9": + "Free models include Big Pickle plus promotional models available at the time, with a quota of 200 requests/day. Go includes GLM-5, Kimi K2.5, and MiniMax M2.5 with higher request quotas enforced across rolling windows (5-hour, weekly, and monthly), roughly equivalent to $12 per 5 hours, $30 per week, and $60 per month (actual request counts vary by model and usage).", + "zen.api.error.rateLimitExceeded": "Rate limit exceeded. Please try again later.", "zen.api.error.modelNotSupported": "Model {{model}} not supported", "zen.api.error.modelFormatNotSupported": "Model {{model}} not supported for format {{format}}", diff --git a/packages/console/app/src/routes/brand/index.css b/packages/console/app/src/routes/brand/index.css index 6fe7c9a8d..8a3265159 100644 --- a/packages/console/app/src/routes/brand/index.css +++ b/packages/console/app/src/routes/brand/index.css @@ -86,10 +86,10 @@ display: flex; justify-content: space-between; align-items: center; - gap: 48px; + gap: 32px; @media (max-width: 55rem) { - gap: 32px; + gap: 24px; } @media (max-width: 48rem) { diff --git a/packages/console/app/src/routes/changelog/index.css b/packages/console/app/src/routes/changelog/index.css index b441754a6..27b44f062 100644 --- a/packages/console/app/src/routes/changelog/index.css +++ b/packages/console/app/src/routes/changelog/index.css @@ -81,10 +81,10 @@ display: flex; justify-content: space-between; align-items: center; - gap: 48px; + gap: 32px; @media (max-width: 55rem) { - gap: 32px; + gap: 24px; } @media (max-width: 48rem) { diff --git a/packages/console/app/src/routes/download/index.css b/packages/console/app/src/routes/download/index.css index 41bb55eee..705302616 100644 --- a/packages/console/app/src/routes/download/index.css +++ b/packages/console/app/src/routes/download/index.css @@ -85,10 +85,10 @@ display: flex; justify-content: space-between; align-items: center; - gap: 48px; + gap: 32px; @media (max-width: 55rem) { - gap: 32px; + gap: 24px; } @media (max-width: 48rem) { diff --git a/packages/console/app/src/routes/enterprise/index.css b/packages/console/app/src/routes/enterprise/index.css index d42a817e6..584c94fa5 100644 --- a/packages/console/app/src/routes/enterprise/index.css +++ b/packages/console/app/src/routes/enterprise/index.css @@ -85,10 +85,10 @@ display: flex; justify-content: space-between; align-items: center; - gap: 48px; + gap: 32px; @media (max-width: 55rem) { - gap: 32px; + gap: 24px; } @media (max-width: 48rem) { diff --git a/packages/console/app/src/routes/go/index.css b/packages/console/app/src/routes/go/index.css new file mode 100644 index 000000000..d3239d482 --- /dev/null +++ b/packages/console/app/src/routes/go/index.css @@ -0,0 +1,1117 @@ +::selection { + background: var(--color-background-interactive); + color: var(--color-text-strong); + + @media (prefers-color-scheme: dark) { + background: var(--color-background-interactive); + color: var(--color-text-inverted); + } +} + +@keyframes go-graph-line { + to { + stroke-dashoffset: 0; + } +} + +@keyframes go-graph-point { + to { + opacity: 1; + transform: scale(1); + } +} + +@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%); + --color-background-strong: hsl(0, 5%, 12%); + --color-background-strong-hover: hsl(0, 5%, 18%); + --color-background-interactive: hsl(62, 84%, 88%); + --color-background-interactive-weaker: hsl(64, 74%, 95%); + + --color-go-1: hsl(61.9, 82.6%, 77.5%); + --color-go-2: hsl(62.4, 78.6%, 56.1%); + --color-go-3: hsl(62.1, 100%, 39.8%); + + --color-text: hsl(0, 1%, 39%); + --color-text-weak: hsl(0, 1%, 74%); + --color-text-weaker: hsl(30, 2%, 81%); + --color-text-strong: hsl(0, 5%, 12%); + --color-text-inverted: hsl(0, 20%, 99%); + + --color-border: hsl(30, 2%, 81%); + --color-border-weak: hsl(0, 1%, 85%); + + --color-icon: hsl(0, 1%, 55%); +} + +[data-page="go"] { + @media (prefers-color-scheme: dark) { + --color-background: hsl(0, 9%, 7%); + --color-background-weak: hsl(0, 6%, 10%); + --color-background-strong: hsl(0, 15%, 94%); + --color-background-strong-hover: hsl(0, 15%, 97%); + --color-background-interactive: hsl(62, 100%, 90%); + --color-background-interactive-weaker: hsl(60, 20%, 8%); + + --color-go-1: hsl(62.1, 64.9%, 25.7%); + --color-go-2: hsl(61.7, 46.4%, 53.9%); + --color-go-3: hsl(61.9, 100%, 50%); + + --color-text: hsl(0, 4%, 71%); + --color-text-weak: hsl(0, 2%, 49%); + --color-text-weaker: hsl(0, 3%, 28%); + --color-text-strong: hsl(0, 15%, 94%); + --color-text-inverted: hsl(0, 9%, 7%); + + --color-border: hsl(0, 3%, 28%); + --color-border-weak: hsl(0, 4%, 23%); + + --color-icon: hsl(10, 3%, 43%); + } +} + +body { + background: var(--color-background); +} + +@supports (background: -webkit-named-image(i)) { + [data-page="opencode"] { + border-top: 1px solid var(--color-border-weak); + } +} + +[data-page="go"] { + background: var(--color-background); + --padding: 5rem; + --vertical-padding: 4rem; + + @media (max-width: 60rem) { + --padding: 1.5rem; + --vertical-padding: 3rem; + } + + display: flex; + gap: var(--vertical-padding); + flex-direction: column; + font-family: var(--font-mono); + color: var(--color-text); + padding-bottom: 5rem; + + a { + color: var(--color-text-strong); + text-decoration: underline; + text-underline-offset: var(--space-1); + text-decoration-thickness: 1px; + } + + p { + line-height: 200%; + + @media (max-width: 60rem) { + line-height: 180%; + } + } + + @media (max-width: 60rem) { + font-size: 15px; + } + + input:-webkit-autofill, + input:-webkit-autofill:hover, + input:-webkit-autofill:focus, + input:-webkit-autofill:active { + transition: background-color 5000000s ease-in-out 0s; + } + + input:-webkit-autofill { + -webkit-text-fill-color: var(--color-text-strong) !important; + } + + input:-moz-autofill { + -moz-text-fill-color: var(--color-text-strong) !important; + } + + [data-component="container"] { + max-width: 67.5rem; + margin: 0 auto; + border: 1px solid var(--color-border-weak); + border-top: none; + + @media (max-width: 65rem) { + border: none; + } + } + + [data-component="content"] { + } + + [data-component="top"] { + padding: 24px var(--padding); + height: 80px; + position: sticky; + top: 0; + display: flex; + justify-content: space-between; + align-items: center; + background: var(--color-background); + border-bottom: 1px solid var(--color-border-weak); + + z-index: 10; + + img { + height: 34px; + width: auto; + } + + [data-component="nav-desktop"] { + ul { + display: flex; + justify-content: space-between; + align-items: center; + gap: 32px; + + @media (max-width: 55rem) { + gap: 24px; + } + + @media (max-width: 48rem) { + gap: 24px; + } + li { + display: inline-block; + a { + text-decoration: none; + span { + color: var(--color-text-weak); + } + } + a:hover { + text-decoration: underline; + text-underline-offset: var(--space-1); + text-decoration-thickness: 1px; + } + [data-slot="cta-button"] { + background: var(--color-background-strong); + color: var(--color-text-inverted); + padding: 8px 16px; + border-radius: 4px; + font-weight: 500; + text-decoration: none; + white-space: nowrap; + + @media (max-width: 55rem) { + display: none; + } + } + [data-slot="cta-button"]:hover { + background: var(--color-background-strong-hover); + text-decoration: none; + } + } + } + + @media (max-width: 40rem) { + display: none; + } + } + + [data-component="nav-mobile"] { + button > svg { + color: var(--color-icon); + } + } + + [data-component="nav-mobile-toggle"] { + border: none; + background: none; + outline: none; + height: 40px; + width: 40px; + cursor: pointer; + } + + [data-component="nav-mobile-toggle"]:hover { + background: var(--color-background-weak); + } + + [data-component="nav-mobile"] { + display: none; + + @media (max-width: 40rem) { + display: block; + + [data-component="nav-mobile-icon"] { + cursor: pointer; + height: 40px; + width: 40px; + display: flex; + align-items: center; + justify-content: center; + } + + [data-component="nav-mobile-menu-list"] { + position: fixed; + background: var(--color-background); + top: 80px; + left: 0; + right: 0; + height: 100vh; + + ul { + list-style: none; + padding: 20px 0; + + li { + a { + text-decoration: none; + padding: 20px; + display: block; + + span { + color: var(--color-text-weak); + } + } + + a:hover { + background: var(--color-background-weak); + } + } + } + } + } + } + + [data-slot="logo dark"] { + display: none; + } + + @media (prefers-color-scheme: dark) { + [data-slot="logo light"] { + display: none; + } + [data-slot="logo dark"] { + display: block; + } + } + } + + [data-component="hero"] { + display: flex; + flex-direction: column; + padding: calc(var(--vertical-padding) * 2) var(--padding); + + [data-slot="zen logo dark"] { + display: none; + } + + @media (max-width: 30rem) { + padding: var(--vertical-padding) var(--padding); + } + + @media (prefers-color-scheme: dark) { + [data-slot="zen logo light"] { + display: none; + } + [data-slot="zen logo dark"] { + display: block; + } + } + } + + [data-slot="hero-copy"] { + img { + margin-bottom: 24px; + } + + h1 { + font-size: 28px; + color: var(--color-text-strong); + font-weight: 700; + margin-bottom: 16px; + display: block; + + @media (max-width: 60rem) { + font-size: 22px; + } + } + + p { + color: var(--color-text); + margin-bottom: 24px; + max-width: 82%; + + @media (max-width: 50rem) { + max-width: 100%; + } + } + + a { + background: var(--color-background-strong); + padding: 8px 12px 8px 20px; + color: var(--color-text-inverted); + border: none; + border-radius: 4px; + font-weight: 500; + cursor: pointer; + margin-bottom: 40px; + display: flex; + width: fit-content; + gap: 12px; + text-decoration: none; + + [data-slot="cta-price"] { + opacity: 0.6; + } + + svg { + opacity: 0.6; + } + } + + a:hover { + background: var(--color-background-strong-hover); + } + } + [data-slot="model-logos"] { + display: flex; + gap: 24px; + margin-bottom: 56px; + + svg { + color: var(--color-background-strong); + } + + @media (prefers-color-scheme: dark) { + svg { + color: var(--color-background-strong); + } + } + } + + [data-slot="pricing-copy"] { + strong { + color: var(--color-text-strong); + font-weight: 500; + } + + p:first-child { + margin-bottom: 0; + color: var(--color-text); + display: flex; + gap: 8px; + + @media (max-width: 40rem) { + flex-direction: column; + gap: 4px; + } + } + } + + [data-component="comparison"] { + border-top: 1px solid var(--color-border-weak); + padding: 0; + background: + radial-gradient(1200px 400px at 15% 0%, rgba(0, 0, 0, 0.035), transparent 55%), + radial-gradient(900px 320px at 85% 15%, rgba(0, 0, 0, 0.02), transparent 60%), var(--color-background-weak); + + @media (prefers-color-scheme: dark) { + background: + radial-gradient(1200px 400px at 15% 0%, rgba(255, 255, 255, 0.03), transparent 55%), + radial-gradient(900px 320px at 85% 15%, rgba(255, 255, 255, 0.02), transparent 60%), + var(--color-background-weak); + + [data-component="limit-graph"] { + --bar-go: #f7ff00; + } + } + + [data-component="limit-graph"] { + margin: 0 auto; + width: calc(100% - 120px); + max-width: calc(100% - 120px); + border: none; + background: transparent; + padding: 58px var(--padding) 56px; + --bar-go: var(--color-go-2); + + @media (max-width: 60rem) { + width: 50%; + max-width: 50%; + } + + [data-slot="plot"] { + position: relative; + overflow: visible; + width: 100%; + margin: 0 auto; + margin-left: -56px; + } + + [data-slot="ylabels"] { + position: absolute; + inset: 0; + pointer-events: none; + } + + [data-slot="xlabels"] { + position: absolute; + inset: 0; + pointer-events: none; + } + + [data-slot="xlabels"] [data-xlabel] { + position: absolute; + left: var(--x); + top: var(--y); + transform: translate(-50%, -50%); + color: var(--color-text-weak); + font-size: 12px; + line-height: 1; + white-space: nowrap; + + @media (prefers-color-scheme: light) { + color: color-mix(in srgb, var(--color-text-weak) 82%, var(--color-text-strong)); + } + } + + [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; + + @media (max-width: 60rem) { + font-size: 13px; + } + } + + [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"] { + position: absolute; + inset: 0; + pointer-events: none; + } + + [data-row-label] { + position: absolute; + left: 0; + top: var(--y); + transform: translateY(-50%); + color: var(--color-text-strong); + font-size: 16px; + font-weight: 700; + } + + svg { + width: 100%; + height: 220px; + display: block; + } + + [data-grid] { + stroke: var(--color-border); + stroke-width: 1; + opacity: 0.6; + } + + [data-tick] { + fill: var(--color-text-weak); + font-size: 12px; + } + + @media (prefers-color-scheme: light) { + [data-tick] { + fill: color-mix(in srgb, var(--color-text-weak) 82%, var(--color-text-strong)); + } + } + + [data-row] { + fill: var(--color-text-strong); + font-size: 13px; + 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: 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(--bar-go); + 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; + stroke-linecap: round; + opacity: 0.65; + } + + [data-point] { + vector-effect: non-scaling-stroke; + stroke-width: 1; + transform-box: fill-box; + transform-origin: center; + } + + [data-point][data-kind="free"] { + fill: var(--color-background); + stroke: var(--color-text-strong); + } + + [data-point][data-kind="go"] { + fill: var(--color-background-interactive); + stroke: var(--color-text-strong); + } + + [data-point][data-kind="go"][data-model="glm"] { + fill: var(--color-go-1); + } + + [data-point][data-kind="go"][data-model="kimi"] { + fill: var(--color-go-2); + } + + [data-point][data-kind="go"][data-model="minimax"] { + fill: var(--color-go-3); + } + + [data-animate="line"] { + stroke-dasharray: 900; + stroke-dashoffset: 900; + } + + &[data-visible] [data-animate="line"] { + animation: go-graph-line 1000ms cubic-bezier(0.2, 0.7, 0.2, 1) forwards; + animation-delay: 80ms; + } + + [data-point] { + opacity: 0; + transform: scale(0.85); + } + + &[data-visible] [data-point] { + animation: go-graph-point 520ms cubic-bezier(0.2, 0.7, 0.2, 1) forwards; + 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; + animation: none; + } + [data-point] { + opacity: 1; + 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; + 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: flex; + width: 100%; + min-width: 0; + justify-content: center; + } + + [data-slot="caption-meta"] { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + gap: 16px; + align-items: baseline; + width: 100%; + max-width: none; + justify-content: center; + } + + [data-slot="caption-label"] { + color: var(--color-text-strong); + font-weight: 650; + white-space: nowrap; + line-height: 1; + } + + [data-slot="caption-link"] { + color: var(--color-text-strong); + text-decoration-thickness: 1px; + width: fit-content; + line-height: 1; + white-space: nowrap; + } + + [data-slot="caption-note"] { + color: var(--color-text-weak); + font-size: 12px; + } + + @media (max-width: 56.25rem) { + [data-slot="caption-meta"] { + gap: 14px; + } + } + } + } + + [data-slot="section-title"] { + margin-bottom: 24px; + + h3 { + font-size: 16px; + font-weight: 700; + color: var(--color-text-strong); + margin-bottom: 12px; + } + + p { + margin-bottom: 12px; + color: var(--color-text); + } + } + + [data-component="problem"] { + border-top: 1px solid var(--color-border-weak); + padding: var(--vertical-padding) var(--padding); + color: var(--color-text); + + p { + margin-bottom: 24px; + } + + ul { + padding: 0; + li { + list-style: none; + margin-bottom: 16px; + display: flex; + gap: 12px; + + span { + color: var(--color-icon); + } + } + li:last-child { + margin-bottom: 0; + } + } + } + + [data-component="how"] { + border-top: 1px solid var(--color-border-weak); + padding: var(--vertical-padding) var(--padding); + color: var(--color-text); + + ul { + padding: 0; + li { + list-style: none; + margin-bottom: 16px; + display: flex; + gap: 12px; + + span { + color: var(--color-icon); + } + strong { + font-weight: 500; + color: var(--color-text-strong); + } + } + li:last-child { + margin-bottom: 0; + } + } + } + + [data-component="email"] { + border-top: 1px solid var(--color-border-weak); + padding: var(--vertical-padding) var(--padding); + color: var(--color-text); + + [data-slot="dock"] { + border-radius: 14px; + border: 0.5px solid rgba(176, 176, 176, 0.6); + background: #f2f1f0; + margin-bottom: 32px; + overflow: hidden; + height: 64px; + width: 185px; + box-shadow: + 0 6px 80px 0 rgba(0, 0, 0, 0.05), + 0 2.507px 33.422px 0 rgba(0, 0, 0, 0.04), + 0 1.34px 17.869px 0 rgba(0, 0, 0, 0.03), + 0 0.751px 10.017px 0 rgba(0, 0, 0, 0.03), + 0 0.399px 5.32px 0 rgba(0, 0, 0, 0.02), + 0 0.166px 2.214px 0 rgba(0, 0, 0, 0.01); + + img { + width: 100%; + height: auto; + } + + @media (prefers-color-scheme: dark) { + background: #312d2d; + } + } + + [data-slot="form"] { + position: relative; + + input { + background: var(--color-background-weak); + border-radius: 6px; + border: 1px solid var(--color-border-weak); + padding: 20px; + width: 100%; + + /* Use color, not -moz-text-fill-color, for normal text */ + color: var(--color-text-strong); + + @media (max-width: 30rem) { + padding-bottom: 80px; + } + + &:not(:focus) { + color: var(--color-text-strong); + } + + &::placeholder { + color: var(--color-text-weak); + opacity: 1; + } + + /* Optional legacy */ + &::-moz-placeholder { + color: var(--color-text-weak); + opacity: 1; + } + } + + input:focus { + background: var(--color-background-interactive-weaker); + outline: none; + border: none; + color: var(--color-text-strong); + + border: 1px solid var(--color-background-strong); /* Tailwind blue-600 as example */ + + /* Tailwind-style ring */ + box-shadow: 0 0 0 3px var(--color-background-interactive); + /* mimics "ring-2 ring-blue-600/50" */ + } + + button { + position: absolute; + height: 40px; + right: 12px; + background: var(--color-background-strong); + padding: 4px 20px; + color: var(--color-text-inverted); + border-radius: 4px; + font-weight: 500; + border: none; + outline: none; + cursor: pointer; + top: 50%; + margin-top: -20px; + + @media (max-width: 30rem) { + left: 20px; + right: 20px; + bottom: 20px; + top: auto; + } + } + } + } + + [data-component="faq"] { + border-top: 1px solid var(--color-border-weak); + padding: var(--vertical-padding) var(--padding); + + ul { + padding: 0; + + li { + list-style: none; + margin-bottom: 24px; + line-height: 200%; + + @media (max-width: 60rem) { + line-height: 180%; + } + } + } + + [data-slot="faq-question"] { + display: flex; + gap: 16px; + margin-bottom: 8px; + color: var(--color-text-strong); + font-weight: 500; + cursor: pointer; + background: none; + border: none; + padding: 0; + + [data-slot="faq-icon-plus"] { + flex-shrink: 0; + color: var(--color-text-weak); + margin-top: 2px; + + [data-closed] & { + display: block; + } + [data-expanded] & { + display: none; + } + } + [data-slot="faq-icon-minus"] { + flex-shrink: 0; + color: var(--color-text-weak); + margin-top: 2px; + + [data-closed] & { + display: none; + } + [data-expanded] & { + display: block; + } + } + [data-slot="faq-question-text"] { + flex-grow: 1; + text-align: left; + } + } + + [data-slot="faq-answer"] { + margin-left: 40px; + margin-bottom: 32px; + } + } + + [data-component="copy-status"] { + @media (max-width: 38rem) { + display: none; + } + + [data-slot="copy"] { + display: block; + width: var(--space-4); + height: var(--space-4); + color: var(--color-text-weaker); + + [data-copied] & { + display: none; + } + } + + [data-slot="check"] { + display: none; + width: var(--space-4); + height: var(--space-4); + color: var(--color-text-strong); + + [data-copied] & { + display: block; + } + } + } + + [data-component="footer"] { + border-top: 1px solid var(--color-border-weak); + display: flex; + flex-direction: row; + + @media (max-width: 65rem) { + border-bottom: 1px solid var(--color-border-weak); + } + + [data-slot="cell"] { + flex: 1; + text-align: center; + + a { + text-decoration: none; + padding: 2rem 0; + width: 100%; + display: block; + + span { + color: var(--color-text-weak); + + @media (max-width: 40rem) { + display: none; + } + } + } + + a:hover { + background: var(--color-background-weak); + text-decoration: underline; + text-underline-offset: var(--space-1); + text-decoration-thickness: 1px; + } + } + + [data-slot="cell"] + [data-slot="cell"] { + border-left: 1px solid var(--color-border-weak); + + @media (max-width: 40rem) { + border-left: none; + } + } + + /* Mobile: third column on its own row */ + @media (max-width: 25rem) { + flex-wrap: wrap; + + [data-slot="cell"] { + flex: 1 0 100%; + border-left: none; + border-top: 1px solid var(--color-border-weak); + } + + [data-slot="cell"]:nth-child(1) { + border-top: none; + } + } + } + + [data-component="legal"] { + color: var(--color-text-weak); + text-align: center; + display: flex; + gap: 32px; + justify-content: center; + + a { + color: var(--color-text-weak); + text-decoration: none; + } + + a:hover { + color: var(--color-text); + text-decoration: underline; + } + } +} diff --git a/packages/console/app/src/routes/go/index.tsx b/packages/console/app/src/routes/go/index.tsx new file mode 100644 index 000000000..7c5981cbc --- /dev/null +++ b/packages/console/app/src/routes/go/index.tsx @@ -0,0 +1,453 @@ +import "./index.css" +import { createAsync, query, redirect } from "@solidjs/router" +import { Title, Meta } from "@solidjs/meta" +import { For, createSignal, onCleanup, onMount } from "solid-js" +//import { HttpHeader } from "@solidjs/start" +import goLogoLight from "../../asset/go-ornate-light.svg" +import goLogoDark from "../../asset/go-ornate-dark.svg" +import { EmailSignup } from "~/component/email-signup" +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" +import { useLanguage } from "~/context/language" +import { LocaleLinks } from "~/component/locale-links" + +const checkLoggedIn = query(async () => { + "use server" + const workspaceID = await getLastSeenWorkspaceID().catch(() => {}) + if (workspaceID) throw redirect(`/workspace/${workspaceID}`) +}, "checkLoggedIn.get") + +function LimitsGraph(props: { href: string }) { + let root!: HTMLElement + const [visible, setVisible] = createSignal(false) + + const i18n = useI18n() + + onMount(() => { + if (typeof IntersectionObserver === "undefined") return setVisible(true) + const observer = new IntersectionObserver( + (entries) => { + const entry = entries[0] + if (!entry?.isIntersecting) return + setVisible(true) + observer.disconnect() + }, + { threshold: 0.35 }, + ) + observer.observe(root) + onCleanup(() => observer.disconnect()) + }) + + const free = 200 + const models = [ + { id: "glm", name: "GLM-5", req: 1150, d: "120ms" }, + { id: "kimi", name: "Kimi K2.5", req: 1850, d: "240ms" }, + { id: "minimax", name: "MiniMax M2.5", req: 20000, d: "360ms" }, + ] + + const w = 720 + const h = 220 + 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 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 ticks = [1, 5, 10, 25, 50, 100].filter((t) => t <= rmax) + 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 shown = ticks.filter((t) => labels.has(t)) + 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) + const ty = py(h - 18) + + return ( +
    +
    + + + + + + + +
    + +
    + +
    +
    + ) +} + +export default function Home() { + const loggedin = createAsync(() => checkLoggedIn()) + const i18n = useI18n() + const language = useLanguage() + return ( +
    + {/**/} + {i18n.t("go.title")} + + + + + + + + + + + + + +
    +
    + +
    +
    +
    + + +

    {i18n.t("go.hero.title")}

    +

    {i18n.t("go.hero.body")}

    +
    + {/* +
    + + + + + + + + +
    +
    + + + + +
    +
    + +
    +
    + + + + +
    + */} +
    + +
    +
    + + + +
    +
    + +
    + {/* +
    + + + +
    + */} +
    + + + + {(part) => { + if (part === "{{text}}") return {i18n.t("go.cta.text")} + if (part === "{{price}}") return {i18n.t("go.cta.price")} + return part + }} + + + + + + +
    +
    +

    {i18n.t("go.pricing.body")}

    +
    +
    + +
    + +
    + +
    +
    +

    {i18n.t("go.problem.title")}

    +

    {i18n.t("go.problem.body")}

    +
    +

    {i18n.t("go.problem.subtitle")}

    +
      +
    • + [*] {i18n.t("go.problem.item1")} +
    • +
    • + [*] {i18n.t("go.problem.item2")} +
    • +
    • + [*] {i18n.t("go.problem.item3")} +
    • +
    • + [*] {i18n.t("go.problem.item4")} +
    • +
    +
    + +
    +
    +

    {i18n.t("go.how.title")}

    +

    {i18n.t("go.how.body")}

    +
    +
      +
    • + [1] +
      + {i18n.t("go.how.step1.title")} - {i18n.t("go.how.step1.beforeLink")}{" "} + + {i18n.t("go.how.step1.link")} + +
      +
    • +
    • + [2] +
      + {i18n.t("go.how.step2.title")} -{" "} + {i18n.t("go.how.step2.link")}{" "} + {i18n.t("go.how.step2.afterLink")} +
      +
    • +
    • + [3] +
      + {i18n.t("go.how.step3.title")} - {i18n.t("go.how.step3.body")} +
      +
    • +
    +
    + +
    +
    +

    {i18n.t("common.faq")}

    +
    +
      +
    • + {i18n.t("go.faq.a1")} +
    • +
    • + {i18n.t("go.faq.a2")} +
    • +
    • + {i18n.t("go.faq.a9")} +
    • +
    • + {i18n.t("go.faq.a3")} +
    • +
    • + + {i18n.t("go.faq.a4.p1.beforePricing")}{" "} + {i18n.t("go.faq.a4.p1.pricingLink")}{" "} + {i18n.t("go.faq.a4.p1.afterPricing")} {i18n.t("go.faq.a4.p2.beforeAccount")}{" "} + {i18n.t("go.faq.a4.p2.accountLink")}. {i18n.t("go.faq.a4.p3")} + +
    • +
    • + + {i18n.t("go.faq.a5.body")} {i18n.t("common.contactUs")}{" "} + {i18n.t("go.faq.a5.contactAfter")} + +
    • +
    • + {i18n.t("go.faq.a6")} +
    • +
    • + {i18n.t("go.faq.a7")} +
    • +
    • + {i18n.t("go.faq.a8")} +
    • +
    +
    + + + +
    +
    +
    + + +
    + ) +} diff --git a/packages/console/app/src/routes/index.css b/packages/console/app/src/routes/index.css index 90ea3d696..d06e26595 100644 --- a/packages/console/app/src/routes/index.css +++ b/packages/console/app/src/routes/index.css @@ -212,10 +212,10 @@ body { display: flex; justify-content: space-between; align-items: center; - gap: 48px; + gap: 32px; @media (max-width: 55rem) { - gap: 32px; + gap: 24px; } @media (max-width: 48rem) { diff --git a/packages/console/app/src/routes/zen/index.css b/packages/console/app/src/routes/zen/index.css index acdfbe85d..1b5755288 100644 --- a/packages/console/app/src/routes/zen/index.css +++ b/packages/console/app/src/routes/zen/index.css @@ -148,10 +148,10 @@ body { display: flex; justify-content: space-between; align-items: center; - gap: 48px; + gap: 32px; @media (max-width: 55rem) { - gap: 32px; + gap: 24px; } @media (max-width: 48rem) {