@@ -33,6 +33,17 @@ interface FileChange {
3333 timestamp : string ;
3434}
3535
36+ function formatMarkdown ( text : string ) : string {
37+ return text
38+ . replace ( / & / g, '&' )
39+ . replace ( / < / g, '<' )
40+ . replace ( / > / g, '>' )
41+ . replace ( / \* \* ( .+ ?) \* \* / g, '<strong class="text-zinc-200">$1</strong>' )
42+ . replace ( / \* ( .+ ?) \* / g, '<em>$1</em>' )
43+ . replace ( / ` ( [ ^ ` ] + ) ` / g, '<code class="bg-zinc-800 text-orange-400 px-1.5 py-0.5 rounded text-xs">$1</code>' )
44+ . replace ( / \n / g, '<br />' ) ;
45+ }
46+
3647export default function JobDetailPage ( ) {
3748 const params = useParams ( ) ;
3849 const jobId = params . id as string ;
@@ -182,78 +193,8 @@ export default function JobDetailPage() {
182193 </ header >
183194
184195 < div className = "flex-1 flex overflow-hidden" >
185- { /* File Changes Panel */ }
186- < div className = "w-1/2 flex flex-col border-r border-zinc-800/50 bg-zinc-900/10" >
187- < div className = "p-4 border-b border-zinc-800/50 flex items-center justify-between bg-zinc-900/20" >
188- < div className = "flex items-center gap-3" >
189- < FileCode className = "w-4 h-4 text-zinc-500" />
190- < span className = "text-sm font-semibold text-zinc-400 uppercase tracking-tight" > Modified files ({ fileChanges . length } )</ span >
191- </ div >
192- </ div >
193-
194- < div className = "flex-1 flex flex-col overflow-hidden relative" >
195- { fileChanges . length === 0 ? (
196- < div className = "flex-1 flex flex-col items-center justify-center p-12 gap-4" >
197- < Loader2 className = "w-6 h-6 text-zinc-800 animate-spin" />
198- < span className = "text-xs text-zinc-700 font-medium uppercase tracking-widest" > Awaiting payload</ span >
199- </ div >
200- ) : (
201- < >
202- < div className = "flex overflow-x-auto bg-zinc-900/30 scrollbar-hide border-b border-zinc-800/50" >
203- { fileChanges . map ( file => (
204- < button
205- key = { file . path }
206- onClick = { ( ) => setSelectedFile ( file . path ) }
207- className = { cn (
208- "px-6 py-3 text-xs font-semibold whitespace-nowrap transition-all relative" ,
209- selectedFile === file . path
210- ? "text-white bg-zinc-900/50"
211- : "text-zinc-600 hover:text-zinc-400"
212- ) }
213- >
214- { file . path . split ( '/' ) . pop ( ) }
215- { selectedFile === file . path && (
216- < div className = "absolute bottom-0 left-0 right-0 h-0.5 bg-orange-600 rounded-full" />
217- ) }
218- </ button >
219- ) ) }
220- </ div >
221-
222- < div className = "flex-1 overflow-auto bg-[#0a0a0a] relative group border-t border-zinc-800/20" >
223- < ReactDiffViewer
224- oldValue = ""
225- newValue = { selectedFileContent }
226- splitView = { true }
227- useDarkTheme = { true }
228- styles = { {
229- variables : {
230- dark : {
231- diffViewerBackground : 'transparent' ,
232- diffViewerColor : '#a1a1aa' ,
233- addedBackground : 'rgba(16, 185, 129, 0.05)' ,
234- addedColor : '#10b981' ,
235- wordAddedBackground : 'rgba(16, 185, 129, 0.1)' ,
236- removedBackground : 'rgba(239, 68, 68, 0.05)' ,
237- removedColor : '#ef4444' ,
238- wordRemovedBackground : 'rgba(239, 68, 68, 0.1)' ,
239- gutterBackground : 'transparent' ,
240- gutterColor : '#3f3f46' ,
241- }
242- } ,
243- line : {
244- fontFamily : 'var(--font-modern), Menlo, monospace' ,
245- fontSize : '13px' ,
246- }
247- } }
248- />
249- </ div >
250- </ >
251- ) }
252- </ div >
253- </ div >
254-
255- { /* Activity Feed Panel */ }
256- < div className = "w-1/2 flex flex-col bg-zinc-900/5" >
196+ { /* Activity Feed Panel - LEFT */ }
197+ < div className = "w-1/2 flex flex-col border-r border-zinc-800/50 bg-zinc-900/5" >
257198 < div className = "p-4 border-b border-zinc-800/50 flex items-center justify-between bg-zinc-900/20" >
258199 < div className = "flex items-center gap-3" >
259200 < Terminal className = "w-4 h-4 text-zinc-500" />
@@ -285,8 +226,8 @@ export default function JobDetailPage() {
285226 "w-8 h-8 rounded-lg flex items-center justify-center border transition-all" ,
286227 log . role === 'user' ? "bg-zinc-900 border-zinc-800 text-zinc-500" : "bg-orange-600/10 border-orange-500/20 text-orange-500"
287228 ) } >
288- { log . type === 'command' ? < Terminal className = "w-4 h-4" /> :
289- log . type === 'file_change' ? < FileCode className = "w-4 h-4" /> :
229+ { log . type === 'command' ? < Terminal className = "w-4 h-4" /> :
230+ log . type === 'file_change' ? < FileCode className = "w-4 h-4" /> :
290231 log . type === 'error' ? < AlertCircle className = "w-4 h-4" /> :
291232 log . role === 'user' ? < User className = "w-4 h-4" /> :
292233 < Cpu className = "w-4 h-4" /> }
@@ -308,7 +249,7 @@ export default function JobDetailPage() {
308249 < span className = "text-sm font-bold text-orange-400 truncate" > { log . metadata ?. file_path } </ span >
309250 </ div >
310251 < p className = "text-xs text-zinc-400 mb-4 line-clamp-2" > { log . content } </ p >
311- < button
252+ < button
312253 onClick = { ( ) => {
313254 if ( log . metadata ?. file_path ) {
314255 setSelectedFile ( log . metadata . file_path ) ;
@@ -346,12 +287,13 @@ export default function JobDetailPage() {
346287 </ div >
347288 </ div >
348289 ) : (
349- < div className = { cn (
350- "text-sm leading-relaxed whitespace-pre-wrap font-medium" ,
351- log . type === 'error' ? "text-red-400" : "text-zinc-400"
352- ) } >
353- { log . content }
354- </ div >
290+ < div
291+ className = { cn (
292+ "text-sm leading-relaxed whitespace-pre-wrap font-medium prose-invert" ,
293+ log . type === 'error' ? "text-red-400" : "text-zinc-400"
294+ ) }
295+ dangerouslySetInnerHTML = { { __html : formatMarkdown ( log . content || '' ) } }
296+ />
355297 ) }
356298 </ div >
357299 </ div >
@@ -361,6 +303,76 @@ export default function JobDetailPage() {
361303 < div ref = { logsEndRef } />
362304 </ div >
363305 </ div >
306+
307+ { /* File Changes Panel - RIGHT */ }
308+ < div className = "w-1/2 flex flex-col bg-zinc-900/10" >
309+ < div className = "p-4 border-b border-zinc-800/50 flex items-center justify-between bg-zinc-900/20" >
310+ < div className = "flex items-center gap-3" >
311+ < FileCode className = "w-4 h-4 text-zinc-500" />
312+ < span className = "text-sm font-semibold text-zinc-400 uppercase tracking-tight" > Modified files ({ fileChanges . length } )</ span >
313+ </ div >
314+ </ div >
315+
316+ < div className = "flex-1 flex flex-col overflow-hidden relative" >
317+ { fileChanges . length === 0 ? (
318+ < div className = "flex-1 flex flex-col items-center justify-center p-12 gap-4" >
319+ < Loader2 className = "w-6 h-6 text-zinc-800 animate-spin" />
320+ < span className = "text-xs text-zinc-700 font-medium uppercase tracking-widest" > Awaiting payload</ span >
321+ </ div >
322+ ) : (
323+ < >
324+ < div className = "flex overflow-x-auto bg-zinc-900/30 scrollbar-hide border-b border-zinc-800/50" >
325+ { fileChanges . map ( file => (
326+ < button
327+ key = { file . path }
328+ onClick = { ( ) => setSelectedFile ( file . path ) }
329+ className = { cn (
330+ "px-6 py-3 text-xs font-semibold whitespace-nowrap transition-all relative" ,
331+ selectedFile === file . path
332+ ? "text-white bg-zinc-900/50"
333+ : "text-zinc-600 hover:text-zinc-400"
334+ ) }
335+ >
336+ { file . path . split ( '/' ) . pop ( ) }
337+ { selectedFile === file . path && (
338+ < div className = "absolute bottom-0 left-0 right-0 h-0.5 bg-orange-600 rounded-full" />
339+ ) }
340+ </ button >
341+ ) ) }
342+ </ div >
343+
344+ < div className = "flex-1 overflow-auto bg-[#0a0a0a] relative group border-t border-zinc-800/20" >
345+ < ReactDiffViewer
346+ oldValue = ""
347+ newValue = { selectedFileContent }
348+ splitView = { false }
349+ useDarkTheme = { true }
350+ styles = { {
351+ variables : {
352+ dark : {
353+ diffViewerBackground : 'transparent' ,
354+ diffViewerColor : '#a1a1aa' ,
355+ addedBackground : 'rgba(16, 185, 129, 0.05)' ,
356+ addedColor : '#10b981' ,
357+ wordAddedBackground : 'rgba(16, 185, 129, 0.1)' ,
358+ removedBackground : 'rgba(239, 68, 68, 0.05)' ,
359+ removedColor : '#ef4444' ,
360+ wordRemovedBackground : 'rgba(239, 68, 68, 0.1)' ,
361+ gutterBackground : 'transparent' ,
362+ gutterColor : '#3f3f46' ,
363+ }
364+ } ,
365+ line : {
366+ fontFamily : 'var(--font-modern), Menlo, monospace' ,
367+ fontSize : '13px' ,
368+ }
369+ } }
370+ />
371+ </ div >
372+ </ >
373+ ) }
374+ </ div >
375+ </ div >
364376 </ div >
365377 </ main >
366378 </ div >
0 commit comments