Skip to content
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
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 apps/web/client/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,4 @@ yarn-error.log*
.mastra/

# Ignore preload script changes unless the script should be updated. Uncomment to update.
/public/onlook-preload-script.js
/public/onlook-preload-script.*
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Broader ignore pattern for preload script LGTM; ensure previously tracked files are untracked

Good call generalizing to onlook-preload-script.*. If any such files were already tracked, remove them from the index so the ignore takes effect.


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Checking for tracked preload script files under apps/web/client/public..."
git ls-files -z | rg -z -nP '^apps/web/client/public/onlook-preload-script\.' || true
echo "If any paths appear above, untrack them with:"
echo "  git rm --cached <path>"

Length of output: 478


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Listing any tracked preload script files under apps/web/client/public:"
git ls-files 'apps/web/client/public/onlook-preload-script.*' || true

Length of output: 282


Remove tracked preload script so the new ignore pattern takes effect

The file apps/web/client/public/onlook-preload-script.js is still in the index, so your .gitignore won’t ignore future changes. Please untrack it:

• Tracked file:

  • apps/web/client/public/onlook-preload-script.js

Run:

git rm --cached apps/web/client/public/onlook-preload-script.js

and commit the removal.

🤖 Prompt for AI Agents
In apps/web/client/.gitignore around line 52 the new pattern
/public/onlook-preload-script.* will not take effect because
apps/web/client/public/onlook-preload-script.js is still tracked; untrack the
file by running git rm --cached apps/web/client/public/onlook-preload-script.js
(or use your GUI equivalent) and then commit the removal so future changes are
ignored.

4 changes: 1 addition & 3 deletions apps/web/client/next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ import path from 'node:path';
import './src/env';

