-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat(landing): Introduce OpenWork Coworker #2316
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
benjaminshafii
wants to merge
6
commits into
dev
Choose a base branch
from
feat/coworker-announcement
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
0d44178
feat(landing): add /cloud page with conversational control demo
benjaminshafii 68f8c66
feat(landing): introduce OpenWork Coworker with /coworker page
benjaminshafii 8126987
feat(landing): strengthen homepage conversion proof
benjaminshafii 12b1354
fix(landing): replace heavy homepage proof with compact marquee
benjaminshafii ca3d0aa
fix(landing): make proof marquee self-contained
benjaminshafii f6c4261
fix(landing): use local logos only in proof marquee
benjaminshafii File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| import { LandingCloud } from "../../components/landing-cloud"; | ||
| import { getGithubData } from "../../lib/github"; | ||
| import { baseOpenGraph } from "../../lib/seo"; | ||
|
|
||
| export const metadata = { | ||
| title: "OpenWork Cloud — Control your team's AI workspace from a conversation", | ||
| description: | ||
| "OpenWork Cloud is the control plane for shared skills, plugins, members, and providers — runnable from plain English. Local-first by default, cloud-ready when your team needs it.", | ||
| alternates: { | ||
| canonical: "/cloud" | ||
| }, | ||
| openGraph: { | ||
| ...baseOpenGraph, | ||
| url: "https://openworklabs.com/cloud" | ||
| } | ||
| }; | ||
|
|
||
| export default async function CloudPage() { | ||
| const github = await getGithubData(); | ||
| const cal = process.env.NEXT_PUBLIC_CAL_URL ?? ""; | ||
|
|
||
| return ( | ||
| <LandingCloud | ||
| stars={github.stars} | ||
| downloadHref={github.downloads.macos} | ||
| callHref={cal} | ||
| /> | ||
| ); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| import { LandingCoworker } from "../../components/landing-coworker"; | ||
| import { getGithubData } from "../../lib/github"; | ||
| import { baseOpenGraph } from "../../lib/seo"; | ||
|
|
||
| export const metadata = { | ||
| title: "OpenWork Coworker — Design and deploy AI coworkers from chat", | ||
| description: | ||
| "Design a full AI coworker right from the OpenWork desktop chat, connect the tools that matter, and deploy to Slack, email, and beyond. Available in private beta.", | ||
| alternates: { | ||
| canonical: "/coworker" | ||
| }, | ||
| openGraph: { | ||
| ...baseOpenGraph, | ||
| url: "https://openworklabs.com/coworker" | ||
| } | ||
| }; | ||
|
|
||
| export default async function CoworkerPage() { | ||
| const github = await getGithubData(); | ||
| const cal = process.env.NEXT_PUBLIC_CAL_URL ?? ""; | ||
|
|
||
| return ( | ||
| <LandingCoworker | ||
| stars={github.stars} | ||
| downloadHref={github.downloads.macos} | ||
| callHref={cal} | ||
| /> | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,172 @@ | ||
| "use client"; | ||
| import { AnimatePresence, motion } from "framer-motion"; | ||
| import { Check, Terminal, Wrench } from "lucide-react"; | ||
| import { useEffect, useState } from "react"; | ||
|
|
||
| type Step = | ||
| | { kind: "user"; content: string } | ||
| | { kind: "tool"; name: string; result: string } | ||
| | { kind: "agent"; content: string }; | ||
|
|
||
| const script: Step[] = [ | ||
| { kind: "user", content: "Hey, invite Omar to my team." }, | ||
| { | ||
| kind: "tool", | ||
| name: "invite_member", | ||
| result: "Invited omar@acme.com as Member" | ||
| }, | ||
| { | ||
| kind: "agent", | ||
| content: | ||
| "Done — Omar's been added to your team. He'll get an email to join the workspace." | ||
| }, | ||
| { | ||
| kind: "user", | ||
| content: | ||
| "Now let's add these skills I have on my computer and share them with him." | ||
| }, | ||
| { | ||
| kind: "tool", | ||
| name: "share_skills", | ||
| result: "Shared 3 skills · Meeting Brief, Contract Reviewer, Outreach CRM" | ||
| }, | ||
| { | ||
| kind: "agent", | ||
| content: | ||
| "Shared. Omar will see Meeting Brief, Contract Reviewer, and Outreach CRM in his workspace the moment he joins." | ||
| } | ||
| ]; | ||
|
|
||
| const STEP_MS = 1700; | ||
| const LOOP_PAUSE_MS = 4200; | ||
|
|
||
| export function LandingCloudChatDemo() { | ||
| const [visible, setVisible] = useState(1); | ||
|
|
||
| useEffect(() => { | ||
| let timer: ReturnType<typeof setTimeout>; | ||
| const total = script.length; | ||
|
|
||
| const tick = (i: number) => { | ||
| if (i < total) { | ||
| setVisible(i + 1); | ||
| timer = setTimeout(() => tick(i + 1), STEP_MS); | ||
| } else { | ||
| timer = setTimeout(() => { | ||
| setVisible(0); | ||
| timer = setTimeout(() => tick(1), 500); | ||
| }, LOOP_PAUSE_MS); | ||
| } | ||
| }; | ||
|
|
||
| tick(1); | ||
| return () => clearTimeout(timer); | ||
| }, []); | ||
|
|
||
| return ( | ||
| <div className="flex w-full max-w-lg flex-col overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm"> | ||
| {/* App chrome */} | ||
| <div className="flex items-center gap-3 border-b border-gray-100 bg-gray-50/80 px-4 py-2.5"> | ||
| <div className="flex gap-1.5"> | ||
| <div className="h-2.5 w-2.5 rounded-full bg-red-400/70" /> | ||
| <div className="h-2.5 w-2.5 rounded-full bg-yellow-400/70" /> | ||
| <div className="h-2.5 w-2.5 rounded-full bg-green-400/70" /> | ||
| </div> | ||
| <div className="flex items-center gap-2 text-[12px] font-medium text-gray-500"> | ||
| <Terminal size={12} /> | ||
| OpenWork Cloud Control | ||
| </div> | ||
| <span className="ml-auto flex items-center gap-1.5 rounded-full border border-green-200 bg-green-50 px-2 py-0.5 text-[10px] font-medium text-green-700"> | ||
| <span className="relative flex h-1.5 w-1.5"> | ||
| <span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-green-400 opacity-75" /> | ||
| <span className="relative inline-flex h-1.5 w-1.5 rounded-full bg-green-500" /> | ||
| </span> | ||
| Live | ||
| </span> | ||
| </div> | ||
|
|
||
| {/* Chat */} | ||
| <div className="flex min-h-[360px] flex-col gap-3 p-4 md:p-5"> | ||
| <AnimatePresence initial={false}> | ||
| {script.slice(0, visible).map((step, i) => { | ||
| if (step.kind === "user") { | ||
| return ( | ||
| <motion.div | ||
| key={`u-${i}`} | ||
| layout | ||
| initial={{ opacity: 0, y: 6 }} | ||
| animate={{ opacity: 1, y: 0 }} | ||
| transition={{ duration: 0.2 }} | ||
| className="self-end rounded-2xl rounded-br-md bg-[#011627] px-4 py-2.5 text-[13px] leading-relaxed text-white shadow-sm" | ||
| > | ||
| {step.content} | ||
| </motion.div> | ||
| ); | ||
| } | ||
|
|
||
| if (step.kind === "tool") { | ||
| return ( | ||
| <motion.div | ||
| key={`t-${i}`} | ||
| layout | ||
| initial={{ opacity: 0, y: 6 }} | ||
| animate={{ opacity: 1, y: 0 }} | ||
| transition={{ duration: 0.2 }} | ||
| className="flex flex-col gap-1.5 self-start" | ||
| > | ||
| <div className="inline-flex items-center gap-2 rounded-lg border border-gray-100 bg-gray-50 px-3 py-1.5 text-[11px] font-medium text-gray-600"> | ||
| <Wrench size={12} className="text-gray-400" /> | ||
| <span className="font-mono text-[11px] text-gray-700"> | ||
| {step.name} | ||
| </span> | ||
| <Check size={12} className="text-green-500" /> | ||
| </div> | ||
| <div className="ml-1 text-[11px] text-gray-400"> | ||
| {step.result} | ||
| </div> | ||
| </motion.div> | ||
| ); | ||
| } | ||
|
|
||
| return ( | ||
| <motion.div | ||
| key={`a-${i}`} | ||
| layout | ||
| initial={{ opacity: 0, y: 6 }} | ||
| animate={{ opacity: 1, y: 0 }} | ||
| transition={{ duration: 0.2 }} | ||
| className="max-w-[90%] self-start rounded-2xl rounded-bl-md bg-gray-100/80 px-4 py-2.5 text-[13px] leading-relaxed text-[#011627]" | ||
| > | ||
| {step.content} | ||
| </motion.div> | ||
| ); | ||
| })} | ||
| </AnimatePresence> | ||
|
|
||
| {visible < script.length ? ( | ||
| <motion.div | ||
| initial={{ opacity: 0 }} | ||
| animate={{ opacity: 1 }} | ||
| className="flex items-center gap-1.5 self-start rounded-2xl rounded-bl-md bg-gray-100/80 px-4 py-3" | ||
| > | ||
| <span className="h-1.5 w-1.5 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.3s]" /> | ||
| <span className="h-1.5 w-1.5 animate-bounce rounded-full bg-gray-400 [animation-delay:-0.15s]" /> | ||
| <span className="h-1.5 w-1.5 animate-bounce rounded-full bg-gray-400" /> | ||
| </motion.div> | ||
| ) : null} | ||
| </div> | ||
|
|
||
| {/* Composer */} | ||
| <div className="border-t border-gray-100 p-3"> | ||
| <div className="flex items-center justify-between rounded-lg border border-gray-200 px-3 py-2"> | ||
| <span className="text-[12px] text-gray-400"> | ||
| Ask anything about your Cloud… | ||
| </span> | ||
| <span className="rounded-full bg-[#011627] px-3 py-1 text-[10px] font-medium text-white"> | ||
| Send | ||
| </span> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| "use client"; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Unnecessary "use client" directive — component is server-compatible and does not require client-side bundling. Prompt for AI agents |
||
| import { Cloud } from "lucide-react"; | ||
| import Link from "next/link"; | ||
|
|
||
| export function LandingCloudSection() { | ||
| return ( | ||
| <section className="landing-shell rounded-[2.5rem] p-8 md:p-12"> | ||
| <div className="mb-4 flex items-center gap-2.5 text-[11px] font-semibold uppercase tracking-[0.2em] text-gray-400"> | ||
| <Cloud size={18} /> | ||
| OpenWork Cloud | ||
| </div> | ||
| <h2 className="mb-5 max-w-2xl text-3xl font-medium leading-[1.15] tracking-tight md:text-4xl lg:text-5xl"> | ||
| Control your team's AI | ||
| <br /> | ||
| workspace from a conversation. | ||
| </h2> | ||
| <p className="mb-8 max-w-2xl text-lg leading-relaxed text-gray-700"> | ||
| OpenWork Cloud is the control plane for shared skills, plugins, | ||
| members, and providers — runnable from plain English. Local-first by | ||
| default, cloud-ready when your team needs it. | ||
| </p> | ||
|
|
||
| <div className="flex flex-col items-start gap-3 sm:flex-row sm:items-center"> | ||
| <Link href="/cloud" className="doc-button inline-flex items-center gap-2"> | ||
| Explore Cloud | ||
| </Link> | ||
| <span className="text-sm text-gray-500"> | ||
| Local-first by default. Cloud when you need it. | ||
| </span> | ||
| </div> | ||
| </section> | ||
| ); | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: Defaulting
NEXT_PUBLIC_CAL_URLto""breaks the Cloud page's Contact sales CTA when the optional env var is unset.Prompt for AI agents