mirror of
https://github.com/lordmathis/lemma.git
synced 2025-11-06 07:54:22 +00:00
Migrate Settings to Mantine ui
This commit is contained in:
@@ -1,3 +1,10 @@
|
|||||||
{
|
{
|
||||||
"presets": ["@babel/preset-env", "@babel/preset-react"]
|
"presets": [
|
||||||
|
"@babel/preset-env",
|
||||||
|
["@babel/preset-react", { "runtime": "automatic" }]
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
"@babel/plugin-transform-class-properties",
|
||||||
|
"@babel/plugin-transform-runtime"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
1691
frontend/package-lock.json
generated
1691
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,7 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/LordMathis/NovaMD#readme",
|
"homepage": "https://github.com/LordMathis/NovaMD#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.25.7",
|
||||||
"@codemirror/commands": "^6.6.2",
|
"@codemirror/commands": "^6.6.2",
|
||||||
"@codemirror/lang-markdown": "^6.2.5",
|
"@codemirror/lang-markdown": "^6.2.5",
|
||||||
"@codemirror/state": "^6.4.1",
|
"@codemirror/state": "^6.4.1",
|
||||||
@@ -31,6 +32,8 @@
|
|||||||
"@geist-ui/icons": "^1.0.2",
|
"@geist-ui/icons": "^1.0.2",
|
||||||
"@mantine/core": "^7.13.2",
|
"@mantine/core": "^7.13.2",
|
||||||
"@mantine/hooks": "^7.13.2",
|
"@mantine/hooks": "^7.13.2",
|
||||||
|
"@mantine/modals": "^7.13.2",
|
||||||
|
"@mantine/notifications": "^7.13.2",
|
||||||
"@tabler/icons-react": "^3.19.0",
|
"@tabler/icons-react": "^3.19.0",
|
||||||
"codemirror": "^6.0.1",
|
"codemirror": "^6.0.1",
|
||||||
"katex": "^0.16.11",
|
"katex": "^0.16.11",
|
||||||
@@ -60,13 +63,20 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.25.2",
|
"@babel/core": "^7.25.7",
|
||||||
"@babel/preset-env": "^7.25.4",
|
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||||
"@babel/preset-react": "^7.24.7",
|
"@babel/plugin-transform-class-properties": "^7.25.7",
|
||||||
|
"@babel/plugin-transform-runtime": "^7.25.7",
|
||||||
|
"@babel/preset-env": "^7.25.7",
|
||||||
|
"@babel/preset-react": "^7.25.7",
|
||||||
"babel-loader": "^9.2.1",
|
"babel-loader": "^9.2.1",
|
||||||
"css-loader": "^7.1.2",
|
"css-loader": "^7.1.2",
|
||||||
"html-webpack-plugin": "^5.6.0",
|
"html-webpack-plugin": "^5.6.0",
|
||||||
"sass": "^1.79.3",
|
"postcss": "^8.4.47",
|
||||||
|
"postcss-loader": "^8.1.1",
|
||||||
|
"postcss-preset-mantine": "^1.17.0",
|
||||||
|
"postcss-simple-vars": "^7.0.1",
|
||||||
|
"sass": "^1.79.4",
|
||||||
"sass-loader": "^16.0.2",
|
"sass-loader": "^16.0.2",
|
||||||
"style-loader": "^4.0.0",
|
"style-loader": "^4.0.0",
|
||||||
"webpack": "^5.94.0",
|
"webpack": "^5.94.0",
|
||||||
|
|||||||
14
frontend/postcss.config.js
Normal file
14
frontend/postcss.config.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
'postcss-preset-mantine': {},
|
||||||
|
'postcss-simple-vars': {
|
||||||
|
variables: {
|
||||||
|
'mantine-breakpoint-xs': '36em',
|
||||||
|
'mantine-breakpoint-sm': '48em',
|
||||||
|
'mantine-breakpoint-md': '62em',
|
||||||
|
'mantine-breakpoint-lg': '75em',
|
||||||
|
'mantine-breakpoint-xl': '88em',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,15 +1,28 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import { GeistProvider, CssBaseline } from '@geist-ui/core';
|
import { GeistProvider, CssBaseline } from '@geist-ui/core';
|
||||||
import { MantineProvider, AppShell, Container } from '@mantine/core';
|
import {
|
||||||
|
MantineProvider,
|
||||||
|
createTheme,
|
||||||
|
AppShell,
|
||||||
|
Container,
|
||||||
|
} from '@mantine/core';
|
||||||
|
import { Notifications } from '@mantine/notifications';
|
||||||
|
import { ModalsProvider } from '@mantine/modals';
|
||||||
import Header from './components/Header';
|
import Header from './components/Header';
|
||||||
import MainContent from './components/MainContent';
|
import MainContent from './components/MainContent';
|
||||||
import { SettingsProvider, useSettings } from './contexts/SettingsContext';
|
import { SettingsProvider, useSettings } from './contexts/SettingsContext';
|
||||||
import { ModalProvider } from './contexts/ModalContext';
|
import { ModalProvider } from './contexts/ModalContext';
|
||||||
import '@mantine/core/styles.css';
|
import '@mantine/core/styles.css';
|
||||||
|
import '@mantine/notifications/styles.css';
|
||||||
import './App.scss';
|
import './App.scss';
|
||||||
|
|
||||||
|
const mantineTheme = createTheme({
|
||||||
|
/** You can add your Mantine theme overrides here */
|
||||||
|
});
|
||||||
|
|
||||||
function AppContent() {
|
function AppContent() {
|
||||||
const { settings, loading } = useSettings();
|
const { settings, loading } = useSettings();
|
||||||
|
const [opened, setOpened] = useState(false);
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <div>Loading...</div>;
|
return <div>Loading...</div>;
|
||||||
@@ -17,40 +30,21 @@ function AppContent() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<GeistProvider themeType={settings.theme}>
|
<GeistProvider themeType={settings.theme}>
|
||||||
<MantineProvider
|
<CssBaseline />
|
||||||
withGlobalStyles
|
<MantineProvider theme={mantineTheme} defaultColorScheme={settings.theme}>
|
||||||
withNormalizeCSS
|
<Notifications />
|
||||||
theme={{
|
<ModalsProvider>
|
||||||
colorScheme: settings.theme,
|
<AppShell header={{ height: 60 }} padding="md">
|
||||||
components: {
|
<AppShell.Header>
|
||||||
Container: {
|
|
||||||
defaultProps: {
|
|
||||||
sizes: {
|
|
||||||
xs: 540,
|
|
||||||
sm: 720,
|
|
||||||
md: 960,
|
|
||||||
lg: 1140,
|
|
||||||
xl: 1320,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CssBaseline />
|
|
||||||
<AppShell header={{ height: 60 }} padding="md">
|
|
||||||
<AppShell.Header>
|
|
||||||
<Container size="xl">
|
|
||||||
<Header />
|
<Header />
|
||||||
</Container>
|
</AppShell.Header>
|
||||||
</AppShell.Header>
|
<AppShell.Main>
|
||||||
|
<Container size="xl">
|
||||||
<AppShell.Main>
|
<MainContent />
|
||||||
<Container size="xl">
|
</Container>
|
||||||
<MainContent />
|
</AppShell.Main>
|
||||||
</Container>
|
</AppShell>
|
||||||
</AppShell.Main>
|
</ModalsProvider>
|
||||||
</AppShell>
|
|
||||||
</MantineProvider>
|
</MantineProvider>
|
||||||
</GeistProvider>
|
</GeistProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import React, { useReducer, useEffect, useCallback, useRef } from 'react';
|
import React, { useReducer, useEffect, useCallback, useRef } from 'react';
|
||||||
import { Modal, Spacer, Dot, useToasts } from '@geist-ui/core';
|
import { Modal, Badge, Button, Group } from '@mantine/core';
|
||||||
|
import { useDisclosure } from '@mantine/hooks';
|
||||||
|
import { notifications } from '@mantine/notifications';
|
||||||
import { useSettings } from '../contexts/SettingsContext';
|
import { useSettings } from '../contexts/SettingsContext';
|
||||||
import AppearanceSettings from './settings/AppearanceSettings';
|
import AppearanceSettings from './settings/AppearanceSettings';
|
||||||
import EditorSettings from './settings/EditorSettings';
|
import EditorSettings from './settings/EditorSettings';
|
||||||
@@ -51,7 +53,6 @@ function settingsReducer(state, action) {
|
|||||||
const Settings = () => {
|
const Settings = () => {
|
||||||
const { settings, updateSettings, updateTheme } = useSettings();
|
const { settings, updateSettings, updateTheme } = useSettings();
|
||||||
const { settingsModalVisible, setSettingsModalVisible } = useModalContext();
|
const { settingsModalVisible, setSettingsModalVisible } = useModalContext();
|
||||||
const { setToast } = useToasts();
|
|
||||||
const [state, dispatch] = useReducer(settingsReducer, initialState);
|
const [state, dispatch] = useReducer(settingsReducer, initialState);
|
||||||
const isInitialMount = useRef(true);
|
const isInitialMount = useRef(true);
|
||||||
const updateThemeTimeoutRef = useRef(null);
|
const updateThemeTimeoutRef = useRef(null);
|
||||||
@@ -71,7 +72,6 @@ const Settings = () => {
|
|||||||
const newTheme = state.localSettings.theme === 'dark' ? 'light' : 'dark';
|
const newTheme = state.localSettings.theme === 'dark' ? 'light' : 'dark';
|
||||||
dispatch({ type: 'UPDATE_LOCAL_SETTINGS', payload: { theme: newTheme } });
|
dispatch({ type: 'UPDATE_LOCAL_SETTINGS', payload: { theme: newTheme } });
|
||||||
|
|
||||||
// Debounce the theme update
|
|
||||||
if (updateThemeTimeoutRef.current) {
|
if (updateThemeTimeoutRef.current) {
|
||||||
clearTimeout(updateThemeTimeoutRef.current);
|
clearTimeout(updateThemeTimeoutRef.current);
|
||||||
}
|
}
|
||||||
@@ -84,20 +84,23 @@ const Settings = () => {
|
|||||||
try {
|
try {
|
||||||
await updateSettings(state.localSettings);
|
await updateSettings(state.localSettings);
|
||||||
dispatch({ type: 'MARK_SAVED' });
|
dispatch({ type: 'MARK_SAVED' });
|
||||||
setToast({ text: 'Settings saved successfully', type: 'success' });
|
notifications.show({
|
||||||
|
message: 'Settings saved successfully',
|
||||||
|
color: 'green',
|
||||||
|
});
|
||||||
setSettingsModalVisible(false);
|
setSettingsModalVisible(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to save settings:', error);
|
console.error('Failed to save settings:', error);
|
||||||
setToast({
|
notifications.show({
|
||||||
text: 'Failed to save settings: ' + error.message,
|
message: 'Failed to save settings: ' + error.message,
|
||||||
type: 'error',
|
color: 'red',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
if (state.hasUnsavedChanges) {
|
if (state.hasUnsavedChanges) {
|
||||||
updateTheme(state.initialSettings.theme); // Revert theme if not saved
|
updateTheme(state.initialSettings.theme);
|
||||||
dispatch({ type: 'RESET' });
|
dispatch({ type: 'RESET' });
|
||||||
}
|
}
|
||||||
setSettingsModalVisible(false);
|
setSettingsModalVisible(false);
|
||||||
@@ -117,38 +120,41 @@ const Settings = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal visible={settingsModalVisible} onClose={handleClose}>
|
<Modal
|
||||||
<Modal.Title>
|
opened={settingsModalVisible}
|
||||||
Settings
|
onClose={handleClose}
|
||||||
{state.hasUnsavedChanges && (
|
title="Settings"
|
||||||
<Dot type="warning" style={{ marginLeft: '8px' }} />
|
centered
|
||||||
)}
|
size="lg"
|
||||||
</Modal.Title>
|
>
|
||||||
<Modal.Content>
|
{state.hasUnsavedChanges && (
|
||||||
<AppearanceSettings
|
<Badge color="yellow" variant="light" mb="md">
|
||||||
themeSettings={state.localSettings.theme}
|
Unsaved Changes
|
||||||
onThemeChange={handleThemeChange}
|
</Badge>
|
||||||
/>
|
)}
|
||||||
<Spacer h={1} />
|
<AppearanceSettings
|
||||||
<EditorSettings
|
themeSettings={state.localSettings.theme}
|
||||||
autoSave={state.localSettings.autoSave}
|
onThemeChange={handleThemeChange}
|
||||||
onAutoSaveChange={(value) => handleInputChange('autoSave', value)}
|
/>
|
||||||
/>
|
<EditorSettings
|
||||||
<Spacer h={1} />
|
autoSave={state.localSettings.autoSave}
|
||||||
<GitSettings
|
onAutoSaveChange={(value) => handleInputChange('autoSave', value)}
|
||||||
gitEnabled={state.localSettings.gitEnabled}
|
/>
|
||||||
gitUrl={state.localSettings.gitUrl}
|
<GitSettings
|
||||||
gitUser={state.localSettings.gitUser}
|
gitEnabled={state.localSettings.gitEnabled}
|
||||||
gitToken={state.localSettings.gitToken}
|
gitUrl={state.localSettings.gitUrl}
|
||||||
gitAutoCommit={state.localSettings.gitAutoCommit}
|
gitUser={state.localSettings.gitUser}
|
||||||
gitCommitMsgTemplate={state.localSettings.gitCommitMsgTemplate}
|
gitToken={state.localSettings.gitToken}
|
||||||
onInputChange={handleInputChange}
|
gitAutoCommit={state.localSettings.gitAutoCommit}
|
||||||
/>
|
gitCommitMsgTemplate={state.localSettings.gitCommitMsgTemplate}
|
||||||
</Modal.Content>
|
onInputChange={handleInputChange}
|
||||||
<Modal.Action passive onClick={handleClose}>
|
/>
|
||||||
Cancel
|
<Group justify="flex-end" mt="xl">
|
||||||
</Modal.Action>
|
<Button variant="default" onClick={handleClose}>
|
||||||
<Modal.Action onClick={handleSubmit}>Save Changes</Modal.Action>
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleSubmit}>Save Changes</Button>
|
||||||
|
</Group>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text, Toggle } from '@geist-ui/core';
|
import { Text, Switch, Stack } from '@mantine/core';
|
||||||
|
|
||||||
const AppearanceSettings = ({ themeSettings, onThemeChange }) => {
|
const AppearanceSettings = ({ themeSettings, onThemeChange }) => {
|
||||||
return (
|
return (
|
||||||
<div className="setting-group">
|
<Stack spacing="xs">
|
||||||
<Text h4>Appearance</Text>
|
<Text fw={500} size="lg">
|
||||||
<div className="setting-item">
|
Appearance
|
||||||
<Text>Dark Mode</Text>
|
</Text>
|
||||||
<Toggle checked={themeSettings === 'dark'} onChange={onThemeChange} />
|
<Switch
|
||||||
</div>
|
label="Dark Mode"
|
||||||
</div>
|
checked={themeSettings === 'dark'}
|
||||||
|
onChange={onThemeChange}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,21 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text, Toggle, Tooltip } from '@geist-ui/core';
|
import { Text, Switch, Stack, Tooltip } from '@mantine/core';
|
||||||
|
|
||||||
const EditorSettings = ({ autoSave, onAutoSaveChange }) => {
|
const EditorSettings = ({ autoSave, onAutoSaveChange }) => {
|
||||||
return (
|
return (
|
||||||
<div className="setting-group">
|
<Stack spacing="xs" mt="md">
|
||||||
<Text h4>Editor</Text>
|
<Text fw={500} size="lg">
|
||||||
<div className="setting-item">
|
Editor
|
||||||
<Text>Auto Save</Text>
|
</Text>
|
||||||
<Tooltip
|
<Tooltip label="Auto Save feature is coming soon!" position="left">
|
||||||
text="Auto Save feature is coming soon!"
|
<Switch
|
||||||
type="dark"
|
label="Auto Save"
|
||||||
placement="left"
|
checked={autoSave}
|
||||||
>
|
onChange={(event) => onAutoSaveChange(event.currentTarget.checked)}
|
||||||
<Toggle
|
disabled
|
||||||
checked={autoSave}
|
/>
|
||||||
onChange={(e) => onAutoSaveChange(e.target.checked)}
|
</Tooltip>
|
||||||
disabled
|
</Stack>
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text, Toggle, Input, Spacer } from '@geist-ui/core';
|
import { Text, Switch, TextInput, Stack, PasswordInput } from '@mantine/core';
|
||||||
|
|
||||||
const GitSettings = ({
|
const GitSettings = ({
|
||||||
gitEnabled,
|
gitEnabled,
|
||||||
@@ -11,60 +11,56 @@ const GitSettings = ({
|
|||||||
onInputChange,
|
onInputChange,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className="setting-group">
|
<Stack spacing="xs" mt="md">
|
||||||
<Text h4>Git Integration</Text>
|
<Text fw={500} size="lg">
|
||||||
<div className="setting-item">
|
Git Integration
|
||||||
<Text>Enable Git</Text>
|
</Text>
|
||||||
<Toggle
|
<Switch
|
||||||
checked={gitEnabled}
|
label="Enable Git"
|
||||||
onChange={(e) => onInputChange('gitEnabled', e.target.checked)}
|
checked={gitEnabled}
|
||||||
/>
|
onChange={(event) =>
|
||||||
</div>
|
onInputChange('gitEnabled', event.currentTarget.checked)
|
||||||
<div className={gitEnabled ? '' : 'disabled'}>
|
}
|
||||||
<Input
|
/>
|
||||||
width="100%"
|
<TextInput
|
||||||
label="Git URL"
|
label="Git URL"
|
||||||
value={gitUrl}
|
value={gitUrl}
|
||||||
onChange={(e) => onInputChange('gitUrl', e.target.value)}
|
onChange={(event) => onInputChange('gitUrl', event.currentTarget.value)}
|
||||||
disabled={!gitEnabled}
|
disabled={!gitEnabled}
|
||||||
/>
|
/>
|
||||||
<Spacer h={0.5} />
|
<TextInput
|
||||||
<Input
|
label="Git Username"
|
||||||
width="100%"
|
value={gitUser}
|
||||||
label="Git Username"
|
onChange={(event) =>
|
||||||
value={gitUser}
|
onInputChange('gitUser', event.currentTarget.value)
|
||||||
onChange={(e) => onInputChange('gitUser', e.target.value)}
|
}
|
||||||
disabled={!gitEnabled}
|
disabled={!gitEnabled}
|
||||||
/>
|
/>
|
||||||
<Spacer h={0.5} />
|
<PasswordInput
|
||||||
<Input.Password
|
label="Git Token"
|
||||||
width="100%"
|
value={gitToken}
|
||||||
label="Git Token"
|
onChange={(event) =>
|
||||||
value={gitToken}
|
onInputChange('gitToken', event.currentTarget.value)
|
||||||
onChange={(e) => onInputChange('gitToken', e.target.value)}
|
}
|
||||||
disabled={!gitEnabled}
|
disabled={!gitEnabled}
|
||||||
/>
|
/>
|
||||||
<Spacer h={0.5} />
|
<Switch
|
||||||
<div className="setting-item">
|
label="Auto Commit"
|
||||||
<Text>Auto Commit</Text>
|
checked={gitAutoCommit}
|
||||||
<Toggle
|
onChange={(event) =>
|
||||||
checked={gitAutoCommit}
|
onInputChange('gitAutoCommit', event.currentTarget.checked)
|
||||||
onChange={(e) => onInputChange('gitAutoCommit', e.target.checked)}
|
}
|
||||||
disabled={!gitEnabled}
|
disabled={!gitEnabled}
|
||||||
/>
|
/>
|
||||||
</div>
|
<TextInput
|
||||||
<Spacer h={0.5} />
|
label="Commit Message Template"
|
||||||
<Input
|
value={gitCommitMsgTemplate}
|
||||||
width="100%"
|
onChange={(event) =>
|
||||||
label="Commit Message Template"
|
onInputChange('gitCommitMsgTemplate', event.currentTarget.value)
|
||||||
value={gitCommitMsgTemplate}
|
}
|
||||||
onChange={(e) =>
|
disabled={!gitEnabled}
|
||||||
onInputChange('gitCommitMsgTemplate', e.target.value)
|
/>
|
||||||
}
|
</Stack>
|
||||||
disabled={!gitEnabled}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -20,11 +20,11 @@ module.exports = (env, argv) => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.scss$/,
|
test: /\.scss$/,
|
||||||
use: ['style-loader', 'css-loader', 'sass-loader'],
|
use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.css$/,
|
test: /\.css$/,
|
||||||
use: ['style-loader', 'css-loader'],
|
use: ['style-loader', 'css-loader', 'postcss-loader'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user