Buckets:
| --- | |
| import { Image } from 'astro:assets'; | |
| import { getRelativeLocaleUrl } from 'astro:i18n'; | |
| import config from "virtual:starlight/user-config"; | |
| import CopyIcon from "../assets/lander/copy.svg"; | |
| import CheckIcon from "../assets/lander/check.svg"; | |
| import TuiScreenshot from "../assets/lander/screenshot-splash.png"; | |
| import VscodeScreenshot from "../assets/lander/screenshot-vscode.png"; | |
| import GithubScreenshot from "../assets/lander/screenshot-github.png"; | |
| const { data } = Astro.locals.starlightRoute.entry; | |
| const { title = data.title, tagline, image, actions = [] } = data.hero || {}; | |
| const imageAttrs = { | |
| loading: 'eager' as const, | |
| decoding: 'async' as const, | |
| width: 400, | |
| alt: image?.alt || '', | |
| }; | |
| const github = (config.social || []).filter(s => s.icon === 'github')[0]; | |
| const discord = (config.social || []).filter(s => s.icon === 'discord')[0]; | |
| const locale = Astro.currentLocale || 'root'; | |
| const t = Astro.locals.t as (key: string) => string; | |
| const docsHref = getRelativeLocaleUrl(locale, "") | |
| const docsCliHref = getRelativeLocaleUrl(locale, "cli") | |
| const docsIdeHref = getRelativeLocaleUrl(locale, "ide") | |
| const docsGithubHref = getRelativeLocaleUrl(locale, "github") | |
| const command = "curl -fsSL" | |
| const protocol = "https://" | |
| const url = "opencode.ai/install" | |
| const bash = "| bash" | |
| let darkImage: ImageMetadata | undefined; | |
| let lightImage: ImageMetadata | undefined; | |
| let rawHtml: string | undefined; | |
| if (image) { | |
| if ('file' in image) { | |
| darkImage = image.file; | |
| } else if ('dark' in image) { | |
| darkImage = image.dark; | |
| lightImage = image.light; | |
| } else { | |
| rawHtml = image.html; | |
| } | |
| } | |
| --- | |
| <div class="hero"> | |
| <section class="top"> | |
| <div class="logo"> | |
| {darkImage && ( | |
| <Image | |
| src={darkImage} | |
| {...imageAttrs} | |
| class:list={{ 'light:sl-hidden': Boolean(lightImage) }} | |
| /> | |
| )} | |
| {lightImage && <Image src={lightImage} {...imageAttrs} class="dark:sl-hidden" />} | |
| </div> | |
| <h1>{t('app.lander.hero.title')}</h1> | |
| </section> | |
| <section class="cta"> | |
| <div class="col1"> | |
| <a href={docsHref}>{t('app.lander.cta.getStarted')}</a> | |
| </div> | |
| <div class="col2"> | |
| <button class="command" data-command={`${command} ${protocol}${url} ${bash}`}> | |
| <code> | |
| <span>{command} </span><span class="protocol">{protocol}</span><span class="highlight">{url}</span> {bash} | |
| </code> | |
| <span class="copy"> | |
| <CopyIcon /> | |
| <CheckIcon /> | |
| </span> | |
| </button> | |
| </div> | |
| </section> | |
| <section class="content"> | |
| <ul> | |
| <li><b>{t('app.lander.features.native_tui.title')}</b>: {t('app.lander.features.native_tui.description')}</li> | |
| <li><b>{t('app.lander.features.lsp_enabled.title')}</b>: {t('app.lander.features.lsp_enabled.description')}</li> | |
| <li><b>{t('app.lander.features.multi_session.title')}</b>: {t('app.lander.features.multi_session.description')}</li> | |
| <li><b>{t('app.lander.features.shareable_links.title')}</b>: {t('app.lander.features.shareable_links.description')}</li> | |
| <li><b>GitHub Copilot</b>: {t('app.lander.features.github_copilot.description')}</li> | |
| <li><b>ChatGPT Plus/Pro</b>: {t('app.lander.features.chatgpt_plus_pro.description')}</li> | |
| <li><b>{t('app.lander.features.use_any_model.title')}</b>: {t('app.lander.features.use_any_model.prefix')} <a href="https://models.dev">Models.dev</a>, {t('app.lander.features.use_any_model.suffix')}</li> | |
| </ul> | |
| </section> | |
| <section class="alternatives"> | |
| <div class="col1"> | |
| <h3>npm</h3> | |
| <button class="command" data-command="npm install -g opencode-ai"> | |
| <code> | |
| <span>npm install -g</span> <span class="highlight">opencode-ai</span> | |
| </code> | |
| <span class="copy"> | |
| <CopyIcon /> | |
| <CheckIcon /> | |
| </span> | |
| </button> | |
| </div> | |
| <div class="col2"> | |
| <h3>Bun</h3> | |
| <button class="command" data-command="bun install -g opencode-ai"> | |
| <code> | |
| <span>bun install -g</span> <span class="highlight">opencode-ai</span> | |
| </code> | |
| <span class="copy"> | |
| <CopyIcon /> | |
| <CheckIcon /> | |
| </span> | |
| </button> | |
| </div> | |
| <div class="col3"> | |
| <h3>Homebrew</h3> | |
| <button class="command" data-command="brew install opencode"> | |
| <code> | |
| <span>brew install</span> <span class="highlight">opencode</span> | |
| </code> | |
| <span class="copy"> | |
| <CopyIcon /> | |
| <CheckIcon /> | |
| </span> | |
| </button> | |
| </div> | |
| <div class="col4"> | |
| <h3>Paru</h3> | |
| <button class="command" data-command="paru -S opencode-bin"> | |
| <code> | |
| <span>paru -S</span> <span class="highlight">opencode-bin</span> | |
| </code> | |
| <span class="copy"> | |
| <CopyIcon /> | |
| <CheckIcon /> | |
| </span> | |
| </button> | |
| </div> | |
| <div class="col4"> | |
| <h3>Mise</h3> | |
| <button class="command" data-command="mise use -g github:anomalyco/opencode"> | |
| <code> | |
| <span>mise use -g</span> <span class="highlight">github:anomalyco/opencode</span> | |
| </code> | |
| <span class="copy"> | |
| <CopyIcon /> | |
| <CheckIcon /> | |
| </span> | |
| </button> | |
| </div> | |
| </section> | |
| <section class="images"> | |
| <div class="left"> | |
| <figure> | |
| <figcaption>{t('app.lander.images.tui.caption')}</figcaption> | |
| <a href={docsCliHref}> | |
| <Image src={TuiScreenshot} alt={t('app.lander.images.tui.alt')} /> | |
| </a> | |
| </figure> | |
| </div> | |
| <div class="right"> | |
| <div class="row1"> | |
| <figure> | |
| <figcaption>{t('app.lander.images.vscode.caption')}</figcaption> | |
| <a href={docsIdeHref}> | |
| <Image src={VscodeScreenshot} alt={t('app.lander.images.vscode.alt')} /> | |
| </a> | |
| </figure> | |
| </div> | |
| <div class="row2"> | |
| <figure> | |
| <figcaption>{t('app.lander.images.github.caption')}</figcaption> | |
| <a href={docsGithubHref}> | |
| <Image src={GithubScreenshot} alt={t('app.lander.images.github.alt')} /> | |
| </a> | |
| </figure> | |
| </div> | |
| </div> | |
| </section> | |
| <section class="footer"> | |
| <div class="col1"> | |
| <a href={github.href} target="_blank" rel="noopener noreferrer">GitHub</a> | |
| </div> | |
| <div class="col2"> | |
| <a href={discord.href} target="_blank" rel="noopener noreferrer">Discord</a> | |
| </div> | |
| <div class="col3"> | |
| <span>©2025 <a href="https://anoma.ly" target="_blank" rel="noopener noreferrer">Anomaly Innovations</a></span> | |
| </div> | |
| </section> | |
| </div> | |
| <style> | |
| .hero { | |
| --padding: 3rem; | |
| --vertical-padding: 1.5rem; | |
| --heading-font-size: var(--sl-text-3xl); | |
| margin: 1rem; | |
| border: 2px solid var(--sl-color-border); | |
| } | |
| @media (max-width: 30rem) { | |
| .hero { | |
| --padding: 1rem; | |
| --vertical-padding: 0.75rem; | |
| --heading-font-size: var(--sl-text-2xl); | |
| margin: 0.5rem; | |
| } | |
| } | |
| section.top { | |
| padding: var(--padding); | |
| h1 { | |
| margin-top: calc(var(--vertical-padding) / 8); | |
| font-size: var(--heading-font-size); | |
| line-height: 1.25; | |
| text-transform: uppercase; | |
| } | |
| img { | |
| height: auto; | |
| width: clamp(200px, 70vw, 400px); | |
| } | |
| } | |
| section.cta { | |
| display: flex; | |
| flex-direction: row; | |
| justify-content: flex-start; | |
| align-items: stretch; | |
| border-top: 2px solid var(--sl-color-border); | |
| @media (max-width: 50rem) { | |
| flex-direction: column; | |
| } | |
| & > div { | |
| line-height: 1.4; | |
| padding: var(--vertical-padding) var(--padding); | |
| a { | |
| font-size: 1rem; | |
| } | |
| } | |
| & > div.col1 { | |
| flex: 0 0 auto; | |
| text-align: center; | |
| text-transform: uppercase; | |
| @media (max-width: 50rem) { | |
| padding-bottom: calc(var(--vertical-padding) + 4px); | |
| } | |
| } | |
| & > div.col2 { | |
| flex: 1; | |
| padding-left: 1rem; | |
| padding-right: 1rem; | |
| } | |
| & > div + div { | |
| border-left: 2px solid var(--sl-color-border); | |
| @media (max-width: 50rem) { | |
| border-left: none; | |
| border-top: 2px solid var(--sl-color-border); | |
| } | |
| } | |
| .command { | |
| all: unset; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.625rem; | |
| justify-content: center; | |
| cursor: pointer; | |
| width: 100%; | |
| code { | |
| color: var(--sl-color-text-secondary); | |
| font-size: 1.125rem; | |
| @media (max-width: 24rem) { | |
| font-size: 0.875rem; | |
| } | |
| @media (max-width: 30rem) { | |
| span.protocol { | |
| display: none; | |
| } | |
| } | |
| @media (max-width: 43rem) { | |
| text-align: center; | |
| span:first-child { | |
| display: block; | |
| } | |
| } | |
| } | |
| code .highlight { | |
| color: var(--sl-color-text); | |
| font-weight: 500; | |
| } | |
| .copy { | |
| line-height: 1; | |
| padding: 0; | |
| @media (max-width: 43rem) { | |
| display: none; | |
| } | |
| } | |
| .copy svg { | |
| width: 1rem; | |
| height: 1rem; | |
| vertical-align: middle; | |
| } | |
| .copy svg:first-child { | |
| color: var(--sl-color-text-dimmed); | |
| } | |
| .copy svg:last-child { | |
| color: var(--sl-color-text); | |
| display: none; | |
| } | |
| &.success .copy { | |
| pointer-events: none; | |
| } | |
| &.success .copy svg:first-child { | |
| display: none; | |
| } | |
| &.success .copy svg:last-child { | |
| display: inline; | |
| } | |
| } | |
| } | |
| section.content { | |
| border-top: 2px solid var(--sl-color-border); | |
| padding: var(--padding); | |
| ul { | |
| padding-left: 1rem; | |
| li + li { | |
| margin-top: calc(var(--vertical-padding) / 2); | |
| } | |
| li b { | |
| text-transform: uppercase; | |
| } | |
| } | |
| } | |
| section.images { | |
| --images-height: 600px; | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| grid-template-rows: var(--images-height); | |
| border-top: 2px solid var(--sl-color-border); | |
| & > div.left { | |
| display: flex; | |
| grid-row: 1; | |
| grid-column: 1; | |
| } | |
| & > div.right { | |
| display: grid; | |
| grid-template-rows: 1fr 1fr; | |
| grid-row: 1; | |
| grid-column: 2; | |
| border-left: 2px solid var(--sl-color-border); | |
| & > div.row1 { | |
| display: flex; | |
| grid-row: 1; | |
| border-bottom: 2px solid var(--sl-color-border); | |
| height: calc(var(--images-height) / 2); | |
| } | |
| & > div.row2 { | |
| display: flex; | |
| grid-row: 2; | |
| height: calc(var(--images-height) / 2); | |
| } | |
| } | |
| figure { | |
| flex: 1; | |
| display: flex; | |
| flex-direction: column; | |
| gap: calc(var(--padding) / 4); | |
| padding: calc(var(--padding) / 2); | |
| border-width: 0; | |
| border-style: solid; | |
| border-color: var(--sl-color-border); | |
| min-height: 0; | |
| overflow: hidden; | |
| & > div, figcaption { | |
| display: flex; | |
| align-items: center; | |
| } | |
| & > div { | |
| flex: 1; | |
| min-height: 0; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| a { | |
| display: flex; | |
| flex: 1; | |
| min-height: 0; | |
| align-items: center; | |
| justify-content: center; | |
| width: 100%; | |
| height: 100%; | |
| } | |
| figcaption { | |
| letter-spacing: -0.03125rem; | |
| text-transform: uppercase; | |
| color: var(--sl-color-text-dimmed); | |
| flex-shrink: 0; | |
| @media (max-width: 30rem) { | |
| font-size: 0.75rem; | |
| } | |
| } | |
| } | |
| & > div.left figure { | |
| height: var(--images-height); | |
| box-sizing: border-box; | |
| } | |
| & > div.right figure { | |
| height: calc(var(--images-height) / 2); | |
| box-sizing: border-box; | |
| } | |
| & > div.left img { | |
| width: 100%; | |
| height: 100%; | |
| min-width: 0; | |
| object-fit: contain; | |
| } | |
| & > div.right img { | |
| width: 100%; | |
| height: calc(100% - 2rem); | |
| object-fit: contain; | |
| display: block; | |
| } | |
| @media (max-width: 30rem) { | |
| & { | |
| --images-height: auto; | |
| grid-template-columns: 1fr; | |
| grid-template-rows: auto auto; | |
| } | |
| & > div.left { | |
| grid-row: 1; | |
| grid-column: 1; | |
| } | |
| & > div.right { | |
| grid-row: 2; | |
| grid-column: 1; | |
| border-left: none; | |
| border-top: 2px solid var(--sl-color-border); | |
| & > div.row1, | |
| & > div.row2 { | |
| height: auto; | |
| } | |
| } | |
| & > div.left figure, | |
| & > div.right figure { | |
| height: auto; | |
| } | |
| & > div.left img, | |
| & > div.right img { | |
| width: 100%; | |
| height: auto; | |
| max-height: none; | |
| } | |
| } | |
| } | |
| section.alternatives { | |
| border-top: 2px solid var(--sl-color-border); | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| grid-template-rows: 1fr 1fr; | |
| @media (max-width: 40rem) { | |
| grid-template-columns: 1fr; | |
| grid-template-rows: auto; | |
| } | |
| & > div { | |
| display: flex; | |
| flex-direction: column; | |
| padding: calc(var(--vertical-padding) / 2) calc(var(--padding) / 2) calc(var(--vertical-padding) / 2 + 0.125rem); | |
| text-align: left; | |
| gap: 0.5rem; | |
| @media (max-width: 30rem) { | |
| gap: 0.3125rem; | |
| } | |
| @media (max-width: 40rem) { | |
| text-align: left; | |
| } | |
| } | |
| & > div.col1 { | |
| border-bottom: 2px solid var(--sl-color-border); | |
| @media (max-width: 40rem) { | |
| border-bottom: none; | |
| } | |
| } | |
| & > div.col2 { | |
| border-left: 2px solid var(--sl-color-border); | |
| border-bottom: 2px solid var(--sl-color-border); | |
| @media (max-width: 40rem) { | |
| border-left: none; | |
| border-bottom: none; | |
| border-top: 2px solid var(--sl-color-border); | |
| } | |
| } | |
| & > div.col3 { | |
| @media (max-width: 40rem) { | |
| border-top: 2px solid var(--sl-color-border); | |
| } | |
| } | |
| & > div.col4 { | |
| border-left: 2px solid var(--sl-color-border); | |
| @media (max-width: 40rem) { | |
| border-left: none; | |
| border-top: 2px solid var(--sl-color-border); | |
| } | |
| } | |
| h3 { | |
| letter-spacing: -0.03125rem; | |
| text-transform: uppercase; | |
| color: var(--sl-color-text-dimmed); | |
| font-weight: normal; | |
| font-size: 1rem; | |
| flex-shrink: 0; | |
| @media (max-width: 30rem) { | |
| font-size: 0.75rem; | |
| } | |
| } | |
| .command { | |
| all: unset; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.625rem; | |
| justify-content: flex-start; | |
| cursor: pointer; | |
| width: 100%; | |
| @media (max-width: 40rem) { | |
| justify-content: flex-start; | |
| } | |
| @media (max-width: 30rem) { | |
| justify-content: center; | |
| } | |
| code { | |
| color: var(--sl-color-text-secondary); | |
| font-size: 1rem; | |
| @media (max-width: 24rem) { | |
| font-size: 0.875rem; | |
| } | |
| } | |
| code .highlight { | |
| color: var(--sl-color-text); | |
| font-weight: 500; | |
| } | |
| .copy { | |
| line-height: 1; | |
| padding: 0; | |
| @media (max-width: 40rem) { | |
| display: none; | |
| } | |
| } | |
| .copy svg { | |
| width: 1rem; | |
| height: 1rem; | |
| vertical-align: middle; | |
| } | |
| .copy svg:first-child { | |
| color: var(--sl-color-text-dimmed); | |
| } | |
| .copy svg:last-child { | |
| color: var(--sl-color-text); | |
| display: none; | |
| } | |
| &.success .copy { | |
| pointer-events: none; | |
| } | |
| &.success .copy svg:first-child { | |
| display: none; | |
| } | |
| &.success .copy svg:last-child { | |
| display: inline; | |
| } | |
| } | |
| } | |
| section.footer { | |
| border-top: 2px solid var(--sl-color-border); | |
| display: flex; | |
| flex-direction: row; | |
| & > div { | |
| flex: 1; | |
| text-align: center; | |
| text-transform: uppercase; | |
| padding: var(--vertical-padding) 0.5rem; | |
| } | |
| & > div + div { | |
| border-left: 2px solid var(--sl-color-border); | |
| } | |
| /* Below 800px: first two columns shrink to content, third expands */ | |
| @media (max-width: 50rem) { | |
| & > div.col1, | |
| & > div.col2 { | |
| flex: 0 0 auto; | |
| padding-left: calc(var(--padding) / 2); | |
| padding-right: calc(var(--padding) / 2); | |
| } | |
| & > div.col3 { | |
| flex: 1; | |
| } | |
| } | |
| /* Mobile: third column on its own row */ | |
| @media (max-width: 30rem) { | |
| flex-wrap: wrap; | |
| & > div.col1, | |
| & > div.col2 { | |
| flex: 1; | |
| } | |
| & > div.col3 { | |
| flex: 1 0 100%; | |
| border-left: none; | |
| border-top: 2px solid var(--sl-color-border); | |
| } | |
| } | |
| } | |
| </style> | |
| <style is:global> | |
| :root[data-has-hero] { | |
| header.header { | |
| display: none; | |
| } | |
| .main-frame { | |
| padding-top: 0; | |
| .main-pane > main { | |
| padding: 0; | |
| } | |
| } | |
| main > .content-panel .sl-markdown-content { | |
| margin-top: 0; | |
| } | |
| } | |
| </style> | |
| <script> | |
| const buttons = document.querySelectorAll("button.command") as NodeListOf<HTMLButtonElement> | |
| buttons.forEach(button => { | |
| button.addEventListener("click", () => { | |
| navigator.clipboard.writeText(button.dataset.command!) | |
| button.classList.toggle("success") | |
| setTimeout(() => { | |
| button.classList.toggle("success"); | |
| }, 1500) | |
| }) | |
| }) | |
| </script> | |
Xet Storage Details
- Size:
- 16.7 kB
- Xet hash:
- e82ac4eca335bb54736d9bcfacc01271a05d98f5a96254e1766a05a3edadd16f
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.