const nextConfig: NextConfig = {
devIndicators: {
buildActivity: false,
},
devIndicators: false,
eslint: {
ignoreDuringBuilds: true,
},
Expand Down
2 changes: 0 additions & 2 deletions apps/web/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@
"clsx": "^2.1.1",
"culori": "^4.0.1",
"date-fns": "^4.1.0",
"dayjs": "^1.11.13",
"embla-carousel-react": "^8.6.0",
"flexsearch": "^0.8.160",
"freestyle-sandboxes": "^0.0.78",
Expand Down Expand Up @@ -99,7 +98,6 @@
"superjson": "^2.2.1",
"tailwind-merge": "^3.2.0",
"tw-animate-css": "^1.2.5",
"url-join": "^5.0.0",
"use-resize-observer": "^9.1.0",
"uuid": "^11.1.0",
"webfontloader": "^1.6.28",
Expand Down
6 changes: 3 additions & 3 deletions apps/web/client/public/onlook-preload-script.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions apps/web/client/src/app/_components/auth-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { GithubLoginButton, GoogleLoginButton } from './login-button';
export function AuthModal() {
const { setIsAuthModalOpen, isAuthModalOpen } = useAuthContext();
const t = useTranslations();

return (
<AlertDialog open={isAuthModalOpen} onOpenChange={setIsAuthModalOpen}>
<AlertDialogContent className="!max-w-sm bg-black">
Expand Down
57 changes: 32 additions & 25 deletions apps/web/client/src/app/_components/hero/create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { useAuthContext } from '@/app/auth/auth-context';
import { DraftImagePill } from '@/app/project/[id]/_components/right-panel/chat-tab/context-pills/draft-image-pill';
import { useCreateManager } from '@/components/store/create';
import { Routes } from '@/utils/constants';
import { MessageContextType, type ImageMessageContext, type User } from '@onlook/models';
import { Button } from '@onlook/ui/button';
import { Card, CardContent, CardHeader } from '@onlook/ui/card';
Expand All @@ -12,11 +13,20 @@ import { Textarea } from '@onlook/ui/textarea';
import { Tooltip, TooltipContent, TooltipPortal, TooltipTrigger } from '@onlook/ui/tooltip';
import { cn } from '@onlook/ui/utils';
import { compressImageInBrowser } from '@onlook/utility';
import localforage from 'localforage';
import { observer } from 'mobx-react-lite';
import { AnimatePresence } from 'motion/react';
import { useRouter } from 'next/navigation';
import { useEffect, useRef, useState } from 'react';

export function Create({
const SAVED_INPUT_KEY = 'create-input';
interface CreateInputContext {
prompt: string;
images: ImageMessageContext[];
timestamp: number;
}

export const Create = observer(({
cardKey,
isCreatingProject,
setIsCreatingProject,
Expand All @@ -26,14 +36,14 @@ export function Create({
isCreatingProject: boolean,
setIsCreatingProject: (isCreatingProject: boolean) => void,
user: User | null,
}) {
}) => {
const createManager = useCreateManager();
const router = useRouter();
const imageRef = useRef<HTMLInputElement>(null);

const { setIsAuthModalOpen } = useAuthContext();
const textareaRef = useRef<HTMLTextAreaElement>(null);
const [inputValue, setInputValue] = useState('');
const [inputValue, setInputValue] = useState<string>('');
const [isDragging, setIsDragging] = useState(false);
const [selectedImages, setSelectedImages] = useState<ImageMessageContext[]>([]);
const [imageTooltipOpen, setImageTooltipOpen] = useState(false);
Expand All @@ -43,27 +53,25 @@ export function Create({

// Restore draft from localStorage if exists
useEffect(() => {
const draft = localStorage.getItem('createProjectDraft');
if (draft && !!user?.id) {
try {
const { prompt, images, timestamp } = JSON.parse(draft);
// Only restore if draft is less than 1 hour old
if (Date.now() - timestamp < 3600000) {
const getDraft = async () => {
const draft = await localforage.getItem<CreateInputContext>(SAVED_INPUT_KEY);
if (draft) {
try {
const { prompt, images } = draft;
// Only restore if draft is less than 1 hour old
setInputValue(prompt);
setSelectedImages(images);

// Clear the draft after restoring
await localforage.removeItem(SAVED_INPUT_KEY);
} catch (error) {
console.error('Error restoring draft:', error);
}
// Clear the draft after restoring
localStorage.removeItem('createProjectDraft');
// Run the submit function
createProject(prompt, images);
} catch (error) {
console.error('Error restoring draft:', error);
localStorage.removeItem('createProjectDraft');
}
}
};
getDraft();
}, []);


const handleSubmit = async () => {
if (isInputInvalid) {
console.warn('Input is too short');
Expand All @@ -76,14 +84,12 @@ export function Create({
if (!user?.id) {
console.error('No user ID found');

// Store the current input and images in localStorage
localStorage.setItem('createProjectDraft', JSON.stringify({
const createInputContext: CreateInputContext = {
prompt,
images,
timestamp: Date.now()
}));
// Store the return URL
localStorage.setItem('returnUrl', window.location.pathname);
};
localforage.setItem(SAVED_INPUT_KEY, createInputContext);
// Open the auth modal
setIsAuthModalOpen(true);
return;
Expand All @@ -95,7 +101,8 @@ export function Create({
if (!project) {
throw new Error('Failed to create project: No project returned');
}
router.push(`/project/${project.id}`);
router.push(`${Routes.PROJECT}/${project.id}`);
await localforage.removeItem(SAVED_INPUT_KEY);
} catch (error) {
console.error('Error creating project:', error);
toast.error('Failed to create project', {
Expand Down Expand Up @@ -402,4 +409,4 @@ export function Create({
</CardContent>
</Card>
);
}
});
24 changes: 8 additions & 16 deletions apps/web/client/src/app/_components/hero/features-hero.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,18 @@
'use client';

import React from 'react';
import { motion } from 'framer-motion';
import { Routes } from '@/utils/constants';
import { Button } from '@onlook/ui/button';
import { motion } from 'framer-motion';
import { useRouter } from 'next/navigation';
import { UnicornBackground } from './unicorn-background';
import { useGitHubStats } from '../top-bar/github';
import { UnicornBackground } from './unicorn-background';

export function FeaturesHero() {
const router = useRouter();
const { formatted: starCount } = useGitHubStats();

const handleStartBuilding = () => {
const heroSection = document.getElementById('hero');
if (heroSection) {
heroSection.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
} else {
router.push('/');
}
router.push(Routes.HOME);
};

return (
Expand Down Expand Up @@ -61,10 +53,10 @@ export function FeaturesHero() {
transition={{ duration: 0.6, delay: 0.3, ease: "easeOut" }}
style={{ willChange: "opacity, filter", transform: "translateZ(0)" }}
>
<Button
variant="secondary"
size="lg"
className="p-6 cursor-pointer hover:bg-foreground-primary hover:text-background-primary transition-colors"
<Button
variant="secondary"
size="lg"
className="p-6 cursor-pointer hover:bg-foreground-primary hover:text-background-primary transition-all duration-300"
onClick={handleStartBuilding}
>
START BUILDING
Expand Down
5 changes: 3 additions & 2 deletions apps/web/client/src/app/_components/hero/import.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
'use client';

import { api } from '@/trpc/react';
import { Routes } from '@/utils/constants';
import { LocalForageKeys, Routes } from '@/utils/constants';
import { Icons } from '@onlook/ui/icons/index';
import localforage from 'localforage';
import { useRouter } from 'next/navigation';
import { useAuthContext } from '../../auth/auth-context';

Expand All @@ -14,7 +15,7 @@ export function Import() {
const handleImportProject = () => {
if (!user?.id) {
// Store the return URL and open auth modal
localStorage.setItem('returnUrl', Routes.IMPORT_PROJECT);
localforage.setItem(LocalForageKeys.RETURN_URL, Routes.IMPORT_PROJECT);
setIsAuthModalOpen(true);
return;
}
Expand Down
5 changes: 3 additions & 2 deletions apps/web/client/src/app/_components/hero/start-blank.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

import { useAuthContext } from '@/app/auth/auth-context';
import { api } from '@/trpc/react';
import { Routes } from '@/utils/constants';
import { LocalForageKeys, Routes } from '@/utils/constants';
import { SandboxTemplates, Templates } from '@onlook/constants';
import type { User } from '@onlook/models';
import { Icons } from '@onlook/ui/icons/index';
import localforage from 'localforage';
import { useRouter } from 'next/navigation';
import { toast } from 'sonner';

Expand All @@ -26,7 +27,7 @@ export function StartBlank({
const handleStartBlankProject = async () => {
if (!user?.id) {
// Store the return URL and open auth modal
localStorage.setItem('returnUrl', window.location.pathname);
localforage.setItem(LocalForageKeys.RETURN_URL, window.location.pathname);
setIsAuthModalOpen(true);
return;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { cn } from '@onlook/ui/utils';

export interface IconProps {
className?: string;
Expand All @@ -8,89 +7,89 @@ export interface IconProps {
export const DesignMockupIcons = {
Add: ({ className, ...props }: IconProps) => (
<svg className={className} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
<g clip-path="url(#clip0_9157_282219)">
<path d="M12 9V14.9998" stroke="currentColor" stroke-width="2" stroke-linecap="square"/>
<path d="M15 12.0002L9 12" stroke="currentColor" stroke-width="2" stroke-linecap="square"/>
<path d="M20 20V4H4V20H20Z" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<g clipPath="url(#clip0_9157_282219)">
<path d="M12 9V14.9998" stroke="currentColor" strokeWidth="2" strokeLinecap="square" />
<path d="M15 12.0002L9 12" stroke="currentColor" strokeWidth="2" strokeLinecap="square" />
<path d="M20 20V4H4V20H20Z" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
</g>
<defs>
<clipPath id="clip0_9157_282219">
<rect width="24" height="24" fill="currentColor"/>
</clipPath>
<clipPath id="clip0_9157_282219">
<rect width="24" height="24" fill="currentColor" />
</clipPath>
</defs>
</svg>
),
CrossL: ({ className, ...props }: IconProps) => (
<svg className={className} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
<path d="M18 6L6 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6 6L18 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M18 6L6 18" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<path d="M6 6L18 18" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
</svg>
),
Explore: ({ className, ...props }: IconProps) => (
<svg className={className} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
<g clip-path="url(#clip0_9157_282181)">
<path d="M21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12Z" stroke="currentColor" stroke-width="2"/>
<path d="M14.8287 9.17188L10.0003 10.0003L9.17188 14.8287L14.0003 14.0003L14.8287 9.17188Z" stroke="currentColor" stroke-width="2"/>
<g clipPath="url(#clip0_9157_282181)">
<path d="M21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12Z" stroke="currentColor" strokeWidth="2" />
<path d="M14.8287 9.17188L10.0003 10.0003L9.17188 14.8287L14.0003 14.0003L14.8287 9.17188Z" stroke="currentColor" strokeWidth="2" />
</g>
<defs>
<clipPath id="clip0_9157_282181">
<rect width="24" height="24" fill="none" />
</clipPath>
<clipPath id="clip0_9157_282181">
<rect width="24" height="24" fill="none" />
</clipPath>
</defs>
</svg>
),
Gear: ({ className, ...props }: IconProps) => (
<svg className={className} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
<g clip-path="url(#clip0_9157_282242)">
<path d="M9.3 5.7L6.375 5.025L5.025 6.375L5.7 9.3L3 11.1V12.9L5.7 14.7L5.025 17.625L6.375 18.975L9.3 18.3L11.1 21H12.9L14.7 18.3L17.625 18.975L18.975 17.625L18.3 14.7L21 12.9V11.1L18.3 9.3L18.975 6.375L17.625 5.025L14.7 5.7L12.9 3H11.1L9.3 5.7Z" stroke="currentColor" stroke-width="2"/>
<path d="M15 12C15 13.6569 13.6569 15 12 15C10.3431 15 9 13.6569 9 12C9 10.3431 10.3431 9 12 9C13.6569 9 15 10.3431 15 12Z" stroke="currentColor" stroke-width="2"/>
<g clipPath="url(#clip0_9157_282242)">
<path d="M9.3 5.7L6.375 5.025L5.025 6.375L5.7 9.3L3 11.1V12.9L5.7 14.7L5.025 17.625L6.375 18.975L9.3 18.3L11.1 21H12.9L14.7 18.3L17.625 18.975L18.975 17.625L18.3 14.7L21 12.9V11.1L18.3 9.3L18.975 6.375L17.625 5.025L14.7 5.7L12.9 3H11.1L9.3 5.7Z" stroke="currentColor" strokeWidth="2" />
<path d="M15 12C15 13.6569 13.6569 15 12 15C10.3431 15 9 13.6569 9 12C9 10.3431 10.3431 9 12 9C13.6569 9 15 10.3431 15 12Z" stroke="currentColor" strokeWidth="2" />
</g>
<defs>
<clipPath id="clip0_9157_282242">
<rect width="24" height="24" fill="none"/>
</clipPath>
<clipPath id="clip0_9157_282242">
<rect width="24" height="24" fill="none" />
</clipPath>
</defs>
</svg>

),
Home: ({ className, ...props }: IconProps) => (
<svg className={className} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
<g clip-path="url(#clip0_9157_282227)">
<path d="M4 9L12 2.5L20 9V20H4V9Z" stroke="currentColor" stroke-width="2"/>
<path d="M9 13H15V20H9V13Z" stroke="currentColor" stroke-width="2"/>
<g clipPath="url(#clip0_9157_282227)">
<path d="M4 9L12 2.5L20 9V20H4V9Z" stroke="currentColor" strokeWidth="2" />
<path d="M9 13H15V20H9V13Z" stroke="currentColor" strokeWidth="2" />
</g>
<defs>
<clipPath id="clip0_9157_282227">
<rect width="24" height="24" fill="currentColor"/>
</clipPath>
<clipPath id="clip0_9157_282227">
<rect width="24" height="24" fill="currentColor" />
</clipPath>
</defs>
</svg>

),
Messages: ({ className, ...props }: IconProps) => (
<svg className={className} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
<g clip-path="url(#clip0_9157_282235)">
<path d="M20.0001 4H4V21.5L8 19H20.0001V4Z" stroke="currentColor" stroke-width="2" stroke-linecap="square"/>
<path d="M8 10.625C8.48325 10.625 8.875 11.0168 8.875 11.5C8.875 11.9832 8.48325 12.375 8 12.375C7.51675 12.375 7.125 11.9832 7.125 11.5C7.125 11.0168 7.51675 10.625 8 10.625ZM12 10.625C12.4832 10.625 12.875 11.0168 12.875 11.5C12.875 11.9832 12.4832 12.375 12 12.375C11.5168 12.375 11.125 11.9832 11.125 11.5C11.125 11.0168 11.5168 10.625 12 10.625ZM16 10.625C16.4832 10.625 16.875 11.0168 16.875 11.5C16.875 11.9832 16.4832 12.375 16 12.375C15.5168 12.375 15.125 11.9832 15.125 11.5C15.125 11.0168 15.5168 10.625 16 10.625Z" fill="currentColor" stroke="currentColor" stroke-width="0.75" stroke-linecap="square"/>
<g clipPath="url(#clip0_9157_282235)">
<path d="M20.0001 4H4V21.5L8 19H20.0001V4Z" stroke="currentColor" strokeWidth="2" strokeLinecap="square" />
<path d="M8 10.625C8.48325 10.625 8.875 11.0168 8.875 11.5C8.875 11.9832 8.48325 12.375 8 12.375C7.51675 12.375 7.125 11.9832 7.125 11.5C7.125 11.0168 7.51675 10.625 8 10.625ZM12 10.625C12.4832 10.625 12.875 11.0168 12.875 11.5C12.875 11.9832 12.4832 12.375 12 12.375C11.5168 12.375 11.125 11.9832 11.125 11.5C11.125 11.0168 11.5168 10.625 12 10.625ZM16 10.625C16.4832 10.625 16.875 11.0168 16.875 11.5C16.875 11.9832 16.4832 12.375 16 12.375C15.5168 12.375 15.125 11.9832 15.125 11.5C15.125 11.0168 15.5168 10.625 16 10.625Z" fill="currentColor" stroke="currentColor" strokeWidth="0.75" strokeLinecap="square" />
</g>
<defs>
<clipPath id="clip0_9157_282235">
<rect width="24" height="24" fill="currentColor" />
</clipPath>
<clipPath id="clip0_9157_282235">
<rect width="24" height="24" fill="currentColor" />
</clipPath>
</defs>
</svg>

),
Notifications: ({ className, ...props }: IconProps) => (
<svg className={className} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
<g clip-path="url(#clip0_9157_282249)">
<path d="M20 17H4V16L5.5 13L5.70037 8.99251C5.86822 5.63561 8.6389 3 12 3C15.3611 3 18.1318 5.63561 18.2996 8.99251L18.5 13L20 16V17Z" stroke="currentColor" stroke-width="2" stroke-linecap="square"/>
<path d="M8.03125 17.5C8.2773 19.4732 9.9605 21 12.0003 21C14.0401 21 15.7233 19.4732 15.9694 17.5" stroke="currentColor" stroke-width="2" stroke-linecap="square"/>
<g clipPath="url(#clip0_9157_282249)">
<path d="M20 17H4V16L5.5 13L5.70037 8.99251C5.86822 5.63561 8.6389 3 12 3C15.3611 3 18.1318 5.63561 18.2996 8.99251L18.5 13L20 16V17Z" stroke="currentColor" strokeWidth="2" strokeLinecap="square" />
<path d="M8.03125 17.5C8.2773 19.4732 9.9605 21 12.0003 21C14.0401 21 15.7233 19.4732 15.9694 17.5" stroke="currentColor" strokeWidth="2" strokeLinecap="square" />
</g>
<defs>
<clipPath id="clip0_9157_282249">
<rect width="24" height="24" fill="none" />
</clipPath>
<clipPath id="clip0_9157_282249">
<rect width="24" height="24" fill="none" />
</clipPath>
</defs>
</svg>

Expand Down
Loading