({
filePath: rawFilePath,
workspacePath,
fileName,
language = 'plaintext',
readOnly = false,
showLineNumbers = true,
showMinimap = true,
className = '',
onContentChange,
onSave,
enableLsp = true,
jumpToLine,
jumpToColumn,
jumpToRange,
navigationToken,
isActiveTab = true,
onFileMissingFromDiskChange,
autoSave = false,
autoSaveDelayMs = 800,
})
| 154 | } |
| 155 | |
| 156 | const CodeEditor: React.FC<CodeEditorProps> = ({ |
| 157 | filePath: rawFilePath, |
| 158 | workspacePath, |
| 159 | fileName, |
| 160 | language = 'plaintext', |
| 161 | readOnly = false, |
| 162 | showLineNumbers = true, |
| 163 | showMinimap = true, |
| 164 | className = '', |
| 165 | onContentChange, |
| 166 | onSave, |
| 167 | enableLsp = true, |
| 168 | jumpToLine, |
| 169 | jumpToColumn, |
| 170 | jumpToRange, |
| 171 | navigationToken, |
| 172 | isActiveTab = true, |
| 173 | onFileMissingFromDiskChange, |
| 174 | autoSave = false, |
| 175 | autoSaveDelayMs = 800, |
| 176 | }) => { |
| 177 | // Decode URL-encoded paths before handing them to the editor. |
| 178 | const filePath = useMemo(() => { |
| 179 | try { |
| 180 | if (rawFilePath.includes('%')) { |
| 181 | return decodeURIComponent(rawFilePath); |
| 182 | } |
| 183 | } catch (err) { |
| 184 | log.warn('Failed to decode path', { rawFilePath, error: err }); |
| 185 | } |
| 186 | return rawFilePath; |
| 187 | }, [rawFilePath]); |
| 188 | |
| 189 | const { t } = useI18n('tools'); |
| 190 | |
| 191 | const detectLanguageFromFileName = useCallback((fileName: string): string => { |
| 192 | const detected = getMonacoLanguage(fileName); |
| 193 | return detected !== 'plaintext' ? detected : (language || 'plaintext'); |
| 194 | }, [language]); |
| 195 | |
| 196 | const [content, setContent] = useState(''); |
| 197 | const [hasChanges, setHasChanges] = useState(false); |
| 198 | const [loading, setLoading] = useState(true); |
| 199 | const [showLoadingOverlay, setShowLoadingOverlay] = useState(false); |
| 200 | const loadingOverlayDelayRef = useRef<ReturnType<typeof setTimeout> | null>(null); |
| 201 | const LOADING_OVERLAY_DELAY_MS = 80; |
| 202 | useEffect(() => { |
| 203 | if (loading) { |
| 204 | const t = setTimeout(() => { |
| 205 | loadingOverlayDelayRef.current = null; |
| 206 | setShowLoadingOverlay(true); |
| 207 | }, LOADING_OVERLAY_DELAY_MS); |
| 208 | loadingOverlayDelayRef.current = t; |
| 209 | return () => { |
| 210 | if (loadingOverlayDelayRef.current) { |
| 211 | clearTimeout(loadingOverlayDelayRef.current); |
| 212 | loadingOverlayDelayRef.current = null; |
| 213 | } |
nothing calls this directly
no test coverage detected