mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-03-04 21:47:43 +00:00
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:
@@ -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,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
Reference in New Issue
Block a user