Neeto - 我所期待的MD编辑器

Neeto - 我所期待的MD编辑器

或许,这个简陋的项目更应该被称为Electron && JavaScript最佳实践

Neeto的起源

最初这个项目是基于Steve Kinney的 《Electron跨平台开发实战》 一书第三章的Fire Sale项目。这本书写的挺好,通俗易懂,并且比较有借鉴价值。本来的计划是抛开那本书重新构建Neeto的架构,但有些代码我也不想做重复工作,所以就没有另开一个工程。

Neeto的背景

这个项目用了很多node仓库里的第三方模块,没有这些模块我也很难在十天左右的开发工期里完成Neeto的编写。比较核心的有下面这几个:

  • "jquery": "^3.4.1"
  • "mdui": "^0.4.3"一个Google MD设计规范的UI库
  • "showdown": "^1.9.1" Neeto使用的markdown语法解析器
  • "showdown-highlight": "^2.1.3" showdown的代码语法解析插件
  • "showdown-katex": "^0.6.0"showdown的LaTeX语法解析插件
  • "simplemde": "^1.11.2" 替代原本的textarea组件,实现更加丰富的操作

不过我在写Neeto时基本没有用到当下流行的第三方库:Vue or React or Angular,因为我想要在下个版本中用Vue重写Neeto,有很多方面都有改进的空间。并且因为我是基本上用的纯JavaScript写的,所以好多地方都十分的原始而简陋。我执意用原生js写的另一个目的就是尽可能熟悉js的一些用法,包括回调,异步,非阻塞性等等。

Neeto的设计思路

说到思路,不得提我最喜欢的Markdown编辑器-Typora 毫无疑问,我相信Typora一定是Windows与MacOs上最优秀的编辑器,简洁,功能丰富,设计优雅。我的数据结构复习与汇编复习都是用Typora完成的,它是如此的完美以至于让我都快忘记了它的一些瑕疵。

它所见即所得(WYSIWYG)的实现效果让我理所当然的觉得,一款编辑器不就应该这样吗。当你写下你的灵感,编辑器将你的灵感完整无误地渲染成你所想的样子。

因此我在改写之时,就准备将Typora作为Neeto最好的榜样,并且我希望能比Typora做的更好!

所以说,这个项目更像是补全,弥补我在Typora中未能得到的遗憾:

  • 自定义图片上传的图床√
  • 云笔记功能-未实现
  • 一次写作,多个写作平台(知乎,微信,CSDN等)完整实现效果√
  • Web网页管理云笔记-未实现
  • 小程序查看云笔记-未实现

可以比较清晰的看到,我所想要实现的几个需求基本上都与网络相关,而Typora给自己的定位就仅仅是

Readable & Writable 读写是Typora的核心功能,可以预见,作者并不准备为Typora提供与网络相关的功能,在win与mac两个平台上,也仅仅只有mac可以用一个插件来上传本地图片。

Neeto的后期计划表里也有打算实现插件系统,不过应该会是很后面

我在这个版本Version_1.0.8中,使用json文件来存储图床的配置信息,这只是实验性的尝试,并不保证长期有效性

后续我会支持更多的图床,如阿里云OSS,腾讯云COS,七牛云CDN等等。

什么是图床:

图床是网络图片的一种存储方式,Web页面通过一条URL来获取图片。用户将图片上传到图床之后,图床服务器返回一条JSON消息,其中包含上传图片的网络URL,使用图床给定的图片链接就可以在多个地方调用图片,而不会出现本地图片无法直接使用的尴尬场面。

一些简单的小功能的实现

一键复制到微信公众号平台

点击Neeto头部的菜单栏,可以看到文件菜单选项。

点击之后会出现复制成功的提示

然后直接前往微信公众号后台粘贴就可以实现带格式粘贴。

使用时需要注意的几个地方:

  • 复制时使用的图片应为网络图片,如果使用本地图片会出现上传失败的问题?
  • 尽量使用体积小的图片。

图片上传技巧

可以将图片直接拖入左侧的文本编辑区域,也可以用QQ,Wechat等软件的截图工具截图后,直接在文本编辑区内粘贴,粘贴成功后顶部会出现LightTip提示上传成功。

图片拖入后上传

这个功能是基于原书上拖入事件更改的,但我觉得是一个很有意思的部分,可以通过这一部分理解JS的处理哲学。

