Skip to content

Commit 8692b62

Browse files
Console Error
In HTML, whitespace text nodes cannot be a child of <tr>
1 parent 9e69082 commit 8692b62

1 file changed

Lines changed: 45 additions & 50 deletions

File tree

  • src/app/(app)/projects/[id]/codespace/files/[[...path]]

src/app/(app)/projects/[id]/codespace/files/[[...path]]/page.tsx

Lines changed: 45 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -12,30 +12,31 @@ import { Skeleton } from '@/components/ui/skeleton';
1212
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
1313
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
1414
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, DialogTrigger, DialogClose } from "@/components/ui/dialog";
15-
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription as UIAlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog";
15+
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription as UIAlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog"; // Renamed to avoid conflict
1616
import { Textarea } from '@/components/ui/textarea';
1717
import { Input } from '@/components/ui/input';
1818
import { Label } from '@/components/ui/label';
1919
import { ArrowLeft, Folder, FileText, FileCode, Loader2, AlertTriangle, Home, ChevronRight, ExternalLink, Image as ImageIcon, Download, Edit, Save, UploadCloud, FolderPlus, FilePlus, Trash2, RefreshCw, FileEdit, Sparkles } from 'lucide-react';
20-
import {
21-
getRepoContentsAction,
22-
getFileContentAction,
23-
fetchProjectAction,
20+
import {
21+
getRepoContentsAction,
22+
getFileContentAction,
23+
fetchProjectAction,
2424
saveFileContentAction,
2525
createGithubFileAction,
2626
createGithubFolderAction,
2727
deleteGithubFileAction,
2828
generateProjectFilesWithAIAction,
2929
type GenerateProjectFilesAIFormState,
30-
editFileWithAIAction, // Added new action
30+
editFileWithAIAction,
31+
type EditFileContentAIOutput,
3132
} from '@/app/(app)/projects/[id]/actions';
3233
import type { GithubRepoContentItem, Project } from '@/types';
3334
import ReactMarkdown from 'react-markdown';
3435
import remarkGfm from 'remark-gfm';
3536
import { Badge } from '@/components/ui/badge';
3637
import NextImage from 'next/image';
37-
import { useForm } from 'react-hook-form';
38-
import { useActionState, startTransition } from 'react'; // Corrected import
38+
import { useForm } from 'react-hook-form';
39+
import { useActionState, startTransition } from 'react';
3940
import { zodResolver } from '@hookform/resolvers/zod';
4041
import * as z from 'zod';
4142

@@ -78,14 +79,14 @@ function FileExplorerContent() {
7879
const { toast } = useToast();
7980

8081
const projectUuid = params.id as string;
81-
82+
8283
const filePathArray = useMemo(() => (params.path || []) as string[], [params.path]);
8384
const currentPath = useMemo(() => filePathArray.join('/'), [filePathArray]);
8485

8586
const [project, setProject] = useState<Project | null>(null);
8687
const [contents, setContents] = useState<GithubRepoContentItem[]>([]);
8788
const [fileData, setFileData] = useState<{ name: string; path: string; content: string; type: 'md' | 'image' | 'html' | 'text' | 'other'; downloadUrl?: string | null, encoding?: string, sha: string } | null>(null);
88-
89+
8990
const [isLoadingProject, setIsLoadingProject] = useState(true);
9091
const [isLoadingPathContent, setIsLoadingPathContent] = useState(true);
9192
const [error, setError] = useState<string | null>(null);
@@ -114,19 +115,20 @@ function FileExplorerContent() {
114115
const newFileForm = useForm<NewFileFormValues>({ resolver: zodResolver(newFileFormSchema), defaultValues: { fileName: '', initialContent: ''}});
115116
const newFolderForm = useForm<NewFolderFormValues>({ resolver: zodResolver(newFolderFormSchema), defaultValues: { folderName: ''}});
116117

118+
117119
const loadContent = useCallback(async (pathToLoad: string) => {
118120
if (!project || !project.githubRepoName || authLoading || isLoadingProject) {
119121
return;
120122
}
121123
setIsLoadingPathContent(true);
122124
setError(null);
123-
setFileData(null);
124-
setIsViewingFile(false);
125+
setFileData(null);
126+
setIsViewingFile(false);
125127

126128
try {
127129
const extension = pathToLoad.includes('.') ? getFileExtension(pathToLoad.split('/').pop()!) : null;
128-
const isFileCandidate = !!extension && !pathToLoad.endsWith('/');
129-
130+
const isFileCandidate = !!extension && !pathToLoad.endsWith('/');
131+
130132
if (isFileCandidate) {
131133
const fetchedFileData = await getFileContentAction(projectUuid, pathToLoad);
132134
if ('content' in fetchedFileData && fetchedFileData.sha) {
@@ -135,7 +137,7 @@ function FileExplorerContent() {
135137
else if (IMAGE_EXTENSIONS.includes(`.${extension}`)) fileDisplayType = 'image';
136138
else if (extension === 'html') fileDisplayType = 'html';
137139
else if (TEXT_EXTENSIONS.includes(`.${extension}`) || fetchedFileData.encoding === 'utf-8' || !fetchedFileData.encoding) fileDisplayType = 'text';
138-
140+
139141
setFileData({
140142
name: pathToLoad.split('/').pop()!,
141143
path: pathToLoad,
@@ -151,7 +153,7 @@ function FileExplorerContent() {
151153
setError(fetchedFileData.error || "Failed to load file content.");
152154
setIsViewingFile(false);
153155
}
154-
} else {
156+
} else {
155157
const dirData = await getRepoContentsAction(projectUuid, pathToLoad);
156158
if (Array.isArray(dirData)) {
157159
setContents(dirData.sort((a, b) => {
@@ -188,7 +190,7 @@ function FileExplorerContent() {
188190
setProject(fetchedProject);
189191
if (!fetchedProject.githubRepoName) {
190192
setError("Project is not linked to a GitHub repository.");
191-
setIsLoadingPathContent(false);
193+
setIsLoadingPathContent(false);
192194
}
193195
}
194196
})
@@ -235,7 +237,7 @@ function FileExplorerContent() {
235237
toast({ title: "File Created", description: `${values.fileName} created successfully.`});
236238
setIsCreateFileModalOpen(false);
237239
newFileForm.reset();
238-
loadContent(currentPath);
240+
loadContent(currentPath);
239241
} else {
240242
toast({ variant: "destructive", title: "Creation Error", description: result.error || "Failed to create file."});
241243
}
@@ -251,7 +253,7 @@ function FileExplorerContent() {
251253
toast({ title: "Folder Created", description: `${values.folderName} created successfully.`});
252254
setIsCreateFolderModalOpen(false);
253255
newFolderForm.reset();
254-
loadContent(currentPath);
256+
loadContent(currentPath);
255257
} else {
256258
toast({ variant: "destructive", title: "Creation Error", description: result.error || "Failed to create folder."});
257259
}
@@ -265,11 +267,11 @@ function FileExplorerContent() {
265267
if (result.success) {
266268
toast({ title: "File Deleted", description: `${contentToDelete.name} deleted successfully.`});
267269
setContentToDelete(null);
268-
if (isViewingFile && fileData?.path === contentToDelete.path) {
270+
if (isViewingFile && fileData?.path === contentToDelete.path) {
269271
const parentPath = currentPath.substring(0, currentPath.lastIndexOf('/'));
270272
router.push(`/projects/${projectUuid}/codespace/files${parentPath ? '/' + parentPath : ''}`);
271273
} else {
272-
loadContent(currentPath);
274+
loadContent(currentPath);
273275
}
274276
} else {
275277
toast({ variant: "destructive", title: "Deletion Error", description: result.error || "Failed to delete file."});
@@ -293,7 +295,7 @@ function FileExplorerContent() {
293295
toast({ title: "AI Scaffold Success", description: aiScaffoldState.message });
294296
setIsAiScaffoldModalOpen(false);
295297
aiScaffoldForm.reset();
296-
loadContent(currentPath);
298+
loadContent(currentPath);
297299
}
298300
if (aiScaffoldState.error) {
299301
toast({ variant: "destructive", title: "AI Scaffold Error", description: aiScaffoldState.error });
@@ -305,8 +307,8 @@ function FileExplorerContent() {
305307
if (!project || !fileData || !editingContent) return;
306308
setIsAiEditingFile(true);
307309
try {
308-
const result = await editFileWithAIAction(project.uuid, editingContent, values.aiEditPrompt);
309-
if (result.newContent) {
310+
const result: EditFileContentAIOutput | { error: string } = await editFileWithAIAction(project.uuid, editingContent, values.aiEditPrompt);
311+
if ('newContent' in result) {
310312
setEditingContent(result.newContent);
311313
toast({ title: "AI Edit Success", description: "Content updated by AI." });
312314
setIsAiEditFileModalOpen(false);
@@ -336,17 +338,17 @@ function FileExplorerContent() {
336338
const isLoadingPage = authLoading || isLoadingProject || isLoadingPathContent;
337339

338340

339-
if (isLoadingPage && !error && !project?.githubRepoName) {
341+
if (isLoadingPage && !error && !project?.githubRepoName) {
340342
return (
341343
<div className="space-y-6">
342344
<div className="flex justify-between items-center">
343-
<Skeleton className="h-9 w-48" />
345+
<Skeleton className="h-9 w-48" />
344346
</div>
345347
<Card>
346348
<CardHeader>
347-
<Skeleton className="h-8 w-1/2 mb-2" />
348-
<Skeleton className="h-5 w-3/4" />
349-
<div className="mt-2 flex space-x-1 pb-1 border-t pt-2">
349+
<Skeleton className="h-8 w-1/2 mb-2" />
350+
<Skeleton className="h-5 w-3/4" />
351+
<div className="mt-2 flex space-x-1 pb-1 border-t pt-2">
350352
<Skeleton className="h-5 w-16" /> <Skeleton className="h-5 w-4" /> <Skeleton className="h-5 w-20" />
351353
</div>
352354
</CardHeader>
@@ -359,7 +361,7 @@ function FileExplorerContent() {
359361
</div>
360362
);
361363
}
362-
364+
363365
return (
364366
<div className="space-y-6">
365367
<div className="flex flex-col sm:flex-row justify-between items-center gap-4">
@@ -506,8 +508,8 @@ function FileExplorerContent() {
506508
</AlertDescription>
507509
</Alert>
508510
) : isViewingFile && fileData ? (
509-
<div className="flex flex-col h-full"> {/* Main container for file view */}
510-
<div className="flex justify-between items-center mb-4 flex-shrink-0"> {/* Header: Title and Buttons */}
511+
<div className="flex flex-col h-full">
512+
<div className="flex justify-between items-center mb-4 flex-shrink-0">
511513
<h3 className="text-xl font-semibold truncate">{fileData.name}</h3>
512514
<div className="flex items-center gap-2">
513515
{canEditCurrentFile && (
@@ -531,9 +533,7 @@ function FileExplorerContent() {
531533
)}
532534
</div>
533535
</div>
534-
535-
{/* Content area that will scroll if needed */}
536-
<div className="overflow-auto flex-grow"> {/* This div handles scrolling for its children */}
536+
<div className="overflow-auto flex-grow">
537537
{fileData.type === 'md' && (
538538
<div className="prose dark:prose-invert max-w-none p-4 border rounded-md bg-muted/30 min-w-max">
539539
<ReactMarkdown remarkPlugins={[remarkGfm]}>{fileData.content}</ReactMarkdown>
@@ -556,11 +556,11 @@ function FileExplorerContent() {
556556
{fileData.type === 'html' && (
557557
<div className="p-4 border rounded-md bg-muted/30">
558558
<h4 className="text-sm font-semibold mb-2">HTML Preview:</h4>
559-
<iframe
560-
srcDoc={fileData.content}
559+
<iframe
560+
srcDoc={fileData.content}
561561
title={`Preview of ${fileData.name}`}
562562
className="w-full h-[50vh] border rounded-md bg-white min-w-[600px]"
563-
sandbox="allow-scripts"
563+
sandbox="allow-scripts"
564564
/>
565565
</div>
566566
)}
@@ -598,7 +598,7 @@ function FileExplorerContent() {
598598
</TableRow>
599599
</TableHeader>
600600
<TableBody>
601-
{filePathArray.length > 0 && (
601+
{filePathArray.length > 0 && (
602602
<TableRow className="hover:bg-muted/30 cursor-pointer">
603603
<TableCell colSpan={4}>
604604
<Link href={
@@ -612,19 +612,15 @@ function FileExplorerContent() {
612612
</TableRow>
613613
)}
614614
{contents.map((item) => (
615-
<TableRow key={item.path}> {/* Changed key to item.path */}
616-
<TableCell>
615+
<TableRow key={item.path}><TableCell>
617616
<Link
618617
href={`/projects/${projectUuid}/codespace/files/${item.path}`}
619618
className="flex items-center hover:underline group"
620619
>
621620
{item.type === 'dir' ? <Folder className="mr-2 h-5 w-5 text-sky-500 group-hover:text-sky-600 flex-shrink-0" /> : <FileText className="mr-2 h-5 w-5 text-gray-500 group-hover:text-gray-700 flex-shrink-0" />}
622621
<span className="truncate">{item.name}</span>
623622
</Link>
624-
</TableCell>
625-
<TableCell className="hidden sm:table-cell capitalize">{item.type === 'dir' ? 'Folder' : 'File'}</TableCell>
626-
<TableCell className="hidden md:table-cell">{item.type === 'file' && item.size > 0 ? `${(item.size / 1024).toFixed(2)} KB` : item.type === 'file' ? '0 KB' : '-'}</TableCell>
627-
<TableCell className="text-right">
623+
</TableCell><TableCell className="hidden sm:table-cell capitalize">{item.type === 'dir' ? 'Folder' : 'File'}</TableCell><TableCell className="hidden md:table-cell">{item.type === 'file' && item.size > 0 ? `${(item.size / 1024).toFixed(2)} KB` : item.type === 'file' ? '0 KB' : '-'}</TableCell><TableCell className="text-right">
628624
<div className="flex items-center justify-end gap-1">
629625
<Button variant="outline" size="sm" asChild>
630626
<Link href={`/projects/${projectUuid}/codespace/files/${item.path}`}>
@@ -638,7 +634,7 @@ function FileExplorerContent() {
638634
<Trash2 className="h-4 w-4" />
639635
</Button>
640636
</AlertDialogTrigger>
641-
{contentToDelete?.path === item.path && (
637+
{contentToDelete?.path === item.path && (
642638
<AlertDialogContent>
643639
<AlertDialogHeader>
644640
<AlertDialogTitle>Delete File: "{contentToDelete.name}"?</AlertDialogTitle>
@@ -654,14 +650,13 @@ function FileExplorerContent() {
654650
)}
655651
</AlertDialog>
656652
)}
657-
{item.type === 'dir' && (
653+
{item.type === 'dir' && (
658654
<Button variant="ghost" size="icon" className="h-8 w-8" title="Delete Folder (Not Implemented)" disabled>
659655
<Trash2 className="h-4 w-4 opacity-50" />
660656
</Button>
661657
)}
662658
</div>
663-
</TableCell>
664-
</TableRow>
659+
</TableCell></TableRow>
665660
))}
666661
</TableBody>
667662
</Table>
@@ -689,7 +684,7 @@ function FileExplorerContent() {
689684
Modify the content of the file. Your changes will be committed to GitHub.
690685
<Dialog open={isAiEditFileModalOpen} onOpenChange={setIsAiEditFileModalOpen}>
691686
<DialogTrigger asChild>
692-
<Button variant="outline" size="sm" disabled={!canEditCurrentFile || isSavingFile}>
687+
<Button variant="outline" size="sm" disabled={!canEditCurrentFile || isSavingFile || isAiEditingFile}>
693688
<Sparkles className="mr-2 h-4 w-4 text-primary" /> Assist with AI
694689
</Button>
695690
</DialogTrigger>

0 commit comments

Comments
 (0)