@@ -8,14 +8,17 @@ import { ChatPicker } from '@/components/chat-picker'
88import { ChatSettings } from '@/components/chat-settings'
99import { NavBar } from '@/components/navbar'
1010import { Preview } from '@/components/preview'
11+ import Sidebar from '@/components/sidebar'
1112import { useAuth } from '@/lib/auth'
13+ import { Project , createProject , saveMessage , getProjectMessages , generateProjectTitle } from '@/lib/database'
1214import { Message , toAISDKMessages , toMessageImage } from '@/lib/messages'
1315import { LLMModelConfig } from '@/lib/models'
1416import modelsList from '@/lib/models.json'
1517import { FragmentSchema , fragmentSchema as schema } from '@/lib/schema'
1618import { supabase } from '@/lib/supabase'
1719import templates , { TemplateId } from '@/lib/templates'
1820import { ExecutionResult } from '@/lib/types'
21+ import { cn } from '@/lib/utils'
1922import { DeepPartial } from 'ai'
2023import { experimental_useObject as useObject } from 'ai/react'
2124import { usePostHog } from 'posthog-js/react'
@@ -25,9 +28,7 @@ import { useLocalStorage } from 'usehooks-ts'
2528export default function Home ( ) {
2629 const [ chatInput , setChatInput ] = useLocalStorage ( 'chat' , '' )
2730 const [ files , setFiles ] = useState < File [ ] > ( [ ] )
28- const [ selectedTemplate , setSelectedTemplate ] = useState < 'auto' | TemplateId > (
29- 'auto' ,
30- )
31+ const [ selectedTemplate , setSelectedTemplate ] = useState < 'auto' | TemplateId > ( 'auto' )
3132 const [ languageModel , setLanguageModel ] = useLocalStorage < LLMModelConfig > (
3233 'languageModel' ,
3334 {
@@ -46,6 +47,14 @@ export default function Home() {
4647 const [ authView , setAuthView ] = useState < ViewType > ( 'sign_in' )
4748 const [ isRateLimited , setIsRateLimited ] = useState ( false )
4849 const [ errorMessage , setErrorMessage ] = useState ( '' )
50+
51+ // Project management state
52+ const [ currentProject , setCurrentProject ] = useState < Project | null > ( null )
53+ const [ isLoadingProject , setIsLoadingProject ] = useState ( false )
54+
55+ // Sidebar open state
56+ const [ isSidebarOpen , setSidebarOpen ] = useState ( false )
57+
4958 const { session, userTeam } = useAuth ( setAuthDialog , setAuthView )
5059
5160 const filteredModels = modelsList . models . filter ( ( model ) => {
@@ -64,6 +73,12 @@ export default function Home() {
6473 : { [ selectedTemplate ] : templates [ selectedTemplate ] }
6574 const lastMessage = messages [ messages . length - 1 ]
6675
76+ useEffect ( ( ) => {
77+ if ( lastMessage ) {
78+ console . log ( 'Last message:' , lastMessage )
79+ }
80+ } , [ lastMessage ] )
81+
6782 const { object, submit, isLoading, stop, error } = useObject ( {
6883 api : '/api/chat' ,
6984 schema,
@@ -72,13 +87,10 @@ export default function Home() {
7287 if ( error . message . includes ( 'limit' ) ) {
7388 setIsRateLimited ( true )
7489 }
75-
7690 setErrorMessage ( error . message )
7791 } ,
7892 onFinish : async ( { object : fragment , error } ) => {
7993 if ( ! error ) {
80- // send it to /api/sandbox
81- console . log ( 'fragment' , fragment )
8294 setIsPreviewLoading ( true )
8395 posthog . capture ( 'fragment_generated' , {
8496 template : fragment ?. template ,
@@ -95,7 +107,6 @@ export default function Home() {
95107 } )
96108
97109 const result = await response . json ( )
98- console . log ( 'result' , result )
99110 posthog . capture ( 'sandbox_created' , { url : result . url } )
100111
101112 setResult ( result )
@@ -107,6 +118,37 @@ export default function Home() {
107118 } ,
108119 } )
109120
121+ useEffect ( ( ) => {
122+ async function loadProjectMessages ( ) {
123+ if ( ! currentProject ) {
124+ setMessages ( [ ] )
125+ return
126+ }
127+
128+ setIsLoadingProject ( true )
129+ const projectMessages = await getProjectMessages ( currentProject . id )
130+ setMessages ( projectMessages )
131+ setIsLoadingProject ( false )
132+ }
133+
134+ loadProjectMessages ( )
135+ } , [ currentProject ] )
136+
137+ useEffect ( ( ) => {
138+ async function saveMessagesToDb ( ) {
139+ if ( ! currentProject || ! session || messages . length === 0 ) return
140+
141+ const lastMessage = messages [ messages . length - 1 ]
142+ const sequenceNumber = messages . length - 1
143+
144+ await saveMessage ( currentProject . id , lastMessage , sequenceNumber )
145+ }
146+
147+ if ( messages . length > 0 && currentProject && session ) {
148+ saveMessagesToDb ( )
149+ }
150+ } , [ messages , currentProject , session ] )
151+
110152 useEffect ( ( ) => {
111153 if ( object ) {
112154 setFragment ( object )
@@ -149,7 +191,6 @@ export default function Home() {
149191 ...previousMessages [ index ?? previousMessages . length - 1 ] ,
150192 ...message ,
151193 }
152-
153194 return updatedMessages
154195 } )
155196 }
@@ -165,6 +206,15 @@ export default function Home() {
165206 stop ( )
166207 }
167208
209+ // Create new project if none exists
210+ if ( ! currentProject ) {
211+ const title = await generateProjectTitle ( chatInput )
212+ const newProject = await createProject ( title , selectedTemplate === 'auto' ? undefined : selectedTemplate )
213+ if ( newProject ) {
214+ setCurrentProject ( newProject )
215+ }
216+ }
217+
168218 const content : Message [ 'content' ] = [ { type : 'text' , text : chatInput } ]
169219 const images = await toMessageImage ( files )
170220
@@ -253,6 +303,7 @@ export default function Home() {
253303 setResult ( undefined )
254304 setCurrentTab ( 'code' )
255305 setIsPreviewLoading ( false )
306+ setCurrentProject ( null )
256307 }
257308
258309 function setCurrentPreview ( preview : {
@@ -268,6 +319,18 @@ export default function Home() {
268319 setCurrentPreview ( { fragment : undefined , result : undefined } )
269320 }
270321
322+ function handleProjectSelect ( project : Project | null ) {
323+ setCurrentProject ( project )
324+ if ( ! project ) {
325+ handleClearChat ( )
326+ }
327+ }
328+
329+ async function handleNewProject ( ) {
330+ setCurrentProject ( null )
331+ handleClearChat ( )
332+ }
333+
271334 return (
272335 < main className = "flex min-h-screen max-h-screen" >
273336 { supabase && (
@@ -278,7 +341,15 @@ export default function Home() {
278341 supabase = { supabase }
279342 />
280343 ) }
281- < div className = "grid w-full md:grid-cols-2" >
344+
345+ < Sidebar onStateChange = { setSidebarOpen } userName = { userTeam ?. name } userPlan = { userTeam ?. tier } />
346+
347+
348+ { /* Main content with left margin to account for collapsed sidebar */ }
349+ < div className = { cn (
350+ "grid w-full md:grid-cols-2 transition-all duration-300" ,
351+ session ? "ml-16" : "ml-0"
352+ ) } >
282353 < div
283354 className = { `flex flex-col w-full max-h-full max-w-[800px] mx-auto px-4 overflow-auto ${ fragment ? 'col-span-1' : 'col-span-2' } ` }
284355 >
@@ -292,11 +363,19 @@ export default function Home() {
292363 canUndo = { messages . length > 1 && ! isLoading }
293364 onUndo = { handleUndo }
294365 />
295- < Chat
296- messages = { messages }
297- isLoading = { isLoading }
298- setCurrentPreview = { setCurrentPreview }
299- />
366+
367+ { isLoadingProject ? (
368+ < div className = "flex items-center justify-center h-32" >
369+ < div className = "text-muted-foreground" > Loading project...</ div >
370+ </ div >
371+ ) : (
372+ < Chat
373+ messages = { messages }
374+ isLoading = { isLoading }
375+ setCurrentPreview = { setCurrentPreview }
376+ />
377+ ) }
378+
300379 < ChatInput
301380 retry = { retry }
302381 isErrored = { error !== undefined }
0 commit comments