|
|
@@ -5,7 +5,9 @@ import type { Props } from '@astrojs/starlight/props';
|
|
|
|
|
|
import CopyIcon from "../assets/lander/copy.svg";
|
|
|
import CheckIcon from "../assets/lander/check.svg";
|
|
|
-import Screenshot from "../assets/lander/screenshot-splash.png";
|
|
|
+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 || {};
|
|
|
@@ -36,7 +38,7 @@ if (image) {
|
|
|
lightImage = image.light;
|
|
|
} else {
|
|
|
rawHtml = image.html;
|
|
|
- }
|
|
|
+ }
|
|
|
}
|
|
|
---
|
|
|
<div class="hero">
|
|
|
@@ -80,24 +82,81 @@ if (image) {
|
|
|
</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 sst/tap/opencode">
|
|
|
+ <code>
|
|
|
+ <span>brew install</span> <span class="highlight">sst/tap/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>
|
|
|
+ </section>
|
|
|
+
|
|
|
<section class="images">
|
|
|
<div class="left">
|
|
|
<figure>
|
|
|
<figcaption>opencode TUI with the tokyonight theme</figcaption>
|
|
|
- <Image src={Screenshot} alt="opencode TUI with the tokyonight theme" />
|
|
|
+ <a href="/docs/cli">
|
|
|
+ <Image src={TuiScreenshot} alt="opencode TUI with the tokyonight theme" />
|
|
|
+ </a>
|
|
|
</figure>
|
|
|
</div>
|
|
|
<div class="right">
|
|
|
<div class="row1">
|
|
|
<figure>
|
|
|
<figcaption>opencode in VS Code</figcaption>
|
|
|
- <Image src={Screenshot} alt="opencode in VS Code" />
|
|
|
+ <a href="/docs/ide">
|
|
|
+ <Image src={VscodeScreenshot} alt="opencode in VS Code" />
|
|
|
+ </a>
|
|
|
</figure>
|
|
|
</div>
|
|
|
<div class="row2">
|
|
|
<figure>
|
|
|
- <figcaption>opencode TUI with the tokyonight theme</figcaption>
|
|
|
- <Image src={Screenshot} alt="opencode TUI with the tokyonight theme" />
|
|
|
+ <figcaption>opencode in GitHub</figcaption>
|
|
|
+ <a href="/docs/github">
|
|
|
+ <Image src={GithubScreenshot} alt="opencode in GitHub" />
|
|
|
+ </a>
|
|
|
</figure>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -105,13 +164,13 @@ if (image) {
|
|
|
|
|
|
<section class="footer">
|
|
|
<div class="col1">
|
|
|
- <a href={github.href}>GitHub</a>
|
|
|
+ <a href={github.href} target="_blank" rel="noopener noreferrer">GitHub</a>
|
|
|
</div>
|
|
|
<div class="col2">
|
|
|
- <a href={discord.href}>Discord</a>
|
|
|
+ <a href={discord.href} target="_blank" rel="noopener noreferrer">Discord</a>
|
|
|
</div>
|
|
|
<div class="col3">
|
|
|
- <span>©2025 <a href="https://anoma.ly">Anomaly Innovations</a></span>
|
|
|
+ <span>©2025 <a href="https://anoma.ly" target="_blank" rel="noopener noreferrer">Anomaly Innovations</a></span>
|
|
|
</div>
|
|
|
</section>
|
|
|
</div>
|
|
|
@@ -301,7 +360,7 @@ section.images {
|
|
|
border-bottom: 2px solid var(--sl-color-border);
|
|
|
height: calc(var(--images-height) / 2);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
& > div.row2 {
|
|
|
display: flex;
|
|
|
grid-row: 2;
|
|
|
@@ -334,6 +393,16 @@ section.images {
|
|
|
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;
|
|
|
@@ -408,6 +477,140 @@ section.images {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+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: 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ .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;
|
|
|
@@ -475,13 +678,15 @@ section.footer {
|
|
|
</style>
|
|
|
|
|
|
<script>
|
|
|
- const button = document.querySelector("button.command") as HTMLButtonElement;
|
|
|
-
|
|
|
- button?.addEventListener("click", () => {
|
|
|
- navigator.clipboard.writeText(button.dataset.command!);
|
|
|
- button.classList.toggle("success");
|
|
|
- setTimeout(() => {
|
|
|
- button.classList.toggle("success");
|
|
|
- }, 1500);
|
|
|
- });
|
|
|
+ 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>
|