import { basename } from 'path' import { IS_PLATFORM } from 'common' import { Circle, Code, Minus, Plus, Wind } from 'lucide-react' import Link from 'next/link' import { useEffect, useMemo, useState } from 'react' import { Card, CardContent, CardHeader, CardTitle, cn, Skeleton } from 'ui' import { DiffEditor } from '@/components/ui/DiffEditor' import type { EdgeFunctionBodyData } from '@/data/edge-functions/edge-function-body-query' import type { EdgeFunctionsDiffResult, FileInfo, FileStatus, } from '@/hooks/branches/useEdgeFunctionsDiff' import { EMPTY_ARR } from '@/lib/void' const EMPTY_FUNCTION_BODY: EdgeFunctionBodyData = { files: EMPTY_ARR, } interface EdgeFunctionsDiffPanelProps { diffResults: EdgeFunctionsDiffResult currentBranchRef?: string } interface FunctionDiffProps { functionSlug: string currentBody: EdgeFunctionBodyData mainBody: EdgeFunctionBodyData currentBranchRef?: string fileInfos: FileInfo[] } // Helper to canonicalize file identifiers to prevent mismatch due to differing root paths const fileKey = (fullPath: string) => basename(fullPath) // Helper to get the status color for file indicators const getStatusColor = (status: FileStatus): string => { switch (status) { case 'added': return 'text-brand' case 'removed': return 'text-destructive' case 'modified': return 'text-warning' case 'unchanged': return 'text-muted' default: return 'text-muted' } } // Helper to get the status icon for file indicators const getStatusIcon = (status: FileStatus) => { switch (status) { case 'added': return Plus case 'removed': return Minus case 'modified': return Circle case 'unchanged': return Circle default: return Circle } } const FunctionDiff = ({ functionSlug, currentBody, mainBody, currentBranchRef, fileInfos, }: FunctionDiffProps) => { // Get all file keys from fileInfos const allFileKeys = useMemo(() => fileInfos.map((info) => info.key), [fileInfos]) const [activeFileKey, setActiveFileKey] = useState(() => allFileKeys[0]) // Keep active tab in sync when allFileKeys changes (e.g. data fetch completes) useEffect(() => { if (!activeFileKey || !allFileKeys.includes(activeFileKey)) { setActiveFileKey(allFileKeys[0]) } }, [allFileKeys, activeFileKey]) const currentFile = currentBody.files.find( (f: EdgeFunctionBodyData['files'][number]) => fileKey(f.name) === activeFileKey ) const mainFile = mainBody.files.find( (f: EdgeFunctionBodyData['files'][number]) => fileKey(f.name) === activeFileKey ) const language = useMemo(() => { if (!activeFileKey) return 'plaintext' if (activeFileKey.endsWith('.ts') || activeFileKey.endsWith('.tsx')) { return 'typescript' } if (activeFileKey.endsWith('.js') || activeFileKey.endsWith('.jsx')) { return 'javascript' } if (activeFileKey.endsWith('.json')) return 'json' if (activeFileKey.endsWith('.sql')) return 'sql' return 'plaintext' }, [activeFileKey]) if (allFileKeys.length === 0) return null return ( {functionSlug}
    {fileInfos.map((fileInfo) => { const Icon = getStatusIcon(fileInfo.status) return (
  • ) })}
) } export const EdgeFunctionsDiffPanel = ({ diffResults, currentBranchRef, }: EdgeFunctionsDiffPanelProps) => { if (diffResults.isLoading) { return } const noChanges = diffResults.addedSlugs.length === 0 && diffResults.modifiedSlugs.length === 0 if (noChanges) { return (

No changes detected between branches

Any changes to your edge functions will be shown here for review

) } return (
{diffResults.addedSlugs.length > 0 && (
{diffResults.addedSlugs.map((slug) => ( ))}
)} {/* TODO: Removing functions is not supported yet */} {/* {diffResults.removedSlugs.length > 0 && (
{diffResults.removedSlugs.map((slug) => ( ))}
)} */} {diffResults.modifiedSlugs.length > 0 && (
{diffResults.modifiedSlugs.map((slug) => ( ))}
)}
) }