@@ -3,6 +3,8 @@ import { FragmentPreview } from './fragment-preview'
33import { FragmentTerminal } from './fragment-terminal'
44import { FragmentInterpreter } from './fragment-interpreter'
55import { CodeEditor } from './code-editor'
6+ import { SandboxFileTree } from './sandbox-file-tree'
7+ import { IDE } from './ide'
68import { Button } from '@/components/ui/button'
79import { Tabs , TabsContent , TabsList , TabsTrigger } from '@/components/ui/tabs'
810import {
@@ -14,8 +16,8 @@ import {
1416import { FragmentSchema } from '@/lib/schema'
1517import { ExecutionResult } from '@/lib/types'
1618import { DeepPartial } from 'ai'
17- import { ChevronsRight , LoaderCircle , Terminal , Code , FileCode } from 'lucide-react'
18- import { Dispatch , SetStateAction } from 'react'
19+ import { ChevronsRight , LoaderCircle , Terminal , Code , FileCode , FolderTree , Folder } from 'lucide-react'
20+ import { Dispatch , SetStateAction , useState } from 'react'
1921
2022export function Preview ( {
2123 teamID,
@@ -29,37 +31,77 @@ export function Preview({
2931 onClose,
3032 code,
3133 selectedFile,
34+ onSelectFile,
3235 onSave,
3336 executeCode,
3437} : {
3538 teamID : string | undefined
3639 accessToken : string | undefined
37- selectedTab : 'code' | 'fragment' | 'terminal' | 'interpreter' | 'editor'
38- onSelectedTabChange : Dispatch < SetStateAction < 'code' | 'fragment' | 'terminal' | 'interpreter' | 'editor' > >
40+ selectedTab : 'code' | 'fragment' | 'terminal' | 'interpreter' | 'editor' | 'files' | 'ide'
41+ onSelectedTabChange : Dispatch < SetStateAction < 'code' | 'fragment' | 'terminal' | 'interpreter' | 'editor' | 'files' | 'ide' > >
3942 isChatLoading : boolean
4043 isPreviewLoading : boolean
4144 fragment ?: DeepPartial < FragmentSchema >
4245 result ?: ExecutionResult
4346 onClose : ( ) => void
4447 code ?: string
4548 selectedFile ?: { path : string ; content : string } | null
49+ onSelectFile ?: ( file : { path : string ; content : string } ) => void
4650 onSave ?: ( path : string , content : string ) => Promise < void >
4751 executeCode ?: ( code : string ) => Promise < any >
4852} ) {
49- if ( ! fragment ) {
50- return null
53+ const [ isRefreshingFiles , setIsRefreshingFiles ] = useState ( false )
54+
55+ async function handleSelectSandboxFile ( path : string ) {
56+ if ( ! result ?. sbxId ) return
57+
58+ try {
59+ const response = await fetch ( `/api/sandbox/${ result . sbxId } /files/content?path=${ encodeURIComponent ( path ) } ` )
60+ const data = await response . json ( )
61+
62+ if ( response . ok && data . content !== undefined ) {
63+ // Update the selected file in the parent component
64+ if ( onSelectFile ) {
65+ onSelectFile ( { path : data . path , content : data . content } )
66+ }
67+ // Switch to editor tab
68+ onSelectedTabChange ( 'editor' )
69+ }
70+ } catch ( error ) {
71+ console . error ( 'Error loading sandbox file:' , error )
72+ }
73+ }
74+
75+ async function handleRefreshFiles ( ) {
76+ if ( ! result ?. sbxId ) return
77+
78+ setIsRefreshingFiles ( true )
79+ try {
80+ const response = await fetch ( `/api/sandbox/${ result . sbxId } /files` )
81+ const data = await response . json ( )
82+
83+ if ( response . ok && data . files ) {
84+ // Files refreshed - this would need to update result.files in parent
85+ console . log ( 'Files refreshed:' , data . files )
86+ }
87+ } catch ( error ) {
88+ console . error ( 'Error refreshing files:' , error )
89+ } finally {
90+ setIsRefreshingFiles ( false )
91+ }
5192 }
5293
5394 return (
5495 < div className = "absolute md:relative z-10 top-0 left-0 shadow-2xl md:rounded-tl-3xl md:rounded-bl-3xl md:border-l md:border-y bg-popover h-full w-full overflow-auto" >
5596 < Tabs
5697 value = { selectedTab }
57- onValueChange = { ( value ) =>
58- onSelectedTabChange ( value as 'code' | 'fragment' | 'terminal' | 'interpreter' | 'editor' )
59- }
98+ onValueChange = { ( value ) => {
99+ console . log ( 'Tab changed to:' , value )
100+ onSelectedTabChange ( value as 'code' | 'fragment' | 'terminal' | 'interpreter' | 'editor' | 'files' | 'ide' )
101+ } }
60102 className = "h-full flex flex-col items-start justify-start"
61103 >
62- < div className = "w-full p-2 grid grid-cols-3 items-center border-b" >
104+ < div className = "w-full p-2 grid grid-cols-3 items-center border-b relative z-10 " >
63105 < TooltipProvider >
64106 < Tooltip delayDuration = { 0 } >
65107 < TooltipTrigger asChild >
@@ -75,8 +117,8 @@ export function Preview({
75117 < TooltipContent > Close sidebar</ TooltipContent >
76118 </ Tooltip >
77119 </ TooltipProvider >
78- < div className = "flex justify-center" >
79- < TabsList className = "px-1 py-0 border h-8" >
120+ < div className = "flex justify-center relative z-20 " >
121+ < TabsList className = "px-1 py-0 border h-8 relative z-30 " >
80122 < TabsTrigger
81123 className = "font-normal text-xs py-1 px-2 gap-1 flex items-center"
82124 value = "code"
@@ -120,28 +162,41 @@ export function Preview({
120162 Interpreter
121163 </ TabsTrigger >
122164 < TabsTrigger
123- disabled = { ! selectedFile }
124165 className = "font-normal text-xs py-1 px-2 gap-1 flex items-center"
125166 value = "editor"
126167 >
127168 < FileCode className = "h-3 w-3" />
128169 Editor
129170 </ TabsTrigger >
171+ < TabsTrigger
172+ disabled = { ! result || ! result . files || result . files . length === 0 }
173+ className = "font-normal text-xs py-1 px-2 gap-1 flex items-center"
174+ value = "files"
175+ >
176+ < FolderTree className = "h-3 w-3" />
177+ Files
178+ </ TabsTrigger >
179+ < TabsTrigger
180+ className = "font-normal text-xs py-1 px-2 gap-1 flex items-center"
181+ value = "ide"
182+ >
183+ < Folder className = "h-3 w-3" />
184+ IDE
185+ </ TabsTrigger >
130186 </ TabsList >
131187 </ div >
132188 < div className = "flex items-center justify-end gap-2" >
133189 { /* Add any additional buttons here */ }
134190 </ div >
135191 </ div >
136- { fragment && (
137- < div className = "overflow-y-auto w-full h-full" >
192+ < div className = "overflow-y-auto w-full h-full" >
138193 < TabsContent value = "code" className = "h-full" >
139- { fragment . code ? (
194+ { fragment ? .code ? (
140195 < FragmentCode
141196 files = { [
142197 {
143- name : fragment . file_path || 'code.txt' ,
144- content : fragment . code || '' ,
198+ name : fragment ? .file_path || 'code.txt' ,
199+ content : fragment ? .code || '' ,
145200 } ,
146201 ] }
147202 />
@@ -155,7 +210,7 @@ export function Preview({
155210 { result ? (
156211 < FragmentPreview
157212 result = { result as ExecutionResult }
158- code = { code || fragment . code || '' }
213+ code = { code || fragment ? .code || '' }
159214 executeCode = { executeCode || ( async ( ) => { } ) }
160215 />
161216 ) : (
@@ -181,7 +236,7 @@ export function Preview({
181236 { result && result . template === 'code-interpreter-v1' ? (
182237 < FragmentInterpreter
183238 result = { result }
184- code = { code || fragment . code || '' }
239+ code = { code || fragment ? .code || '' }
185240 executeCode = { executeCode || ( async ( ) => { } ) }
186241 />
187242 ) : result ? (
@@ -211,8 +266,26 @@ export function Preview({
211266 </ div >
212267 ) }
213268 </ TabsContent >
269+ < TabsContent value = "files" className = "h-full" >
270+ { result && result . files && result . files . length > 0 ? (
271+ < SandboxFileTree
272+ files = { result . files }
273+ onSelectFile = { handleSelectSandboxFile }
274+ onRefresh = { handleRefreshFiles }
275+ isLoading = { isRefreshingFiles }
276+ />
277+ ) : (
278+ < div className = "flex items-center justify-center h-full text-muted-foreground" >
279+ No sandbox files available
280+ </ div >
281+ ) }
282+ </ TabsContent >
283+ < TabsContent value = "ide" className = "h-full m-0 p-0" >
284+ < div className = "h-full w-full" >
285+ < IDE />
286+ </ div >
287+ </ TabsContent >
214288 </ div >
215- ) }
216289 </ Tabs >
217290 </ div >
218291 )
0 commit comments