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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
An open source personal site builder. Powered by Together.ai.
</p>

## Tech stack
## Tech stack!!!!

- Together.ai for the LLM
- Vercel's AI SDK as the LLM framework
Expand Down
17 changes: 16 additions & 1 deletion app/(private)/preview/client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,21 @@ export default function PreviewClient({ messageTip }: { messageTip?: string }) {
setHasUnsavedChanges(true);
};

if (resumeQuery.isError || usernameQuery.isError) {
const errorMessage =
resumeQuery.error instanceof Error
? resumeQuery.error.message
: usernameQuery.error instanceof Error
? usernameQuery.error.message
: 'Something went wrong. Please refresh the page.';
return (
<div className="flex flex-col items-center justify-center min-h-screen gap-4">
<p className="text-red-500 text-lg">{errorMessage}</p>
<Button onClick={() => window.location.reload()}>Refresh Page</Button>
</div>
);
}

if (
resumeQuery.isLoading ||
usernameQuery.isLoading ||
Expand Down Expand Up @@ -237,7 +252,7 @@ export default function PreviewClient({ messageTip }: { messageTip?: string }) {
)}
</div>

<div className="max-w-3xl mx-auto w-full md:rounded-lg border-[0.5px] border-neutral-300 flex items-center justify-between px-4">
<div className="max-w-3xl mx-auto w-full md:rounded-lg border-[0.5px] border-border flex items-center justify-between px-4">
{isEditMode ? (
<EditResume
resume={localResumeData}
Expand Down
26 changes: 13 additions & 13 deletions app/(private)/upload/client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,10 @@ export default function UploadPageClient() {
{fileState.status !== 'empty' && (
<button
onClick={handleReset}
className="absolute top-2 right-2 p-1 hover:bg-gray-100 rounded-full z-10"
className="absolute top-2 right-2 p-1 hover:bg-muted rounded-full z-10"
disabled={isUpdating}
>
<X className="h-4 w-4 text-gray-500" />
<X className="h-4 w-4 text-muted-foreground" />
</button>
)}

Expand All @@ -86,20 +86,20 @@ export default function UploadPageClient() {
maxFiles={1}
icon={
fileState.status !== 'empty' ? (
<img src="/uploaded-pdf.svg" className="h-6 w-6" />
<img src="/uploaded-pdf.svg" className="h-6 w-6 dark:invert" />
) : (
<Linkedin className="h-6 w-6 text-gray-600" />
<Linkedin className="h-6 w-6 text-muted-foreground" />
)
}
title={
<span className="text-base font-bold text-center text-design-black">
<span className="text-base font-bold text-center text-foreground">
{fileState.status !== 'empty'
? fileState.file.name
: 'Upload PDF'}
</span>
}
description={
<span className="text-xs font-light text-center text-design-gray">
<span className="text-xs font-light text-center text-muted-foreground">
{fileState.status !== 'empty'
? `${(fileState.file.size / 1024 / 1024).toFixed(2)} MB`
: 'Resume or LinkedIn'}
Expand All @@ -117,19 +117,19 @@ export default function UploadPageClient() {
<DialogTrigger asChild>
<Button
variant="ghost"
className="mt-3 hover:bg-white border border-transparent hover:border-gray-200 font-mono text-center cursor-help flex flex-row gap-1.5 justify-center mx-auto"
className="mt-3 hover:bg-muted border border-transparent hover:border-border font-mono text-center cursor-help flex flex-row gap-1.5 justify-center mx-auto"
>
<span className="ml-1 inline-block w-4 h-4 rounded-full border border-gray-300 items-center justify-center text-xs cursor-help">
<span className="ml-1 inline-block w-4 h-4 rounded-full border border-border items-center justify-center text-xs cursor-help">
i
</span>
<p className="text-xs text-center text-design-gray whitespace-normal">
<p className="text-xs text-center text-muted-foreground whitespace-normal">
How to upload LinkedIn profile
</p>
</Button>
</DialogTrigger>
<DialogContent className="w-full max-w-[652px] text-center font-mono !p-0 gap-0">
<DialogTitle className="font-mono text-base text-center text-design-gray px-7 py-4">
Go to your profile → Click on Resources → Then Save to PDF
<DialogTitle className="font-mono text-base text-center text-muted-foreground px-7 py-4">
Go to your profile → Click on &quot;Resources&quot; → Then &quot;Save to PDF&quot;
</DialogTitle>
<img src="/linkedin-save-to-pdf.png" className="h-auto w-full" />
</DialogContent>
Expand All @@ -138,7 +138,7 @@ export default function UploadPageClient() {
<div className="font-mono">
<div className="relative">
<Button
className="px-4 py-3 h-auto bg-design-black hover:bg-design-black/95"
className="px-4 py-3 h-auto bg-primary hover:bg-primary/90 text-primary-foreground"
disabled={fileState.status === 'empty' || isUpdating}
onClick={() => router.push('/pdf')}
>
Expand All @@ -152,7 +152,7 @@ export default function UploadPageClient() {
<img
src="/sparkle.png"
alt="Sparkle Icon"
className="h-5 w-5 mr-2"
className="h-5 w-5 mr-2 dark:invert"
/>
Generate Website
</>
Expand Down
9 changes: 6 additions & 3 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ClerkProvider } from '@clerk/nextjs';
import './globals.css';
import { Toaster } from '@/components/ui/sonner';
import { ReactQueryClientProvider } from '@/components/ReactQueryClientProvider';
import { ThemeProvider } from '@/components/theme-provider';
import { Metadata } from 'next';
import PlausibleProvider from 'next-plausible';

Expand All @@ -28,7 +29,7 @@ export default function RootLayout({
<ClerkProvider>
<PlausibleProvider domain="self.so">
<ReactQueryClientProvider>
<html lang="en">
<html lang="en" suppressHydrationWarning>
<head>
{/* {process.env.NODE_ENV === "development" && (
<script
Expand All @@ -39,8 +40,10 @@ export default function RootLayout({
{/* rest of your scripts go under */}
</head>
<body className={`${mono.className} min-h-screen flex flex-col`}>
<main className="flex-1 flex flex-col">{children}</main>
<Toaster richColors position="bottom-center" />
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
<main className="flex-1 flex flex-col">{children}</main>
<Toaster richColors position="bottom-center" />
</ThemeProvider>
</body>
</html>
</ReactQueryClientProvider>
Expand Down
16 changes: 8 additions & 8 deletions app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,18 @@ export default function Home() {
{/* Left side - Call to action */}
<div className="w-full md:w-1/2 max-w-[378px] flex flex-col justify-center items-center md:items-start ">
<div className="max-w-md text-center md:text-left">
<div className="inline-block font-mono gap-2.5 px-2.5 py-1.5 rounded bg-gray-100 text-sm mb-5 text-design-gray">
<div className="inline-block font-mono gap-2.5 px-2.5 py-1.5 rounded bg-muted text-sm mb-5 text-muted-foreground">
100% free & open source
</div>

<h1 className="text-[32px] font-bold mb-4 flex items-center justify-center md:justify-start gap-4 flex-wrap text-design-black font-mono leading-4">
<h1 className="text-[32px] font-bold mb-4 flex items-center justify-center md:justify-start gap-4 flex-wrap text-foreground font-mono leading-4">
<span>LinkedIn</span>
<img
src="/right-arrow.png"
alt="Arrow Right Icon"
width={32}
height={32}
className="inline size-8"
className="inline size-8 dark:invert"
/>
<span>Website</span>
<br />
Expand All @@ -40,30 +40,30 @@ export default function Home() {
alt="Pointer Icon"
width={37}
height={37}
className="size-[37px] text-gray-400"
className="size-[37px] text-gray-400 dark:invert"
/>
</h1>

<p className="text-base text-gray-600 mb-[30px] font-mono text-center md:text-left">
<p className="text-base text-muted-foreground mb-[30px] font-mono text-center md:text-left">
Turn your resume/LinkedIn
<br /> into a professional website.
</p>

<div className="relative flex flex-col items-center font-mono w-full md:w-fit">
<Link href="/upload">
<Button className="relative group flex items-center bg-design-black hover:bg-design-black/95 text-white px-6 py-3 h-auto text-base overflow-hidden">
<Button className="relative group flex items-center bg-primary hover:bg-primary/90 text-primary-foreground px-6 py-3 h-auto text-base overflow-hidden">
<div className="h-[120px] w-10 bg-gradient-to-r from-white/10 via-white/50 to-white/10 absolute blur-sm -rotate-45 -left-16 group-hover:left-[150%] duration-500 delay-200" />
<img
src="/sparkle.png"
alt="Sparkle Icon"
className="h-5 w-5 mr-2 relative"
className="h-5 w-5 mr-2 relative dark:invert"
/>
<span className="relative">Upload Resume</span>
<BorderBeam colorFrom="#5d5d5d" colorTo="#ffffff" />
</Button>
</Link>

<p className="text-sm text-gray-500 mt-4 text-center">
<p className="text-sm text-muted-foreground mt-4 text-center">
Takes 1 minute!
</p>
</div>
Expand Down
2 changes: 1 addition & 1 deletion components/CustomSpinner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const CustomSpinner = ({ className }: { className?: string }) => {
viewBox="0 0 28 28"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={cn('animate-spin', className)}
className={cn('animate-spin dark:invert', className)}
>
<g clipPath="url(#clip0_199_1000)">
<path
Expand Down
16 changes: 8 additions & 8 deletions components/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@ import { TOGETHER_LINK } from '@/lib/utils';

export function Footer() {
return (
<footer className="w-full py-4 px-6 mt-auto border-t border-gray-200">
<footer className="w-full py-4 px-6 mt-auto border-t border-border">
<div className="max-w-4xl justify-between items-center mx-auto w-full flex flex-col-reverse md:flex-row gap-2">
<div className="text-sm text-design-gray font-mono font-bold">
<div className="text-sm text-muted-foreground font-mono font-bold">
Powered by{' '}
<a
target="_blank"
href={TOGETHER_LINK}
className="text-design-black underline underline-offset-2"
className="text-foreground underline underline-offset-2"
>
Together.ai
</a>{' '}
&{' '}
<a
target="_blank"
href={TOGETHER_LINK}
className="text-design-black underline underline-offset-2"
className="text-foreground underline underline-offset-2"
>
Qwen Next
</a>
Expand All @@ -28,18 +28,18 @@ export function Footer() {
target="_blank"
rel="noopener noreferrer"
href="https://github.com/Nutlope/self.so"
className="size-6 flex items-center justify-center border-design-gray border rounded-md"
className="size-6 flex items-center justify-center border border-border rounded-md"
>
<img src="/footer/github.svg" className="size-4" />
<img src="/footer/github.svg" className="size-4 dark:invert" />
<span className="sr-only">GitHub</span>
</a>
<a
target="_blank"
rel="noopener noreferrer"
href="https://x.com/nutlope"
className="size-6 flex items-center justify-center border-design-gray border rounded-md"
className="size-6 flex items-center justify-center border border-border rounded-md"
>
<img src="/footer/x.svg" className="size-4" />
<img src="/footer/x.svg" className="size-4 dark:invert" />
<span className="sr-only">Social</span>
</a>
</div>
Expand Down
16 changes: 8 additions & 8 deletions components/PreviewActionbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ export default function PreviewActionbar({

return (
<>
<div className="w-full rounded-lg bg-[#fcfcfc] border-[0.5px] border-neutral-300 flex items-center justify-between py-3 px-5 sm:px-4 sm:py-2.5 flex-col sm:flex-row gap-4">
<div className="w-full rounded-lg bg-card border-[0.5px] border-border flex items-center justify-between py-3 px-5 sm:px-4 sm:py-2.5 flex-col sm:flex-row gap-4">
<div className="flex flex-col sm:flex-row items-center gap-4 w-full">
<div className="flex items-center gap-1 mr-1">
<img
src="/link-icon.png"
className={cn(
'w-4 h-4 text-design-black ',
'w-4 h-4 dark:invert',
status === 'live' && 'cursor-pointer'
)}
onClick={() => {
Expand All @@ -50,11 +50,11 @@ export default function PreviewActionbar({
toast.success('Copied link to your website');
}}
/>
<p className="text-sm text-design-black">{prefix}</p>
<p className="text-sm text-foreground">{prefix}</p>
</div>

<div className="overflow-hidden rounded bg-white border-[0.5px] border-neutral-300 flex flex-row md:w-80 w-full">
<span className="flex-1 p-3 text-sm text-[#5d5d5d] border-none outline-none focus:ring-0 bg-transparent w-fit truncate">
<div className="overflow-hidden rounded bg-background border-[0.5px] border-border flex flex-row md:w-80 w-full">
<span className="flex-1 p-3 text-sm text-muted-foreground border-none outline-none focus:ring-0 bg-transparent w-fit truncate">
{initialUsername}
</span>

Expand Down Expand Up @@ -113,13 +113,13 @@ export default function PreviewActionbar({
onClick={handleStatusChange}
className={`flex items-center min-w-[100px] min-h-8 gap-1.5 px-3 py-1.5 h-auto ${
status === 'draft'
? 'bg-design-black hover:bg-[#333333] text-[#fcfcfc]'
: 'bg-design-white text-design-black hover:bg-gray-100'
? 'bg-primary hover:bg-primary/90 text-primary-foreground'
: 'bg-background text-foreground hover:bg-muted border border-border'
}`}
>
{isChangingStatus ? (
<>
<span className="mr-2 h-3 w-3 animate-spin rounded-full border-2 border-white border-t-transparent"></span>
<span className="mr-2 h-3 w-3 animate-spin rounded-full border-2 border-current border-t-transparent"></span>
</>
) : (
<span className="text-sm">
Expand Down
41 changes: 41 additions & 0 deletions components/ThemeToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use client';

import { Moon, Sun, Monitor } from 'lucide-react';
import { useTheme } from 'next-themes';
import { Button } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';

export function ThemeToggle() {
const { theme, setTheme } = useTheme();

return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="icon" className="size-9">
<Sun className="size-4 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon className="absolute size-4 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span className="sr-only">Toggle theme</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => setTheme('light')}>
<Sun className="mr-2 size-4" />
Light
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme('dark')}>
<Moon className="mr-2 size-4" />
Dark
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme('system')}>
<Monitor className="mr-2 size-4" />
System
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}
Loading