summaryrefslogtreecommitdiffhomepage
path: root/packages/console/app/src/component
diff options
context:
space:
mode:
authorDax <[email protected]>2025-10-01 19:38:15 -0400
committerGitHub <[email protected]>2025-10-01 19:38:15 -0400
commita782e3dac2c60429c2f06ccb1b20edaf5797ca0f (patch)
treebee5e0104d9e5721d8d48df899c45a292cfb6889 /packages/console/app/src/component
parent70da3a9399d3385b53f7831beb08f716b972860d (diff)
downloadopencode-a782e3dac2c60429c2f06ccb1b20edaf5797ca0f.tar.gz
opencode-a782e3dac2c60429c2f06ccb1b20edaf5797ca0f.zip
Zen lander (#2907)
Co-authored-by: David Hill <[email protected]> Co-authored-by: GitHub Action <[email protected]> Co-authored-by: Adam <[email protected]> Co-authored-by: Jay V <[email protected]>
Diffstat (limited to 'packages/console/app/src/component')
-rw-r--r--packages/console/app/src/component/email-signup.tsx49
-rw-r--r--packages/console/app/src/component/faq.tsx33
-rw-r--r--packages/console/app/src/component/footer.tsx34
-rw-r--r--packages/console/app/src/component/header.tsx127
-rw-r--r--packages/console/app/src/component/icon.tsx33
-rw-r--r--packages/console/app/src/component/legal.tsx9
6 files changed, 260 insertions, 25 deletions
diff --git a/packages/console/app/src/component/email-signup.tsx b/packages/console/app/src/component/email-signup.tsx
new file mode 100644
index 000000000..fec516a5f
--- /dev/null
+++ b/packages/console/app/src/component/email-signup.tsx
@@ -0,0 +1,49 @@
+import { action, useSubmission } from "@solidjs/router"
+import dock from "../asset/lander/dock.png"
+import { Resource } from "sst"
+import { Show } from "solid-js"
+
+const emailSignup = action(async (formData: FormData) => {
+ "use server"
+ const emailAddress = formData.get("email")!
+ const listId = "8b9bb82c-9d5f-11f0-975f-0df6fd1e4945"
+ const response = await fetch(`https://api.emailoctopus.com/lists/${listId}/contacts`, {
+ method: "PUT",
+ headers: {
+ Authorization: `Bearer ${Resource.EMAILOCTOPUS_API_KEY.value}`,
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ email_address: emailAddress,
+ }),
+ })
+ console.log(response)
+ return true
+})
+
+export function EmailSignup() {
+ const submission = useSubmission(emailSignup)
+ return (
+ <section data-component="email">
+ <div data-slot="dock">
+ <img src={dock} alt="" />
+ </div>
+ <div data-slot="section-title">
+ <h3>OpenCode will be available on desktop soon</h3>
+ <p>Join the waitlist for early access.</p>
+ </div>
+ <form data-slot="form" action={emailSignup} method="post">
+ <input type="email" name="email" placeholder="Email address" required />
+ <button type="submit" disabled={submission.pending}>
+ Subscribe
+ </button>
+ </form>
+ <Show when={submission.result}>
+ <div style="color: #03B000; margin-top: 24px;">Almost done, check your inbox and confirm your email address</div>
+ </Show>
+ <Show when={submission.error}>
+ <div style="color: #FF408F; margin-top: 24px;">{submission.error}</div>
+ </Show>
+ </section>
+ )
+}
diff --git a/packages/console/app/src/component/faq.tsx b/packages/console/app/src/component/faq.tsx
new file mode 100644
index 000000000..2b28fc0fe
--- /dev/null
+++ b/packages/console/app/src/component/faq.tsx
@@ -0,0 +1,33 @@
+import { Collapsible } from "@kobalte/core/collapsible"
+import { ParentProps } from "solid-js"
+
+export function Faq(props: ParentProps & { question: string }) {
+ return (
+ <Collapsible data-slot="faq-item">
+ <Collapsible.Trigger data-slot="faq-question">
+ <svg
+ data-slot="faq-icon-plus"
+ width="24"
+ height="24"
+ viewBox="0 0 24 24"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path d="M12.5 11.5H19V12.5H12.5V19H11.5V12.5H5V11.5H11.5V5H12.5V11.5Z" fill="#6D717D" />
+ </svg>
+ <svg
+ data-slot="faq-icon-minus"
+ width="24"
+ height="24"
+ viewBox="0 0 24 24"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path d="M5 11.5H19V12.5H5Z" fill="#6D717D" />
+ </svg>
+ <div data-slot="faq-question-text">{props.question}</div>
+ </Collapsible.Trigger>
+ <Collapsible.Content data-slot="faq-answer">{props.children}</Collapsible.Content>
+ </Collapsible>
+ )
+}
diff --git a/packages/console/app/src/component/footer.tsx b/packages/console/app/src/component/footer.tsx
new file mode 100644
index 000000000..f4ff846db
--- /dev/null
+++ b/packages/console/app/src/component/footer.tsx
@@ -0,0 +1,34 @@
+import { A, createAsync } from "@solidjs/router"
+import { createMemo } from "solid-js"
+import { github } from "~/lib/github"
+
+export function Footer() {
+ const githubData = createAsync(() => github())
+ const starCount = createMemo(() =>
+ githubData()?.stars
+ ? new Intl.NumberFormat("en-US", {
+ notation: "compact",
+ compactDisplay: "short",
+ }).format(githubData()!.stars!)
+ : "25K",
+ )
+
+ return (
+ <footer data-component="footer">
+ <div data-slot="cell">
+ <A href="https://github.com/sst/opencode" target="_blank">
+ GitHub <span>[{starCount()}]</span>
+ </A>
+ </div>
+ <div data-slot="cell">
+ <A href="/docs">Docs</A>
+ </div>
+ <div data-slot="cell">
+ <A href="https://opencode.ai/discord">Discord</A>
+ </div>
+ <div data-slot="cell">
+ <A href="https://x/opencode">X</A>
+ </div>
+ </footer>
+ )
+}
diff --git a/packages/console/app/src/component/header.tsx b/packages/console/app/src/component/header.tsx
new file mode 100644
index 000000000..c5447d1c1
--- /dev/null
+++ b/packages/console/app/src/component/header.tsx
@@ -0,0 +1,127 @@
+import logoLight from "../asset/logo-ornate-light.svg"
+import logoDark from "../asset/logo-ornate-dark.svg"
+import { A, createAsync } from "@solidjs/router"
+import { createMemo, Match, Show, Switch } from "solid-js"
+import { createStore } from "solid-js/store"
+import { github } from "~/lib/github"
+
+export function Header(props: { zen?: boolean }) {
+ const githubData = createAsync(() => github())
+ const starCount = createMemo(() =>
+ githubData()?.stars
+ ? new Intl.NumberFormat("en-US", {
+ notation: "compact",
+ compactDisplay: "short",
+ }).format(githubData()?.stars!)
+ : "25K",
+ )
+
+ const [store, setStore] = createStore({
+ mobileMenuOpen: false,
+ })
+
+ return (
+ <section data-component="top">
+ <A href="/">
+ <img data-slot="logo light" src={logoLight} alt="opencode logo light" />
+ <img data-slot="logo dark" src={logoDark} alt="opencode logo dark" />
+ </A>
+ <nav data-component="nav-desktop">
+ <ul>
+ <li>
+ <A href="https://github.com/sst/opencode" target="_blank">
+ GitHub <span>[{starCount()}]</span>
+ </A>
+ </li>
+ <li>
+ <A href="/docs">Docs</A>
+ </li>
+ <li>
+ <Switch>
+ <Match when={props.zen}>
+ <A href="/auth">Login</A>
+ </Match>
+ <Match when={!props.zen}>
+ <A href="/zen">Zen</A>
+ </Match>
+ </Switch>
+ </li>
+ </ul>
+ </nav>
+ <nav data-component="nav-mobile">
+ <button
+ type="button"
+ data-component="nav-mobile-toggle"
+ aria-expanded="false"
+ aria-controls="nav-mobile-menu"
+ class="nav-toggle"
+ onClick={() => setStore("mobileMenuOpen", !store.mobileMenuOpen)}
+ >
+ <span class="sr-only">Open menu</span>
+ <Switch>
+ <Match when={store.mobileMenuOpen}>
+ <svg
+ class="icon icon-close"
+ width="24"
+ height="24"
+ viewBox="0 0 24 24"
+ fill="none"
+ aria-hidden="true"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d="M12.7071 11.9993L18.0104 17.3026L17.3033 18.0097L12 12.7064L6.6967 18.0097L5.98959 17.3026L11.2929 11.9993L5.98959 6.69595L6.6967 5.98885L12 11.2921L17.3033 5.98885L18.0104 6.69595L12.7071 11.9993Z"
+ fill="currentColor"
+ />
+ </svg>
+ </Match>
+ <Match when={!store.mobileMenuOpen}>
+ <svg
+ class="icon icon-hamburger"
+ width="24"
+ height="24"
+ viewBox="0 0 24 24"
+ fill="none"
+ aria-hidden="true"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path d="M19 17H5V16H19V17Z" fill="currentColor" />
+ <path d="M19 8H5V7H19V8Z" fill="currentColor" />
+ </svg>
+ </Match>
+ </Switch>
+ </button>
+
+ <Show when={store.mobileMenuOpen}>
+ <div id="nav-mobile-menu" data-component="nav-mobile">
+ <nav data-component="nav-mobile-menu-list">
+ <ul>
+ <li>
+ <A href="/">Home</A>
+ </li>
+ <li>
+ <A href="https://github.com/sst/opencode" target="_blank">
+ GitHub <span>[{starCount()}]</span>
+ </A>
+ </li>
+ <li>
+ <A href="/docs">Docs</A>
+ </li>
+ <li>
+ <Switch>
+ <Match when={props.zen}>
+ <A href="/auth">Login</A>
+ </Match>
+ <Match when={!props.zen}>
+ <A href="/zen">Zen</A>
+ </Match>
+ </Switch>
+ </li>
+ </ul>
+ </nav>
+ </div>
+ </Show>
+ </nav>
+ </section>
+ )
+}
diff --git a/packages/console/app/src/component/icon.tsx b/packages/console/app/src/component/icon.tsx
index a82572e62..fa28316e8 100644
--- a/packages/console/app/src/component/icon.tsx
+++ b/packages/console/app/src/component/icon.tsx
@@ -34,38 +34,21 @@ export function IconLogo(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
export function IconCopy(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
return (
- <svg {...props} viewBox="0 0 512 512">
- <rect
- width="336"
- height="336"
- x="128"
- y="128"
- fill="none"
- stroke="currentColor"
- stroke-linejoin="round"
- stroke-width="32"
- rx="57"
- ry="57"
- ></rect>
+ <svg {...props} width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
- fill="none"
- stroke="currentColor"
- stroke-linecap="round"
- stroke-linejoin="round"
- stroke-width="32"
- d="m383.5 128l.5-24a56.16 56.16 0 0 0-56-56H112a64.19 64.19 0 0 0-64 64v216a56.16 56.16 0 0 0 56 56h24"
- ></path>
+ d="M8.75 8.75V2.75H21.25V15.25H15.25M15.25 8.75H2.75V21.25H15.25V8.75Z"
+ stroke="#8E8B8B"
+ stroke-width="1.5"
+ stroke-linecap="square"
+ />
</svg>
)
}
export function IconCheck(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
return (
- <svg {...props} viewBox="0 0 24 24">
- <path
- fill="currentColor"
- d="M9 16.17L5.53 12.7a.996.996 0 1 0-1.41 1.41l4.18 4.18c.39.39 1.02.39 1.41 0L20.29 7.71a.996.996 0 1 0-1.41-1.41z"
- ></path>
+ <svg {...props} width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <path d="M2.75 15.0938L9 20.25L21.25 3.75" stroke="#03B000" stroke-width="2" stroke-linecap="square" />
</svg>
)
}
diff --git a/packages/console/app/src/component/legal.tsx b/packages/console/app/src/component/legal.tsx
new file mode 100644
index 000000000..d692e4622
--- /dev/null
+++ b/packages/console/app/src/component/legal.tsx
@@ -0,0 +1,9 @@
+export function Legal() {
+ return (
+ <div data-component="legal">
+ <span>
+ ©{new Date().getFullYear()} <a href="https://anoma.ly">Anomaly</a>
+ </span>
+ </div>
+ )
+}