diff --git a/frontend/src/App.js b/frontend/src/App.js index 9cb3f91..b379d9b 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -1,8 +1,8 @@ -import React, { useState, useEffect } from 'react'; -import { GeistProvider, CssBaseline } from '@geist-ui/core'; +import React, { useState, useEffect, useCallback } from 'react'; +import { GeistProvider, CssBaseline, useToasts } from '@geist-ui/core'; import Editor from './components/Editor'; import FileTree from './components/FileTree'; -import { fetchFileList, fetchFileContent } from './services/api'; +import { fetchFileList, fetchFileContent, saveFileContent } from './services/api'; import './App.scss'; function App() { @@ -10,6 +10,7 @@ function App() { const [files, setFiles] = useState([]); const [selectedFile, setSelectedFile] = useState(null); const [error, setError] = useState(null); + const { setToast } = useToasts(); useEffect(() => { const loadFileList = async () => { @@ -34,12 +35,23 @@ function App() { const fileContent = await fetchFileContent(filePath); setContent(fileContent); setSelectedFile(filePath); + setError(null); } catch (error) { console.error('Failed to load file content:', error); setError('Failed to load file content. Please try again.'); } }; + const handleSave = useCallback(async (filePath, fileContent) => { + try { + await saveFileContent(filePath, fileContent); + setToast({ text: 'File saved successfully', type: 'success' }); + } catch (error) { + console.error('Error saving file:', error); + setToast({ text: 'Failed to save file. Please try again.', type: 'error' }); + } + }, [setToast]); + return ( @@ -57,7 +69,12 @@ function App() {

NovaMD

- +
diff --git a/frontend/src/components/Editor.js b/frontend/src/components/Editor.js index ed1c412..c16ef73 100644 --- a/frontend/src/components/Editor.js +++ b/frontend/src/components/Editor.js @@ -5,17 +5,27 @@ import { EditorView, keymap } from "@codemirror/view"; import { markdown } from "@codemirror/lang-markdown"; import { defaultKeymap } from "@codemirror/commands"; -const Editor = ({ content, onChange }) => { +const Editor = ({ content, onChange, onSave, filePath }) => { const editorRef = useRef(); const viewRef = useRef(); useEffect(() => { + const handleSave = (view) => { + onSave(filePath, view.state.doc.toString()); + return true; + }; + const state = EditorState.create({ doc: content, extensions: [ basicSetup, markdown(), keymap.of(defaultKeymap), + keymap.of([{ + key: "Ctrl-s", + run: handleSave, + preventDefault: true + }]), EditorView.updateListener.of((update) => { if (update.docChanged) { onChange(update.state.doc.toString()); @@ -34,7 +44,7 @@ const Editor = ({ content, onChange }) => { return () => { view.destroy(); }; - }, []); + }, [filePath]); useEffect(() => { if (viewRef.current && content !== viewRef.current.state.doc.toString()) { diff --git a/frontend/src/services/api.js b/frontend/src/services/api.js index 3056dbe..3febe66 100644 --- a/frontend/src/services/api.js +++ b/frontend/src/services/api.js @@ -24,4 +24,20 @@ export const fetchFileContent = async (filePath) => { console.error('Error fetching file content:', error); throw error; } -}; \ No newline at end of file +}; + +export const saveFileContent = async (filePath, content) => { + const response = await fetch(`/api/v1/files/${filePath}`, { + method: 'POST', + headers: { + 'Content-Type': 'text/plain', + }, + body: content, + }); + + if (!response.ok) { + throw new Error('Failed to save file'); + } + + return await response.text(); + }; \ No newline at end of file