@@ -6,7 +6,7 @@ import { TreeItem, TreeItemIndex } from 'react-complex-tree';
6
6
import { ProjectContext } from './project-context' ;
7
7
import CodeTab from './tabs/code-tab' ;
8
8
import PreviewTab from './tabs/preview-tab' ;
9
- import ConsoleTab from './tabs/preview -tab' ;
9
+ import ConsoleTab from './tabs/console -tab' ;
10
10
import ResponsiveToolbar from './responsive-toolbar' ;
11
11
import SaveChangesBar from './save-changes-bar' ;
12
12
@@ -19,14 +19,14 @@ export function CodeEngine({
19
19
isProjectReady ?: boolean ;
20
20
projectId ?: string ;
21
21
} ) {
22
- // Initialize state and context
23
- const { curProject , filePath } = useContext ( ProjectContext ) ;
24
- console . log ( 'filepath' , filePath ) ;
25
- console . log ( 'chatid' , chatId ) ;
26
- console . log ( 'projectid' , projectId ) ;
22
+ const { curProject , projectLoading , pollChatProject } =
23
+ useContext ( ProjectContext ) ;
24
+ const [ localProject , setLocalProject ] = useState ( null ) ;
25
+ const [ isLoading , setIsLoading ] = useState ( true ) ;
26
+ const [ filePath , setFilePath ] = useState < string | null > ( null ) ;
27
27
const [ saving , setSaving ] = useState ( false ) ;
28
- const [ preCode , setPrecode ] = useState ( '// some comment ' ) ;
29
- const [ newCode , setCode ] = useState ( '// some comment ' ) ;
28
+ const [ preCode , setPrecode ] = useState ( '// Loading... ' ) ;
29
+ const [ newCode , setCode ] = useState ( '// Loading... ' ) ;
30
30
const [ activeTab , setActiveTab ] = useState < 'preview' | 'code' | 'console' > (
31
31
'code'
32
32
) ;
@@ -35,81 +35,193 @@ export function CodeEngine({
35
35
Record < TreeItemIndex , TreeItem < any > >
36
36
> ( { } ) ;
37
37
38
- // Reference to the editor instance
39
38
const editorRef = useRef ( null ) ;
39
+ const projectPathRef = useRef ( null ) ;
40
+
41
+ // Poll for project if needed using chatId
42
+ useEffect ( ( ) => {
43
+ if ( ! curProject && chatId && ! projectLoading ) {
44
+ const loadProjectFromChat = async ( ) => {
45
+ try {
46
+ setIsLoading ( true ) ;
47
+ const project = await pollChatProject ( chatId ) ;
48
+ if ( project ) {
49
+ setLocalProject ( project ) ;
50
+ }
51
+ } catch ( error ) {
52
+ console . error ( 'Failed to load project from chat:' , error ) ;
53
+ } finally {
54
+ setIsLoading ( false ) ;
55
+ }
56
+ } ;
57
+
58
+ loadProjectFromChat ( ) ;
59
+ } else {
60
+ setIsLoading ( projectLoading ) ;
61
+ }
62
+ } , [ chatId , curProject , projectLoading , pollChatProject ] ) ;
63
+
64
+ // Use either curProject from context or locally polled project
65
+ const activeProject = curProject || localProject ;
66
+
67
+ // Update projectPathRef when project changes
68
+ useEffect ( ( ) => {
69
+ if ( activeProject ?. projectPath ) {
70
+ projectPathRef . current = activeProject . projectPath ;
71
+ }
72
+ } , [ activeProject ] ) ;
40
73
41
- // Fetch file structure function (only for code tab)
42
74
async function fetchFiles ( ) {
43
- if ( ! curProject ?. projectPath ) {
75
+ const projectPath = activeProject ?. projectPath || projectPathRef . current ;
76
+ if ( ! projectPath ) {
44
77
return ;
45
78
}
46
79
47
80
try {
48
81
setIsFileStructureLoading ( true ) ;
49
- const response = await fetch (
50
- `/api/project?path=${ curProject . projectPath } `
51
- ) ;
82
+ const response = await fetch ( `/api/project?path=${ projectPath } ` ) ;
83
+ if ( ! response . ok ) {
84
+ throw new Error ( `Failed to fetch file structure: ${ response . status } ` ) ;
85
+ }
52
86
const data = await response . json ( ) ;
53
- setFileStructureData ( data . res || { } ) ;
87
+ if ( data && data . res ) {
88
+ setFileStructureData ( data . res ) ;
89
+ } else {
90
+ console . warn ( 'Empty or invalid file structure data received' ) ;
91
+ }
54
92
} catch ( error ) {
55
93
console . error ( 'Error fetching file structure:' , error ) ;
56
94
} finally {
57
95
setIsFileStructureLoading ( false ) ;
58
96
}
59
97
}
60
98
61
- // Effect: Fetch file structure when needed for code tab
99
+ // Effect for loading file structure when project is ready
62
100
useEffect ( ( ) => {
101
+ const shouldFetchFiles =
102
+ isProjectReady &&
103
+ ( activeProject ?. projectPath || projectPathRef . current ) &&
104
+ Object . keys ( fileStructureData ) . length === 0 &&
105
+ ! isFileStructureLoading ;
106
+
107
+ if ( shouldFetchFiles ) {
108
+ fetchFiles ( ) ;
109
+ }
110
+ } , [
111
+ isProjectReady ,
112
+ activeProject ,
113
+ isFileStructureLoading ,
114
+ fileStructureData ,
115
+ ] ) ;
116
+
117
+ // Effect for selecting default file once structure is loaded
118
+ useEffect ( ( ) => {
119
+ if (
120
+ ! isFileStructureLoading &&
121
+ Object . keys ( fileStructureData ) . length > 0 &&
122
+ ! filePath
123
+ ) {
124
+ selectDefaultFile ( ) ;
125
+ }
126
+ } , [ isFileStructureLoading , fileStructureData , filePath ] ) ;
127
+
128
+ // Retry mechanism for fetching files if needed
129
+ useEffect ( ( ) => {
130
+ let retryTimeout ;
131
+
63
132
if (
64
- activeTab === 'code' &&
133
+ isProjectReady &&
134
+ activeProject ?. projectPath &&
65
135
Object . keys ( fileStructureData ) . length === 0 &&
66
- curProject ?. projectPath
136
+ ! isFileStructureLoading
67
137
) {
68
- fetchFiles ( ) ;
138
+ retryTimeout = setTimeout ( ( ) => {
139
+ console . log ( 'Retrying file structure fetch...' ) ;
140
+ fetchFiles ( ) ;
141
+ } , 3000 ) ;
69
142
}
70
- } , [ activeTab , fileStructureData , curProject ?. projectPath ] ) ;
71
143
72
- // Reset code to previous state
144
+ return ( ) => {
145
+ if ( retryTimeout ) clearTimeout ( retryTimeout ) ;
146
+ } ;
147
+ } , [
148
+ isProjectReady ,
149
+ activeProject ,
150
+ fileStructureData ,
151
+ isFileStructureLoading ,
152
+ ] ) ;
153
+
154
+ function selectDefaultFile ( ) {
155
+ const defaultFiles = [
156
+ 'src/App.tsx' ,
157
+ 'src/App.js' ,
158
+ 'src/index.tsx' ,
159
+ 'src/index.js' ,
160
+ 'app/page.tsx' ,
161
+ 'pages/index.tsx' ,
162
+ 'index.html' ,
163
+ 'README.md' ,
164
+ ] ;
165
+
166
+ for ( const defaultFile of defaultFiles ) {
167
+ if ( fileStructureData [ `root/${ defaultFile } ` ] ) {
168
+ setFilePath ( defaultFile ) ;
169
+ return ;
170
+ }
171
+ }
172
+
173
+ const firstFile = Object . entries ( fileStructureData ) . find (
174
+ ( [ key , item ] ) =>
175
+ key . startsWith ( 'root/' ) && ! item . isFolder && key !== 'root/'
176
+ ) ;
177
+
178
+ if ( firstFile ) {
179
+ setFilePath ( firstFile [ 0 ] . replace ( 'root/' , '' ) ) ;
180
+ }
181
+ }
182
+
73
183
const handleReset = ( ) => {
74
184
setCode ( preCode ) ;
75
185
editorRef . current ?. setValue ( preCode ) ;
76
186
setSaving ( false ) ;
77
187
} ;
78
188
79
- // Update file content on the server
80
189
const updateCode = async ( value ) => {
81
- if ( ! curProject ) return ;
190
+ const projectPath = activeProject ?. projectPath || projectPathRef . current ;
191
+ if ( ! projectPath || ! filePath ) return ;
82
192
83
193
try {
84
194
const response = await fetch ( '/api/file' , {
85
195
method : 'POST' ,
86
196
credentials : 'include' ,
87
197
headers : { 'Content-Type' : 'application/json' } ,
88
198
body : JSON . stringify ( {
89
- filePath : `${ curProject . projectPath } /${ filePath } ` ,
199
+ filePath : `${ projectPath } /${ filePath } ` ,
90
200
newContent : JSON . stringify ( value ) ,
91
201
} ) ,
92
202
} ) ;
203
+
204
+ if ( ! response . ok ) {
205
+ throw new Error ( `Failed to update file: ${ response . status } ` ) ;
206
+ }
207
+
93
208
await response . json ( ) ;
94
209
} catch ( error ) {
95
- console . error ( error ) ;
210
+ console . error ( 'Error updating file:' , error ) ;
96
211
}
97
212
} ;
98
213
99
- // Save the new code and update the previous state
100
214
const handleSave = ( ) => {
101
215
setSaving ( false ) ;
102
216
setPrecode ( newCode ) ;
103
217
updateCode ( newCode ) ;
104
218
} ;
105
219
106
- // Track unsaved changes
107
220
const updateSavingStatus = ( value ) => {
108
221
setCode ( value ) ;
109
222
setSaving ( true ) ;
110
223
} ;
111
224
112
- // Render appropriate content based on active tab
113
225
const renderTabContent = ( ) => {
114
226
switch ( activeTab ) {
115
227
case 'code' :
@@ -120,6 +232,8 @@ export function CodeEngine({
120
232
newCode = { newCode }
121
233
isFileStructureLoading = { isFileStructureLoading }
122
234
updateSavingStatus = { updateSavingStatus }
235
+ filePath = { filePath }
236
+ setFilePath = { setFilePath }
123
237
/>
124
238
) ;
125
239
case 'preview' :
@@ -131,20 +245,54 @@ export function CodeEngine({
131
245
}
132
246
} ;
133
247
134
- // Render the CodeEngine layout
248
+ useEffect ( ( ) => {
249
+ async function getCode ( ) {
250
+ const projectPath = activeProject ?. projectPath || projectPathRef . current ;
251
+ if ( ! projectPath || ! filePath ) return ;
252
+
253
+ const file_node = fileStructureData [ `root/${ filePath } ` ] ;
254
+ if ( ! file_node ) return ;
255
+
256
+ const isFolder = file_node . isFolder ;
257
+ if ( isFolder ) return ;
258
+
259
+ try {
260
+ const res = await fetch (
261
+ `/api/file?path=${ encodeURIComponent ( `${ projectPath } /${ filePath } ` ) } `
262
+ ) ;
263
+
264
+ if ( ! res . ok ) {
265
+ throw new Error ( `Failed to fetch file content: ${ res . status } ` ) ;
266
+ }
267
+
268
+ const data = await res . json ( ) ;
269
+ setCode ( data . content ) ;
270
+ setPrecode ( data . content ) ;
271
+ } catch ( error ) {
272
+ console . error ( 'Error loading file content:' , error ) ;
273
+ }
274
+ }
275
+
276
+ getCode ( ) ;
277
+ } , [ filePath , activeProject , fileStructureData ] ) ;
278
+
279
+ // Determine if we're truly ready to render
280
+ const showLoader =
281
+ ! isProjectReady ||
282
+ isLoading ||
283
+ ( ! activeProject ?. projectPath && ! projectPathRef . current && ! localProject ) ;
284
+
135
285
return (
136
286
< div className = "rounded-lg border shadow-sm overflow-scroll h-full" >
137
- { /* Header Bar */ }
138
287
< ResponsiveToolbar
139
- isLoading = { ! isProjectReady }
288
+ isLoading = { showLoader }
140
289
activeTab = { activeTab }
141
290
setActiveTab = { setActiveTab }
142
291
/>
143
292
144
- { /* Main Content Area with Loading */ }
145
293
< div className = "relative h-[calc(100vh-48px-4rem)]" >
146
294
< AnimatePresence >
147
- { ! isProjectReady && (
295
+ { showLoader && (
148
296
< motion . div
149
297
key = "loader"
150
298
initial = { { opacity : 0 } }
@@ -154,15 +302,16 @@ export function CodeEngine({
154
302
>
155
303
< Loader className = "w-8 h-8 text-primary animate-spin" />
156
304
< p className = "text-sm text-muted-foreground" >
157
- Initializing project...
305
+ { projectLoading
306
+ ? 'Loading project...'
307
+ : 'Initializing project...' }
158
308
</ p >
159
309
</ motion . div >
160
310
) }
161
311
</ AnimatePresence >
162
312
163
313
< div className = "flex h-full" > { renderTabContent ( ) } </ div >
164
314
165
- { /* Save Changes Bar */ }
166
315
{ saving && < SaveChangesBar onSave = { handleSave } onReset = { handleReset } /> }
167
316
</ div >
168
317
</ div >
0 commit comments