summaryrefslogtreecommitdiffhomepage
path: root/cloud/app/src/routes
diff options
context:
space:
mode:
authorDax Raad <[email protected]>2025-08-15 19:29:24 -0400
committerDax Raad <[email protected]>2025-08-15 19:29:42 -0400
commit07cf8847fb1908ff5dc47a771f57d23926baa1ce (patch)
treeaef73a8ac42e755404cb56107137a7fc4eff3ffd /cloud/app/src/routes
parent650e67f1dfd4790152c70864da6c1ade4884ab58 (diff)
downloadopencode-07cf8847fb1908ff5dc47a771f57d23926baa1ce.tar.gz
opencode-07cf8847fb1908ff5dc47a771f57d23926baa1ce.zip
wip: cloud stuff
Diffstat (limited to 'cloud/app/src/routes')
-rw-r--r--cloud/app/src/routes/[...404].tsx19
-rw-r--r--cloud/app/src/routes/index.css264
-rw-r--r--cloud/app/src/routes/index.tsx169
3 files changed, 452 insertions, 0 deletions
diff --git a/cloud/app/src/routes/[...404].tsx b/cloud/app/src/routes/[...404].tsx
new file mode 100644
index 000000000..4ea71ec7f
--- /dev/null
+++ b/cloud/app/src/routes/[...404].tsx
@@ -0,0 +1,19 @@
+import { Title } from "@solidjs/meta";
+import { HttpStatusCode } from "@solidjs/start";
+
+export default function NotFound() {
+ return (
+ <main>
+ <Title>Not Found</Title>
+ <HttpStatusCode code={404} />
+ <h1>Page Not Found</h1>
+ <p>
+ Visit{" "}
+ <a href="https://start.solidjs.com" target="_blank">
+ start.solidjs.com
+ </a>{" "}
+ to learn how to build SolidStart apps.
+ </p>
+ </main>
+ );
+}
diff --git a/cloud/app/src/routes/index.css b/cloud/app/src/routes/index.css
new file mode 100644
index 000000000..e3b11c605
--- /dev/null
+++ b/cloud/app/src/routes/index.css
@@ -0,0 +1,264 @@
+[data-page="home"] {
+ --color-bg: oklch(0.2097 0.008 274.53);
+ --color-border: oklch(0.46 0.02 269.88);
+ --color-text: #ffffff;
+ --color-text-secondary: oklch(0.72 0.01 270.15);
+ --color-text-dimmed: hsl(224, 7%, 46%);
+ padding: var(--space-6);
+ font-family: var(--font-mono);
+ color: var(--color-text);
+
+ a {
+ color: var(--color-text);
+ text-decoration: underline;
+ text-underline-offset: 0.1875rem;
+ }
+
+ background: var(--color-bg);
+ position: fixed;
+ overflow-y: scroll;
+ inset: 0;
+
+ [data-component="content"] {
+ max-width: 67.5rem;
+ margin: 0 auto;
+ border: 2px solid var(--color-border);
+ }
+
+ [data-component="top"] {
+ padding: var(--space-12);
+ display: flex;
+ flex-direction: column;
+ align-items: start;
+ gap: var(--space-4);
+
+ [data-slot="logo"] {
+ height: 70px;
+ }
+
+ [data-slot="title"] {
+ font-size: var(--font-size-2xl);
+ text-transform: uppercase;
+ }
+ }
+
+ [data-component="cta"] {
+ height: var(--space-19);
+ border-top: 2px solid var(--color-border);
+ display: flex;
+
+ [data-slot="left"] {
+ display: flex;
+ padding: 0 var(--space-12);
+ text-transform: uppercase;
+ text-decoration: underline;
+ align-items: center;
+ justify-content: center;
+ text-underline-offset: 0.1875rem;
+ border-right: 2px solid var(--color-border);
+
+ a {
+ color: var(--color-text);
+ text-decoration: underline;
+ }
+ }
+
+ [data-slot="right"] {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.625rem;
+ padding: 0 var(--space-6);
+ }
+
+ [data-slot="command"] {
+ all: unset;
+ display: flex;
+ align-items: center;
+ cursor: pointer;
+ color: var(--color-text-secondary);
+ font-size: 1.125rem;
+ font-family: var(--font-mono);
+ gap: var(--space-2);
+ }
+
+ [data-slot="highlight"] {
+ color: var(--color-text);
+ font-weight: 500;
+ }
+ }
+
+ [data-component="features"] {
+ border-top: 2px solid var(--color-border);
+ padding: var(--space-12);
+
+ [data-slot="list"] {
+ padding-left: var(--space-4);
+ margin: 0;
+ list-style: disc;
+
+ li {
+ margin-bottom: var(--space-4);
+
+ strong {
+ text-transform: uppercase;
+ font-weight: 600;
+ }
+ }
+
+ li:last-child {
+ margin-bottom: 0;
+ }
+ }
+ }
+
+ [data-component="install"] {
+ border-top: 2px solid var(--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;
+ }
+ }
+
+ [data-component="title"] {
+ letter-spacing: -0.03125rem;
+ text-transform: uppercase;
+ font-weight: 400;
+ font-size: var(--font-size-md);
+ flex-shrink: 0;
+ color: oklch(0.55 0.02 269.87);
+ }
+
+ [data-component="method"] {
+ padding: var(--space-4) var(--space-6);
+ display: flex;
+ flex-direction: column;
+ align-items: start;
+ gap: var(--space-3);
+
+ &:nth-child(1) {}
+
+ &:nth-child(2) {
+ border-left: 2px solid var(--color-border);
+ }
+
+ &:nth-child(3) {
+ border-top: 2px solid var(--color-border);
+ }
+
+ &:nth-child(4) {
+ border-top: 2px solid var(--color-border);
+ border-left: 2px solid var(--color-border);
+ }
+
+ [data-slot="button"] {
+ all: unset;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ color: var(--color-text-secondary);
+ gap: var(--space-2);
+
+ strong {
+ color: var(--color-text);
+ font-weight: 500;
+ }
+ }
+ }
+
+ [data-component="screenshots"] {
+ border-top: 2px solid var(--color-border);
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 0;
+
+ [data-slot="left"] {
+ padding: var(--space-8) var(--space-6);
+ display: flex;
+ flex-direction: column;
+
+ img {
+ width: 100%;
+ height: "auto";
+ }
+ }
+
+ [data-slot="right"] {
+ display: grid;
+ grid-template-rows: 1fr 1fr;
+ border-left: 2px solid var(--color-border);
+ }
+
+ [data-slot="filler"] {
+ display: flex;
+ flex-grow: 1;
+ align-items: center;
+ justify-content: center;
+ }
+
+ [data-slot="cell"] {
+ padding: var(--space-8) var(--space-6);
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-4);
+
+ &:nth-child(2) {
+ border-top: 2px solid var(--color-border);
+ }
+
+ img {
+ width: 80%;
+ height: "auto";
+ }
+ }
+ }
+
+ [data-component="copy-status"] {
+ [data-slot="copy"] {
+ display: block;
+ width: 16px;
+ height: 16px;
+ color: var(--color-text-dimmed);
+
+ [data-copied] & {
+ display: none;
+ }
+ }
+
+ [data-slot="check"] {
+ display: none;
+ width: 16px;
+ height: 16px;
+ color: white;
+
+ [data-copied] & {
+ display: block;
+ }
+ }
+ }
+
+ [data-component="footer"] {
+ border-top: 2px solid var(--color-border);
+ display: grid;
+ grid-template-columns: 1fr 1fr 1fr;
+ font-size: var(--font-size-lg);
+ height: var(--space-20);
+
+ [data-slot="cell"] {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-right: 2px solid var(--color-border);
+ text-transform: uppercase;
+
+ &:last-child {
+ border-right: none;
+ }
+ }
+ }
+}
diff --git a/cloud/app/src/routes/index.tsx b/cloud/app/src/routes/index.tsx
new file mode 100644
index 000000000..90062d0b5
--- /dev/null
+++ b/cloud/app/src/routes/index.tsx
@@ -0,0 +1,169 @@
+import { Title } from "@solidjs/meta"
+import { onCleanup, onMount } from "solid-js"
+import "./index.css"
+import logo from "../asset/logo-ornate-dark.svg"
+import IMG_SPLASH from "../asset/screenshot-splash.webp"
+import IMG_VSCODE from "../asset/screenshot-vscode.webp"
+import IMG_GITHUB from "../asset/screenshot-github.webp"
+import { IconCopy, IconCheck } from "../component/icon"
+
+function CopyStatus() {
+ return (
+ <div data-component="copy-status">
+ <IconCopy data-slot="copy" />
+ <IconCheck data-slot="check" />
+ </div>
+ )
+}
+
+export default function Home() {
+ onMount(() => {
+ const commands = document.querySelectorAll("[data-copy]")
+ for (const button of commands) {
+ const callback = () => {
+ const text = button.textContent
+ alert(text)
+ if (text) {
+ navigator.clipboard.writeText(text)
+ button.setAttribute("data-copied", "")
+ setTimeout(() => {
+ button.removeAttribute("data-copied")
+ }, 1500)
+ }
+ }
+ button.addEventListener("click", callback)
+ onCleanup(() => {
+ button.removeEventListener("click", callback)
+ })
+ }
+ })
+
+ return (
+ <main data-page="home">
+ <Title>opencode | AI coding agent built for the terminal</Title>
+ <div data-component="content">
+ <section data-component="top">
+ <img data-slot="logo" src={logo} alt="logo" />
+ <h1 data-slot="title">The AI coding agent built for the terminal.</h1>
+ </section>
+
+ <section data-component="cta">
+ <div data-slot="left">
+ <a href="/docs">Get Started</a>
+ </div>
+ <div data-slot="right">
+ <button data-copy data-slot="command" data-command="curl -fsSL https://opencode.ai/install | bash">
+ <span>
+ <span>curl -fsSL&nbsp;</span>
+ <span data-slot="protocol">https://</span>
+ <span data-slot="highlight">opencode.ai/install</span>
+ &nbsp;| bash
+ </span>
+ <CopyStatus />
+ </button>
+ </div>
+ </section>
+
+ <section data-component="features">
+ <ul data-slot="list">
+ <li>
+ <strong>Native TUI</strong>: A responsive, native, themeable terminal UI.
+ </li>
+ <li>
+ <strong>LSP enabled</strong>: Automatically loads the right LSPs for the LLM.
+ </li>
+ <li>
+ <strong>Multi-session</strong>: Start multiple agents in parallel on the same project.
+ </li>
+ <li>
+ <strong>Shareable links</strong>: Share a link to any sessions for reference or to debug.
+ </li>
+ <li>
+ <strong>Claude Pro</strong>: Log in with Anthropic to use your Claude Pro or Max account.
+ </li>
+ <li>
+ <strong>Use any model</strong>: Supports 75+ LLM providers through{" "}
+ <a href="https://models.dev">Models.dev</a>, including local models.
+ </li>
+ </ul>
+ </section>
+
+ <section data-component="install">
+ <div data-component="method">
+ <h3 data-component="title">npm</h3>
+ <button data-copy data-slot="button">
+ <span>
+ npm install -g&nbsp;<strong>opencode-ai</strong>
+ </span>
+ <CopyStatus />
+ </button>
+ </div>
+ <div data-component="method">
+ <h3 data-component="title">bun</h3>
+ <button data-copy data-slot="button">
+ <span>
+ bun install -g&nbsp;<strong>opencode-ai</strong>
+ </span>
+ <CopyStatus />
+ </button>
+ </div>
+ <div data-component="method">
+ <h3 data-component="title">homebrew</h3>
+ <button data-copy data-slot="button">
+ <span>
+ brew install&nbsp;<strong>sst/tap/opencode</strong>
+ </span>
+ <CopyStatus />
+ </button>
+ </div>
+ <div data-component="method">
+ <h3 data-component="title">paru</h3>
+ <button data-copy data-slot="button">
+ <span>
+ paru -S&nbsp;<strong>opencode-bin</strong>
+ </span>
+ <CopyStatus />
+ </button>
+ </div>
+ </section>
+
+ <section data-component="screenshots">
+ <div data-slot="left">
+ <div data-component="title">opencode TUI with tokyonight theme</div>
+ <div data-slot="filler">
+ <img src={IMG_SPLASH} alt="opencode TUI with tokyonight theme" />
+ </div>
+ </div>
+ <div data-slot="right">
+ <div data-slot="cell">
+ <div data-component="title">opencode in VS Code</div>
+ <div data-slot="filler">
+ <img src={IMG_VSCODE} alt="opencode in VS Code" />
+ </div>
+ </div>
+ <div data-slot="cell">
+ <div data-component="title">opencode in GitHub</div>
+ <div data-slot="filler">
+ <img src={IMG_GITHUB} alt="opencode in GitHub" />
+ </div>
+ </div>
+ </div>
+ </section>
+
+ <footer data-component="footer">
+ <div data-slot="cell">
+ <a href="https://github.com/sst/opencode">GitHub</a>
+ </div>
+ <div data-slot="cell">
+ <a href="https://opencode.ai/discord">Discord</a>
+ </div>
+ <div data-slot="cell">
+ <span>
+ ©2025 <a href="https://anoma.ly">Anomaly Innovations</a>
+ </span>
+ </div>
+ </footer>
+ </div>
+ </main>
+ )
+}