测试monaco代替codemirror

This commit is contained in:
hanhh 2021-07-26 00:26:14 +08:00
parent eff109bf81
commit fd7153403d
11 changed files with 235 additions and 145 deletions

View File

@ -28,19 +28,11 @@ export default defineConfig({
externals: {
react: 'window.React',
'react-dom': 'window.ReactDOM',
codemirror: 'window.CodeMirror',
darkreader: 'window.DarkReader',
},
scripts: [
'https://gw.alipayobjects.com/os/lib/react/16.13.1/umd/react.production.min.js',
'https://gw.alipayobjects.com/os/lib/react-dom/16.13.1/umd/react-dom.production.min.js',
'https://cdn.jsdelivr.net/npm/codemirror@5.62.0/lib/codemirror.min.js',
'https://cdn.jsdelivr.net/npm/darkreader@4.9.27/darkreader.min.js',
'https://cdn.jsdelivr.net/npm/codemirror@5.62.0/mode/shell/shell.js',
'https://cdn.jsdelivr.net/npm/codemirror@5.62.0/mode/python/python.js',
'https://cdn.jsdelivr.net/npm/codemirror@5.62.0/mode/javascript/javascript.js',
'https://cdn.jsdelivr.net/npm/codemirror@5.62.0/addon/dialog/dialog.js',
'https://cdn.jsdelivr.net/npm/codemirror@5.62.0/addon/search/search.js',
'https://cdn.jsdelivr.net/npm/codemirror@5.62.0/addon/search/searchcursor.js',
'https://cdn.jsdelivr.net/npm/darkreader@4.9.34/darkreader.min.js',
],
});

View File

