fix(code-editor): stabilize document loading deps and replace blocking save alerts

The code editor document hook was reloading more often than necessary because
`useEffect` depended on the full `file` object (and `projectPath`, which was not
used in the effect body). Save flow also emitted verbose production debug logs
and used a blocking `alert()` for errors.

Changes:
- Refactored `useCodeEditorDocument` to derive stable file primitives:
  `fileProjectName`, `filePath`, `fileName`, `fileDiffNewString`,
  `fileDiffOldString`.
- Updated `loadFileContent` to use those stable fields while preserving existing
  diff-first loading logic (`file.diffInfo`, `new_string`, `old_string`).
- Replaced the effect dependency array with only stable primitive values used by
  the effect and removed `projectPath` from dependencies.
- Removed debug `console.log` calls in `handleSave`:
  - "Saving file:"
  - "Save response:"
  - "Save successful:"
- Kept error logging via `console.error` for failure diagnostics.
- Replaced blocking `alert()` on save failure with non-blocking UI feedback:
  - Added local `saveError` state in the hook.
  - Set `saveError` from `getErrorMessage(error)` in the save catch path.
  - Exposed `saveError` to `CodeEditor` and rendered an inline error banner.
- Preserved save lifecycle behavior:
  - `setSaving(false)` still runs in `finally`.
  - `setSaveSuccess(true)` and 2s timeout reset behavior remain unchanged.
This commit is contained in:
Haileyesus
2026-02-23 09:35:59 +03:00
parent c6f752a096
commit 4178ccdd5e
2 changed files with 25 additions and 22 deletions

View File

@@ -15,11 +15,17 @@ const getErrorMessage = (error: unknown) => {
return String(error); return String(error);
}; };
export const useCodeEditorDocument = ({ file, projectPath }: UseCodeEditorDocumentParams) => { export const useCodeEditorDocument = ({ file }: UseCodeEditorDocumentParams) => {
const [content, setContent] = useState(''); const [content, setContent] = useState('');
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false); const [saving, setSaving] = useState(false);
const [saveSuccess, setSaveSuccess] = useState(false); const [saveSuccess, setSaveSuccess] = useState(false);
const [saveError, setSaveError] = useState<string | null>(null);
const fileProjectName = file.projectName;
const filePath = file.path;
const fileName = file.name;
const fileDiffNewString = file.diffInfo?.new_string;
const fileDiffOldString = file.diffInfo?.old_string;
useEffect(() => { useEffect(() => {
const loadFileContent = async () => { const loadFileContent = async () => {
@@ -27,13 +33,13 @@ export const useCodeEditorDocument = ({ file, projectPath }: UseCodeEditorDocume
setLoading(true); setLoading(true);
// Diff payload may already include full old/new snapshots, so avoid disk read. // Diff payload may already include full old/new snapshots, so avoid disk read.
if (file.diffInfo && file.diffInfo.new_string !== undefined && file.diffInfo.old_string !== undefined) { if (file.diffInfo && fileDiffNewString !== undefined && fileDiffOldString !== undefined) {
setContent(file.diffInfo.new_string); setContent(fileDiffNewString);
setLoading(false); setLoading(false);
return; return;
} }
const response = await api.readFile(file.projectName, file.path); const response = await api.readFile(fileProjectName, filePath);
if (!response.ok) { if (!response.ok) {
throw new Error(`Failed to load file: ${response.status} ${response.statusText}`); throw new Error(`Failed to load file: ${response.status} ${response.statusText}`);
} }
@@ -43,31 +49,21 @@ export const useCodeEditorDocument = ({ file, projectPath }: UseCodeEditorDocume
} catch (error) { } catch (error) {
const message = getErrorMessage(error); const message = getErrorMessage(error);
console.error('Error loading file:', error); console.error('Error loading file:', error);
setContent(`// Error loading file: ${message}\n// File: ${file.name}\n// Path: ${file.path}`); setContent(`// Error loading file: ${message}\n// File: ${fileName}\n// Path: ${filePath}`);
} finally { } finally {
setLoading(false); setLoading(false);
} }
}; };
loadFileContent(); loadFileContent();
}, [file, projectPath]); }, [fileDiffNewString, fileDiffOldString, fileName, filePath, fileProjectName]);
const handleSave = useCallback(async () => { const handleSave = useCallback(async () => {
setSaving(true); setSaving(true);
setSaveError(null);
try { try {
console.log('Saving file:', { const response = await api.saveFile(fileProjectName, filePath, content);
projectName: file.projectName,
path: file.path,
contentLength: content?.length,
});
const response = await api.saveFile(file.projectName, file.path, content);
console.log('Save response:', {
status: response.status,
ok: response.ok,
contentType: response.headers.get('content-type'),
});
if (!response.ok) { if (!response.ok) {
const contentType = response.headers.get('content-type'); const contentType = response.headers.get('content-type');
@@ -81,19 +77,18 @@ export const useCodeEditorDocument = ({ file, projectPath }: UseCodeEditorDocume
throw new Error(`Save failed: ${response.status} ${response.statusText}`); throw new Error(`Save failed: ${response.status} ${response.statusText}`);
} }
const result = await response.json(); await response.json();
console.log('Save successful:', result);
setSaveSuccess(true); setSaveSuccess(true);
setTimeout(() => setSaveSuccess(false), 2000); setTimeout(() => setSaveSuccess(false), 2000);
} catch (error) { } catch (error) {
const message = getErrorMessage(error); const message = getErrorMessage(error);
console.error('Error saving file:', error); console.error('Error saving file:', error);
alert(`Error saving file: ${message}`); setSaveError(message);
} finally { } finally {
setSaving(false); setSaving(false);
} }
}, [content, file.path, file.projectName]); }, [content, filePath, fileProjectName]);
const handleDownload = useCallback(() => { const handleDownload = useCallback(() => {
const blob = new Blob([content], { type: 'text/plain' }); const blob = new Blob([content], { type: 'text/plain' });
@@ -116,6 +111,7 @@ export const useCodeEditorDocument = ({ file, projectPath }: UseCodeEditorDocume
loading, loading,
saving, saving,
saveSuccess, saveSuccess,
saveError,
handleSave, handleSave,
handleDownload, handleDownload,
}; };

View File

@@ -53,6 +53,7 @@ export default function CodeEditor({
loading, loading,
saving, saving,
saveSuccess, saveSuccess,
saveError,
handleSave, handleSave,
handleDownload, handleDownload,
} = useCodeEditorDocument({ } = useCodeEditorDocument({
@@ -201,6 +202,12 @@ export default function CodeEditor({
}} }}
/> />
{saveError && (
<div className="px-3 py-1.5 text-xs text-red-700 bg-red-50 border-b border-red-200 dark:bg-red-900/20 dark:text-red-300 dark:border-red-900/40">
{saveError}
</div>
)}
<div className="flex-1 overflow-hidden"> <div className="flex-1 overflow-hidden">
<CodeEditorSurface <CodeEditorSurface
content={content} content={content}