Skip to content

Commit d2f9c6a

Browse files
authoredJul 4, 2021
Merge pull request #9 from BUPTlhuanyu/restructure
Restructure
2 parents ca1b0b6 + 974356e commit d2f9c6a

36 files changed

+935
-716
lines changed
 

‎.eslintrc.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"no-use-before-define": [0],
1919
"@typescript-eslint/no-use-before-define": [1],
2020
"operator-linebreak": "off",
21-
"@typescript-eslint/no-parameter-properties": "off"
21+
"@typescript-eslint/no-parameter-properties": "off",
22+
"spaced-comment": "off"
2223
}
2324
}

‎README.md

+13-1
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,25 @@
55
> 基于 vscode 架构,利用 【依赖注入】【多进程】等思想实现,用于编辑 `markdown` 文件,并输出为 【微信公众号文章】
66
77
<div align=center>
8-
<img src="./doc/cover.png" width="800px" />
8+
<img src="./doc/home.png" width="800px" />
99
</div>
1010

1111
# 工具集合
1212
- [ ] 插件化
1313
## markdown
1414

15+
<div align=center>
16+
<img src="./doc/cover.png" width="800px" />
17+
</div>
18+
19+
<div align=center>
20+
<img src="./doc/toc.png" width="800px" />
21+
</div>
22+
23+
<div align=center>
24+
<img src="./doc/search.png" width="800px" />
25+
</div>
26+
1527
### 文件功能
1628
- [x] 搜索
1729
- [x] 文件管理(保存,重命名,新建)

‎doc/cover.png

-45.3 KB
Loading

‎doc/home.png

1.19 MB
Loading

‎doc/search.png

2.33 MB
Loading

‎doc/toc.png

1.89 MB
Loading

‎package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
"release:mac": "yarn workspace workbench-electron release:mac"
1818
},
1919
"workspaces": {
20-
"packages": ["packages/*"],
20+
"packages": [
21+
"packages/*"
22+
],
2123
"nohoist": [
2224
"electron"
2325
]

‎packages/views/config-overrides.js

+48-23
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,6 @@ const publicPathPlugin = (config) => {
3737
return config;
3838
}
3939

40-
const electronConfig = (config) => {
41-
// 关闭自动打开浏览器:node_modules/react-dev-utils/openBrowser.js
42-
process.env.BROWSER = "none";
43-
config.entry = path.resolve(__dirname, './src/index.tsx');
44-
config.target = 'electron-renderer';
45-
config.externals = {
46-
electron : 'require("electron")'
47-
};
48-
return config;
49-
}
50-
5140
//更改打包是图片加载模式,解决electron打包后图片无法加载问题
5241
const customizeFileLoaderOptions = config => {
5342
for (let item of config.module.rules) {
@@ -67,21 +56,57 @@ const customizeFileLoaderOptions = config => {
6756
}
6857
}
6958

59+
const electronConfig = (config) => {
60+
// config.entry = [];
61+
// 关闭自动打开浏览器:node_modules/react-dev-utils/openBrowser.js
62+
process.env.BROWSER = "none";
63+
// config.entry = path.resolve(__dirname, './src/index.tsx');
64+
config.target = 'electron-renderer';
65+
config.externals = {
66+
electron : 'require("electron")'
67+
};
68+
return config;
69+
}
70+
7071
const webConfig = config => {
72+
// config.entry = [];
7173
config.devServer = {
7274
open: true
7375
};
74-
config.entry = path.resolve(__dirname, './src/index.web.tsx');
76+
// config.entry = path.resolve(__dirname, './src/index.web.tsx');
7577
return config;
76-
}
78+
};
7779

78-
module.exports = override(
79-
building && addWebpackPlugin(compiledHook),
80-
isWeb ? webConfig : electronConfig,
81-
publicPathPlugin,
82-
fixBabelImports("import", {
83-
libraryName: "antd", libraryDirectory: "es", style: 'css' // change importing css to less
84-
}),
85-
craBabelBugFix,
86-
buildingNotWeb && customizeFileLoaderOptions
87-
);
80+
const entry = [
81+
{
82+
entry: isWeb ? './src/pages/editor/index.web.tsx' : './src/pages/editor/index.tsx',
83+
template: 'public/index.html',
84+
outPath: '/index.html',
85+
},
86+
{
87+
entry: './src/pages/home/index.tsx',
88+
template: 'public/index.html',
89+
outPath: '/home.html',
90+
}
91+
]
92+
93+
const multipleEntry = require('react-app-rewire-multiple-entry')(entry);
94+
95+
const addEntry = () => config => {
96+
multipleEntry.addMultiEntry(config);
97+
return config;
98+
};
99+
100+
module.exports = {
101+
webpack: override(
102+
building && addWebpackPlugin(compiledHook),
103+
isWeb ? webConfig : electronConfig,
104+
publicPathPlugin,
105+
fixBabelImports("import", {
106+
libraryName: "antd", libraryDirectory: "es", style: 'css' // change importing css to less
107+
}),
108+
craBabelBugFix,
109+
buildingNotWeb && customizeFileLoaderOptions,
110+
addEntry()
111+
)
112+
}

‎packages/views/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"core": "0.0.0",
2121
"immer": "^9.0.2",
2222
"markdown-it": "^12.0.4",
23+
"react-app-rewire-multiple-entry": "^2.2.1",
2324
"react-split-pane": "^0.1.92",
2425
"web-vitals": "^0.2.4"
2526
},
104 KB
Loading

‎packages/views/src/index.scss

-11
This file was deleted.

‎packages/views/src/index.tsx

+3-27
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,3 @@
1-
import React from 'react';
2-
import ReactDOM from 'react-dom';
3-
import './index.scss';
4-
import Editor from './pages/editor';
5-
import reportWebVitals from './reportWebVitals';
6-
import EditorStore from './pages/editor/store';
7-
8-
import {registerContextMenu, registerTopMenuListener} from './electron/menu';
9-
10-
ReactDOM.render(
11-
<React.StrictMode>
12-
<EditorStore>
13-
<Editor />
14-
</EditorStore>
15-
</React.StrictMode>,
16-
document.getElementById('root')
17-
);
18-
19-
// 设置electron上下文菜单
20-
registerContextMenu();
21-
// 设置顶部菜单监听
22-
registerTopMenuListener();
23-
24-
// If you want to start measuring performance in your app, pass a function
25-
// to log results (for example: reportWebVitals(console.log))
26-
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
27-
reportWebVitals();
1+
// must be empty file
2+
// https://github.com/facebook/create-react-app/blob/64df135c29208f08a175c941a0e94d9a56d9e4af/packages/react-scripts/config/paths.js#L18
3+
export {};

‎packages/views/src/index.web.tsx

