FileContextMenu.jsx
1 import React, { useState, useEffect, useRef } from 'react'; 2 import { BLACK, BLUE, WHITE } from '../constants/colors'; 3 import { getAllRepoNamesAndTypes } from '../services/electronService'; 4 5 const FileContextMenu = ({ x, y, file, repoName, onClose, onProcessFile }) => { 6 const [showProcessMenu, setShowProcessMenu] = useState(false); 7 const [ideaRepos, setIdeaRepos] = useState([]); 8 const menuRef = useRef(null); 9 const processMenuRef = useRef(null); 10 11 useEffect(() => { 12 const fetchIdeaRepos = async () => { 13 try { 14 const repos = await getAllRepoNamesAndTypes(); 15 console.log('All repos:', repos); // Debug log 16 const filteredRepos = repos.filter(repo => repo.name !== repoName); 17 console.log('Filtered repos:', filteredRepos); // Debug log 18 setIdeaRepos(filteredRepos); 19 } catch (error) { 20 console.error('Error fetching repos:', error); 21 } 22 }; 23 fetchIdeaRepos(); 24 }, [repoName]); 25 26 useEffect(() => { 27 const handleClickOutside = (event) => { 28 if (menuRef.current && !menuRef.current.contains(event.target)) { 29 onClose(); 30 } 31 }; 32 33 document.addEventListener('mousedown', handleClickOutside); 34 return () => { 35 document.removeEventListener('mousedown', handleClickOutside); 36 }; 37 }, [onClose]); 38 39 useEffect(() => { 40 const positionProcessMenu = () => { 41 if (showProcessMenu && processMenuRef.current && menuRef.current) { 42 const menu = processMenuRef.current; 43 const parentRect = menuRef.current.getBoundingClientRect(); 44 const viewportHeight = window.innerHeight; 45 const viewportWidth = window.innerWidth; 46 const menuHeight = menu.offsetHeight; 47 const menuWidth = menu.offsetWidth; 48 49 let topPosition = parentRect.top; 50 let leftPosition = parentRect.right + 5; // Add a small gap 51 52 if (topPosition + menuHeight > viewportHeight) { 53 topPosition = Math.max(0, viewportHeight - menuHeight); 54 } 55 56 if (leftPosition + menuWidth > viewportWidth) { 57 leftPosition = parentRect.left - menuWidth - 5; // Position to the left if not enough space on the right 58 } 59 60 menu.style.top = `${topPosition}px`; 61 menu.style.left = `${leftPosition}px`; 62 } 63 }; 64 65 positionProcessMenu(); 66 67 // Add window resize listener 68 window.addEventListener('resize', positionProcessMenu); 69 70 // Cleanup 71 return () => { 72 window.removeEventListener('resize', positionProcessMenu); 73 }; 74 }, [showProcessMenu]); 75 76 const handleProcess = async (processorRepo) => { 77 console.log(`Processing file: ${file} in repo: ${repoName} with processor: ${processorRepo}`); 78 try { 79 const result = await window.electron.fileSystem.processFile(repoName, file, processorRepo); 80 if (result && result.success) { 81 console.log('File processed successfully'); 82 alert(`File processed successfully! ${result.message}`); 83 if (onProcessFile) { 84 onProcessFile(); 85 } 86 } else { 87 const errorMessage = result && result.error ? result.error : 'Unknown error occurred'; 88 console.error('Error processing file:', errorMessage); 89 alert(`Error processing file: ${errorMessage}`); 90 } 91 } catch (error) { 92 console.error('Error in handleProcess:', error); 93 alert(`Error processing file: ${error.message || 'Unknown error occurred'}`); 94 } 95 onClose(); 96 }; 97 98 return ( 99 <div 100 ref={menuRef} 101 style={{ 102 position: 'fixed', 103 top: y, 104 left: x, 105 backgroundColor: BLACK, 106 color: WHITE, 107 borderRadius: '4px', 108 overflow: 'visible', 109 zIndex: 1000, 110 border: `1px solid ${BLUE}`, 111 }} 112 onClick={(e) => e.stopPropagation()} 113 > 114 <ul style={{ listStyle: 'none', padding: 0, margin: 0, fontSize: '0.9em' }}> 115 <li style={{ padding: '6px 10px', borderBottom: `1px solid ${BLUE}` }}> 116 File: {file} 117 </li> 118 <li 119 onMouseEnter={(e) => { 120 e.target.style.backgroundColor = BLUE; 121 setShowProcessMenu(true); 122 }} 123 style={{ 124 padding: '6px 10px', 125 cursor: 'pointer', 126 transition: 'background-color 0.2s ease', 127 position: 'relative', 128 }} 129 > 130 Process with ▶ 131 {showProcessMenu && ( 132 <ul 133 ref={processMenuRef} 134 onMouseEnter={() => setShowProcessMenu(true)} 135 onMouseLeave={() => setShowProcessMenu(false)} 136 style={{ 137 position: 'fixed', 138 backgroundColor: BLACK, 139 border: `1px solid ${BLUE}`, 140 borderRadius: '4px', 141 padding: '4px 0', 142 margin: 0, 143 listStyle: 'none', 144 zIndex: 1001, 145 minWidth: '150px', 146 maxHeight: '300px', 147 overflowY: 'auto', 148 scrollbarWidth: 'thin', 149 scrollbarColor: `${BLUE} ${BLACK}`, 150 boxShadow: '0 2px 10px rgba(0, 0, 255, 0.3)', 151 }} 152 > 153 {ideaRepos.length > 0 ? ( 154 ideaRepos.map((repo) => ( 155 <li 156 key={repo.name} 157 onClick={(e) => { 158 e.stopPropagation(); 159 handleProcess(repo.name); 160 }} 161 style={{ 162 padding: '8px 12px', 163 cursor: 'pointer', 164 transition: 'background-color 0.2s ease', 165 whiteSpace: 'nowrap', 166 }} 167 onMouseEnter={(e) => e.target.style.backgroundColor = BLUE} 168 onMouseLeave={(e) => e.target.style.backgroundColor = 'transparent'} 169 > 170 {repo.name} 171 </li> 172 )) 173 ) : ( 174 <li style={{ padding: '8px 12px', color: 'gray' }}> 175 {ideaRepos.length === 0 ? 'Loading repos...' : 'No repos available'} 176 </li> 177 )} 178 </ul> 179 )} 180 </li> 181 </ul> 182 </div> 183 ); 184 }; 185 186 export default FileContextMenu;