mirror of
https://github.com/lordmathis/lemma.git
synced 2025-11-06 16:04:23 +00:00
Set up and run eslint and prettier
This commit is contained in:
31
.eslintrc.json
Normal file
31
.eslintrc.json
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"es2021": true,
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:react/recommended",
|
||||||
|
"plugin:react-hooks/recommended"
|
||||||
|
],
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"jsx": true
|
||||||
|
},
|
||||||
|
"ecmaVersion": 12,
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"react"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"react/prop-types": "off",
|
||||||
|
"no-unused-vars": "warn"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"react": {
|
||||||
|
"version": "detect"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
.prettierrc.json
Normal file
7
.prettierrc.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"trailingComma": "es5"
|
||||||
|
}
|
||||||
|
|
||||||
@@ -13,7 +13,6 @@
|
|||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"markdown",
|
"markdown",
|
||||||
"hypermd",
|
|
||||||
"editor"
|
"editor"
|
||||||
],
|
],
|
||||||
"author": "Matúš Námešný",
|
"author": "Matúš Námešný",
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ function App() {
|
|||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<Page>
|
<Page>
|
||||||
<Header currentTheme={themeType} onThemeChange={setTheme} />
|
<Header currentTheme={themeType} onThemeChange={setTheme} />
|
||||||
<Page.Content className='page-content'>
|
<Page.Content className="page-content">
|
||||||
<MainContent
|
<MainContent
|
||||||
content={content}
|
content={content}
|
||||||
files={files}
|
files={files}
|
||||||
|
|||||||
@@ -15,12 +15,11 @@ $navbar-height: 64px;
|
|||||||
.file-tree-container {
|
.file-tree-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
&>div {
|
& > div {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-content {
|
.page-content {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import React, { useEffect, useRef } from "react";
|
import React, { useEffect, useRef } from 'react';
|
||||||
import { basicSetup } from "codemirror";
|
import { basicSetup } from 'codemirror';
|
||||||
import { EditorState } from "@codemirror/state";
|
import { EditorState } from '@codemirror/state';
|
||||||
import { EditorView, keymap } from "@codemirror/view";
|
import { EditorView, keymap } from '@codemirror/view';
|
||||||
import { markdown } from "@codemirror/lang-markdown";
|
import { markdown } from '@codemirror/lang-markdown';
|
||||||
import { defaultKeymap } from "@codemirror/commands";
|
import { defaultKeymap } from '@codemirror/commands';
|
||||||
import { oneDark } from "@codemirror/theme-one-dark";
|
import { oneDark } from '@codemirror/theme-one-dark';
|
||||||
|
|
||||||
const Editor = ({ content, onChange, onSave, filePath, themeType }) => {
|
const Editor = ({ content, onChange, onSave, filePath, themeType }) => {
|
||||||
const editorRef = useRef();
|
const editorRef = useRef();
|
||||||
@@ -17,20 +17,20 @@ const Editor = ({ content, onChange, onSave, filePath, themeType }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const theme = EditorView.theme({
|
const theme = EditorView.theme({
|
||||||
"&": {
|
'&': {
|
||||||
height: "100%",
|
height: '100%',
|
||||||
fontSize: "14px",
|
fontSize: '14px',
|
||||||
},
|
},
|
||||||
".cm-scroller": {
|
'.cm-scroller': {
|
||||||
overflow: "auto",
|
overflow: 'auto',
|
||||||
},
|
},
|
||||||
".cm-gutters": {
|
'.cm-gutters': {
|
||||||
backgroundColor: themeType === "dark" ? "#1e1e1e" : "#f5f5f5",
|
backgroundColor: themeType === 'dark' ? '#1e1e1e' : '#f5f5f5',
|
||||||
color: themeType === "dark" ? "#858585" : "#999",
|
color: themeType === 'dark' ? '#858585' : '#999',
|
||||||
border: "none",
|
border: 'none',
|
||||||
},
|
},
|
||||||
".cm-activeLineGutter": {
|
'.cm-activeLineGutter': {
|
||||||
backgroundColor: themeType === "dark" ? "#2c313a" : "#e8e8e8",
|
backgroundColor: themeType === 'dark' ? '#2c313a' : '#e8e8e8',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -41,18 +41,20 @@ const Editor = ({ content, onChange, onSave, filePath, themeType }) => {
|
|||||||
markdown(),
|
markdown(),
|
||||||
EditorView.lineWrapping,
|
EditorView.lineWrapping,
|
||||||
keymap.of(defaultKeymap),
|
keymap.of(defaultKeymap),
|
||||||
keymap.of([{
|
keymap.of([
|
||||||
key: "Ctrl-s",
|
{
|
||||||
|
key: 'Ctrl-s',
|
||||||
run: handleSave,
|
run: handleSave,
|
||||||
preventDefault: true
|
preventDefault: true,
|
||||||
}]),
|
},
|
||||||
|
]),
|
||||||
EditorView.updateListener.of((update) => {
|
EditorView.updateListener.of((update) => {
|
||||||
if (update.docChanged) {
|
if (update.docChanged) {
|
||||||
onChange(update.state.doc.toString());
|
onChange(update.state.doc.toString());
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
theme,
|
theme,
|
||||||
themeType === "dark" ? oneDark : [],
|
themeType === 'dark' ? oneDark : [],
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -71,7 +73,11 @@ const Editor = ({ content, onChange, onSave, filePath, themeType }) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (viewRef.current && content !== viewRef.current.state.doc.toString()) {
|
if (viewRef.current && content !== viewRef.current.state.doc.toString()) {
|
||||||
viewRef.current.dispatch({
|
viewRef.current.dispatch({
|
||||||
changes: { from: 0, to: viewRef.current.state.doc.length, insert: content }
|
changes: {
|
||||||
|
from: 0,
|
||||||
|
to: viewRef.current.state.doc.length,
|
||||||
|
insert: content,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [content]);
|
}, [content]);
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Tree, Button, Tooltip, Spacer, ButtonGroup } from '@geist-ui/core';
|
import { Tree, Button, Tooltip, Spacer, ButtonGroup } from '@geist-ui/core';
|
||||||
import { File, Folder, GitPullRequest, GitCommit, Plus, Trash } from '@geist-ui/icons';
|
import {
|
||||||
|
File,
|
||||||
|
Folder,
|
||||||
|
GitPullRequest,
|
||||||
|
GitCommit,
|
||||||
|
Plus,
|
||||||
|
Trash,
|
||||||
|
} from '@geist-ui/icons';
|
||||||
|
|
||||||
const FileTree = ({
|
const FileTree = ({
|
||||||
files = [],
|
files = [],
|
||||||
@@ -11,7 +18,7 @@ const FileTree = ({
|
|||||||
onPull = () => {},
|
onPull = () => {},
|
||||||
onCommitAndPush = () => {},
|
onCommitAndPush = () => {},
|
||||||
onCreateFile = () => {},
|
onCreateFile = () => {},
|
||||||
onDeleteFile = () => {}
|
onDeleteFile = () => {},
|
||||||
}) => {
|
}) => {
|
||||||
if (files.length === 0) {
|
if (files.length === 0) {
|
||||||
return <div>No files to display</div>;
|
return <div>No files to display</div>;
|
||||||
@@ -30,26 +37,30 @@ const FileTree = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderIcon = ({ type }) => type === 'directory' ? <Folder /> : <File />;
|
const renderIcon = ({ type }) =>
|
||||||
|
type === 'directory' ? <Folder /> : <File />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<ButtonGroup className='file-tree-buttons'>
|
<ButtonGroup className="file-tree-buttons">
|
||||||
<Tooltip text="Create new file" type="dark">
|
<Tooltip text="Create new file" type="dark">
|
||||||
<Button
|
<Button
|
||||||
icon={<Plus />}
|
icon={<Plus />}
|
||||||
auto
|
auto
|
||||||
scale={2/3}
|
scale={2 / 3}
|
||||||
onClick={onCreateFile}
|
onClick={onCreateFile}
|
||||||
px={0.6}
|
px={0.6}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Spacer w={0.5} />
|
<Spacer w={0.5} />
|
||||||
<Tooltip text={selectedFile ? "Delete current file" : "No file selected"} type="dark">
|
<Tooltip
|
||||||
|
text={selectedFile ? 'Delete current file' : 'No file selected'}
|
||||||
|
type="dark"
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
icon={<Trash />}
|
icon={<Trash />}
|
||||||
auto
|
auto
|
||||||
scale={2/3}
|
scale={2 / 3}
|
||||||
onClick={onDeleteFile}
|
onClick={onDeleteFile}
|
||||||
disabled={!selectedFile}
|
disabled={!selectedFile}
|
||||||
type="error"
|
type="error"
|
||||||
@@ -57,26 +68,34 @@ const FileTree = ({
|
|||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Spacer w={0.5} />
|
<Spacer w={0.5} />
|
||||||
<Tooltip text={gitEnabled ? "Pull changes from remote" : "Git is not enabled"} type="dark">
|
<Tooltip
|
||||||
|
text={gitEnabled ? 'Pull changes from remote' : 'Git is not enabled'}
|
||||||
|
type="dark"
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
icon={<GitPullRequest />}
|
icon={<GitPullRequest />}
|
||||||
auto
|
auto
|
||||||
scale={2/3}
|
scale={2 / 3}
|
||||||
onClick={onPull}
|
onClick={onPull}
|
||||||
disabled={!gitEnabled}
|
disabled={!gitEnabled}
|
||||||
px={0.6}
|
px={0.6}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Spacer w={0.5} />
|
<Spacer w={0.5} />
|
||||||
<Tooltip text={
|
<Tooltip
|
||||||
!gitEnabled ? "Git is not enabled" :
|
text={
|
||||||
gitAutoCommit ? "Auto-commit is enabled" :
|
!gitEnabled
|
||||||
"Commit and push changes"
|
? 'Git is not enabled'
|
||||||
} type="dark">
|
: gitAutoCommit
|
||||||
|
? 'Auto-commit is enabled'
|
||||||
|
: 'Commit and push changes'
|
||||||
|
}
|
||||||
|
type="dark"
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
icon={<GitCommit />}
|
icon={<GitCommit />}
|
||||||
auto
|
auto
|
||||||
scale={2/3}
|
scale={2 / 3}
|
||||||
onClick={onCommitAndPush}
|
onClick={onCommitAndPush}
|
||||||
disabled={!gitEnabled || gitAutoCommit}
|
disabled={!gitEnabled || gitAutoCommit}
|
||||||
px={0.6}
|
px={0.6}
|
||||||
|
|||||||
@@ -1,5 +1,15 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Grid, Breadcrumbs, Tabs, Dot, useTheme, useToasts, Modal, Input, Button } from '@geist-ui/core';
|
import {
|
||||||
|
Grid,
|
||||||
|
Breadcrumbs,
|
||||||
|
Tabs,
|
||||||
|
Dot,
|
||||||
|
useTheme,
|
||||||
|
useToasts,
|
||||||
|
Modal,
|
||||||
|
Input,
|
||||||
|
Button,
|
||||||
|
} from '@geist-ui/core';
|
||||||
import { Code, Eye } from '@geist-ui/icons';
|
import { Code, Eye } from '@geist-ui/icons';
|
||||||
import Editor from './Editor';
|
import Editor from './Editor';
|
||||||
import FileTree from './FileTree';
|
import FileTree from './FileTree';
|
||||||
@@ -29,7 +39,10 @@ const MainContent = ({
|
|||||||
await pullLatestChanges();
|
await pullLatestChanges();
|
||||||
setToast({ text: 'Successfully pulled latest changes', type: 'success' });
|
setToast({ text: 'Successfully pulled latest changes', type: 'success' });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setToast({ text: 'Failed to pull changes: ' + error.message, type: 'error' });
|
setToast({
|
||||||
|
text: 'Failed to pull changes: ' + error.message,
|
||||||
|
type: 'error',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -38,11 +51,17 @@ const MainContent = ({
|
|||||||
const message = prompt('Enter commit message:');
|
const message = prompt('Enter commit message:');
|
||||||
if (message) {
|
if (message) {
|
||||||
await commitAndPush(message);
|
await commitAndPush(message);
|
||||||
setToast({ text: 'Changes committed and pushed successfully', type: 'success' });
|
setToast({
|
||||||
|
text: 'Changes committed and pushed successfully',
|
||||||
|
type: 'success',
|
||||||
|
});
|
||||||
await pullLatestChanges(); // Pull changes after successful push
|
await pullLatestChanges(); // Pull changes after successful push
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setToast({ text: 'Failed to commit and push changes: ' + error.message, type: 'error' });
|
setToast({
|
||||||
|
text: 'Failed to commit and push changes: ' + error.message,
|
||||||
|
type: 'error',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -58,7 +77,10 @@ const MainContent = ({
|
|||||||
await pullLatestChanges(); // Refresh file list
|
await pullLatestChanges(); // Refresh file list
|
||||||
onFileSelect(newFileName); // Select the new file
|
onFileSelect(newFileName); // Select the new file
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setToast({ text: 'Failed to create new file: ' + error.message, type: 'error' });
|
setToast({
|
||||||
|
text: 'Failed to create new file: ' + error.message,
|
||||||
|
type: 'error',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setNewFileModalVisible(false);
|
setNewFileModalVisible(false);
|
||||||
@@ -67,7 +89,9 @@ const MainContent = ({
|
|||||||
|
|
||||||
const handleDeleteFile = async () => {
|
const handleDeleteFile = async () => {
|
||||||
if (selectedFile) {
|
if (selectedFile) {
|
||||||
const confirmDelete = window.confirm(`Are you sure you want to delete "${selectedFile}"?`);
|
const confirmDelete = window.confirm(
|
||||||
|
`Are you sure you want to delete "${selectedFile}"?`
|
||||||
|
);
|
||||||
if (confirmDelete) {
|
if (confirmDelete) {
|
||||||
try {
|
try {
|
||||||
await deleteFile(selectedFile);
|
await deleteFile(selectedFile);
|
||||||
@@ -75,7 +99,10 @@ const MainContent = ({
|
|||||||
await pullLatestChanges(); // Refresh file list
|
await pullLatestChanges(); // Refresh file list
|
||||||
onFileSelect(null); // Deselect the file
|
onFileSelect(null); // Deselect the file
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setToast({ text: 'Failed to delete file: ' + error.message, type: 'error' });
|
setToast({
|
||||||
|
text: 'Failed to delete file: ' + error.message,
|
||||||
|
type: 'error',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,7 +118,9 @@ const MainContent = ({
|
|||||||
<Breadcrumbs.Item key={index}>{part}</Breadcrumbs.Item>
|
<Breadcrumbs.Item key={index}>{part}</Breadcrumbs.Item>
|
||||||
))}
|
))}
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
{hasUnsavedChanges && <Dot type="warning" className="unsaved-indicator" />}
|
{hasUnsavedChanges && (
|
||||||
|
<Dot type="warning" className="unsaved-indicator" />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -114,7 +143,14 @@ const MainContent = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid xs={24} sm={18} md={19} lg={20} height="100%" className="main-content">
|
<Grid
|
||||||
|
xs={24}
|
||||||
|
sm={18}
|
||||||
|
md={19}
|
||||||
|
lg={20}
|
||||||
|
height="100%"
|
||||||
|
className="main-content"
|
||||||
|
>
|
||||||
<div className="content-header">
|
<div className="content-header">
|
||||||
{renderBreadcrumbs()}
|
{renderBreadcrumbs()}
|
||||||
<Tabs value={activeTab} onChange={setActiveTab}>
|
<Tabs value={activeTab} onChange={setActiveTab}>
|
||||||
@@ -137,7 +173,10 @@ const MainContent = ({
|
|||||||
</div>
|
</div>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid.Container>
|
</Grid.Container>
|
||||||
<Modal visible={newFileModalVisible} onClose={() => setNewFileModalVisible(false)}>
|
<Modal
|
||||||
|
visible={newFileModalVisible}
|
||||||
|
onClose={() => setNewFileModalVisible(false)}
|
||||||
|
>
|
||||||
<Modal.Title>Create New File</Modal.Title>
|
<Modal.Title>Create New File</Modal.Title>
|
||||||
<Modal.Content>
|
<Modal.Content>
|
||||||
<Input
|
<Input
|
||||||
@@ -147,7 +186,9 @@ const MainContent = ({
|
|||||||
onChange={(e) => setNewFileName(e.target.value)}
|
onChange={(e) => setNewFileName(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</Modal.Content>
|
</Modal.Content>
|
||||||
<Modal.Action passive onClick={() => setNewFileModalVisible(false)}>Cancel</Modal.Action>
|
<Modal.Action passive onClick={() => setNewFileModalVisible(false)}>
|
||||||
|
Cancel
|
||||||
|
</Modal.Action>
|
||||||
<Modal.Action onClick={handleNewFileSubmit}>Create</Modal.Action>
|
<Modal.Action onClick={handleNewFileSubmit}>Create</Modal.Action>
|
||||||
</Modal>
|
</Modal>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ const MarkdownPreview = ({ content }) => {
|
|||||||
remarkPlugins={[remarkMath]}
|
remarkPlugins={[remarkMath]}
|
||||||
rehypePlugins={[rehypeKatex]}
|
rehypePlugins={[rehypeKatex]}
|
||||||
components={{
|
components={{
|
||||||
code({node, inline, className, children, ...props}) {
|
code({ node, inline, className, children, ...props }) {
|
||||||
const match = /language-(\w+)/.exec(className || '')
|
const match = /language-(\w+)/.exec(className || '');
|
||||||
return !inline && match ? (
|
return !inline && match ? (
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
style={vscDarkPlus}
|
style={vscDarkPlus}
|
||||||
@@ -28,8 +28,8 @@ const MarkdownPreview = ({ content }) => {
|
|||||||
<code className={className} {...props}>
|
<code className={className} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</code>
|
</code>
|
||||||
)
|
);
|
||||||
}
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
|
|||||||
@@ -1,5 +1,15 @@
|
|||||||
import React, { useState, useEffect, useCallback } from 'react';
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
import { Modal, Text, Toggle, Input, Spacer, useTheme, Button, Dot, useToasts } from '@geist-ui/core';
|
import {
|
||||||
|
Modal,
|
||||||
|
Text,
|
||||||
|
Toggle,
|
||||||
|
Input,
|
||||||
|
Spacer,
|
||||||
|
useTheme,
|
||||||
|
Button,
|
||||||
|
Dot,
|
||||||
|
useToasts,
|
||||||
|
} from '@geist-ui/core';
|
||||||
import { saveUserSettings, fetchUserSettings } from '../services/api';
|
import { saveUserSettings, fetchUserSettings } from '../services/api';
|
||||||
|
|
||||||
const Settings = ({ visible, onClose, currentTheme, onThemeChange }) => {
|
const Settings = ({ visible, onClose, currentTheme, onThemeChange }) => {
|
||||||
@@ -42,13 +52,14 @@ const Settings = ({ visible, onClose, currentTheme, onThemeChange }) => {
|
|||||||
}, [isInitialized, loadSettings]);
|
}, [isInitialized, loadSettings]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const settingsChanged = JSON.stringify(settings) !== JSON.stringify(originalSettings);
|
const settingsChanged =
|
||||||
|
JSON.stringify(settings) !== JSON.stringify(originalSettings);
|
||||||
const themeChanged = themeSettings !== originalTheme;
|
const themeChanged = themeSettings !== originalTheme;
|
||||||
setHasUnsavedChanges(settingsChanged || themeChanged);
|
setHasUnsavedChanges(settingsChanged || themeChanged);
|
||||||
}, [settings, themeSettings, originalSettings, originalTheme]);
|
}, [settings, themeSettings, originalSettings, originalTheme]);
|
||||||
|
|
||||||
const handleInputChange = (key, value) => {
|
const handleInputChange = (key, value) => {
|
||||||
setSettings(prev => ({ ...prev, [key]: value }));
|
setSettings((prev) => ({ ...prev, [key]: value }));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleThemeChange = () => {
|
const handleThemeChange = () => {
|
||||||
@@ -70,7 +81,10 @@ const Settings = ({ visible, onClose, currentTheme, onThemeChange }) => {
|
|||||||
onClose();
|
onClose();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to save settings:', error);
|
console.error('Failed to save settings:', error);
|
||||||
setToast({ text: 'Failed to save settings: ' + error.message, type: 'error' });
|
setToast({
|
||||||
|
text: 'Failed to save settings: ' + error.message,
|
||||||
|
type: 'error',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -111,7 +125,9 @@ const Settings = ({ visible, onClose, currentTheme, onThemeChange }) => {
|
|||||||
<Text>Enable Git</Text>
|
<Text>Enable Git</Text>
|
||||||
<Toggle
|
<Toggle
|
||||||
checked={settings.gitEnabled}
|
checked={settings.gitEnabled}
|
||||||
onChange={(e) => handleInputChange('gitEnabled', e.target.checked)}
|
onChange={(e) =>
|
||||||
|
handleInputChange('gitEnabled', e.target.checked)
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={settings.gitEnabled ? '' : 'disabled'}>
|
<div className={settings.gitEnabled ? '' : 'disabled'}>
|
||||||
@@ -143,7 +159,9 @@ const Settings = ({ visible, onClose, currentTheme, onThemeChange }) => {
|
|||||||
<Text>Auto Commit</Text>
|
<Text>Auto Commit</Text>
|
||||||
<Toggle
|
<Toggle
|
||||||
checked={settings.gitAutoCommit}
|
checked={settings.gitAutoCommit}
|
||||||
onChange={(e) => handleInputChange('gitAutoCommit', e.target.checked)}
|
onChange={(e) =>
|
||||||
|
handleInputChange('gitAutoCommit', e.target.checked)
|
||||||
|
}
|
||||||
disabled={!settings.gitEnabled}
|
disabled={!settings.gitEnabled}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -152,13 +170,17 @@ const Settings = ({ visible, onClose, currentTheme, onThemeChange }) => {
|
|||||||
width="100%"
|
width="100%"
|
||||||
label="Commit Message Template"
|
label="Commit Message Template"
|
||||||
value={settings.gitCommitMsgTemplate}
|
value={settings.gitCommitMsgTemplate}
|
||||||
onChange={(e) => handleInputChange('gitCommitMsgTemplate', e.target.value)}
|
onChange={(e) =>
|
||||||
|
handleInputChange('gitCommitMsgTemplate', e.target.value)
|
||||||
|
}
|
||||||
disabled={!settings.gitEnabled}
|
disabled={!settings.gitEnabled}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal.Content>
|
</Modal.Content>
|
||||||
<Modal.Action passive onClick={onClose}>Cancel</Modal.Action>
|
<Modal.Action passive onClick={onClose}>
|
||||||
|
Cancel
|
||||||
|
</Modal.Action>
|
||||||
<Modal.Action onClick={handleSubmit}>Save Changes</Modal.Action>
|
<Modal.Action onClick={handleSubmit}>Save Changes</Modal.Action>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import { useToasts } from '@geist-ui/core';
|
import { useToasts } from '@geist-ui/core';
|
||||||
import { fetchFileList, fetchFileContent, saveFileContent, pullChanges } from '../services/api';
|
import {
|
||||||
|
fetchFileList,
|
||||||
|
fetchFileContent,
|
||||||
|
saveFileContent,
|
||||||
|
pullChanges,
|
||||||
|
} from '../services/api';
|
||||||
|
|
||||||
const DEFAULT_FILE = {
|
const DEFAULT_FILE = {
|
||||||
name: 'New File.md',
|
name: 'New File.md',
|
||||||
path: 'New File.md',
|
path: 'New File.md',
|
||||||
content: '# Welcome to NovaMD\n\nStart editing here!'
|
content: '# Welcome to NovaMD\n\nStart editing here!',
|
||||||
};
|
};
|
||||||
|
|
||||||
const useFileManagement = (gitEnabled = false) => {
|
const useFileManagement = (gitEnabled = false) => {
|
||||||
@@ -35,11 +40,17 @@ const useFileManagement = (gitEnabled = false) => {
|
|||||||
if (gitEnabled) {
|
if (gitEnabled) {
|
||||||
try {
|
try {
|
||||||
await pullChanges();
|
await pullChanges();
|
||||||
setToast({ text: 'Latest changes pulled successfully', type: 'success' });
|
setToast({
|
||||||
|
text: 'Latest changes pulled successfully',
|
||||||
|
type: 'success',
|
||||||
|
});
|
||||||
await loadFileList(); // Reload file list after pulling changes
|
await loadFileList(); // Reload file list after pulling changes
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to pull latest changes:', error);
|
console.error('Failed to pull latest changes:', error);
|
||||||
setToast({ text: 'Failed to pull latest changes: ' + error.message, type: 'error' });
|
setToast({
|
||||||
|
text: 'Failed to pull latest changes: ' + error.message,
|
||||||
|
type: 'error',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [gitEnabled, loadFileList, setToast]);
|
}, [gitEnabled, loadFileList, setToast]);
|
||||||
@@ -58,7 +69,9 @@ const useFileManagement = (gitEnabled = false) => {
|
|||||||
|
|
||||||
const handleFileSelect = async (filePath) => {
|
const handleFileSelect = async (filePath) => {
|
||||||
if (hasUnsavedChanges) {
|
if (hasUnsavedChanges) {
|
||||||
const confirmSwitch = window.confirm('You have unsaved changes. Are you sure you want to switch files?');
|
const confirmSwitch = window.confirm(
|
||||||
|
'You have unsaved changes. Are you sure you want to switch files?'
|
||||||
|
);
|
||||||
if (!confirmSwitch) return;
|
if (!confirmSwitch) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +93,8 @@ const useFileManagement = (gitEnabled = false) => {
|
|||||||
setHasUnsavedChanges(true);
|
setHasUnsavedChanges(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSave = useCallback(async (filePath, fileContent) => {
|
const handleSave = useCallback(
|
||||||
|
async (filePath, fileContent) => {
|
||||||
try {
|
try {
|
||||||
await saveFileContent(filePath, fileContent);
|
await saveFileContent(filePath, fileContent);
|
||||||
setToast({ text: 'File saved successfully', type: 'success' });
|
setToast({ text: 'File saved successfully', type: 'success' });
|
||||||
@@ -91,9 +105,14 @@ const useFileManagement = (gitEnabled = false) => {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error saving file:', error);
|
console.error('Error saving file:', error);
|
||||||
setToast({ text: 'Failed to save file. Please try again.', type: 'error' });
|
setToast({
|
||||||
|
text: 'Failed to save file. Please try again.',
|
||||||
|
type: 'error',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}, [setToast, isNewFile, loadFileList]);
|
},
|
||||||
|
[setToast, isNewFile, loadFileList]
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content,
|
content,
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React from "react";
|
import React from 'react';
|
||||||
import ReactDOM from "react-dom/client";
|
import ReactDOM from 'react-dom/client';
|
||||||
import App from "./App";
|
import App from './App';
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(document.getElementById("root"));
|
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||||
root.render(
|
root.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<App />
|
||||||
|
|||||||
@@ -40,9 +40,9 @@ export const saveFileContent = async (filePath, content) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return await response.text();
|
return await response.text();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const deleteFile = async (filePath) => {
|
export const deleteFile = async (filePath) => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${API_BASE_URL}/files/${filePath}`, {
|
const response = await fetch(`${API_BASE_URL}/files/${filePath}`, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
@@ -55,9 +55,9 @@ export const saveFileContent = async (filePath, content) => {
|
|||||||
console.error('Error deleting file:', error);
|
console.error('Error deleting file:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fetchUserSettings = async (userId) => {
|
export const fetchUserSettings = async (userId) => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${API_BASE_URL}/settings?userId=${userId}`);
|
const response = await fetch(`${API_BASE_URL}/settings?userId=${userId}`);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -82,7 +82,9 @@ export const saveUserSettings = async (settings) => {
|
|||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorData = await response.json().catch(() => null);
|
const errorData = await response.json().catch(() => null);
|
||||||
throw new Error(errorData?.message || `HTTP error! status: ${response.status}`);
|
throw new Error(
|
||||||
|
errorData?.message || `HTTP error! status: ${response.status}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await response.json();
|
return await response.json();
|
||||||
@@ -105,9 +107,9 @@ export const pullChanges = async () => {
|
|||||||
console.error('Error pulling changes:', error);
|
console.error('Error pulling changes:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const commitAndPush = async (message) => {
|
export const commitAndPush = async (message) => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${API_BASE_URL}/git/commit`, {
|
const response = await fetch(`${API_BASE_URL}/git/commit`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -124,4 +126,4 @@ export const pullChanges = async () => {
|
|||||||
console.error('Error committing and pushing changes:', error);
|
console.error('Error committing and pushing changes:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user