diff --git a/packages/website/app/assets/blob-comfy.png b/packages/website/app/assets/blob-comfy.png new file mode 100644 index 0000000..4c09920 Binary files /dev/null and b/packages/website/app/assets/blob-comfy.png differ diff --git a/packages/website/app/assets/cursor-ibeam.png b/packages/website/app/assets/cursor-ibeam.png new file mode 100644 index 0000000..44e5411 Binary files /dev/null and b/packages/website/app/assets/cursor-ibeam.png differ diff --git a/packages/website/app/assets/cursor.png b/packages/website/app/assets/cursor.png new file mode 100644 index 0000000..f097a66 Binary files /dev/null and b/packages/website/app/assets/cursor.png differ diff --git a/packages/website/app/modules/app/app-logo.tsx b/packages/website/app/modules/app/app-logo.tsx new file mode 100644 index 0000000..3043616 --- /dev/null +++ b/packages/website/app/modules/app/app-logo.tsx @@ -0,0 +1,20 @@ +import * as React from "react" + +export function AppLogo(props: React.SVGProps) { + return ( + + + + + + ) +} diff --git a/packages/website/app/root.tsx b/packages/website/app/root.tsx index 6c057c0..bf728a5 100644 --- a/packages/website/app/root.tsx +++ b/packages/website/app/root.tsx @@ -5,6 +5,7 @@ import { LiveReload, Meta, Outlet, + Scripts, ScrollRestoration, useLoaderData, } from "remix" @@ -72,6 +73,7 @@ export default function App() { + {process.env.NODE_ENV === "development" && } diff --git a/packages/website/app/routes/index.tsx b/packages/website/app/routes/index.tsx index bfee2e5..88e663b 100644 --- a/packages/website/app/routes/index.tsx +++ b/packages/website/app/routes/index.tsx @@ -1,7 +1,11 @@ -import packageJson from "reacord/package.json" +import clsx from "clsx" +import { useEffect, useRef, useState } from "react" +import blobComfyUrl from "~/assets/blob-comfy.png" +import cursorIbeamUrl from "~/assets/cursor-ibeam.png" +import cursorUrl from "~/assets/cursor.png" import dotsBackgroundUrl from "~/assets/dots-background.svg" import { AppFooter } from "~/modules/app/app-footer" -import LandingExample from "~/modules/landing/landing-example.mdx" +import { AppLogo } from "~/modules/app/app-logo" import { MainNavigation } from "~/modules/navigation/main-navigation" import { maxWidthContainer } from "~/modules/ui/components" @@ -12,27 +16,218 @@ export default function Landing() { className="fixed inset-0 rotate-6 scale-125 opacity-20" style={{ backgroundImage: `url(${dotsBackgroundUrl})` }} /> -
+
-
-
-

reacord

-
- -
-

{packageJson.description}

- - Get Started - -
+
+ +
+ +
+

+ Create interactive Discord messages with React. +

+ {/* */} +
+
+
-
) } + +const defaultState = { + chatInputText: "", + chatInputCursorVisible: true, + messageVisible: false, + count: 0, + cursorLeft: "25%", + cursorBottom: "-15px", +} + +const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) + +const animationFrame = () => + new Promise((resolve) => requestAnimationFrame(resolve)) + +function LandingAnimation() { + const [state, setState] = useState(defaultState) + const chatInputRef = useRef(null) + const addRef = useRef(null) + const deleteRef = useRef(null) + const cursorRef = useRef(null) + + useEffect(() => { + const animateClick = (element: HTMLElement) => + element.animate( + [{ transform: `translateY(2px)` }, { transform: `translateY(0px)` }], + 300, + ) + + let running = true + + void (async () => { + await delay(1000) + + while (running) { + setState(defaultState) + await delay(1000) + + for (const letter of "/counter") { + setState((state) => ({ + ...state, + chatInputText: state.chatInputText + letter, + })) + await delay(100) + } + + await delay(1000) + + setState((state) => ({ + ...state, + messageVisible: true, + chatInputText: "", + })) + await delay(1000) + + setState((state) => ({ + ...state, + cursorLeft: "70px", + cursorBottom: "40px", + })) + await delay(1500) + + for (let i = 0; i < 3; i++) { + setState((state) => ({ + ...state, + count: state.count + 1, + chatInputCursorVisible: false, + })) + animateClick(addRef.current!) + await delay(700) + } + + await delay(500) + + setState((state) => ({ + ...state, + cursorLeft: "140px", + })) + await delay(1000) + + animateClick(deleteRef.current!) + setState((state) => ({ ...state, messageVisible: false })) + await delay(1000) + + setState(() => ({ + ...defaultState, + chatInputCursorVisible: false, + })) + await delay(500) + } + })() + + return () => { + running = false + } + }, []) + + useEffect(() => { + let running = true + + void (async () => { + while (running) { + // check if the cursor is in the input + const cursorRect = cursorRef.current!.getBoundingClientRect() + const chatInputRect = chatInputRef.current!.getBoundingClientRect() + + const isOverInput = + cursorRef.current && + chatInputRef.current && + cursorRect.top + cursorRect.height / 2 > chatInputRect.top + + cursorRef.current!.src = isOverInput ? cursorIbeamUrl : cursorUrl + + await animationFrame() + } + })() + + return () => { + running = false + } + }) + + return ( +
+
+
+
+ +
+
+

comfybot

+

this button was clicked {state.count} times

+
+ + +
+
+
+
+
+ + {state.chatInputText || ( + + Message #showing-off-reacord + + )} + +
+ + +
+ ) +}