Skip to content
Merged
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
32 changes: 32 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: CI

on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]

permissions:
contents: read

jobs:
lint-and-typecheck:
name: Lint and Typecheck
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.2.21

- name: Install dependencies
run: bun install --frozen-lockfile

- name: ESLint
run: bun run lint

- name: TypeScript typecheck
run: bunx tsc --noEmit
28 changes: 28 additions & 0 deletions .github/workflows/pr-size.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: PR Size Labels

on:
pull_request:
types: [opened, synchronize, reopened]

permissions:
contents: read
pull-requests: write

jobs:
label:
runs-on: ubuntu-latest
steps:
- uses: codelytv/pr-size-labeler@v2
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
xs_label: "size/xs"
s_label: "size/s"
m_label: "size/m"
l_label: "size/l"
xl_label: "size/xl"
xxl_label: "size/xxl"
xs_max_size: "10"
s_max_size: "50"
m_max_size: "200"
l_max_size: "500"
xl_max_size: "1000"
4 changes: 2 additions & 2 deletions app/generation/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { useState, useEffect, useRef, Suspense } from 'react';
import { useSearchParams, useRouter } from 'next/navigation';
import { useSafeUser } from '@/lib/safe-clerk-hooks';
import { useSafeUser, hasClerkKeys } from '@/lib/safe-clerk-hooks';
import { appConfig } from '@/config/app.config';
import { getBestAvailableModelClient } from '@/lib/model-detector';
import HeroInput from '@/components/HeroInput';
Expand Down Expand Up @@ -152,7 +152,7 @@ function AISandboxPage() {
// All useEffect hooks
// Redirect to sign-in if not authenticated
useEffect(() => {
if (isLoaded && !isSignedIn) {
if (isLoaded && !isSignedIn && hasClerkKeys()) {
router.push('/sign-in');
}
}, [isLoaded, isSignedIn, router]);
Expand Down
16 changes: 13 additions & 3 deletions app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import GithubIcon from "@/components/shared/header/Github/_svg/GithubIcon";
import ButtonUI from "@/components/ui/shadcn/button";
import ZapDevIcon from "@/components/ZapdevIcon";
import ZapDevLogo from "@/components/ZapdevLogo";
import { SignInButton } from '@clerk/nextjs';
import ChatHistory from "@/components/app/(home)/sections/chat-history/ChatHistory";

interface SearchResult {
url: string;
Expand Down Expand Up @@ -278,10 +280,16 @@ export default function HomePage() {
toast.error(
<div className="flex flex-col gap-2">
<span>Please sign in to continue</span>
{hasClerkKeys() && (
<button className="px-3 py-1 text-xs bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors">
{hasClerkKeys() ? (
<SignInButton mode="modal">
<button className="px-3 py-1 text-xs bg-orange-500 text-white rounded hover:bg-orange-600 transition-colors">
Sign In
</button>
</SignInButton>
) : (
<Link href="/sign-in" className="px-3 py-1 text-xs bg-orange-500 text-white rounded hover:bg-orange-600 transition-colors">
Sign In
</button>
</Link>
)}
</div>
);
Expand Down Expand Up @@ -568,6 +576,8 @@ export default function HomePage() {
</div>
</section>

<ChatHistory />

{/* Full-width oval carousel section */}
{showSearchTiles && hasSearched && (
<section className={`carousel-section relative w-full overflow-hidden mt-32 mb-32 transition-opacity duration-500 ${
Expand Down
113 changes: 113 additions & 0 deletions components/app/(home)/sections/chat-history/ChatHistory.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
"use client";

import Link from "next/link";
import { useQuery } from "convex/react";
import { api } from "@/convex/_generated/api";
import { SignInButton } from "@clerk/nextjs";
import { hasClerkKeys, useSafeUser } from "@/lib/safe-clerk-hooks";

export default function ChatHistory() {
const { isSignedIn } = useSafeUser();

const enabled = hasClerkKeys() && isSignedIn;

if (!hasClerkKeys()) {
return null;
}

if (!isSignedIn) {
return (
<section className="container px-16 mt-24">
<div className="rounded-20 border border-border-faint bg-white p-20 text-center">
<h3 className="text-title-medium mb-6">Your recent projects</h3>
<p className="text-body-medium text-black-alpha-64 mb-10">
Sign in to view and continue your projects.
</p>
<SignInButton mode="modal">
<button className="inline-flex items-center gap-2 px-6 py-2 text-sm font-medium text-white bg-orange-500 hover:bg-orange-600 rounded-md shadow-sm transition-colors">
Sign In
</button>
</SignInButton>
</div>
</section>
);
}

return <ChatHistoryList />;
}

function ChatHistoryList() {
const data = useQuery(api.chats.getUserChats, { limit: 10 });

return (
<section className="container px-16 mt-24">
<div className="flex items-center justify-between mb-6">
<h3 className="text-title-medium">Your recent projects</h3>
<Link href="/generation" className="text-label-medium text-blue-600 hover:underline">
New project
</Link>
</div>

{!data && (
<div className="rounded-20 border border-border-faint bg-white p-20 text-center text-body-medium text-black-alpha-64">
Loading your projects...
</div>
)}

{data && data.chats && data.chats.length === 0 && (
<div className="rounded-20 border border-border-faint bg-white p-20 text-center">
<p className="text-body-medium text-black-alpha-64 mb-4">No projects yet</p>
<Link
href="/generation"
className="inline-flex items-center gap-2 px-6 py-2 text-sm font-medium text-white bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700 rounded-lg shadow-lg hover:shadow-xl transform hover:scale-105 transition-all duration-200"
>
Start your first project
</Link>
</div>
)}

{data && data.chats && data.chats.length > 0 && (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-12">
{data.chats.map((chat: any) => (
<div key={chat._id} className="rounded-16 overflow-hidden border border-border-faint bg-white">
{chat.screenshot ? (
// eslint-disable-next-line @next/next/no-img-element
<img src={chat.screenshot} alt={chat.title} className="w-full h-40 object-cover" />
) : (
<div className="w-full h-40 bg-gray-100 flex items-center justify-center text-black-alpha-32 text-label-large">
No preview
</div>
)}
<div className="p-14">
<div className="text-label-large font-medium mb-2 truncate" title={chat.title}>
{chat.title}
</div>
<div className="text-label-small text-black-alpha-48 mb-4">
Updated {new Date(chat.updatedAt).toLocaleString()}
</div>
<div className="flex items-center justify-between">
<Link
href={chat.sandboxId ? `/generation?sandbox=${encodeURIComponent(chat.sandboxId)}` : "/generation"}
className="text-label-medium text-blue-600 hover:underline"
>
Continue
</Link>
{chat.sandboxUrl && (
<a
href={chat.sandboxUrl}
target="_blank"
rel="noreferrer"
className="text-label-medium text-black-alpha-64 hover:text-black"
>
Open preview
</a>
)}
</div>
</div>
</div>
))}
</div>
)}
</section>
);
}
4 changes: 2 additions & 2 deletions components/shared/header/UserAuth/UserAuth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default function UserAuth() {
if (!hasClerkKeys()) {
return (
<div className="flex items-center gap-4">
<button className="flex items-center gap-2 px-6 py-2 text-sm font-medium text-white bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700 rounded-lg shadow-lg hover:shadow-xl transform hover:scale-105 transition-all duration-200">
<button className="flex items-center gap-2 px-4 py-2 text-sm font-medium text-white bg-orange-500 hover:bg-orange-600 rounded-md shadow-sm transition-colors">
<User size={16} />
Sign In
</button>
Expand All @@ -20,7 +20,7 @@ export default function UserAuth() {
<div className="flex items-center gap-4">
<SignedOut>
<SignInButton mode="modal">
<button className="flex items-center gap-2 px-6 py-2 text-sm font-medium text-white bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700 rounded-lg shadow-lg hover:shadow-xl transform hover:scale-105 transition-all duration-200">
<button className="flex items-center gap-2 px-4 py-2 text-sm font-medium text-white bg-orange-500 hover:bg-orange-600 rounded-md shadow-sm transition-colors">
<User size={16} />
Sign In
</button>
Expand Down
Loading