-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrenderer.js
501 lines (464 loc) · 13.9 KB
/
renderer.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
const { ipcRenderer, app , clipboard} = require('electron');
const menu = require('./menu');
const path = require('path');
const {BrowserWindow, dialog, Menu , getCurrentWindow } = require('@electron/remote')
const fs = require('fs');
let isSave = true; // 初始状态无需保存
let currentFile = null; // 初始状态无文件路径
let isQuit = true;
let isInit = false;
let originalContent = null;
let isOutlineVisible = false;
let isBottomBarVisible = true;
let isEditorLock = false;
let isSourceMode = false;
let isTyprwriterMode = false;
var config = {
height: "100%",
value: "",
mode: "ir",
preview: {
mode: "editor"
},
toolbar:[],
toolbarConfig : {
hide : true,
pin : true
},
outline:{
enable: false,
position: "left"
},
typewriterMode: false,
cdn: "./node_modules/vditor",
after: () => {
if (!isInit){
initMd();
}
},
counter :{
enable : true,
type: "markdown",
after: (length, counter) => {
document.getElementById('wordCount').textContent = `${length} 词`;
contentModification();
}
},
// _lutePath: './node_modules/vditor/src/js/lute/lute.min.js'
};
var vditor = new Vditor('vditor', config);
// 创建菜单
const contextMenu = Menu.buildFromTemplate(menu.contextMenuTemplate);
window.addEventListener('contextmenu', (e) => {
e.preventDefault(); // 阻止默认的右键菜单
contextMenu.popup(BrowserWindow.getFocusedWindow());
});
menu.emitter.on("editor", handleEditorCommand);
function handleEditorCommand(args) {
switch (args) {
case 'bold':
const boldContent = "**" + vditor.getSelection() + "**";
document.execCommand('insertText', false, boldContent);
break;
case 'italic':
const italicContent = "*" + vditor.getSelection() + "*";
document.execCommand('insertText', false, italicContent);
break;
case 'underline':
const underlineContent = "<u>" + vditor.getSelection() + "</u>";
document.execCommand('insertText', false, underlineContent);
break;
case 'code':
const codeContent = "`" + vditor.getSelection() + "`";
document.execCommand('insertText', false, codeContent);
break;
case 'deleteline':
const deletelineContent = "~~" + vditor.getSelection() + "~~";
document.execCommand('insertText', false, deletelineContent);
break;
case 'comment':
const commentContent = "<!--" + vditor.getSelection() + "-->";
document.execCommand('insertText', false, commentContent);
break;
case 'ordered-list':
const orderedlistContent = "1. " + vditor.getSelection();
document.execCommand('insertText', false, orderedlistContent);
break;
case 'unordered-list':
const unorderedlistContent = "- " + vditor.getSelection();
document.execCommand('insertText', false, unorderedlistContent);
break;
case 'task-list':
const tasklistContent = "- [ ] " + vditor.getSelection();
document.execCommand('insertText', false, tasklistContent);
break;
case 'set-quote':
setQuote();
break;
case 'copy2text':
const content = vditor.getSelection();
if (content) {
clipboard.writeText(content);
}
break;
case 'paste2md':
vditor.insertValue(vditor.html2md(clipboard.readText()));
break;
}
}
ipcRenderer.on('editor', (event, args) => {
handleEditorCommand(args);
});
ipcRenderer.on('view', (event, args) => {
switch (args) {
case 'toggleOutline':
toggleOutline(!isOutlineVisible);
break;
case 'toggleBottomBar':
toggleBottomBar(!isBottomBarVisible);
break;
case 'toggleEditorLock':
toggleEditorLock(!isEditorLock);
break;
case 'toggleTypewriterMode':
toggleTypewriterMode(!isTyprwriterMode);
break;
case 'toggleSourceMode':
toggleSourceMode(!isSourceMode);
break;
}
});
ipcRenderer.on('theme', (event, args) => {
switch (args) {
case 'classic':
vditor.setTheme("classic","light");
break;
case 'dark':
vditor.setTheme("dark","dark")
break;
}
});
ipcRenderer.on('help', (event, args) => {
switch (args) {
case 'about':
dialog.showMessageBox(menu.aboutDialog);
break;
}
});
ipcRenderer.on('action',(event, args) =>{
switch (args) {
case 'new':
initMd();
break;
case 'open':
askSaveNeed();
openFile();
break;
case 'save':
saveCurrentMd();
break;
case 'save-as':
currentFile = null;
saveCurrentMd();
break;
case 'quit':
askSaveNeed();
if(isQuit) {
ipcRenderer.send('exit');
}
break;
}
});
menu.emitter.on("set-title", (numberOfHashes) => {
setTitle(numberOfHashes)
});
ipcRenderer.on('set-title', (event,numberOfHashes) => {
setTitle(numberOfHashes)
});
function setTitle(numberOfHashes) {
const hashes = '#'.repeat(numberOfHashes);
const content = hashes + " " + vditor.getSelection();
vditor.focus();
document.execCommand('insertText', false, content);
}
ipcRenderer.on('edit', (event,args) => {
handleEditCommand(args);
});
menu.emitter.on("edit", handleEditCommand);
function handleEditCommand(args) {
switch (args) {
case 'set-formula':
setFormula();
break;
case 'set-code':
setCode();
break;
case 'set-quote':
setQuote();
break;
case 'set-footnote':
setFootnote();
break;
case 'set-divider':
setDivider();
break;
}
}
function setDivider() { //水平分割线
const content = '______';
vditor.focus();
document.execCommand('insertText', false, content);
}
function setCode() { //代码块
const content = '```';
vditor.focus();
document.execCommand('insertText', false, content);
}
function setFormula() { //公式块
const content = '$$';
vditor.focus();
document.execCommand('insertText', false, content);
}
function setFootnote() {
const hashes = '[^]:';
const content = hashes + " " + vditor.getSelection();
vditor.focus();
document.execCommand('insertText', false, content);
}
function setQuote() {
const hashes = '>';
const content = hashes + " " + vditor.getSelection();
vditor.focus();
document.execCommand('insertText', false, content);
}
ipcRenderer.on('open-cmd', (event, currentFilePath) => {
currentFile = currentFilePath;
const txtRead = readText(currentFile);
vditor.setValue(txtRead);
document.title = currentFile + ' - Hypora';
originalContent = vditor.getValue();
isSave = true;
isInit = true;
});
function toggleTypewriterMode(bool) {
isTyprwriterMode = bool;
config.value = vditor.getValue();
if(isTyprwriterMode)
{
config.typewriterMode = true;
vditor.destroy();
vditor = new Vditor('vditor',config);
showToast('可在视图菜单中关闭','打字机模式 已开启');
}
else if(!isTyprwriterMode)
{
config.typewriterMode = false;
vditor.destroy();
vditor = new Vditor('vditor',config);
}
toggleEditorLock(isEditorLock);
toggleOutline(isOutlineVisible)
toggleSourceMode(isSourceMode);
}
function toggleSourceMode(bool) {
isSourceMode = bool;
config.value = vditor.getValue();
if(isSourceMode)
{
config.mode = "sv";
vditor.destroy();
vditor = new Vditor('vditor',config);
}
else if(!isSourceMode)
{
config.mode = "ir";
vditor.destroy();
vditor = new Vditor('vditor',config);
}
toggleEditorLock(isEditorLock);
toggleOutline(isOutlineVisible);
toggleTypewriterMode(isTyprwriterMode);
}
function toggleEditorLock(bool) {
isEditorLock = bool
if(isEditorLock == false)
{
vditor.enable();
}
else if(isEditorLock == true)
{
vditor.disabled();
showToast('可在视图菜单中关闭','只读模式 已开启');
}
}
function toggleOutline(bool) {
const outlineElement = document.querySelector('.vditor-outline');
isOutlineVisible = bool;
if (outlineElement) {
outlineElement.style.display = isOutlineVisible ? 'block' : 'none';
}
}
function toggleBottomBar(bool) {
const bottomBar = document.getElementById('bottomBar');
isBottomBarVisible = bool;
if (bottomBar) {
// 仅更改display属性,而不更改其他布局相关样式
bottomBar.style.display = isBottomBarVisible ? 'flex' : 'none';
}
// 显示一个 Toast 消息
}
var sourceCodeModeCheckbox = document.getElementById('sourceCodeMode');
sourceCodeModeCheckbox.addEventListener('change', function() {
var isChecked = sourceCodeModeCheckbox.checked;
if (isChecked) {
toggleSourceMode(true);
} else {
toggleSourceMode(false);
}
});
var outlineCheckbox = document.getElementById('outline');
outlineCheckbox.addEventListener('change', function() {
var isChecked = outlineCheckbox.checked;
if (isChecked) {
toggleOutline(true);
} else {
toggleOutline(false);
}
});
function showToast(message, title = '通知', ms = 3000) {
// 创建一个新的 div 元素,用于 Toast
const toastElement = document.createElement('div');
toastElement.className = 'toast';
toastElement.setAttribute('role', 'alert');
toastElement.setAttribute('aria-live', 'assertive');
toastElement.setAttribute('aria-atomic', 'true');
toastElement.innerHTML = `
<div class="toast-header">
<strong class="me-auto">${title}</strong>
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body">
${message}
</div>
`;
// 将 Toast 元素添加到页面中
var toastContainer = document.querySelector('.toast-container');
if (!toastContainer) {
toastContainer = document.createElement('div');
toastContainer.className = 'toast-container position-fixed top-0 end-0 p-3';
document.body.appendChild(toastContainer);
}
toastContainer.appendChild(toastElement);
// 初始化并显示 Toast
const bootstrapToast = new bootstrap.Toast(toastElement);
bootstrapToast.show();
// 如果设置了自动隐藏,添加一个计时器来关闭 Toast
if (ms > 0) {
setTimeout(() => {
bootstrapToast.hide();
}, ms);
}
}
function initMd() {
currentFile = null;
vditor.setValue('');
document.title = 'Untitled - Hypora';
isSave = true;
isInit = true;
originalContent = vditor.getValue();
ipcRenderer.send('isInit');
}
function askSaveNeed() {
// 检测是否需要执行保存命令
if (isSave) {
return;
}
// 弹窗类型为 message
const options = {
type: 'question',
message: '请问是否保存当前文档?',
buttons: [ 'Yes', 'No', 'Cancel']
}
// 处理弹窗操作结果
const selection = dialog.showMessageBoxSync(getCurrentWindow() , options);
// 按钮 yes no cansel 分别为 [0, 1, 2]
if (selection === 0) {
saveCurrentMd();
} else if(selection === 1) {
console.log('Cancel and Quit!');
} else { // 点击 cancel 或者关闭弹窗则禁止退出操作
console.log('Cancel and Hold On!');
isQuit = false; // 阻止执行退出
}
}
// 保存文档,判断新文档or旧文档
function saveCurrentMd() {
// 新文档则执行弹窗保存操作
if(!currentFile) {
const options = {
title: 'Save',
filters: [
{ name: 'Markdown Files', extensions: ['md'] },
{ name: 'All Files', extensions: ['*'] }
]
}
const paths = dialog.showSaveDialogSync(getCurrentWindow(), options);
if(paths) {
currentFile = paths;
}
}
// 旧文档直接执行保存操作
if(currentFile) {
const txtSave = vditor.getValue();
saveText(currentFile, txtSave);
isSave = true;
document.title = currentFile + " - Hypora";
}
}
function saveText( file, text ) {
const fs = require('fs');
fs.writeFileSync( file, text );
originalContent = vditor.getValue();
}
// 选择文档路径
function openFile() {
// 弹窗类型为openFile
const options = {
filters: [
{ name: 'Markdown Files', extensions: ['md'] },
{ name: 'All Files', extensions: ['*'] }
],
properties: ['openFile']
}
// 处理弹窗结果
const file = dialog.showOpenDialogSync(getCurrentWindow(), options);
if(file) {
currentFile = file[0];
const txtRead = readText(currentFile);
vditor.setValue(txtRead);
document.title = currentFile + ' - Hypora';
originalContent = vditor.getValue();
isSave = true;
isInit = true;
}
}
// 读取文档方法
function readText(file) {
const fs = require('fs');
return fs.readFileSync(file, 'utf8');
}
//
function contentModification() {
if (!(vditor === null)) {
if (isSave) {
if(originalContent !== null)
{
if(vditor.getValue() !== originalContent)
{
isSave = false;
document.title = document.title.replace(/ - /g, '● - ');
}
}
}
}
}