@ -32,18 +32,19 @@
"express": "^4.17.1",
"express-jwt": "^6.0.0",
"jsonwebtoken": "^8.5.1",
"lodash": "^4.17.21",
"nedb": "^1.8.0",
"node-fetch": "^2.6.1",
"node-schedule": "^2.0.0",
"p-queue": "6.6.2",
"reflect-metadata": "^0.1.13",
"typedi": "^0.8.0",
"lodash": "^4.17.21",
"winston": "^3.3.3"
},
"devDependencies": {
"@ant-design/icons": "^4.6.2",
"@ant-design/pro-layout": "^6.5.0",
"@monaco-editor/react": "^4.2.1",
"@types/cors": "^2.8.10",
"@types/express": "^4.17.8",
"@types/express-jwt": "^6.0.1",
@ -57,7 +58,6 @@
"@types/react-dom": "^17.0.0",
"@umijs/plugin-antd": "^0.9.1",
"@umijs/test": "^3.3.9",
"codemirror": "^5.59.4",
"compression-webpack-plugin": "6.1.1",
"darkreader": "^4.9.27",
"lint-staged": "^10.0.7",
@ -65,11 +65,11 @@
"prettier": "^2.2.0",
"qrcode.react": "^1.0.1",
"react": "17.x",
"react-codemirror2": "^7.2.1",
"react-diff-viewer": "^3.1.1",
"react-dnd": "^14.0.2",
"react-dnd-html5-backend": "^14.0.0",
"react-dom": "17.x",
"react-split-pane": "^0.1.92",
"ts-node": "^9.0.0",
"typescript": "^4.1.2",
"umi": "^3.3.9",

View File

@ -3,6 +3,7 @@ lockfileVersion: 5.3
specifiers:
'@ant-design/icons': ^4.6.2
'@ant-design/pro-layout': ^6.5.0
'@monaco-editor/react': ^4.2.1
'@types/cors': ^2.8.10
'@types/express': ^4.17.8
'@types/express-jwt': ^6.0.1
@ -18,7 +19,6 @@ specifiers:
'@umijs/test': ^3.3.9
body-parser: ^1.19.0
celebrate: ^13.0.3
codemirror: ^5.59.4
compression-webpack-plugin: 6.1.1
cors: ^2.8.5
cron-parser: ^3.5.0
@ -37,11 +37,11 @@ specifiers:
prettier: ^2.2.0
qrcode.react: ^1.0.1
react: 17.x
react-codemirror2: ^7.2.1
react-diff-viewer: ^3.1.1
react-dnd: ^14.0.2
react-dnd-html5-backend: ^14.0.0
react-dom: 17.x
react-split-pane: ^0.1.92
reflect-metadata: ^0.1.13
ts-node: ^9.0.0
typedi: ^0.8.0
@ -74,6 +74,7 @@ dependencies:
devDependencies:
'@ant-design/icons': 4.6.2_react-dom@17.0.2+react@17.0.2
'@ant-design/pro-layout': 6.18.0_react-dom@17.0.2+react@17.0.2
'@monaco-editor/react': 4.2.1_react-dom@17.0.2+react@17.0.2
'@types/cors': 2.8.10
'@types/express': 4.17.11
'@types/express-jwt': 6.0.1
@ -87,7 +88,6 @@ devDependencies:
'@types/react-dom': 17.0.5
'@umijs/plugin-antd': 0.9.1_5ccfec03b6e15849b3687a64fe975f75
'@umijs/test': 3.4.20_ts-node@9.1.1
codemirror: 5.61.0
compression-webpack-plugin: 6.1.1_webpack@5.37.0
darkreader: 4.9.32
lint-staged: 10.5.4
@ -95,11 +95,11 @@ devDependencies:
prettier: 2.3.0
qrcode.react: 1.0.1_react@17.0.2
react: 17.0.2
react-codemirror2: 7.2.1_codemirror@5.61.0+react@17.0.2
react-diff-viewer: 3.1.1_react-dom@17.0.2+react@17.0.2
react-dnd: 14.0.2_695545ed68ea337339babea285839fc0
react-dnd-html5-backend: 14.0.0
react-dom: 17.0.2_react@17.0.2
react-split-pane: 0.1.92_react-dom@17.0.2+react@17.0.2
ts-node: 9.1.1_typescript@4.2.4
typescript: 4.2.4
umi: 3.4.20
@ -866,6 +866,28 @@ packages:
chalk: 4.1.1
dev: true
/@monaco-editor/loader/1.1.1:
resolution: {integrity: sha512-mkT4r4xDjIyOG9o9M6rJDSzEIeonwF80sYErxEvAAL4LncFVdcbNli8Qv6NDqF6nyv6sunuKkDzo4iFjxPL+uQ==}
peerDependencies:
monaco-editor: '>= 0.21.0 < 1'
dependencies:
state-local: 1.0.7
dev: true
/@monaco-editor/react/4.2.1_react-dom@17.0.2+react@17.0.2:
resolution: {integrity: sha512-yN8qVY0PyFIbqPjfrZ5TbR/wrcfeiwoys8+0QkmyfiOzG74vXxSBOPIUxk7Ly+qCj7qWHPq1uDJskzFGaIqaPA==}
peerDependencies:
monaco-editor: '>= 0.25.0 < 1'
react: ^16.8.0 || ^17.0.0
react-dom: ^16.8.0 || ^17.0.0
dependencies:
'@monaco-editor/loader': 1.1.1
prop-types: 15.7.2
react: 17.0.2
react-dom: 17.0.2_react@17.0.2
state-local: 1.0.7
dev: true
/@npmcli/move-file/1.1.2:
resolution: {integrity: sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==}
engines: {node: '>=10'}
@ -2546,10 +2568,6 @@ packages:
engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
dev: true
/codemirror/5.61.0:
resolution: {integrity: sha512-D3wYH90tYY1BsKlUe0oNj2JAhQ9TepkD51auk3N7q+4uz7A/cgJ5JsWHreT0PqieW1QhOuqxQ2reCXV1YXzecg==}
dev: true
/collect-v8-coverage/1.0.1:
resolution: {integrity: sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==}
dev: true
@ -7592,16 +7610,6 @@ packages:
strip-json-comments: 2.0.1
dev: true
/react-codemirror2/7.2.1_codemirror@5.61.0+react@17.0.2:
resolution: {integrity: sha512-t7YFmz1AXdlImgHXA9Ja0T6AWuopilub24jRaQdPVbzUJVNKIYuy3uCFZYa7CE5S3UW6SrSa5nAqVQvtzRF9gw==}
peerDependencies:
codemirror: 5.x
react: '>=15.5 <=16.x'
dependencies:
codemirror: 5.61.0
react: 17.0.2
dev: true
/react-diff-viewer/3.1.1_react-dom@17.0.2+react@17.0.2:
resolution: {integrity: sha512-rmvwNdcClp6ZWdS11m1m01UnBA4OwYaLG/li0dB781e/bQEzsGyj+qewVd6W5ztBwseQ72pO7nwaCcq5jnlzcw==}
engines: {node: '>= 8'}
@ -7743,6 +7751,25 @@ packages:
tiny-warning: 1.0.3
dev: true
/react-split-pane/0.1.92_react-dom@17.0.2+react@17.0.2:
resolution: {integrity: sha512-GfXP1xSzLMcLJI5BM36Vh7GgZBpy+U/X0no+VM3fxayv+p1Jly5HpMofZJraeaMl73b3hvlr+N9zJKvLB/uz9w==}
peerDependencies:
react: ^16.0.0-0
react-dom: ^16.0.0-0
dependencies:
prop-types: 15.7.2
react: 17.0.2
react-dom: 17.0.2_react@17.0.2
react-lifecycles-compat: 3.0.4
react-style-proptype: 3.2.2
dev: true
/react-style-proptype/3.2.2:
resolution: {integrity: sha512-ywYLSjNkxKHiZOqNlso9PZByNEY+FTyh3C+7uuziK0xFXu9xzdyfHwg4S9iyiRRoPCR4k2LqaBBsWVmSBwCWYQ==}
dependencies:
prop-types: 15.7.2
dev: true
/react-tween-state/0.1.5:
resolution: {integrity: sha1-6YsGZVHvuTy5LdG+FJlcLj3q4zk=}
dependencies:
@ -8595,6 +8622,10 @@ packages:
escape-string-regexp: 2.0.0
dev: true
/state-local/1.0.7:
resolution: {integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==}
dev: true
/static-extend/0.1.2:
resolution: {integrity: sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=}
engines: {node: '>=0.10.0'}

View File

@ -13,15 +13,32 @@ body {
background-color: rgb(248, 248, 248);
}
@import '~codemirror/lib/codemirror.css';
@import '~codemirror/addon/dialog/dialog.css';
.ant-modal {
padding-bottom: 0 !important;
width: 580px !important;
}
.ql-container-wrapper {
.CodeMirror {
position: absolute;
height: calc(100vh - 128px);
height: calc(100vh - var(--vh-offset, 0px) - 128px);
width: calc(100% - 32px);
.monaco-editor:not(.rename-box) {
height: calc(100vh - 128px) !important;
height: calc(100vh - var(--vh-offset, 0px) - 128px) !important;
.view-overlays .current-line{
border-width: 0;
}
}
.log-modal {
.monaco-editor:not(.rename-box) {
height: calc(100vh - 176px) !important;
height: calc(100vh - var(--vh-offset, 0px) - 176px) !important;
background-color: transparent !important;
}
}
.rename-box {
height: 0;
.rename-input{
height: 0;
padding: 0 !important;
}
}
@ -29,7 +46,7 @@ body {
max-width: unset !important;
overflow: auto;
.ant-pro-page-container-children-content {
overflow: auto;
overflow: visible;
height: calc(100vh - 96px);
height: calc(100vh - var(--vh-offset, 0px) - 96px);
background-color: #fff;
@ -127,15 +144,14 @@ input:-webkit-autofill:active {
height: calc(100vh - 184px);
height: calc(100vh - var(--vh-offset, 0px) - 184px);
}
.CodeMirror {
height: calc(100vh - 216px);
height: calc(100vh - var(--vh-offset, 0px) - 216px);
width: calc(100vw - 80px);
.monaco-editor:not(.rename-box) {
height: calc(100vh - 216px) !important;
height: calc(100vh - var(--vh-offset, 0px) - 216px) !important;
}
}
.CodeMirror {
height: calc(100vh - 176px);
height: calc(100vh - var(--vh-offset, 0px) - 176px);
.monaco-editor:not(.rename-box) {
height: calc(100vh - 176px) !important;
height: calc(100vh - var(--vh-offset, 0px) - 176px) !important;
}
}

View File

@ -2,8 +2,8 @@ import React, { PureComponent, Fragment, useState, useEffect } from 'react';
import { Button, message, Modal, TreeSelect } from 'antd';
import config from '@/utils/config';
import { PageContainer } from '@ant-design/pro-layout';
import { Controlled as CodeMirror } from 'react-codemirror2';
import { request } from '@/utils/http';
import Editor from "@monaco-editor/react";
const Config = () => {
const [width, setWidth] = useState('100%');
@ -14,6 +14,7 @@ const Config = () => {
const [title, setTitle] = useState('config.sh');
const [select, setSelect] = useState('config.sh');
const [data, setData] = useState<any[]>([]);
const [theme, setTheme] = useState<string>('');
const getConfig = (name: string) => {
request.get(`${config.apiPrefix}configs/${name}`).then((data: any) => {
@ -61,6 +62,22 @@ const Config = () => {
getConfig('config.sh');
}, []);
useEffect(()=>{
const media = window.matchMedia('(prefers-color-scheme: dark)');
const storageTheme = localStorage.getItem('qinglong_dark_theme');
const isDark = (media.matches && storageTheme !== 'light') || storageTheme === 'dark';
setTheme(isDark?'vs-dark':'vs');
media.addEventListener('change',(e)=>{
if(storageTheme === 'auto' || !storageTheme){
if(e.matches){
setTheme('vs-dark')
}else{
setTheme('vs');
}
}
})
},[])
return (
<PageContainer
className="ql-container-wrapper config-wrapper"
@ -93,18 +110,18 @@ const Config = () => {
},
}}
>
<CodeMirror
<Editor
defaultLanguage="shell"
value={value}
theme={theme}
options={{
lineNumbers: true,
styleActiveLine: true,
matchBrackets: true,
mode: 'shell',
fontSize: 12,
minimap: {enabled: width==='100%'},
lineNumbersMinChars: 3,
folding: false,
glyphMargin: false
}}
onBeforeChange={(editor, data, value) => {
setValue(value);
}}
onChange={(editor, data, value) => {}}
onChange={(val) => {setValue(val as string)}}
/>
</PageContainer>
);

View File

@ -6,7 +6,7 @@ import {
Loading3QuartersOutlined,
CheckCircleOutlined,
} from '@ant-design/icons';
import { Controlled as CodeMirror } from 'react-codemirror2';
import Editor from "@monaco-editor/react";
enum CrontabStatus {
'running',
@ -28,6 +28,7 @@ const CronLogModal = ({
const [loading, setLoading] = useState<any>(true);
const [excuting, setExcuting] = useState<any>(true);
const [isPhone, setIsPhone] = useState(false);
const [theme, setTheme] = useState<string>('');
const getCronLog = (isFirst?: boolean) => {
if (isFirst) {
@ -91,40 +92,50 @@ const CronLogModal = ({
setIsPhone(document.body.clientWidth < 768);
}, []);
useEffect(()=>{
const media = window.matchMedia('(prefers-color-scheme: dark)');
const storageTheme = localStorage.getItem('qinglong_dark_theme');
const isDark = (media.matches && storageTheme !== 'light') || storageTheme === 'dark';
setTheme(isDark?'vs-dark':'vs');
media.addEventListener('change',(e)=>{
if(storageTheme === 'auto' || !storageTheme){
if(e.matches){
setTheme('vs-dark')
}else{
setTheme('vs');
}
}
})
},[])
return (
<Modal
title={titleElement()}
visible={visible}
centered
bodyStyle={{
overflowY: 'auto',
maxHeight: 'calc(80vh - var(--vh-offset, 0px))',
}}
// bodyStyle={{
// overflowY: 'auto',
// maxHeight: 'calc(80vh - var(--vh-offset, 0px))',
// }}
forceRender
wrapClassName="log-modal"
onOk={() => cancel()}
onCancel={() => cancel()}
>
{!loading && value && (
<pre
style={
!isPhone
? {
whiteSpace: 'break-spaces',
lineHeight: '17px',
marginBottom: 0,
}
: {
whiteSpace: 'break-spaces',
lineHeight: '17px',
marginBottom: 0,
fontFamily: 'Source Code Pro',
width: 375,
zoom: 0.83,
}
}
>
{value}
</pre>
<Editor
defaultLanguage="shell"
value={value}
theme={theme}
options={{
fontSize: 12,
minimap: {enabled: false},
lineNumbersMinChars: 3,
folding: false,
fontFamily: 'Source Code Pro',
glyphMargin: false
}}
/>
)}
</Modal>
);

View File

@ -5,6 +5,7 @@ import { PageContainer } from '@ant-design/pro-layout';
import { request } from '@/utils/http';
import ReactDiffViewer from 'react-diff-viewer';
import './index.less';
import { DiffEditor } from "@monaco-editor/react";
const Crontab = () => {
const [width, setWidth] = useState('100%');
@ -13,6 +14,7 @@ const Crontab = () => {
const [value, setValue] = useState('');
const [sample, setSample] = useState('');
const [loading, setLoading] = useState(true);
const [theme, setTheme] = useState<string>('');
const getConfig = () => {
request.get(`${config.apiPrefix}configs/config.sh`).then((data) => {
@ -44,6 +46,22 @@ const Crontab = () => {
getSample();
}, []);
useEffect(()=>{
const media = window.matchMedia('(prefers-color-scheme: dark)');
const storageTheme = localStorage.getItem('qinglong_dark_theme');
const isDark = (media.matches && storageTheme !== 'light') || storageTheme === 'dark';
setTheme(isDark?'vs-dark':'vs');
media.addEventListener('change',(e)=>{
if(storageTheme === 'auto' || !storageTheme){
if(e.matches){
setTheme('vs-dark')
}else{
setTheme('vs');
}
}
})
},[])
return (
<PageContainer
className="ql-container-wrapper"
@ -62,37 +80,21 @@ const Crontab = () => {
},
}}
>
<ReactDiffViewer
styles={{
diffContainer: {
overflowX: 'auto',
minWidth: 768,
},
diffRemoved: {
overflowX: 'auto',
maxWidth: 300,
},
diffAdded: {
overflowX: 'auto',
maxWidth: 300,
},
line: {
wordBreak: 'break-word',
},
<DiffEditor
language={"shell"}
original={sample}
modified={value}
options={{
readOnly: true,
fontSize: 12,
minimap: {enabled: width==='100%'},
lineNumbersMinChars: 3,
folding: false,
glyphMargin: false,
renderSideBySide: width==='100%'
}}
oldValue={value}
newValue={sample}
splitView={true}
leftTitle="config.sh"
rightTitle="config.sample.sh"
disableWordDiff={true}
theme={theme}
/>
{/* <CodeDiff
style={{ height: 'calc(100vh - 72px)', overflowY: 'auto' }}
outputFormat="side-by-side"
oldStr={value}
newStr={sample}
/> */}
</PageContainer>
);
};

View File

@ -14,9 +14,7 @@
&-scroller {
flex: 1;
overflow: auto;
}
&-search {
margin-bottom: 8px;
border-right: 1px dashed #f0f0f0;
}
}
@ -29,9 +27,5 @@
.ant-pro-grid-content.wide .ant-pro-page-container-children-content {
background-color: #f8f8f8;
}
.CodeMirror {
width: calc(100% - 32px - @tree-width);
}
}
}

View File

@ -2,7 +2,7 @@ import { useState, useEffect, useCallback, Key, useRef } from 'react';
import { TreeSelect, Tree, Input } from 'antd';
import config from '@/utils/config';
import { PageContainer } from '@ant-design/pro-layout';
import { Controlled as CodeMirror } from 'react-codemirror2';
import Editor from "@monaco-editor/react";
import { request } from '@/utils/http';
import styles from './index.module.less';
@ -48,6 +48,7 @@ const Log = () => {
const [isPhone, setIsPhone] = useState(false);
const [height, setHeight] = useState<number>();
const treeDom = useRef<any>();
const [theme, setTheme] = useState<string>('');
const getLogs = () => {
setLoading(true);
@ -119,6 +120,22 @@ const Log = () => {
setHeight(treeDom.current.clientHeight);
}, []);
useEffect(()=>{
const media = window.matchMedia('(prefers-color-scheme: dark)');
const storageTheme = localStorage.getItem('qinglong_dark_theme');
const isDark = (media.matches && storageTheme !== 'light') || storageTheme === 'dark';
setTheme(isDark?'vs-dark':'vs');
media.addEventListener('change',(e)=>{
if(storageTheme === 'auto' || !storageTheme){
if(e.matches){
setTheme('vs-dark')
}else{
setTheme('vs');
}
}
})
},[])
return (
<PageContainer
className="ql-container-wrapper log-wrapper"
@ -170,19 +187,19 @@ const Log = () => {
</div>
</div>
)}
<CodeMirror
<Editor
language="shell"
theme={theme}
value={value}
options={{
lineNumbers: true,
lineWrapping: true,
styleActiveLine: true,
matchBrackets: true,
readOnly: true,
fontSize: 12,
minimap: {enabled: width==='100%'},
lineNumbersMinChars: 3,
folding: false,
glyphMargin: false
}}
onBeforeChange={(editor, data, value) => {
setValue(value);
}}
onChange={(editor, data, value) => {}}
onChange={(val, ev) => {setValue(val as string)}}
/>
</div>
</PageContainer>

View File

@ -14,9 +14,7 @@
&-scroller {
flex: 1;
overflow: auto;
}
&-search {
margin-bottom: 8px;
border-right: 1px dashed #f0f0f0;
}
}
@ -29,9 +27,5 @@
.ant-pro-grid-content.wide .ant-pro-page-container-children-content {
background-color: #f8f8f8;
}
.CodeMirror {
width: calc(100% - 32px - @tree-width);
}
}
}

View File

@ -2,7 +2,7 @@ import { useState, useEffect, useCallback, Key, useRef } from 'react';
import { TreeSelect, Tree, Input } from 'antd';
import config from '@/utils/config';
import { PageContainer } from '@ant-design/pro-layout';
import { Controlled as CodeMirror } from 'react-codemirror2';
import Editor from "@monaco-editor/react";
import { request } from '@/utils/http';
import styles from './index.module.less';
@ -39,6 +39,7 @@ const Script = () => {
const [mode, setMode] = useState('');
const [height, setHeight] = useState<number>();
const treeDom = useRef<any>();
const [theme, setTheme] = useState<string>('');
const getScripts = () => {
setLoading(true);
@ -94,6 +95,22 @@ const Script = () => {
setHeight(treeDom.current.clientHeight);
}, []);
useEffect(()=>{
const media = window.matchMedia('(prefers-color-scheme: dark)');
const storageTheme = localStorage.getItem('qinglong_dark_theme');
const isDark = (media.matches && storageTheme !== 'light') || storageTheme === 'dark';
setTheme(isDark?'vs-dark':'vs');
media.addEventListener('change',(e)=>{
if(storageTheme === 'auto' || !storageTheme){
if(e.matches){
setTheme('vs-dark')
}else{
setTheme('vs');
}
}
})
},[])
return (
<PageContainer
className="ql-container-wrapper log-wrapper"
@ -145,20 +162,19 @@ const Script = () => {
</div>
</div>
)}
<CodeMirror
<Editor
language={mode}
value={value}
theme={theme}
options={{
lineNumbers: true,
lineWrapping: true,
styleActiveLine: true,
matchBrackets: true,
mode,
readOnly: true,
fontSize: 12,
minimap: {enabled: width==='100%'},
lineNumbersMinChars: 3,
folding: false,
glyphMargin: false
}}
onBeforeChange={(editor, data, value) => {
setValue(value);
}}
onChange={(editor, data, value) => {}}
onChange={(val) => {setValue(val as string)}}
/>
</div>
</PageContainer>