-20
This file was deleted.
+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
.pandora-editor {
2+
display: flex;
3+
height: 100%;
4+
width: 100%;
5+
font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;
6+
.editor-file-folder {
7+
width:30%;
8+
transition: all .5s;
9+
background-color: #f3f3f7;
10+
max-width: 400px;
11+
overflow: auto;
12+
}
13+
.editor-container {
14+
overflow: hidden;
15+
width:100%;
16+
transition: all .5s;
17+
.editor-wrapper {
18+
display: flex;
19+
flex-direction: row;
20+
justify-content: center;
21+
position: relative;
22+
width: 100%;
23+
height: calc(100% - 50px);
24+
.md-view-wrapper {
25+
position: relative;
26+
flex: 1;
27+
margin: auto;
28+
width: 400px;
29+
padding-left: 35px;
30+
padding-right: 55px;
31+
height: 100%;
32+
overflow: auto;
33+
.md-view-layer {
34+
height: 673px;
35+
position: relative;
36+
overflow: auto;
37+
top: 36px;
38+
overflow: auto;
39+
.md-view-container {
40+
overflow: auto;
41+
padding: 0 4px;
42+
}
43+
}
44+
.iphone-frame {
45+
position: absolute;
46+
top: 0;
47+
left: 0;
48+
width: 400px;
49+
height: 758px;
50+
background: url('../../assets/img/iPhone-11.png') no-repeat;
51+
background-size: contain;
52+
background-position: top;
53+
pointer-events: none;
54+
}
55+
}
56+
.md-editor-wrapper {
57+
flex: 1;
58+
.md-editor-container {
59+
height: 100%;
60+
& > div {
61+
height: 100%;
62+
}
63+
}
64+
}
65+
.Resizer {
66+
background: #000;
67+
opacity: 0.2;
68+
z-index: 1;
69+
-moz-box-sizing: border-box;
70+
-webkit-box-sizing: border-box;
71+
box-sizing: border-box;
72+
-moz-background-clip: padding;
73+
-webkit-background-clip: padding;
74+
background-clip: padding-box;
75+
}
76+
77+
.Resizer:hover {
78+
-webkit-transition: all 2s ease;
79+
transition: all 2s ease;
80+
}
81+
82+
.Resizer.horizontal {
83+
height: 11px;
84+
margin: -5px 0;
85+
border-top: 5px solid rgba(255, 255, 255, 0);
86+
border-bottom: 5px solid rgba(255, 255, 255, 0);
87+
cursor: row-resize;
88+
width: 100%;
89+
}
90+
91+
.Resizer.horizontal:hover {
92+
border-top: 5px solid rgba(0, 0, 0, 0.5);
93+
border-bottom: 5px solid rgba(0, 0, 0, 0.5);
94+
}
95+
96+
.Resizer.vertical {
97+
width: 11px;
98+
margin: 0 -5px;
99+
border-left: 5px solid rgba(255, 255, 255, 0);
100+
border-right: 5px solid rgba(255, 255, 255, 0);
101+
cursor: col-resize;
102+
}
103+
104+
.Resizer.vertical:hover {
105+
border-left: 5px solid rgba(0, 0, 0, 0.5);
106+
border-right: 5px solid rgba(0, 0, 0, 0.5);
107+
}
108+
.Resizer.disabled {
109+
cursor: not-allowed;
110+
}
111+
.Resizer.disabled:hover {
112+
border-color: transparent;
113+
}
114+
}
115+
}
116+
}
117+
118+
.pandora-message-success,
119+
.pandora-message-loading,
120+
.pandora-message-error
121+
{
122+
.ant-message-notice-content {
123+
padding: 5px 16px;
124+
border-radius: 8px;
125+
.ant-message-success .anticon {
126+
color: #ffb531;
127+
}
128+
}
129+
}
+203
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
import './editor.scss';
2+
3+
import React, {useRef, useEffect, useCallback, useState, useContext} from 'react';
4+
import {useMount} from 'ahooks';
5+
import useCodemirror from '../../components/useCodemirror';
6+
import {FileContext} from './store/sidbar';
7+
import {EditorContext} from 'views/src/pages/editor/store/editor';
8+
9+
import SplitPane, {Pane} from 'react-split-pane';
10+
import Sider from './components/sider';
11+
import MdView from '../../components/md-view';
12+
import ToolBar from './components/tool-bar';
13+
import Footer from './components/footer';
14+
15+
import {success, error} from '../../utils/message';
16+
import {isFilePath} from 'views/src/utils/tools';
17+
18+
import {fileEvent, FS_SAVE} from '../../utils/event';
19+
import {pandora} from 'views/src/services/pandora';
20+
21+
function Editor() {
22+
const [storeState] = useContext(FileContext);
23+
const {
24+
code,
25+
setCode,
26+
setCodemirrorEle,
27+
scroll: coScroll,
28+
editor,
29+
count
30+
} = useCodemirror();
31+
32+
/* -------------------------------------------------------------------------- */
33+
/* store 全局状态 */
34+
/* -------------------------------------------------------------------------- */
35+
const [, dispatch] = useContext(EditorContext);
36+
useEffect(() => {
37+
dispatch({
38+
type: 'storeeditor',
39+
payload: editor
40+
});
41+
}, [editor]);
42+
43+
/* -------------------------------------------------------------------------- */
44+
/* 侧边栏展开 */
45+
/* -------------------------------------------------------------------------- */
46+
const sideRef = useRef<HTMLDivElement | null>(null);
47+
useEffect(() => {
48+
if (sideRef && sideRef.current) {
49+
sideRef.current.style.width = storeState.sidbarOpened ? '30%' : '0px';
50+
}
51+
}, [storeState.sidbarOpened, sideRef.current]);
52+
53+
/* -------------------------------------------------------------------------- */
54+
/* 文件操作 */
55+
/* -------------------------------------------------------------------------- */
56+
const scrollTarget = useRef<number>(-1);
57+
const [mdScroll, setMdScroll] = useState<any>({
58+
scrollTop: 0,
59+
scrollHeight: 0
60+
});
61+
// 获取文件code.TODO: 确保组件没有卸载
62+
useEffect(() => {
63+
if (isFilePath(storeState.selectedFilePath) && pandora) {
64+
pandora.ipcRenderer.invoke('pandora:readFile', storeState.selectedFilePath).then((resStr: string) => {
65+
setCode(resStr);
66+
editor?.getDoc().setValue(resStr);
67+
}).catch(err => {
68+
console.warn(err);
69+
});
70+
} else {
71+
setCode('');
72+
editor?.getDoc().setValue('');
73+
}
74+
}, [storeState.selectedFilePath, setCode, editor]);
75+
76+
// 保存文件内容
77+
const saveFileCb = useCallback(async () => {
78+
const content = editor?.getDoc().getValue() || '';
79+
if (!storeState.selectedFilePath) {
80+
// TODO:保存文件弹窗
81+
error('保存文件失败');
82+
return;
83+
}
84+
// TODO:错误处理
85+
pandora && pandora.ipcRenderer.invoke('pandora:writeFile', storeState.selectedFilePath, content).then(() => {
86+
success('保存文件成功');
87+
}).catch(err => {
88+
console.log(err);
89+
});
90+
}, [storeState.selectedFilePath]);
91+
92+
// 监听保存文件内容的事件 TODO: saveFileCb 会一直变化
93+
useEffect(() => {
94+
fileEvent.removeAllListeners(FS_SAVE);
95+
fileEvent.on(FS_SAVE, saveFileCb);
96+
}, [saveFileCb]);
97+
98+
/* -------------------------------------------------------------------------- */
99+
/* 滚动相关 */
100+
/* -------------------------------------------------------------------------- */
101+
const mdRef = useRef<HTMLDivElement | null>(null);
102+
const editorRef: React.RefObject<HTMLDivElement> = useRef(null);
103+
useMount(() => {
104+
setCodemirrorEle(editorRef.current as Element);
105+
const ele = mdRef.current;
106+
if (ele) {
107+
ele.addEventListener('scroll', mdScrollHandler);
108+
ele.addEventListener('mouseover', mdMouseHandler);
109+
}
110+
return () => {
111+
mdRef.current?.removeEventListener('scroll', mdScrollHandler);
112+
mdRef.current?.removeEventListener('mouseover', mdMouseHandler);
113+
};
114+
});
115+
116+
const mdScrollHandler = useCallback(
117+
(evt: any) => {
118+
let {scrollTop, scrollHeight} = evt.target;
119+
setMdScroll({scrollTop, scrollHeight});
120+
},
121+
[],
122+
);
123+
const mdMouseHandler = useCallback(
124+
() => {
125+
scrollTarget.current = 1;
126+
},
127+
[scrollTarget],
128+
);
129+
const getCoViewEle = useCallback(
130+
(ele: HTMLDivElement) => {
131+
ele && ele.addEventListener('mouseover', () => {
132+
scrollTarget.current = 0;
133+
});
134+
},
135+
[]
136+
);
137+
138+
useEffect(() => {
139+
const layerDom = mdRef.current;
140+
let mdScrollHeight = layerDom!.scrollHeight - layerDom!.clientHeight;
141+
let coScrollHeight = coScroll.scrollHeight - coScroll.clientHeight;
142+
if (scrollTarget.current === 1) {
143+
let coScrollTop = mdScroll.scrollTop / mdScrollHeight * coScrollHeight;
144+
editor && editor.scrollTo(null, coScrollTop);
145+
}
146+
else {
147+
let mdScrollTop = coScroll.scrollTop / coScrollHeight * mdScrollHeight;
148+
mdRef.current && mdRef.current.scrollTo(0, mdScrollTop);
149+
}
150+
}, [
151+
coScroll.scrollTop,
152+
coScroll.scrollHeight,
153+
coScroll.clientHeight,
154+
mdScroll.scrollTop,
155+
mdScroll.scrollHeight,
156+
editor
157+
]);
158+
159+
return (
160+
<div className="pandora-editor">
161+
{
162+
pandora && <Sider
163+
className="editor-file-folder"
164+
ref={sideRef}
165+
/>
166+
}
167+
<div className="editor-container">
168+
<ToolBar />
169+
<div className="editor-wrapper">
170+
<SplitPane
171+
split="vertical"
172+
defaultSize="50%"
173+
paneStyle={{overflow: 'auto'}}
174+
style={{position: 'relative'}}
175+
>
176+
<Pane
177+
eleRef={getCoViewEle}
178+
className="md-editor-wrapper"
179+
style={{height: '100%'}}
180+
>
181+
<div
182+
ref={editorRef}
183+
className="md-editor-container"
184+
></div>
185+
</Pane>
186+
<Pane className="md-view-wrapper">
187+
<div ref={mdRef} className="md-view-layer">
188+
<MdView
189+
value={code}
190+
className="md-view-container"
191+
/>
192+
</div>
193+
<div className="iphone-frame"></div>
194+
</Pane>
195+
</SplitPane>
196+
</div>
197+
<Footer count={count} />
198+
</div>
199+
</div>
200+
);
201+
}
202+
203+
export default Editor;
+8-126
Original file line numberDiff line numberDiff line change
@@ -1,129 +1,11 @@
1-
.pandora-editor {
2-
display: flex;
3-
height: 100%;
1+
@import 'views/src/theme/codemirror.scss';
2+
html, body, #root, .app {
43
width: 100%;
5-
font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;
6-
.editor-file-folder {
7-
width:30%;
8-
transition: all .5s;
9-
background-color: #f3f3f7;
10-
max-width: 400px;
11-
overflow: auto;
12-
}
13-
.editor-container {
14-
overflow: hidden;
15-
width:100%;
16-
transition: all .5s;
17-
.editor-wrapper {
18-
display: flex;
19-
flex-direction: row;
20-
justify-content: center;
21-
position: relative;
22-
width: 100%;
23-
height: calc(100% - 50px);
24-
.md-view-wrapper {
25-
position: relative;
26-
flex: 1;
27-
margin: auto;
28-
width: 400px;
29-
padding-left: 35px;
30-
padding-right: 55px;
31-
height: 100%;
32-
overflow: auto;
33-
.md-view-layer {
34-
height: 673px;
35-
position: relative;
36-
overflow: auto;
37-
top: 36px;
38-
overflow: auto;
39-
.md-view-container {
40-
overflow: auto;
41-
padding: 0 4px;
42-
}
43-
}
44-
.iphone-frame {
45-
position: absolute;
46-
top: 0;
47-
left: 0;
48-
width: 400px;
49-
height: 758px;
50-
background: url('../../assets/img/iPhone-11.png') no-repeat;
51-
background-size: contain;
52-
background-position: top;
53-
pointer-events: none;
54-
}
55-
}
56-
.code-mirror-wrapper {
57-
flex: 1;
58-
.code-mirror-container {
59-
height: 100%;
60-
& > div {
61-
height: 100%;
62-
}
63-
}
64-
}
65-
.Resizer {
66-
background: #000;
67-
opacity: 0.2;
68-
z-index: 1;
69-
-moz-box-sizing: border-box;
70-
-webkit-box-sizing: border-box;
71-
box-sizing: border-box;
72-
-moz-background-clip: padding;
73-
-webkit-background-clip: padding;
74-
background-clip: padding-box;
75-
}
76-
77-
.Resizer:hover {
78-
-webkit-transition: all 2s ease;
79-
transition: all 2s ease;
80-
}
81-
82-
.Resizer.horizontal {
83-
height: 11px;
84-
margin: -5px 0;
85-
border-top: 5px solid rgba(255, 255, 255, 0);
86-
border-bottom: 5px solid rgba(255, 255, 255, 0);
87-
cursor: row-resize;
88-
width: 100%;
89-
}
90-
91-
.Resizer.horizontal:hover {
92-
border-top: 5px solid rgba(0, 0, 0, 0.5);
93-
border-bottom: 5px solid rgba(0, 0, 0, 0.5);
94-
}
95-
96-
.Resizer.vertical {
97-
width: 11px;
98-
margin: 0 -5px;
99-
border-left: 5px solid rgba(255, 255, 255, 0);
100-
border-right: 5px solid rgba(255, 255, 255, 0);
101-
cursor: col-resize;
102-
}
103-
104-
.Resizer.vertical:hover {
105-
border-left: 5px solid rgba(0, 0, 0, 0.5);
106-
border-right: 5px solid rgba(0, 0, 0, 0.5);
107-
}
108-
.Resizer.disabled {
109-
cursor: not-allowed;
110-
}
111-
.Resizer.disabled:hover {
112-
border-color: transparent;
113-
}
114-
}
115-
}
4+
height: 100%;
5+
font-family: Raleway,'Source Sans Pro',sans-serif;
1166
}
1177