1
2
3
4
5
6
7
8
9
// 监听drop事件--JavaScript是事件驱动的
smde.codemirror.on("drop", function (editor, e) {
//获取文件对象
const file = getDroppedFile(e)
//获取dataTransfer对象
var df = e.dataTransfer
//处理文件
dealWithFile(file, df)
});
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
const dealWithFile = (file, df) => {
// 文件对象数组
var dropFiles = []
console.log(file)
if (fileTypeIsSupported(file)) {
// 如果这个文件是图片类型的,就去处理这个文件对象
if (file.type.indexOf("image") !== -1) {
// 获取图片的File对象
if (df.items !== undefined) {
// Chrome有items属性,对Chrome的单独处理
for (var i = 0; i < df.items.length; i++) {
var item = df.items[i];
console.log(item.getAsFile())
// 用webkitGetAsEntry禁止上传目录
if (item.kind === "file" && item.webkitGetAsEntry().isFile) {
var dropFile = item.getAsFile();
// 判断完这个对象之后压入数组中
dropFiles.push(dropFile);
}
}
}
// 上传到图床
dropFiles.forEach(file => {
// formdata的作用是构建post的请求body部分
const formdata = new FormData()
formdata.append('image', file)
// loading是加载动画的一段语句,在图片上传的过程中用于占位
smde.codemirror.doc.replaceSelection(loading)
if (baseConfig.picBedUrl) {
uploadToPicBeds(formdata, baseConfig.picBedUrl, baseConfig.token)
.then(res => {
var finalUrl = `<img src="${res}">`
smde.codemirror.doc.undoSelection()
smde.codemirror.doc.replaceSelection(finalUrl)
rendererMarkDownToHtml(smde.value())
//LightTip是LuLuUI库的一个组件,我认为挺好看的,所以很多地方都用到了这个组件
new LightTip().success('图床图片上传成功', 2000);
})
.catch(res => {
smde.codemirror.doc.undoSelection()
new LightTip().error('图床图片上传失败,请检查图床配置', 4000);
finalUrl = `![${file.name}](${file.path})`
smde.codemirror.doc.replaceSelection(finalUrl)
rendererMarkDownToHtml(smde.value())
})

} else {
var finalUrl = `![${file.name}](${file.path})`
smde.codemirror.doc.undoSelection()
smde.codemirror.doc.replaceSelection(finalUrl)
rendererMarkDownToHtml(smde.value())
new LightTip().success('本地图片添加成功', 2000);
}
});
} else {
mainProcess.openFile(currentWindow, file.path)
}
} else {
new LightTip().error('该文件类型暂时无法上传', 4000);
}
}

直面问题

不得不承认,有很多地方我都没有做完,甚至完成度只有$60%$,但我急于进入Vue的学习开发,就不得不将纯JavaScript的版本早点完结。

  • 左右界面的同步滚动问题,由于我一直找不到好的同步滚动解决方案,因此我采用的是两个显示部分距离顶部的长度相等,这样确实不太好很多时候都会出现奇怪的问题…
  • 左侧UI布局不是特别的合理
  • 很多按钮都是废弃的按钮,我已经没有什么精力去将他们一一修正了。
  • tasklist列表还是显示不正常

如果你使用了Neeto并且有任何意见或是反馈,请及时告知我,告知的方法也很简单,点击左侧下部的小飞机按钮,填写建议还有您的联系方式即可,万分感谢

附录

MarkDown语法

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

Markdown Guide
Emphasis
**bold**
*italics*
~~strikethrough~~
Headers
# Big header
## Medium header
### Small header
#### Tiny header
Lists
* Generic list item
* Generic list item
* Generic list item

1. Numbered list item
2. Numbered list item
3. Numbered list item

Links
[Text to display](http://www.example.com)

Quotes
> This is a quote.
> It can span multiple lines!

Images Need to upload an image? Imgur has a great interface.
![](http://www.example.com/image.jpg)

Tables
| Column 1 | Column 2 | Column 3 |
| -------- | -------- | -------- |
| John | Doe | Male |
| Mary | Smith | Female |

Or without aligning the columns...

| Column 1 | Column 2 | Column 3 |
| -------- | -------- | -------- |
| John | Doe | Male |
| Mary | Smith | Female |

Displaying code
`var example = "hello!";`

Or spanning multiple lines...

code
var example = "hello!";
alert(example);
code

LaTeX

数学公式实现:支持LaTeX语法

$\alpha, \beta, \Beta, \gamma, \Gamma, \pi, \Pi, \phi, \varphi, \mu, \Phi$

$\cos(2 \theta) = \cos ^ 2 \theta - \sin ^ 2 \theta$

$\lim_{x \to \infty}^ (e^(-x)) = 0$

写在最后

说实话吧,好长一段时间里我都听不喜欢前端的,因为在我的刻板前端不就是切图吗,然后写点小动画,有什么前途,但是说来容易做时难啊,真正放开手去做的时候才能切身感受到前端也已经不仅仅是前端了,不过,还是要两头兼顾,前后端都要掌握。

最后一些屁话,应该没有人真的会看到这里吧。说真的,大学越活越糊涂我是真的有点难受,学习也整不起来,技术也学不明白,身体也没有很好//PS:本武汉(回来的)人已经14天未见异常了,所以我不是患者!!但是我好像有点感冒…

如果你对Neeto感兴趣

可以前往Neeto官网 查看源码和更多信息,项目已经在GitHub和Gitea开源了,如果可以的话,能为我点个Star吗!!!!这对我真的很重要(cxx语气)

Neeto - 我所期待的MD编辑器

https://www.tanknee.cn/2020/01/21/neetoad/

作者

TankNee

发布于

01/21/2020

更新于

06/18/2022

许可协议

评论