diff --git a/frontend/src/App.js b/frontend/src/App.js
index 6d8e561..069539c 100644
--- a/frontend/src/App.js
+++ b/frontend/src/App.js
@@ -11,6 +11,7 @@ import { FileListProvider } from './contexts/FileListContext';
import { FileSelectionProvider } from './contexts/FileSelectionContext';
import { FileOperationsProvider } from './contexts/FileOperationsContext';
import { EditorContentProvider } from './contexts/EditorContentContext';
+import { FileManagementProvider } from './contexts/FileManagementContext';
import './App.scss';
function AppContent() {
@@ -40,13 +41,15 @@ function App() {
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/components/Editor.js b/frontend/src/components/Editor.js
index d9554ca..8e84b6e 100644
--- a/frontend/src/components/Editor.js
+++ b/frontend/src/components/Editor.js
@@ -77,7 +77,13 @@ const Editor = () => {
}, [selectedFile, settings.theme, handleContentChange, handleSave]);
useEffect(() => {
+ console.log('Editor: content or selectedFile changed', {
+ content,
+ selectedFile,
+ });
+
if (viewRef.current && content !== viewRef.current.state.doc.toString()) {
+ console.log('Editor: updating content in CodeMirror');
viewRef.current.dispatch({
changes: {
from: 0,
diff --git a/frontend/src/contexts/EditorContentContext.js b/frontend/src/contexts/EditorContentContext.js
index 4bfdd00..2ab0fdc 100644
--- a/frontend/src/contexts/EditorContentContext.js
+++ b/frontend/src/contexts/EditorContentContext.js
@@ -1,22 +1,23 @@
-import React, { createContext, useContext, useMemo } from 'react';
-import { useFileContent } from '../hooks/useFileContent';
+import React, { createContext, useContext, useEffect } from 'react';
+import { useFileManagementContext } from './FileManagementContext';
const EditorContentContext = createContext();
export const EditorContentProvider = ({ children }) => {
- const { content, handleContentChange, handleSave } = useFileContent();
+ const { content, handleContentChange, handleSave, selectedFile } =
+ useFileManagementContext();
- const value = useMemo(
- () => ({
+ useEffect(() => {
+ console.log('EditorContentProvider: content or selectedFile updated', {
content,
- handleContentChange,
- handleSave,
- }),
- [content, handleContentChange, handleSave]
- );
+ selectedFile,
+ });
+ }, [content, selectedFile]);
return (
-
+
{children}
);
diff --git a/frontend/src/contexts/FileManagementContext.js b/frontend/src/contexts/FileManagementContext.js
new file mode 100644
index 0000000..2a6e89b
--- /dev/null
+++ b/frontend/src/contexts/FileManagementContext.js
@@ -0,0 +1,36 @@
+import React, { createContext, useContext, useMemo } from 'react';
+import { useFileManagement } from '../hooks/useFileManagement';
+
+const FileManagementContext = createContext();
+
+export const FileManagementProvider = ({ children }) => {
+ const fileManagement = useFileManagement();
+
+ const value = useMemo(
+ () => fileManagement,
+ [
+ fileManagement.selectedFile,
+ fileManagement.isNewFile,
+ fileManagement.content,
+ fileManagement.isLoading,
+ fileManagement.error,
+ fileManagement.hasUnsavedChanges,
+ ]
+ );
+
+ return (
+
+ {children}
+
+ );
+};
+
+export const useFileManagementContext = () => {
+ const context = useContext(FileManagementContext);
+ if (context === undefined) {
+ throw new Error(
+ 'useFileManagementContext must be used within a FileManagementProvider'
+ );
+ }
+ return context;
+};
diff --git a/frontend/src/contexts/FileSelectionContext.js b/frontend/src/contexts/FileSelectionContext.js
index d89adeb..4a01af9 100644
--- a/frontend/src/contexts/FileSelectionContext.js
+++ b/frontend/src/contexts/FileSelectionContext.js
@@ -1,24 +1,15 @@
-import React, { createContext, useContext, useMemo } from 'react';
-import { useFileContent } from '../hooks/useFileContent';
+import React, { createContext, useContext } from 'react';
+import { useFileManagementContext } from './FileManagementContext';
const FileSelectionContext = createContext();
export const FileSelectionProvider = ({ children }) => {
- const { selectedFile, isNewFile, hasUnsavedChanges, handleFileSelect } =
- useFileContent();
+ const { selectedFile, handleFileSelect } = useFileManagementContext();
- const value = useMemo(
- () => ({
- selectedFile,
- isNewFile,
- hasUnsavedChanges,
- handleFileSelect,
- }),
- [selectedFile, isNewFile, hasUnsavedChanges, handleFileSelect]
- );
+ console.log('FileSelectionProvider rendering', { selectedFile });
return (
-
+
{children}
);
diff --git a/frontend/src/hooks/useFileContent.js b/frontend/src/hooks/useFileContent.js
index 8d49de0..7679f60 100644
--- a/frontend/src/hooks/useFileContent.js
+++ b/frontend/src/hooks/useFileContent.js
@@ -1,114 +1,47 @@
import { useState, useCallback } from 'react';
-import { fetchFileContent, saveFileContent, deleteFile } from '../services/api';
+import { fetchFileContent } from '../services/api';
import { isImageFile } from '../utils/fileHelpers';
-import { useToasts } from '@geist-ui/core';
-import { useFileListContext } from '../contexts/FileListContext';
-
-const DEFAULT_FILE = {
- name: 'New File.md',
- path: 'New File.md',
- content: '# Welcome to NovaMD\n\nStart editing here!',
-};
+import { DEFAULT_FILE } from '../utils/constants';
export const useFileContent = () => {
const [content, setContent] = useState(DEFAULT_FILE.content);
- const [selectedFile, setSelectedFile] = useState(DEFAULT_FILE.path);
- const [isNewFile, setIsNewFile] = useState(true);
- const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
+ const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
- const { setToast } = useToasts();
- const { loadFileList } = useFileListContext();
+ const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
- const handleFileSelect = useCallback(
- async (filePath) => {
- console.log('handleFileSelect', filePath);
- try {
- if (filePath === DEFAULT_FILE.path) {
- setContent(DEFAULT_FILE.content);
- setSelectedFile(DEFAULT_FILE.path);
- setIsNewFile(true);
- } else if (!isImageFile(filePath)) {
- const fileContent = await fetchFileContent(filePath);
- setContent(fileContent);
- setSelectedFile(filePath);
- setIsNewFile(false);
- } else {
- setContent(''); // Set empty content for image files
- setSelectedFile(filePath);
- setIsNewFile(false);
- }
- setHasUnsavedChanges(false);
- setError(null);
- } catch (error) {
- console.error('Failed to load file content:', error);
- setError('Failed to load file content. Please try again.');
+ const loadFileContent = useCallback(async (filePath) => {
+ setIsLoading(true);
+ setError(null);
+ try {
+ if (filePath === DEFAULT_FILE.path) {
+ setContent(DEFAULT_FILE.content);
+ } else if (!isImageFile(filePath)) {
+ const fileContent = await fetchFileContent(filePath);
+ setContent(fileContent);
+ } else {
+ setContent(''); // Set empty content for image files
}
- },
- [hasUnsavedChanges]
- );
+ setHasUnsavedChanges(false);
+ } catch (err) {
+ setError('Failed to load file content');
+ console.error(err);
+ } finally {
+ setIsLoading(false);
+ }
+ }, []);
const handleContentChange = useCallback((newContent) => {
setContent(newContent);
setHasUnsavedChanges(true);
}, []);
- const handleSave = useCallback(async (filePath, fileContent) => {
- try {
- await saveFileContent(filePath, fileContent);
- setIsNewFile(false);
- setHasUnsavedChanges(false);
- return true;
- } catch (error) {
- console.error('Error saving file:', error);
- setError('Failed to save file. Please try again.');
- return false;
- }
- }, []);
-
- const handleCreateNewFile = useCallback(
- async (fileName, initialContent = '') => {
- try {
- await saveFileContent(fileName, initialContent);
- setToast({ text: 'New file created successfully', type: 'success' });
- await loadFileList(); // Refresh the file list
- handleFileSelect(fileName);
- } catch (error) {
- setToast({
- text: 'Failed to create new file: ' + error.message,
- type: 'error',
- });
- }
- },
- [setToast, loadFileList, handleFileSelect]
- );
-
- const handleDeleteFile = useCallback(async () => {
- if (!selectedFile) return;
- try {
- await deleteFile(selectedFile);
- setToast({ text: 'File deleted successfully', type: 'success' });
- await loadFileList(); // Refresh the file list
- setSelectedFile(null);
- setContent('');
- } catch (error) {
- setToast({
- text: 'Failed to delete file: ' + error.message,
- type: 'error',
- });
- }
- }, [selectedFile, setToast, loadFileList]);
-
return {
content,
- selectedFile,
- isNewFile,
- hasUnsavedChanges,
+ setContent,
+ isLoading,
error,
- handleFileSelect,
+ hasUnsavedChanges,
+ loadFileContent,
handleContentChange,
- handleSave,
- handleCreateNewFile,
- handleDeleteFile,
- DEFAULT_FILE,
};
};
diff --git a/frontend/src/hooks/useFileManagement.js b/frontend/src/hooks/useFileManagement.js
index 8620be7..8d531dd 100644
--- a/frontend/src/hooks/useFileManagement.js
+++ b/frontend/src/hooks/useFileManagement.js
@@ -1,34 +1,47 @@
-import { useFileList } from './useFileList';
+import { useEffect, useCallback } from 'react';
+import { useFileSelection } from './useFileSelection';
import { useFileContent } from './useFileContent';
-import { useGitOperations } from './useGitOperations';
+import { useFileOperations } from './useFileOperations';
-export const useFileManagement = (gitEnabled) => {
- const { files, error: fileListError, loadFileList } = useFileList(gitEnabled);
+export const useFileManagement = () => {
+ const { selectedFile, isNewFile, handleFileSelect } = useFileSelection();
const {
content,
- selectedFile,
- isNewFile,
+ isLoading,
+ error,
hasUnsavedChanges,
- error: fileContentError,
- handleFileSelect,
+ loadFileContent,
handleContentChange,
- handleSave,
+ setHasUnsavedChanges,
} = useFileContent();
- const { pullLatestChanges, handleCommitAndPush } =
- useGitOperations(gitEnabled);
+ const { handleSave, handleDelete, handleCreateNewFile } =
+ useFileOperations(setHasUnsavedChanges);
+
+ useEffect(() => {
+ if (selectedFile) {
+ loadFileContent(selectedFile);
+ }
+ }, [selectedFile, loadFileContent]);
+
+ const handleFileSelectAndLoad = useCallback(
+ async (filePath) => {
+ await handleFileSelect(filePath);
+ await loadFileContent(filePath);
+ },
+ [handleFileSelect, loadFileContent]
+ );
return {
- files,
- content,
selectedFile,
isNewFile,
+ content,
+ isLoading,
+ error,
hasUnsavedChanges,
- error: fileListError || fileContentError,
- handleFileSelect,
+ handleFileSelect: handleFileSelectAndLoad,
handleContentChange,
- handleSave,
- pullLatestChanges,
- handleCommitAndPush,
- loadFileList,
+ handleSave: (filePath) => handleSave(filePath, content),
+ handleDelete,
+ handleCreateNewFile,
};
};
diff --git a/frontend/src/hooks/useFileOperations.js b/frontend/src/hooks/useFileOperations.js
new file mode 100644
index 0000000..beebeae
--- /dev/null
+++ b/frontend/src/hooks/useFileOperations.js
@@ -0,0 +1,46 @@
+import { useCallback } from 'react';
+import { saveFileContent, deleteFile } from '../services/api';
+
+export const useFileOperations = (setHasUnsavedChanges) => {
+ const handleSave = useCallback(
+ async (filePath, content) => {
+ try {
+ await saveFileContent(filePath, content);
+ setHasUnsavedChanges(false);
+ console.log('File saved successfully');
+ return true;
+ } catch (error) {
+ console.error('Error saving file:', error);
+ return false;
+ }
+ },
+ [setHasUnsavedChanges]
+ );
+
+ const handleDelete = useCallback(async (filePath) => {
+ try {
+ await deleteFile(filePath);
+ console.log('File deleted successfully');
+ return true;
+ } catch (error) {
+ console.error('Error deleting file:', error);
+ return false;
+ }
+ }, []);
+
+ const handleCreateNewFile = useCallback(
+ async (fileName, initialContent = '') => {
+ try {
+ await saveFileContent(fileName, initialContent);
+ console.log('New file created successfully');
+ return true;
+ } catch (error) {
+ console.error('Error creating new file:', error);
+ return false;
+ }
+ },
+ []
+ );
+
+ return { handleSave, handleDelete, handleCreateNewFile };
+};
diff --git a/frontend/src/hooks/useFileSelection.js b/frontend/src/hooks/useFileSelection.js
new file mode 100644
index 0000000..6fa7d01
--- /dev/null
+++ b/frontend/src/hooks/useFileSelection.js
@@ -0,0 +1,15 @@
+import { useState, useCallback } from 'react';
+import { DEFAULT_FILE } from '../utils/constants'; // Assuming you have this constant defined
+
+export const useFileSelection = () => {
+ const [selectedFile, setSelectedFile] = useState(DEFAULT_FILE.path);
+ const [isNewFile, setIsNewFile] = useState(true);
+
+ const handleFileSelect = useCallback(async (filePath) => {
+ console.log('File selected:', filePath);
+ setSelectedFile(filePath);
+ setIsNewFile(filePath === DEFAULT_FILE.path);
+ }, []);
+
+ return { selectedFile, isNewFile, handleFileSelect };
+};
diff --git a/frontend/src/utils/constants.js b/frontend/src/utils/constants.js
new file mode 100644
index 0000000..6b7f0a4
--- /dev/null
+++ b/frontend/src/utils/constants.js
@@ -0,0 +1,48 @@
+export const API_BASE_URL = window.API_BASE_URL;
+
+export const THEMES = {
+ LIGHT: 'light',
+ DARK: 'dark',
+};
+
+export const FILE_ACTIONS = {
+ CREATE: 'create',
+ DELETE: 'delete',
+ RENAME: 'rename',
+};
+
+export const MODAL_TYPES = {
+ NEW_FILE: 'newFile',
+ DELETE_FILE: 'deleteFile',
+ COMMIT_MESSAGE: 'commitMessage',
+};
+
+export const IMAGE_EXTENSIONS = [
+ '.jpg',
+ '.jpeg',
+ '.png',
+ '.gif',
+ '.webp',
+ '.svg',
+];
+
+export const DEFAULT_SETTINGS = {
+ theme: THEMES.LIGHT,
+ autoSave: false,
+ gitEnabled: false,
+ gitUrl: '',
+ gitUser: '',
+ gitToken: '',
+ gitAutoCommit: false,
+ gitCommitMsgTemplate: 'Update ${filename}',
+};
+
+export const DEFAULT_FILE = {
+ name: 'New File.md',
+ path: 'New File.md',
+ content: '# Welcome to NovaMD\n\nStart editing here!',
+};
+
+export const MARKDOWN_REGEX = {
+ WIKILINK: /(!?)\[\[(.*?)\]\]/g,
+};