118-
.pandora-message-success,
119-
.pandora-message-loading,
120-
.pandora-message-error
121-
{
122-
.ant-message-notice-content {
123-
padding: 5px 16px;
124-
border-radius: 8px;
125-
.ant-message-success .anticon {
126-
color: #ffb531;
127-
}
128-
}
129-
}
8+
body {
9+
margin: 0;
10+
padding: 0;
11+
}
+20-191
Original file line numberDiff line numberDiff line change
@@ -1,192 +1,21 @@
1+
import React from 'react';
2+
import ReactDOM from 'react-dom';
13
import './index.scss';
2-
3-
import React, {useRef, useEffect, useCallback, useState, useContext} from 'react';
4-
import {useMount} from 'ahooks';
5-
import useCodemirror from '../../components/useCodemirror';
6-
import {FileContext} from './store/sidbar';
7-
import {EditorContext} from 'views/src/pages/editor/store/editor';
8-
9-
import SplitPane, {Pane} from 'react-split-pane';
10-
import Sider from './components/sider';
11-
import MdView from '../../components/md-view';
12-
import ToolBar from './components/tool-bar';
13-
import Footer from './components/footer';
14-
15-
import {success, error} from '../../utils/message';
16-
import {isFilePath} from 'views/src/utils/tools';
17-
18-
import {fileEvent, FS_SAVE} from '../../utils/event';
19-
import {pandora} from 'views/src/services/pandora';
20-
21-
function Editor() {
22-
const [storeState] = useContext(FileContext);
23-
const [, dispatch] = useContext(EditorContext);
24-
const editorRef: React.RefObject<HTMLDivElement> = useRef(null);
25-
let {
26-
code,
27-
setCode,
28-
setCodemirrorEle,
29-
scroll: coScroll,
30-
editor,
31-
count
32-
} = useCodemirror();
33-
const mdRef = useRef<HTMLDivElement | null>(null);
34-
const sideRef = useRef<HTMLDivElement | null>(null);
35-
const scrollTarget = useRef<number>(-1);
36-
const containerHeight = useRef<number>(0);
37-
const [mdScroll, setMdScroll] = useState<any>({
38-
scrollTop: 0,
39-
scrollHeight: 0
40-
});
41-
42-
useEffect(() => {
43-
dispatch({
44-
type: 'storeeditor',
45-
payload: editor
46-
});
47-
}, [editor]);
48-
49-
useEffect(() => {
50-
if (sideRef && sideRef.current) {
51-
sideRef.current.style.width = storeState.sidbarOpened ? '30%' : '0px';
52-
}
53-
}, [storeState.sidbarOpened, sideRef.current]);
54-
55-
// 获取文件code.TODO: 确保组件没有卸载
56-
useEffect(() => {
57-
if (isFilePath(storeState.selectedFilePath) && pandora) {
58-
pandora.ipcRenderer.invoke('pandora:readFile', storeState.selectedFilePath).then((resStr: string) => {
59-
setCode(resStr);
60-
editor?.getDoc().setValue(resStr);
61-
}).catch(err => {
62-
console.warn(err);
63-
});
64-
} else {
65-
setCode('');
66-
editor?.getDoc().setValue('');
67-
}
68-
}, [storeState.selectedFilePath, setCode, editor]);
69-
70-
// 保存文件内容
71-
const saveFileCb = useCallback(async () => {
72-
const content = editor?.getDoc().getValue() || '';
73-
if (!storeState.selectedFilePath) {
74-
// TODO:保存文件弹窗
75-
error('保存文件失败');
76-
return;
77-
}
78-
// TODO:错误处理
79-
pandora && pandora.ipcRenderer.invoke('pandora:writeFile', storeState.selectedFilePath, content).then(() => {
80-
success('保存文件成功');
81-
}).catch(err => {
82-
console.log(err);
83-
});
84-
}, [storeState.selectedFilePath]);
85-
86-
// 监听保存文件内容的事件 TODO: saveFileCb 会一直变化
87-
useEffect(() => {
88-
fileEvent.removeAllListeners(FS_SAVE);
89-
fileEvent.on(FS_SAVE, saveFileCb);
90-
}, [saveFileCb]);
91-
92-
useEffect(() => {
93-
const layerDom = mdRef.current;
94-
let mdScrollHeight = layerDom!.scrollHeight - layerDom!.clientHeight;
95-
let coScrollHeight = coScroll.scrollHeight - coScroll.clientHeight;
96-
if (scrollTarget.current === 1) {
97-
let coScrollTop = mdScroll.scrollTop / mdScrollHeight * coScrollHeight;
98-
editor && editor.scrollTo(null, coScrollTop);
99-
}
100-
else {
101-
let mdScrollTop = coScroll.scrollTop / coScrollHeight * mdScrollHeight;
102-
mdRef.current && mdRef.current.scrollTo(0, mdScrollTop);
103-
}
104-
}, [
105-
coScroll.scrollTop,
106-
coScroll.scrollHeight,
107-
coScroll.clientHeight,
108-
mdScroll.scrollTop,
109-
mdScroll.scrollHeight,
110-
editor
111-
]);
112-
const mdScrollHandler = useCallback(
113-
(evt: any) => {
114-
let {scrollTop, scrollHeight} = evt.target;
115-
setMdScroll({scrollTop, scrollHeight});
116-
},
117-
[],
118-
);
119-
const mdMouseHandler = useCallback(
120-
() => {
121-
scrollTarget.current = 1;
122-
},
123-
[scrollTarget],
124-
);
125-
useMount(() => {
126-
let containerDom = document.querySelector('.editor-wrapper');
127-
containerHeight.current = containerDom ? containerDom.clientHeight : 0;
128-
setCodemirrorEle(editorRef.current as Element);
129-
const ele = mdRef.current;
130-
if (ele) {
131-
// TODO: ref 放到 MdView 上
132-
ele.addEventListener('scroll', mdScrollHandler);
133-
ele.addEventListener('mouseover', mdMouseHandler);
134-
}
135-
return () => {
136-
mdRef.current?.removeEventListener('scroll', mdScrollHandler);
137-
mdRef.current?.removeEventListener('mouseover', mdMouseHandler);
138-
};
139-
});
140-
const getCoViewEle = useCallback(
141-
(ele: HTMLDivElement) => {
142-
ele && ele.addEventListener('mouseover', () => {
143-
scrollTarget.current = 0;
144-
});
145-
},
146-
[]
147-
);
148-
return (
149-
<div className="pandora-editor">
150-
{
151-
pandora && <Sider
152-
className="editor-file-folder"
153-
ref={sideRef}
154-
/>
155-
}
156-
<div className="editor-container">
157-
<ToolBar />
158-
<div className="editor-wrapper">
159-
<SplitPane
160-
split="vertical"
161-
defaultSize="50%"
162-
paneStyle={{overflow: 'auto'}}
163-
style={{position: 'relative'}}
164-
>
165-
<Pane
166-
eleRef={getCoViewEle}
167-
className="code-mirror-wrapper"
168-
style={{height: '100%'}}
169-
>
170-
<div
171-
ref={editorRef}
172-
className="code-mirror-container"
173-
></div>
174-
</Pane>
175-
<Pane className="md-view-wrapper">
176-
<div ref={mdRef} className="md-view-layer">
177-
<MdView
178-
value={code}
179-
className="md-view-container"
180-
/>
181-
</div>
182-
<div className="iphone-frame"></div>
183-
</Pane>
184-
</SplitPane>
185-
</div>
186-
<Footer count={count} />
187-
</div>
188-
</div>
189-
);
190-
}
191-
192-
export default Editor;
4+
import Editor from './editor';
5+
import EditorStore from './store';
6+
7+
import {registerContextMenu, registerTopMenuListener} from 'views/src/electron/menu';
8+
9+
ReactDOM.render(
10+
<React.StrictMode>
11+
<EditorStore>
12+
<Editor />
13+
</EditorStore>
14+
</React.StrictMode>,
15+
document.getElementById('root')
16+
);
17+
18+
// 设置electron上下文菜单
19+
registerContextMenu();
20+
// 设置顶部菜单监听
21+
registerTopMenuListener();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import React from 'react';
2+
import ReactDOM from 'react-dom';
3+
import './index.scss';
4+
import Editor from './editor';
5+
import EditorStore from './store';
6+
7+
ReactDOM.render(
8+
<React.StrictMode>
9+
<EditorStore>
10+
<Editor />
11+
</EditorStore>
12+
</React.StrictMode>,
13+
document.getElementById('root')
14+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
.pandora-home-content {
2+
flex: 1;
3+
width: 100%;
4+
padding: 40px 8vw 0;
5+
color: #ffffff;
6+
box-sizing: border-box;
7+
overflow: auto;
8+
&-banner {
9+
background-color: #1C1C24;
10+
border-radius: 20px;
11+
width: 100%;
12+
margin: 25px 0;
13+
display: flex;
14+
flex-direction: row;
15+
justify-content: space-between;
16+
align-items: center;
17+
padding: 20px;
18+
box-sizing: border-box;
19+
&-img {
20+
width: 40vw;
21+
height: 20vw;
22+
border-radius: 20px;
23+
background: #F2C9C7;
24+
box-sizing: border-box;
25+
background: linear-gradient(288.274deg, rgba(221, 132, 255, 0.68), rgba(255, 85, 125, 0.82));
26+
}
27+
&-info {
28+
width: 36vw;
29+
height: 20vw;
30+
display: flex;
31+
flex-direction: column;
32+
justify-content: center;
33+
&-title {
34+
display: inline-block;
35+
line-height: 1.5;
36+
font-size: 28px;
37+
word-wrap: break-word;
38+
text-overflow: ellipsis;
39+
font-weight: 500;
40+
}
41+
&-desc {
42+
margin: 12px 0;
43+
display: inline-block;
44+
line-height: 1.2;
45+
font-size: 16px;
46+
overflow: auto;
47+
word-wrap: break-word;
48+
font-weight: 200;
49+
}
50+
&-time {
51+
display: inline-block;
52+
font-size: 12px;
53+
color: #A6ADB1;
54+
}
55+
}
56+
}
57+
&-box {
58+
margin: 25px 0;
59+
&-item {
60+
width: 240px;
61+
height: 268px;
62+
background-color: #1C1C24;
63+
border-radius: 20px;
64+
display: flex;
65+
flex-direction: column;
66+
align-items: center;
67+
justify-content: start;
68+
padding: 10px;
69+
box-sizing: border-box;
70+
&:hover {
71+
box-shadow: 2px 2px 6px #ffffff;
72+
}
73+
&-img {
74+
width: 200px;
75+
height: 180px;
76+
border-radius: 10px;
77+
background: url('../../../../assets/img/logo.png') no-repeat;
78+
background-size: contain;
79+
background-position: center;
80+
}
81+
&-info {
82+
width: 200px;
83+
font-size: 14px;
84+
color: #A6ADB1;
85+
margin-top: 20px;
86+
}
87+
}
88+
}
89+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* @file
3+
*/
4+
import './index.scss';
5+
import * as React from 'react';
6+
import {pandora} from 'views/src/services/pandora';
7+
8+
const prefix = 'pandora-home-content';
9+
10+
function Content() {
11+
const edEditor = React.useCallback(() => {
12+
pandora.ipcRenderer.invoke('pandora:mdEditor').then((resStr: string) => {
13+
console.log(resStr);
14+
}).catch(err => {
15+
console.warn(err);
16+
});
17+
}, []);
18+
return (
19+
<div className={`${prefix}`}>
20+
<span>Most popular</span>
21+
<div className={`${prefix}-banner`}>
22+
<div className={`${prefix}-banner-img`}></div>
23+
<div className={`${prefix}-banner-info`}>
24+
<div className={`${prefix}-banner-info-title`}>
25+
将进酒
26+
</div>
27+
<div className={`${prefix}-banner-info-desc`}>
28+
君不见黄河之水天上来②,奔流到海不复回。
29+
君不见高堂明镜悲白发③,朝如青丝暮成雪④。
30+
人生得意须尽欢⑤,莫使金樽空对月⑥。
31+
天生我材必有用⑦,千金散尽还复来⑧。
32+
烹羊宰牛且为乐,会须一饮三百杯⑨。
33+
岑夫子⑩,丹丘生⑪,将进酒,杯莫停⑫。
34+
与君歌一曲⑬,请君为我倾耳听⑭。
35+
钟鼓馔玉不足贵⑮,但愿长醉不复醒⑯。
36+
古来圣贤皆寂寞⑰,惟有饮者留其名。
37+
陈王昔时宴平乐⑱,斗酒十千恣欢谑⑲。
38+
主人何为言少钱⑳,径须沽取对君酌㉑。
39+
五花马㉒、千金裘㉓,呼儿将出换美酒㉔,与尔同销万古愁
40+
</div>
41+
<div className={`${prefix}-banner-info-time`}>
42+
唐代李白诗作
43+
</div>
44+
</div>
45+
</div>
46+
<span>Toolkits</span>
47+
<div className={`${prefix}-box`}>
48+
<div className={`${prefix}-box-item`} onClick={edEditor}>
49+
<div className={`${prefix}-box-item-img`}></div>
50+
<div className={`${prefix}-box-item-info`}>
51+
Make wechart article easy!
52+
</div>
53+
</div>
54+
</div>
55+
</div>
56+
);
57+
}
58+
59+
export default Content;

‎packages/views/src/pages/home/container/footer/index.scss

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* @file
3+
*/
4+
import './index.scss';
5+
import * as React from 'react';
6+
7+
function Footer() {
8+
return (
9+
<div className="pandora-home-footer">
10+
点击跳转
11+
</div>
12+
);
13+
}
14+
15+
export default Footer;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.pandora-home-header {
2+
-webkit-app-region: drag;
3+
background-color: #231F20;
4+
width: 100%;
5+
height: 30px;
6+
padding: 0 8vw;
7+
box-sizing: border-box;
8+
color: #ffffff;
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* @file
3+
*/
4+
import './index.scss';
5+
import * as React from 'react';
6+
7+
function Header() {
8+
return (
9+
<div className="pandora-home-header">
10+
</div>
11+
);
12+
}
13+
14+
export default Header;
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
body, html, #root, .pandora-home {
2+
border: 0;
3+
margin: 0;
4+
height: 100%;
5+
font-size: 18px;
6+
}
7+
8+
.pandora-home {
9+
display: flex;
10+
align-items: center;
11+
flex-direction: column;
12+
}
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* @file
3+
*/
4+
import './index.scss';
5+
import React from 'react';
6+
import ReactDOM from 'react-dom';
7+
8+
import Header from 'views/src/pages/home/container/header';
9+
import Content from 'views/src/pages/home/container/content';
10+
import Footer from 'views/src/pages/home/container/footer';
11+
12+
// import React, {useRef, useEffect, useCallback, useState, useContext} from 'react';
13+
14+
function Home() {
15+
return (
16+
<div className="pandora-home">
17+
<Header />
18+
<Content />
19+
<Footer />
20+
</div>
21+
);
22+
}
23+
24+
ReactDOM.render(
25+
<Home />,
26+
document.getElementById('root')
27+
);
28+
29+
export default Home;

‎packages/views/src/react-app-env.d.ts

-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
// @ts-nocheck
21
/// <reference types="react-scripts" />

‎packages/views/src/reportWebVitals.ts

-15
This file was deleted.

‎packages/views/src/setupTests.ts

-5
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/**
2+
* @file
3+
*/
4+
import {Menubar} from '../menu/menubar';
5+
import {registerContextMenuListener} from '../contextmenu/electron-main/contextmenu';
6+
7+
import path from 'path';
8+
import {app, BrowserWindow, ipcMain, Menu} from 'electron';
9+
import {DEVELOP_PORT} from 'shared/common/constant';
10+
11+
import CodeApplication, {IPC_CHANNEL} from './initService';
12+
import {Injector} from 'core/base/dependency-inject';
13+
import {IFileService} from 'services/files/files';
14+
import {FileService} from 'services/files/fileService';
15+
import {IDialogService, DialogService} from 'services/dialog/dialog';
16+
import {INativeService, NativeService} from 'services/native/native';
17+
import {ICommandService, CommandService} from 'services/command';
18+
// single service
19+
import 'services/search/electron-browser/searchServices';
20+
21+
const MODE = process.env.NODE_ENV === 'production';
22+
function createWindow() {
23+
const html = MODE
24+
? `file://${path.resolve(app.getAppPath(), './dist/index.html')}`
25+
: `http://localhost:${DEVELOP_PORT}/index.html`;
26+
const preload = MODE
27+
? path.resolve(app.getAppPath(), './dist/preload.js')
28+
: path.resolve(app.getAppPath(), './preload.js');
29+
const win = new BrowserWindow({
30+
minWidth: 800,
31+
minHeight: 600,
32+
width: 1350,
33+
height: 900,
34+
title: '',
35+
backgroundColor: '#262626',
36+
// titleBarStyle: 'hidden',
37+
// trafficLightPosition: {
38+
// x: 0,
39+
// y: 0
40+
// },
41+
webPreferences: {
42+
preload,
43+
nodeIntegration: true,
44+
enableRemoteModule: true
45+
}
46+
});
47+
48+
win.loadURL(html).catch(e => console.error(e));
49+
50+
win.webContents.on('did-finish-load', () => {
51+
// splashWindow?.destroy();
52+
});
53+
54+
win.on('close', () => {
55+
IPC_CHANNEL.forEach((item: string) => {
56+
ipcMain.removeHandler(item);
57+
});
58+
Menu.setApplicationMenu(new Menu());
59+
});
60+
}
61+
62+
export class CodeMain {
63+
constructor() {
64+
this.initServices();
65+
}
66+
async startUp(): Promise<void> {
67+
createWindow();
68+
registerContextMenuListener();
69+
}
70+
71+
private initServices() {
72+
const injector = new Injector();
73+
74+
injector.add(IFileService, {
75+
useClass: FileService
76+
});
77+
78+
injector.add(IDialogService, {
79+
useClass: DialogService
80+
});
81+
82+
injector.add(INativeService, {
83+
useClass: NativeService
84+
});
85+
86+
injector.add(ICommandService, {
87+
useClass: CommandService
88+
});
89+
90+
injector.createInstance(CodeApplication);
91+
injector.createInstance(Menubar);
92+
}
93+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/**
2+
* @file 应用启动的时候需要启动的service
3+
*/
4+
import {ipcMain} from 'electron';
5+
import {IFileService} from 'services/files/files';
6+
import {IDialogService} from 'services/dialog/dialog';
7+
import {INativeService} from 'services/native/native';
8+
import {ISearchService} from 'services/search/common/searchService';
9+
10+
export const IPC_CHANNEL = [
11+
'pandora:writeFile',
12+
'pandora:readFile',
13+
'pandora:renameFile',
14+
'pandora:renameDir',
15+
'pandora:getDirFiles',
16+
'pandora:dialog',
17+
'pandora:revealFileInOs',
18+
'pandora:moveFileToTrash',
19+
'pandora:fileSearch'
20+
];
21+
22+
class CodeApplication {
23+
constructor(
24+
@IFileService private readonly fileService: IFileService,
25+
@IDialogService private readonly dialogService: IDialogService,
26+
@INativeService private readonly nativeService: INativeService,
27+
@ISearchService private readonly diskSearch: ISearchService
28+
) {
29+
// TODO:返回数据格式
30+
IPC_CHANNEL.forEach((item: string) => {
31+
const handlerName = item.split(':')[1];
32+
ipcMain.handle(item, (this[handlerName as keyof CodeApplication] as Function).bind(this))
33+
});
34+
}
35+
36+
private async writeFile (event: Electron.IpcMainInvokeEvent, path: string, data: string) {
37+
if (typeof data !== 'string') {
38+
throw new Error('Invalid operation (vscode:writeNlsFile)');
39+
}
40+
41+
return this.fileService.writeFile(path, data);
42+
}
43+
44+
private async readFile (event: Electron.IpcMainInvokeEvent, path: string) {
45+
return this.fileService.readFile(path);
46+
}
47+
48+
private async renameFile (event: Electron.IpcMainInvokeEvent, oldPath: string, newPath: string, data: string) {
49+
const result = await this.fileService.renameFile(oldPath, newPath, data).catch(err => console.log(err));
50+
if (result) {
51+
return {
52+
success: true,
53+
data: result
54+
};
55+
} else {
56+
return {
57+
success: false,
58+
data: ''
59+
};
60+
}
61+
}
62+
63+
private async renameDir (event: Electron.IpcMainInvokeEvent, oldPath: string, newPath: string) {
64+
const result = await this.fileService.renameDir(oldPath, newPath).catch(err => console.log(err));
65+
if (result) {
66+
return {
67+
success: true,
68+
data: result
69+
};
70+
} else {
71+
return {
72+
success: false,
73+
data: ''
74+
};
75+
}
76+
}
77+
78+
private async getDirFiles (event: Electron.IpcMainInvokeEvent, dirPath: string) {
79+
return this.fileService.getDirTree(dirPath);
80+
}
81+
82+
private async dialog () {
83+
const result: Record<string, any> = await this.dialogService.openDialog();
84+
let treeData = null;
85+
if (!result.canceled && result.filePaths) {
86+
treeData = this.fileService.getDirTree(result.filePaths[0]);
87+
}
88+
return treeData;
89+
}
90+
91+
private async revealFileInOs (event: Electron.IpcMainInvokeEvent, fullpath: string) {
92+
this.nativeService.revealFileInOs(fullpath);
93+
};
94+
95+
private async moveFileToTrash (event: Electron.IpcMainInvokeEvent, fullpath: string) {
96+
this.nativeService.moveFileToTrash(fullpath);
97+
}
98+
99+
private async fileSearch (event: Electron.IpcMainInvokeEvent, dir: string, query: Record<string, string>) {
100+
const res = await this.diskSearch.fileSearch({
101+
dir,
102+
query
103+
});
104+
return {
105+
status: res.data && res.data.length && 0,
106+
data: res.data
107+
};
108+
}
109+
}
110+
111+
export default CodeApplication;

‎packages/workbench-electron/main/initService.ts

-92
This file was deleted.

‎packages/workbench-electron/main/main.ts

+37-199
Original file line numberDiff line numberDiff line change
@@ -2,188 +2,39 @@
22
* @file app
33
*/
44
import path from 'path';
5-
import {app, BrowserWindow} from 'electron';
6-
import logoIcon from '../assets/png/logo.svg';
7-
import pkg from '../package.json';
8-
import {DEVELOP_PORT} from '../../shared/common/constant';
5+
import {app, BrowserWindow, ipcMain, Menu} from 'electron';
6+
import {DEVELOP_PORT} from 'shared/common/constant';
97

108
import {registerContextMenuListener} from './contextmenu/electron-main/contextmenu';
11-
import {Menubar} from './menu/menubar';
9+
import {CodeMain} from './editor/editorMain';
1210

13-
import {Injector} from 'core/base/dependency-inject';
14-
import CodeApplication from './initService';
15-
import {IFileService} from 'services/files/files';
16-
import {FileService} from 'services/files/fileService';
17-
import {IDialogService, DialogService} from 'services/dialog/dialog';
18-
import {INativeService, NativeService} from 'services/native/native';
19-
import {ICommandService, CommandService} from 'services/command';
20-
21-
// single service
22-
import 'services/search/electron-browser/searchServices';
11+
/// #if IS_DEV
12+
import installExtension, {REACT_DEVELOPER_TOOLS} from 'electron-devtools-installer';
13+
/// #endif
2314

2415
let splashWindow: BrowserWindow | null = null;
2516

2617
const MODE = process.env.NODE_ENV === 'production';
2718

28-
// eslint-disable-next-line
29-
function createSplashWindow() {
30-
return new Promise(resolve => {
31-
const html = `
32-
<!DOCTYPE html>
33-
<html>
34-
<head>
35-
<meta charset="utf-8">
36-
<style>
37-
html {
38-
background-color: rgb(24,24,24,0.9);
39-
}
40-
body,
41-
html {
42-
width: 100%;
43-
height: 100%;
44-
margin: 0;
45-
overflow: hidden;
46-
position: relative;
47-
background-repeat: no-repeat;
48-
-webkit-user-select: none;
49-
}
50-
.logo {
51-
width: 90px;
52-
height: 90px;
53-
margin-right: 20px;
54-
}
55-
h1 {
56-
margin: 0;
57-
padding: 0;
58-
line-height: 48px;
59-
font-size: 48px;
60-
text-align: center;
61-
color: #f1f1f1;
62-
}
63-
.top {
64-
display: flex;
65-
align-items: center;
66-
justify-content: center;
67-
margin-top: 150px;
68-
}
69-
.version {
70-
margin-top: 50px;
71-
text-align: center;
72-
font-weight: bold;
73-
color: #ddd;
74-
}
75-
.tip {
76-
position: absolute;
77-
right: 20px;
78-
bottom: 20px;
79-
margin: 0;
80-
color: #999;
81-
}
82-
.animate__rubberBand {
83-
-webkit-animation-name: rubberBand;
84-
animation-name: rubberBand;
85-
}
86-
.animate__animated {
87-
-webkit-animation-duration: 1s;
88-
animation-duration: 1s;
89-
-webkit-animation-duration: var(--animate-duration);
90-
animation-duration: var(--animate-duration);
91-
-webkit-animation-fill-mode: both;
92-
animation-fill-mode: both;
93-
animation-delay: 150ms;
94-
}
95-
:root {
96-
--animate-duration: 1s;
97-
--animate-delay: 1s;
98-
--animate-repeat: 1;
99-
}
100-
@keyframes rubberBand {
101-
from {
102-
-webkit-transform: scale3d(1, 1, 1);
103-
transform: scale3d(1, 1, 1);
104-
}
105-
106-
30% {
107-
-webkit-transform: scale3d(1.25, 0.75, 1);
108-
transform: scale3d(1.25, 0.75, 1);
109-
}
110-
111-
40% {
112-
-webkit-transform: scale3d(0.75, 1.25, 1);
113-
transform: scale3d(0.75, 1.25, 1);
114-
}
115-
116-
50% {
117-
-webkit-transform: scale3d(1.15, 0.85, 1);
118-
transform: scale3d(1.15, 0.85, 1);
119-
}
120-
121-
65% {
122-
-webkit-transform: scale3d(0.95, 1.05, 1);
123-
transform: scale3d(0.95, 1.05, 1);
124-
}
125-
126-
75% {
127-
-webkit-transform: scale3d(1.05, 0.95, 1);
128-
transform: scale3d(1.05, 0.95, 1);
129-
}
130-
131-
to {
132-
-webkit-transform: scale3d(1, 1, 1);
133-
transform: scale3d(1, 1, 1);
134-
}
135-
}
136-
</style>
137-
</head>
138-
<body>
139-
<div class="top">
140-
<img class="logo" src="${logoIcon}"/>
141-
<h1 id="name" class="animate__rubberBand animate__animated">${pkg.productName}</h1>
142-
</div>
143-
<p class="version">Version ${pkg.version}</p>
144-
</body>
145-
</html>
146-
`;
147-
splashWindow = new BrowserWindow({
148-
width: 600,
149-
height: 400,
150-
show: false,
151-
frame: false,
152-
movable: true,
153-
resizable: false,
154-
autoHideMenuBar: true,
155-
transparent: true
156-
});
157-
splashWindow
158-
.loadURL('data:text/html;charset=UTF-8,' + encodeURIComponent(html))
159-
.then(() => splashWindow?.show());
160-
splashWindow.webContents.on('did-finish-load', () => {
161-
setTimeout(() => {
162-
resolve('');
163-
}, 1500);
164-
});
165-
});
166-
}
167-
168-
function createWindow() {
19+
function createHome() {
16920
const html = MODE
170-
? `file://${path.resolve(app.getAppPath(), './dist/index.html')}`
171-
: `http://localhost:${DEVELOP_PORT}/index.html`;
21+
? `file://${path.resolve(app.getAppPath(), './dist/home.html')}`
22+
: `http://localhost:${DEVELOP_PORT}/home.html`;
17223
const preload = MODE
17324
? path.resolve(app.getAppPath(), './dist/preload.js')
17425
: path.resolve(app.getAppPath(), './preload.js');
17526
const win = new BrowserWindow({
17627
minWidth: 800,
17728
minHeight: 600,
178-
width: 1350,
179-
height: 900,
29+
width: 800,
30+
height: 600,
18031
title: '',
181-
backgroundColor: '#262626',
182-
// titleBarStyle: 'hidden',
183-
// trafficLightPosition: {
184-
// x: 0,
185-
// y: 0
186-
// },
32+
backgroundColor: '#131419',
33+
titleBarStyle: 'hidden',
34+
trafficLightPosition: {
35+
x: 0,
36+
y: 0
37+
},
18738
webPreferences: {
18839
preload,
18940
nodeIntegration: true,
@@ -198,21 +49,22 @@ function createWindow() {
19849
});
19950
}
20051

201-
class CodeMain {
202-
constructor() {
52+
class Home {
53+
constructor () {
54+
Menu.setApplicationMenu(new Menu());
20355
this.registerListeners();
20456
}
205-
async startUp(): Promise<void> {
206-
this.initServices();
207-
}
208-
private registerListeners() {
57+
private registerListeners () {
20958
process.on('uncaughtException', err => this.onUnexpectedError(err));
210-
// TODO: 如何处理promise的错误
211-
// process.on('unhandledRejection', (reason: unknown) => onUnexpectedError(reason));
212-
21359
app.whenReady().then(async () => {
60+
/// #if IS_DEV
61+
installExtension(REACT_DEVELOPER_TOOLS)
62+
.then(name => console.log(`Added Extension: ${name}`))
63+
.catch(err => console.log('An error occurred: ', err));
64+
/// #endif
65+
21466
// await createSplashWindow();
215-
await createWindow();
67+
await createHome();
21668
registerContextMenuListener();
21769
});
21870

@@ -224,10 +76,11 @@ class CodeMain {
22476

22577
app.on('activate', () => {
22678
if (BrowserWindow.getAllWindows().length === 0) {
227-
createWindow();
79+
createHome();
22880
}
22981
});
23082
}
83+
23184
private onUnexpectedError(err: Error): void {
23285
if (err) {
23386
// take only the message and stack property
@@ -243,29 +96,14 @@ class CodeMain {
24396
// TODO: logService
24497
}
24598

246-
private initServices() {
247-
const injector = new Injector();
248-
249-
injector.add(IFileService, {
250-
useClass: FileService
99+
startUp() {
100+
ipcMain.handle('pandora:mdEditor', async (event, dirPath) => {
101+
const codeMain = new CodeMain();
102+
codeMain.startUp();
103+
return;
251104
});
252-
253-
injector.add(IDialogService, {
254-
useClass: DialogService
255-
});
256-
257-
injector.add(INativeService, {
258-
useClass: NativeService
259-
});
260-
261-
injector.add(ICommandService, {
262-
useClass: CommandService
263-
});
264-
265-
injector.createInstance(CodeApplication);
266-
injector.createInstance(Menubar);
267105
}
268106
}
269107

270-
const main = new CodeMain();
271-
main.startUp();
108+
const home = new Home();
109+
home.startUp();

‎packages/workbench-electron/main/menu/menubar.ts

+6
Original file line numberDiff line numberDiff line change
@@ -93,17 +93,23 @@ function __separator__(): MenuItem {
9393
}
9494

9595
export class Menubar {
96+
private oldMenus: Menu[];
9697
// private menubarMenus: {[id: string]: IMenubarMenu};
9798
constructor(
9899
@ICommandService private readonly commandService: ICommandService
99100
) {
100101
// this.menubarMenus = Object.create(null);
102+
this.oldMenus = [];
101103
this.install();
102104
}
103105

104106
// private addFallbackHandlers(): void {}
105107

106108
private install(): void {
109+
const oldMenu = Menu.getApplicationMenu();
110+
if (oldMenu) {
111+
this.oldMenus.push(oldMenu);
112+
}
107113
// Menus
108114
const menubar = new Menu();
109115

‎packages/workbench-electron/package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "workbench-electron",
3-
"version": "0.0.5",
3+
"version": "0.0.6",
44
"description": "Pandora Workbench",
55
"productName": "Pandora Workbench",
66
"private": true,
@@ -29,6 +29,8 @@
2929
"cross-env": "^7.0.3",
3030
"electron": "^11.2.0",
3131
"electron-builder": "^22.10.5",
32+
"electron-devtools-installer": "^3.2.0",
33+
"ifdef-loader": "^2.3.0",
3234
"png2icons": "^2.0.1",
3335
"ts-loader": "^8.1.0",
3436
"webpack": "^4.43.0",

‎packages/workbench-electron/webpack.config.js

+14-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ const {createConfig} = require('build-tools');
88
const compiledHook = require('build-tools/plugins/compilationFinished');
99

1010
const isProd = process.env.NODE_ENV === 'production';
11+
12+
const opts = {
13+
IS_DEV: !isProd
14+
};
15+
1116
module.exports = createConfig({
1217
target: 'electron-main',
1318
entry: {
@@ -28,7 +33,7 @@ module.exports = createConfig({
2833
loader: 'babel-loader',
2934
options: {
3035
plugins: [
31-
["@babel/plugin-proposal-decorators", { "legacy": true }],
36+
['@babel/plugin-proposal-decorators', {'legacy': true}],
3237
['@babel/plugin-proposal-class-properties'],
3338
'babel-plugin-parameter-decorator',
3439
'@babel/plugin-proposal-export-default-from',
@@ -37,9 +42,16 @@ module.exports = createConfig({
3742
],
3843
presets: [
3944
require.resolve('@babel/preset-env'),
40-
[require.resolve('@babel/preset-typescript'), {allExtensions: true, onlyRemoveTypeImports: true}]
45+
[
46+
require.resolve('@babel/preset-typescript'),
47+
{allExtensions: true, onlyRemoveTypeImports: true}
48+
]
4149
]
4250
}
51+
},
52+
{
53+
loader: 'ifdef-loader',
54+
options: opts
4355
}
4456
].filter(Boolean)
4557
}

0 commit comments

Comments
 (0)
Please sign in to comment.