Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
},
"css.customData": [".vscode/tailwind.json"],
"editor.codeActionsOnSave": {
"quickfix.biome": "explicit",
"source.organizeImports.biome": "explicit"
"source.fixAll.biome": "explicit"
},
"editor.defaultFormatter": "biomejs.biome",
"editor.formatOnSave": true,
Expand Down
622 changes: 0 additions & 622 deletions bun.lock

This file was deleted.

1 change: 1 addition & 0 deletions next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference path="./.next/types/routes.d.ts" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
9 changes: 8 additions & 1 deletion next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@ const rehypeOptions = {

const nextConfig = {
pageExtensions: ['md', 'mdx', 'ts', 'tsx'],
reactStrictMode: true
reactStrictMode: true,
transpilePackages: [
'@rubriclab/actions',
'@rubriclab/agents',
'@rubriclab/blocks',
'@rubriclab/events',
'@rubriclab/ui'
]
} satisfies NextConfig

const withMDX = createMDX({
Expand Down
53 changes: 30 additions & 23 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,37 +1,44 @@
{
"dependencies": {
"@mdx-js/loader": "^3.1.0",
"@mdx-js/react": "^3.1.0",
"@next/mdx": "^15.2.2",
"@mdx-js/loader": "^3.1.1",
"@mdx-js/react": "^3.1.1",
"@next/mdx": "^15.5.3",
"@rubric-warehouse/posthog-api": "^0.0.1",
"@rubriclab/actions": "*",
"@rubriclab/agents": "*",
"@rubriclab/blocks": "*",
"@rubriclab/config": "*",
"@t3-oss/env-nextjs": "^0.11.1",
"@tailwindcss/postcss": "^4.0.10",
"@types/node": "^22.13.10",
"@vimeo/player": "^2.26.0",
"@rubriclab/events": "*",
"@rubriclab/ui": "*",
"@t3-oss/env-nextjs": "^0.13.8",
"@tailwindcss/postcss": "^4.1.13",
"@types/node": "^24.3.1",
"@vimeo/player": "^2.29.6",
"clsx": "^2.1.1",
"framer-motion": "^11.11.10",
"hls.js": "^1.5.20",
"next": "^15.2.1",
"posthog-js": "^1.232.7",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"framer-motion": "^12.23.12",
"hls.js": "^1.6.12",
"next": "^15.5.3",
"posthog-js": "^1.265.0",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-intersection-observer": "^9.16.0",
"recharts": "^3.2.0",
"rehype-pretty-code": "^0.14.1",
"shiki": "^3.2.1",
"sonner": "^1.5.0",
"tailwind-merge": "^3.0.2",
"tailwindcss": "^4.0.10",
"typescript": "^5.8.2",
"zod": "3.24.2"
"shiki": "^3.12.2",
"sonner": "^2.0.7",
"tailwind-merge": "^3.3.1",
"tailwindcss": "^4.1.13",
"typescript": "^5.9.2",
"zod": "latest"
},
"description": "Rubric website",
"devDependencies": {
"@types/bun": "^1.2.6",
"@types/bun": "^1.2.21",
"@types/mdx": "^2.0.13",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"@types/react": "^19.1.13",
"@types/react-dom": "^19.1.9",
"@types/vimeo__player": "^2.18.3",
"postcss": "^8.5.3"
"postcss": "^8.5.6"
},
"license": "gonuts",
"name": "website",
Expand Down
10 changes: 2 additions & 8 deletions src/app/(rest)/contact/contact-form.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
'use client'

import { Button } from '@rubriclab/ui'
import { useRef } from 'react'
import { createContactRequest } from '~/lib/actions/create-contact-request'
import { useShortcut } from '~/lib/hooks/use-shortcut'
import { Button } from '~/ui/button'
import { Form } from '~/ui/form'

export const ContactForm = () => {
Expand Down Expand Up @@ -39,13 +39,7 @@ export const ContactForm = () => {
/>
</div>
</div>
<Button type="submit" disabled={pending || !!state?.success} className="w-full">
<p>Submit</p>
<div className="ml-auto hidden items-center gap-0.5 sm:flex">
<kbd>⌘</kbd>
<kbd>⏎</kbd>
</div>
</Button>
<Button type="submit" disabled={pending || !!state?.success} label="Submit ⌘+⏎" />
</>
)}
</Form>
Expand Down
11 changes: 4 additions & 7 deletions src/app/(rest)/newsletter/newsletter-form.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
'use client'

import { Button } from '@rubriclab/ui'
import { createNewsletterSubscriber } from '~/lib/actions/create-newsletter-subscriber'
import { cn } from '~/lib/utils/cn'
import { Button } from '~/ui/button'
import { Form } from '~/ui/form'
import { Arrow } from '~/ui/icons/arrow'

export const NewsletterForm = ({ className }: { className?: string }) => {
return (
Expand All @@ -21,13 +20,11 @@ export const NewsletterForm = ({ className }: { className?: string }) => {
<div className="relative flex w-full items-center">
<input placeholder="Email" className="w-full" name="email" type="email" required />
<Button
label="Subscribe"
type="submit"
icon="arrowRight"
disabled={pending || !!state?.success}
variant="icon"
className="-translate-y-1/2 absolute top-1/2 right-2"
>
<Arrow />
</Button>
/>
</div>
)}
</Form>
Expand Down
1 change: 1 addition & 0 deletions src/app/api/events/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { GET, maxDuration } from '~/lib/events/server'
49 changes: 48 additions & 1 deletion src/app/globals.css
Original file line number Diff line number Diff line change
@@ -1,9 +1,56 @@
/** biome-ignore-all lint/suspicious/noUnknownAtRules: Tailwind Grammar */

@import "tailwindcss";
@source "../../node_modules/@rubriclab/ui";
@source "../../../../node_modules/@rubriclab/ui";

:root {
--background: #ffffff;
--foreground: #171717;
--primary: oklch(54.6% 0.245 262.881);
--accent: oklch(96.53% 0.004 262.56);
--border: oklch(86.68% 0.017 261.56);
--hover: oklch(92.05% 0.008 262.67);
--destructive: oklch(62.72% 0.233 29.22);
--muted: oklch(98.27% 0.001 95.47);
--muted-foreground: oklch(13.68% 0.034 0.09);
/* Optional but recommended */
--accent-foreground: #111111;
}

@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
--primary: oklch(54.6% 0.245 262.881);
--accent: oklch(21.41% 0.007 89.49 / 60%);
--border: oklch(26.98% 0.009 89.63);
--hover: oklch(21.41% 0.007 89.49);
--destructive: oklch(62.72% 0.233 29.22);
--muted: oklch(15.48% 0.006 89.58);
--muted-foreground: oklch(86.68% 0.017 261.56);
/* Optional but recommended */
--accent-foreground: #fafafa;
}
}

@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-primary: var(--primary);
--color-border: var(--border);
--color-hover: var(--hover);
--color-destructive: var(--destructive);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
}

:root {
--primary: black;
--secondary: #666;
--background: #f5f0ec;
--background: #fff;
--negative: #222;
--subtle: #0000001a; /* black/10 */
--danger: red;
Expand Down
2 changes: 1 addition & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const metadata: Metadata = {

export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<html lang="en" className="dark:bg-black">
<body
className={`${matter.className} relative flex h-full min-h-screen w-full flex-col items-center`}
>
Expand Down
45 changes: 28 additions & 17 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
'use client'

import { useFold } from '~/lib/hooks/use-fold'
// import { useFold } from '~/lib/hooks/use-fold'
import { cn } from '~/lib/utils/cn'
import { CTA } from '~/ui/cta'
import { Footer } from '~/ui/footer'
// import { Arrow } from '~/ui/icons/arrow'
import { Partners } from '~/ui/partners'
import { ScrollButton } from '~/ui/scroll-button'
import { Testimonials } from '~/ui/testimonials'
import { TrustedBy } from '~/ui/trusted-by'
import { Video } from '~/ui/video/video'

// import { VimeoVideo } from '~/ui/video/vimeo-video'

// import { Video } from '~/ui/video/video'

const Section = ({ children, className }: { children: React.ReactNode; className?: string }) => (
<div
className={cn(
Expand All @@ -22,25 +26,26 @@ const Section = ({ children, className }: { children: React.ReactNode; className
)

const Hero = () => {
const { isBelowFold } = useFold()
// const { isBelowFold } = useFold()
return (
<Section className="relative h-screen">
<div className="flex h-full w-full flex-col items-center justify-center">
<div className="w-fit max-w-5xl">
<h2 className="mb-4 text-2xl">
We&apos;re an applied AI Lab helping companies get intelligence to production.
</h2>
<Video
hlsUrl="https://d2os0zhpsj02b0.cloudfront.net/hero/hls/master.m3u8"
mp4Url="https://d2os0zhpsj02b0.cloudfront.net/hero/preview.mp4"
posterUrl="/images/video-thumbnail.jpg"
transcriptionUrl="/transcripts/hero.vtt"
/>
<div className="flex w-fit max-w-5xl flex-col items-center gap-24">
<div className="flex flex-col items-center gap-4">
<h1 className="text-2xl">Rubric Agency</h1>
<h1 className="mb-4 text-4xl">
We build{' '}
<span className="bg-gradient-to-r from-gray-800 to-blue-600 bg-clip-text text-transparent dark:from-gray-200 dark:to-blue-600">
powerful
</span>{' '}
AI systems.
</h1>
</div>

{/* <Chart /> */}
</div>
</div>
<ScrollButton
className={cn('absolute bottom-6 transition-opacity', { 'opacity-0': isBelowFold })}
/>
<TrustedBy />
</Section>
)
}
Expand All @@ -50,10 +55,16 @@ export default function Page() {
<div className="flex flex-col items-center">
<Hero />
<Section className="space-y-40">
<TrustedBy />
<Testimonials />
<Partners />
<CTA />
{/* <VimeoVideo /> */}
<Video
hlsUrl="https://d2os0zhpsj02b0.cloudfront.net/hero/hls/master.m3u8"
mp4Url="https://d2os0zhpsj02b0.cloudfront.net/hero/preview.mp4"
posterUrl="/images/video-thumbnail.jpg"
transcriptionUrl="/transcripts/hero.vtt"
/>
</Section>
<Footer />
</div>
Expand Down
5 changes: 5 additions & 0 deletions src/lib/actions/posthog/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { webAnalyticsOverviewRetrieve } from '@rubric-warehouse/posthog-api'

export const posthogActions = {
webAnalyticsOverviewRetrieve
}
22 changes: 22 additions & 0 deletions src/lib/agents/ai.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use server'

import { env } from '../env'
import { publish } from '../events/server'
import { executeDemoAgent } from './demo'

export async function sendMessage({ userId, message }: { userId: string; message: string }) {
await executeDemoAgent({
messages: [{ content: message, role: 'user' }],
onEvent: async events => {
switch (events.type) {
case 'assistant_message':
await publish({
channel: userId,
eventType: events.type,
payload: events
})
}
},
openAIKey: env.OPENAI_API_KEY
})
}
12 changes: 12 additions & 0 deletions src/lib/agents/demo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { createAgent } from '@rubriclab/agents'

const { executeAgent, eventTypes, __ToolEvent, __ResponseEvent } = createAgent({
model: 'gpt-5-mini',
systemPrompt: 'You are a demo agent',
tools: {}
})

export { executeAgent as executeDemoAgent, eventTypes as demoAgentEventTypes }

export type DemoAgentToolEvent = typeof __ToolEvent
export type DemoAgentResponseEvent = typeof __ResponseEvent
Empty file added src/lib/blocks/button.tsx
Empty file.
Empty file added src/lib/blocks/chart.tsx
Empty file.
Empty file added src/lib/blocks/form.tsx
Empty file.
Empty file added src/lib/blocks/image.tsx
Empty file.
17 changes: 17 additions & 0 deletions src/lib/blocks/stats.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { createBlock } from '@rubriclab/blocks'
import { z } from 'zod/v4'
import { Stats } from '~/ui/demo/blocks/stats'

export const stats = createBlock({
description: 'Stats block',
render: ({ statistic, subtitle, title }) => {
return <Stats data={{ statistic, subtitle, title }} />
},
schema: {
input: z.object({
statistic: z.number(),
subtitle: z.string(),
title: z.string()
})
}
})
Empty file added src/lib/blocks/table.tsx
Empty file.
Empty file added src/lib/blocks/video.tsx
Empty file.
4 changes: 4 additions & 0 deletions src/lib/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,18 @@ export const env = createEnv({
NEXT_PUBLIC_POSTHOG_HOST: process.env.NEXT_PUBLIC_POSTHOG_HOST,
NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY,
NODE_ENV: process.env.NODE_ENV,
OPENAI_API_KEY: process.env.OPENAI_API_KEY,
ROS_API_URL: process.env.ROS_API_URL,
ROS_SECRET: process.env.ROS_SECRET,
UPSTASH_REDIS_URL: process.env.UPSTASH_REDIS_URL,
VERCEL_URL: process.env.VERCEL_URL
},
server: {
NODE_ENV: z.string().min(1),
OPENAI_API_KEY: z.string().min(1),
ROS_API_URL: z.string().min(1),
ROS_SECRET: z.string().min(1),
UPSTASH_REDIS_URL: z.string().min(1),
VERCEL_URL: z.string().min(1)
}
})
7 changes: 7 additions & 0 deletions src/lib/events/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { createEventsClient } from '@rubriclab/events/client'
import { eventTypes } from './types'

export const { useEvents } = createEventsClient({
eventTypes,
url: '/api/events'
})
Loading