CoolFace
Apppublic

admin08077/Githubgemini

sourceHugging Faceotherupdated 10mo agoView on Hugging Face
0likes
ProjectExplorer-QQGxpQE-.js.map1 linesDownload Raw Back to assets
1{"version":3,"file":"ProjectExplorer-QQGxpQE-.js","sources":["../../components/features/ProjectExplorer.tsx"],"sourcesContent":["import React, { useState, useEffect, useCallback } from 'react';\nimport { useGlobalState } from '../../contexts/GlobalStateContext.tsx';\nimport { useNotification } from '../../contexts/NotificationContext.tsx';\nimport { useOctokit } from '../../contexts/OctokitContext.tsx';\nimport { getRepos, getRepoTree, getFileContent, commitFiles } from '../../services/githubService.ts';\nimport { generateCommitMessageStream } from '../../services/index.ts';\nimport type { Repo, FileNode } from '../../types.ts';\nimport { FolderIcon, DocumentIcon } from '../icons.tsx';\nimport { LoadingSpinner } from '../shared/index.tsx';\nimport * as Diff from 'diff';\n\nconst FileTree: React.FC<{ node: FileNode, onFileSelect: (path: string, name: string) => void, activePath: string | null }> = ({ node, onFileSelect, activePath }) => {\n    const [isOpen, setIsOpen] = useState(true);\n\n    if (node.type === 'file') {\n        const isActive = activePath === node.path;\n        return (\n            <div\n                className={`flex items-center space-x-2 pl-4 py-1 cursor-pointer rounded ${isActive ? 'bg-primary/10 text-primary' : 'hover:bg-gray-100 dark:hover:bg-slate-700'}`}\n                onClick={() => onFileSelect(node.path, node.name)}\n            >\n                <DocumentIcon />\n                <span>{node.name}</span>\n            </div>\n        );\n    }\n\n    return (\n        <div>\n            <div\n                className=\"flex items-center space-x-2 py-1 cursor-pointer hover:bg-gray-100 dark:hover:bg-slate-700 rounded\"\n                onClick={() => setIsOpen(!isOpen)}\n            >\n                <div className={`transform transition-transform ${isOpen ? 'rotate-90' : ''}`}>▶</div>\n                <FolderIcon />\n                <span className=\"font-semibold\">{node.name}</span>\n            </div>\n            {isOpen && node.children && (\n                <div className=\"pl-4 border-l border-border ml-3\">\n                    {node.children.map(child => <FileTree key={child.path} node={child} onFileSelect={onFileSelect} activePath={activePath} />)}\n                </div>\n            )}\n        </div>\n    );\n};\n\nexport const ProjectExplorer: React.FC = () => {\n    const { state, dispatch } = useGlobalState();\n    const { user, githubUser, selectedRepo, projectFiles } = state;\n    const { addNotification } = useNotification();\n    const { octokit, reinitialize } = useOctokit();\n    const [repos, setRepos] = useState<Repo[]>([]);\n    const [isLoading, setIsLoading] = useState<'repos' | 'tree' | 'file' | 'commit' | null>(null);\n    const [error, setError] = useState('');\n    const [activeFile, setActiveFile] = useState<{ path: string; name: string; originalContent: string; editedContent: string} | null>(null);\n    \n    const handleApiError = useCallback((err: any) => {\n        if (err.status === 401) {\n            dispatch({ type: 'SET_GITHUB_USER', payload: null });\n            addNotification('GitHub token is invalid or expired. Please update it in the Connections Hub.', 'error');\n            setError('GitHub authentication failed. Please update your token.');\n        } else {\n            setError(err instanceof Error ? err.message : 'An unexpected error occurred.');\n        }\n    }, [dispatch, addNotification]);\n    \n    useEffect(() => {\n        if (!octokit && githubUser) {\n            reinitialize();\n        }\n    }, [octokit, githubUser, reinitialize]);\n\n    useEffect(() => {\n        const loadRepos = async () => {\n            if (user && githubUser && octokit) {\n                setIsLoading('repos');\n                setError('');\n                try {\n                    const userRepos = await getRepos(octokit);\n                    setRepos(userRepos);\n                } catch (err) {\n                    handleApiError(err);\n                } finally {\n                    setIsLoading(null);\n                }\n            } else {\n                setRepos([]);\n            }\n        };\n        loadRepos();\n    }, [user, githubUser, octokit, handleApiError]);\n\n    const loadTree = useCallback(async (repoToLoad: { owner: { login: string }, name: string, full_name: string }) => {\n        if (user && githubUser && octokit) {\n            setIsLoading('tree');\n            setError('');\n            setActiveFile(null);\n            try {\n                const tree = await getRepoTree(octokit, repoToLoad.owner.login, repoToLoad.name);\n                dispatch({ type: 'LOAD_PROJECT_FILES', payload: tree });\n            } catch (err) {\n                handleApiError(err);\n            } finally {\n                setIsLoading(null);\n            }\n        }\n    }, [user, githubUser, octokit, dispatch, handleApiError]);\n\n    // Re-fetches the tree if a repo is selected from a previous session\n    useEffect(() => {\n        if (selectedRepo && octokit && (!projectFiles || projectFiles.name !== selectedRepo.repo)) {\n             loadTree({\n                name: selectedRepo.repo,\n                full_name: selectedRepo.full_name,\n                owner: { login: selectedRepo.owner }\n            });\n        }\n    }, [selectedRepo, projectFiles, octokit, loadTree]);\n\n    const handleFileSelect = async (path: string, name: string) => {\n        if (!selectedRepo || !octokit) return;\n        setIsLoading('file');\n        try {\n            const content = await getFileContent(octokit, selectedRepo.owner, selectedRepo.repo, path);\n            setActiveFile({ path, name, originalContent: content, editedContent: content });\n        } catch (err) {\n            handleApiError(err);\n        } finally {\n            setIsLoading(null);\n        }\n    };\n\n    const handleCommit = async () => {\n        if (!activeFile || !selectedRepo || !octokit || activeFile.originalContent === activeFile.editedContent) return;\n\n        setIsLoading('commit');\n        setError('');\n        try {\n            const diff = Diff.createPatch(activeFile.path, activeFile.originalContent, activeFile.editedContent);\n            \n            const stream = generateCommitMessageStream(diff);\n            let commitMessage = '';\n            for await (const chunk of stream) { commitMessage += chunk; }\n            \n            const finalMessage = window.prompt(\"Confirm or edit commit message:\", commitMessage);\n            if (!finalMessage) {\n                setIsLoading(null);\n                return;\n            }\n\n            await commitFiles(\n                octokit,\n                selectedRepo.owner,\n                selectedRepo.repo,\n                [{ path: activeFile.path, content: activeFile.editedContent }],\n                finalMessage\n            );\n            \n            addNotification(`Successfully committed to ${selectedRepo.repo}`, 'success');\n            setActiveFile(prev => prev ? { ...prev, originalContent: prev.editedContent } : null);\n\n        } catch (err) {\n            handleApiError(err);\n        } finally {\n            setIsLoading(null);\n        }\n    };\n    \n    if (!user) {\n        return (\n            <div className=\"h-full flex flex-col items-center justify-center text-center text-text-secondary p-4\">\n                <FolderIcon />\n                <h2 className=\"text-lg font-semibold mt-2\">Please Sign In</h2>\n                <p>Sign in to explore your repositories.</p>\n            </div>\n        );\n    }\n    \n    if (!githubUser) {\n         return (\n            <div className=\"h-full flex flex-col items-center justify-center text-center text-text-secondary p-4\">\n                <FolderIcon />\n                <h2 className=\"text-lg font-semibold mt-2\">Connect to GitHub</h2>\n                <p>Please go to the \"Connections\" tab and provide a Personal Access Token to explore your repositories.</p>\n            </div>\n        );\n    }\n\n    const hasChanges = activeFile ? activeFile.originalContent !== activeFile.editedContent : false;\n\n    return (\n        <div className=\"h-full flex flex-col text-text-primary\">\n            <header className=\"p-4 border-b border-border flex-shrink-0\">\n                <h1 className=\"text-xl font-bold flex items-center\"><FolderIcon /><span className=\"ml-3\">Project Explorer</span></h1>\n                <div className=\"mt-2\">\n                    <select\n                        value={selectedRepo?.full_name ?? ''}\n                        onChange={e => {\n                            const repo = repos.find(r => r.full_name === e.target.value);\n                            if (repo) {\n                                dispatch({ type: 'SET_SELECTED_REPO', payload: { owner: repo.owner.login, repo: repo.name, full_name: repo.full_name, name: repo.name } });\n                            }\n                        }}\n                        className=\"w-full p-2 bg-surface border border-border rounded-md text-sm\"\n                    >\n                        <option value=\"\" disabled>{isLoading === 'repos' ? 'Loading...' : 'Select a repository'}</option>\n                        {repos.map(r => <option key={r.id} value={r.full_name}>{r.full_name}</option>)}\n                    </select>\n                </div>\n                {error && <p className=\"text-red-500 text-xs mt-2\">{error}</p>}\n            </header>\n            <div className=\"flex-grow flex min-h-0\">\n                <aside className=\"w-1/3 bg-background border-r border-border p-4 overflow-y-auto\">\n                    {isLoading === 'tree' && <div className=\"flex justify-center\"><LoadingSpinner /></div>}\n                    {projectFiles && <FileTree node={projectFiles} onFileSelect={handleFileSelect} activePath={activeFile?.path ?? null} />}\n                </aside>\n                <main className=\"flex-1 bg-surface flex flex-col\">\n                     <div className=\"flex justify-between items-center p-2 border-b border-border bg-gray-50 dark:bg-slate-800\">\n                        <span className=\"text-sm font-semibold\">{activeFile?.name || 'No file selected'}</span>\n                        <button onClick={handleCommit} disabled={!hasChanges || isLoading === 'commit'} className=\"btn-primary px-4 py-1 text-sm flex items-center justify-center min-w-[100px]\">\n                           {isLoading === 'commit' ? <LoadingSpinner/> : 'Commit'}\n                        </button>\n                     </div>\n                     {isLoading === 'file' ? <div className=\"flex items-center justify-center h-full\"><LoadingSpinner /></div> :\n                        <textarea \n                            value={activeFile?.editedContent ?? 'Select a file to view its content.'}\n                            onChange={e => setActiveFile(prev => prev ? { ...prev, editedContent: e.target.value } : null)}\n                            disabled={!activeFile}\n                            className=\"w-full h-full p-4 text-sm font-mono bg-transparent resize-none focus:outline-none\"\n                        />\n                    }\n                </main>\n            </div>\n        </div>\n    );\n};"],"names":["FileTree","node","onFileSelect","activePath","isOpen","setIsOpen","useState","isActive","jsxs","jsx","DocumentIcon","FolderIcon","child","ProjectExplorer","state","dispatch","useGlobalState","user","githubUser","selectedRepo","projectFiles","addNotification","useNotification","octokit","reinitialize","useOctokit","repos","setRepos","isLoading","setIsLoading","error","setError","activeFile","setActiveFile","handleApiError","useCallback","err","useEffect","userRepos","getRepos","loadTree","repoToLoad","tree","getRepoTree","handleFileSelect","path","name","content","getFileContent","handleCommit","diff","Diff.createPatch","stream","generateCommitMessageStream","commitMessage","chunk","finalMessage","commitFiles","prev","hasChanges","e","repo","r","LoadingSpinner"],"mappings":"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"}