Compare commits

..

No commits in common. "master" and "v5.15.11" have entirely different histories.

195 changed files with 1104 additions and 8347 deletions

View File

@ -2,8 +2,3 @@ node_modules
.git .git
dist dist
.history .history
images
docs
Dockerfile
README.md
build.sh

View File

@ -1,5 +1,6 @@
FROM mafgwo/wkhtmltopdf-nodejs:11.15.0 FROM mafgwo/wkhtmltopdf-nodejs:11.15.0
RUN mkdir -p /opt/stackedit
WORKDIR /opt/stackedit WORKDIR /opt/stackedit
COPY package*json /opt/stackedit/ COPY package*json /opt/stackedit/

125
README.md
View File

@ -1,59 +1,18 @@
<h1 align="center" style="text-align:center;"> # StackEdit中文版
<img src="chrome-app/icon-512.png" width="128" />
<br />
StackEdit中文版
</h1>
<p align="center">
<strong>笔记利器在线Markdown编辑器。</strong><br>
项目clone自<a href="https://gitee.com/mafgwo/stackedit" target="_blank" title="豆萁">豆萁/stackedit</a>如果你喜欢该项目请过去点一下Star您的肯定是作者最大的动力
</p>
<p align="center">
<a href="https://stackedit.cn/">https://stackedit.cn</a>
</p>
<p align="center">
<a target="_blank" href="https://www.apache.org/licenses/LICENSE-2.0.txt">
<img src="https://img.shields.io/:license-Apache2-blue.svg" alt="Apache 2" />
</a>
<a target="_blank" href="https://hub.docker.com/r/mafgwo/stackedit">
<img src="https://img.shields.io/docker/pulls/mafgwo/stackedit.svg" alt="Docker Pulls" />
</a>
<a target="_blank" href='https://gitee.com/mafgwo/stackedit/stargazers'>
<img src='https://gitee.com/mafgwo/stackedit/badge/star.svg' alt='gitee star'/>
</a>
</p>
<br/>
<hr />
1 笔记支持Gitee、GitHub、Gitea等Git仓库存储。<br>
2 支持直接上传图片也支持多种外部图床GitHub、Gitea、SM.MS、自定义图床粘贴或拖拽上传。<br>
3 编辑区域支持选择主题或自定义,总有你喜欢的主题。<br>
4 支持历史版本管理,不用担心编辑覆盖后无法回滚。<br>
5 支持ChatGPT辅助写作。<br>
6 支持KaTeX数学表达式、Mermaid UML图、乐谱等扩展。
<hr />
## 说明 **StackEdit中文版官方地址https://stackedit.cn**
本项目为本人clone修改自用如果你也喜欢请至原作者处获取及交流。 如果你喜欢该项目请点一下Star您的肯定是作者最大的动力
## 截图 StackEdit中文版的docker镜像地址[mafgwo/stackedit](https://hub.docker.com/r/mafgwo/stackedit)
**亮暗主题切换、编辑主题切换** **示例截图-暗色主题**
![](./images/theme.gif) ![](./images/dark.png)
**支持的文档空间** **示例截图-亮色主题**
![](./images/workspace.png) ![](./images/light.png)
**拖拽粘贴上传图片**
![](./images/uploadimg.gif)
**支持文档搜索**
![](./images/search.gif)
**ChatGPT集成协助写作**
![](./images/chatgpt.gif)
## 相比国外开源版本的区别: ## 相比国外开源版本的区别:
- 修复了Github授权登录问题 - 修复了Github授权登录问题
- 支持了Gitee仓库2022-05-25 - 支持了Gitee仓库2022-05-25
- 支持了Gitea仓库2022-05-25 - 支持了Gitea仓库2022-05-25
@ -66,33 +25,16 @@ StackEdit中文版
- 支持了右上角一键切换主题补全了深色主题的样式2022-08-07 - 支持了右上角一键切换主题补全了深色主题的样式2022-08-07
- 编辑与预览区域样式优化2022-08-10 - 编辑与预览区域样式优化2022-08-10
- 左边栏文件资源管理支持搜索文件2022-08-17 - 左边栏文件资源管理支持搜索文件2022-08-17
- 支持[TOC]目录2022-09-04
- 发布支持填写提交信息[针对Gitee、GitHub、Gitea、Gitlab]2022-09-10
- 支持文档空间关闭自动同步[针对Gitee、GitHub、Gitea、Gitlab]关闭后可自定义提交信息2022-09-23
- Gitea支持后端配置指定应用ID和Secret2022-10-03
- 支持编辑区域选择主题样式2022-10-06
- 支持图片直接存储到当前文档空间2022-10-29
- 支持MD文档之间链接跳转2022-11-20
- 支持预览区域选择主题样式2022-12-04
- Gitlab的支持优化2023-02-23
- 导出HTML、PDF支持带预览主题导出2023-02-26
- 支持分享文档2023-03-30
- 支持ChatGPT生成内容2023-04-10
- GitLab授权接口调整2023-08-26
- 主文档空间支持GitHub登录2023-10-19
## 国外开源版本弊端: ## 国外开源版本弊端:
- 作者已经不维护了
- 作者已经不维护了或很少维护了 - Github授权登录存在问题
- 不支持国内常用Gitee - 不支持国内常用Gitee
- 强依赖GoogleDrive而Google Drive在国内不能正常访问 - 强依赖GoogleDrive而Google Drive在国内不能正常访问
## 部署说明 ## 部署说明
> 建议docker-compose方式部署其他部署方式如遇到问题欢迎提issue。 > 建议docker-compose方式部署其他部署方式如遇到问题欢迎提issue。
docker官方仓库下载太慢可以使用阿里云的镜像仓库镜像仓库地址registry.cn-hangzhou.aliyuncs.com/mafgwo/stackedit:【版本号】
`docker-compose.yml`如下: `docker-compose.yml`如下:
```yaml ```yaml
@ -113,20 +55,13 @@ services:
- GITEE_CLIENT_SECRET=【不需要支持则删掉】 - GITEE_CLIENT_SECRET=【不需要支持则删掉】
- GOOGLE_CLIENT_ID=【不需要支持则删掉】 - GOOGLE_CLIENT_ID=【不需要支持则删掉】
- GOOGLE_API_KEY=【不需要支持则删掉】 - GOOGLE_API_KEY=【不需要支持则删掉】
- GITEA_CLIENT_ID=【不需要支持则删掉】
- GITEA_CLIENT_SECRET=【不需要支持则删掉】
- GITEA_URL=【不需要支持则删掉】
- GITLAB_CLIENT_ID=【不需要支持则删掉】
- GITLAB_CLIENT_SECRET=【不需要支持则删掉】
- GITLAB_URL=【不需要支持则删掉】
ports: ports:
- 8080:8080/tcp - 8080:8080/tcp
network_mode: bridge network_mode: bridge
restart: always restart: always
``` ```
docker-compose方式的启动或停止命令 启动或停止命令
```bash ```bash
# 在 docker-compose.yml 文件目录下 启动命令 # 在 docker-compose.yml 文件目录下 启动命令
docker-compose up -d docker-compose up -d
@ -135,45 +70,7 @@ docker-compose down
# 更新镜像只需要修改docker-compose.yml中镜像版本执行再停止、启动命令即可 # 更新镜像只需要修改docker-compose.yml中镜像版本执行再停止、启动命令即可
``` ```
或者可以直接通过Docker命名直接启动命令如下
```bash
docker run -itd --name stackedit \
-p 8080:8080 \
-e LISTENING_PORT=8080 \
-e ROOT_URL=/ \
-e USER_BUCKET_NAME=root \
-e DROPBOX_APP_KEY=【不需要支持则删掉】 \
-e DROPBOX_APP_KEY_FULL=【不需要支持则删掉】 \
-e GITHUB_CLIENT_ID=【不需要支持则删掉】 \
-e GITHUB_CLIENT_SECRET=【不需要支持则删掉】 \
-e GITEE_CLIENT_ID=【不需要支持则删掉】 \
-e GITEE_CLIENT_SECRET=【不需要支持则删掉】 \
-e GOOGLE_CLIENT_ID=【不需要支持则删掉】 \
-e GOOGLE_API_KEY=【不需要支持则删掉】 \
-e GITEA_CLIENT_ID=【不需要支持则删掉】 \
-e GITEA_CLIENT_SECRET=【不需要支持则删掉】 \
-e GITEA_URL=【不需要支持则删掉】 \
-e GITLAB_CLIENT_ID=【不需要支持则删掉】 \
-e GITLAB_CLIENT_SECRET=【不需要支持则删掉】 \
-e GITLAB_URL=【不需要支持则删掉】 \
mafgwo/stackedit:【docker中央仓库找到最新版本】
```
## 如何创建三方平台应用
> 部署时如果需要支持Gitee或GitHub则需要自行到对应三方平台创建应用获取到应用ID和秘钥替换到以上的环境变量中再启动应用。
- Gitee的环境变量GITEE_CLIENT_ID、GITEE_CLIENT_SECRET**[如何创建Gitee应用](./docs/部署之Gitee应用创建.md)**
- GitHub的环境变量GITHUB_CLIENT_ID、GITEE_CLIENT_SECRET**[如何创建GitHub应用](./docs/部署之GitHub应用创建.md)**
- Gitea可选择性配置环境变量未配置则在关联时前端指定有配置则仅允许配置的应用信息GITEA_CLIENT_ID、GITEA_CLIENT_SECRET、GITEA_URL**[如何创建Gitea应用](./docs/部署之Gitea应用创建.md)**
- Gitlab可选择性配置环境变量未配置则在关联时前端指定有配置则仅允许配置的应用信息GITLAB_CLIENT_ID、GITLAB_CLIENT_SECRET、GITLAB_URL **如何创建Gitlab应用(待补充文档)**
特别说明自建的Gitea、Gitlab要能接入stackedit必须支持跨域
## 编译与运行 ## 编译与运行
> 编译运行的nodejs版本选择11.15.0版本 > 编译运行的nodejs版本选择11.15.0版本
```bash ```bash

View File

@ -1,37 +0,0 @@
#!/bin/bash
# 检查参数是否提供版本号
if [ -z "$1" ]; then
echo "请提供版本号作为参数"
exit 1
fi
# 定义版本号变量
VERSION="$1"
IMAGE_NAME="mafgwo/stackedit"
# 构建 Docker 镜像
build_image() {
docker build -t "$IMAGE_NAME" .
}
# 标记 Docker 镜像
tag_image() {
docker tag "$IMAGE_NAME" "$IMAGE_NAME:$VERSION"
docker tag "$IMAGE_NAME" "registry.cn-hangzhou.aliyuncs.com/$IMAGE_NAME:$VERSION"
}
# 推送 Docker 镜像
push_image() {
docker push "$IMAGE_NAME"
docker push "registry.cn-hangzhou.aliyuncs.com/$IMAGE_NAME"
docker push "$IMAGE_NAME:$VERSION"
docker push "registry.cn-hangzhou.aliyuncs.com/$IMAGE_NAME:$VERSION"
}
# 执行构建、标记和推送
build_image
tag_image
push_image
echo "操作完成"

View File

@ -15,11 +15,6 @@ wordpressSecret: ""
paypalReceiverEmail: "" paypalReceiverEmail: ""
awsAccessKeyId: "" awsAccessKeyId: ""
awsSecretAccessKey: "" awsSecretAccessKey: ""
giteaClientId: ""
giteaClientSecret: ""
giteaUrl: ""
gitlabClientId: ""
gitlabUrl: ""
replicaCount: 1 replicaCount: 1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 960 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -1,28 +1,28 @@
{ {
"name": "StackEdit中文版", "name": "StackEdit中文版",
"description": "支持Gitee仓库/粘贴图片自动上传的浏览器内 Markdown 编辑器", "description": "支持Gitee仓库/粘贴图片自动上传的浏览器内 Markdown 编辑器",
"version": "5.15.17", "version": "1.0.0",
"manifest_version": 2, "manifest_version": 2,
"container": "GITEE", "container" : "GITEE",
"api_console_project_id": "241271498917", "api_console_project_id" : "241271498917",
"icons": { "icons": {
"16": "icon-16.png", "16": "icon-16.png",
"32": "icon-32.png", "32": "icon-32.png",
"64": "icon-64.png", "64": "icon-64.png",
"128": "icon-128.png", "128": "icon-128.png",
"256": "icon-256.png", "256": "icon-256.png",
"512": "icon-512.png" "512": "icon-512.png"
}, },
"app": { "app": {
"urls": [ "urls": [
"https://md.jonylee.top/" "https://stackedit.cn/"
], ],
"launch": { "launch": {
"web_url": "https://md.jonylee.top/app" "web_url": "https://stackedit.cn/app"
} }
}, },
"offline_enabled": true, "offline_enabled": true,
"permissions": [ "permissions": [
"unlimitedStorage" "unlimitedStorage"
] ]
} }

View File

@ -9,10 +9,4 @@ module.exports = merge(prodEnv, {
GITEE_CLIENT_ID: '"925ba7c78b85dec984f7877e4aca5cab10ae333c6d68e761bdb0b9dfb8f55672"', GITEE_CLIENT_ID: '"925ba7c78b85dec984f7877e4aca5cab10ae333c6d68e761bdb0b9dfb8f55672"',
GITEE_CLIENT_SECRET: '"f05731066e42d307339dc8ebbb037a103881dafc7207a359a393b87749f1c562"', GITEE_CLIENT_SECRET: '"f05731066e42d307339dc8ebbb037a103881dafc7207a359a393b87749f1c562"',
CLIENT_ID: '"thF3qCGLN39OtafjGnqHyj6n02WwE6xD"', CLIENT_ID: '"thF3qCGLN39OtafjGnqHyj6n02WwE6xD"',
// GITEA_CLIENT_ID: '"fe30f8f9-b1e8-4531-8f72-c1a5d3912805"',
// GITEA_CLIENT_SECRET: '"lus7oMnb3H6M1hsChndphArE20Txr7erwJLf7SDBQWTw"',
// GITEA_URL: '"https://gitea.test.com"',
GITLAB_CLIENT_ID: '"074cd5103c62dea0f479dac861039656ac80935e304c8113a02cc64c629496ae"',
GITLAB_CLIENT_SECRET: '"6f406f24216b686d55d28313dec1913c2a8e599afdb08380d5e8ce838e16e41e"',
GITLAB_URL: '"http://gitlab.qicoder.com"',
}) })

View File

@ -1,10 +0,0 @@
# 大文档导出PDF方式说明
> 由于大文档导出PDF需要消费非常多的服务器资源而且很容易导致导出超时故导出PDF的MD文档过大时可以使用 **[wkhtmltopdf](https://wkhtmltopdf.org/downloads.html)** 工具导出。
# 操作步骤
- 先在 **[StackEdit中文版](https://stackedit.cn/app)** 中使用 `导出为HTML` 功能导出MD文档导出后可以得到一个HTML文档。
- 到 **[wkhtmltopdf](https://wkhtmltopdf.org/downloads.html)** 官网下载安装程序。
- 使用 wkhtmltopdf 的导出PDF的命令 `wkhtmltopdf [GLOBAL OPTION]... [OBJECT]... <output file>` 把HTML导出为PDF如简单的导出命令`wkhtmltopdf test.html test.pdf`,具体的 `GLOBAL OPTION` 参数说明可以通过 `wkhtmltopdf -H` 查看帮助文档。

View File

@ -1,20 +0,0 @@
# GitHub应用配置说明
> StackEdit中文版部署如果需要支持GitHub则需要到GitHub创建一个应用并复制其中的clientId和clientSecret填充到环境变量 GITHUB_CLIENT_ID 和 GITHUB_CLIENT_SECRET 中。
# 如何创建GitHub应用
按下面图的指示创建
![](../images/github/github01.png)
![](../images/github/github02.png)
![](../images/github/github03.png)
![](../images/github/github04.png)
![](../images/github/github05.png)

View File

@ -1,35 +0,0 @@
# Gitea应用配置说明
> StackEdit中文版支持Gitea则需要到Gitea创建一个应用在StackEdit中文版绑定Gitea账号的时候填入。
# 如何创建Gitea应用
按下面图的指示创建
![](../images/gitea/gitea01.png)
![](../images/gitea/gitea02.png)
![](../images/gitea/gitea03.png)
![](../images/gitea/gitea04.png)
创建成功后即可看到应用ID 和 应用秘钥。
# Gitea跨域问题
由于StackEdit中文版是从浏览器直接访问Gitea接口故个人部署的Gitea需要支持跨域至于如何支持跨域请参考官方文档https://docs.gitea.io/en-us/config-cheat-sheet/#cors-cors (官方跨域的支持好像存在问题我个人包括很多网友通过这个配置支持跨域都失败了如果你也失败了可以试试用nginx代理实现跨域)
nginx配置实现跨域的配置如下
```
add_header Access-Control-Allow-Headers *;
add_header Access-Control-Allow-Origin $http_origin;
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;
if ($request_method = 'OPTIONS') {
return 204;
}
```

View File

@ -1,19 +0,0 @@
# Gitee应用配置说明
> StackEdit中文版部署如果需要支持Gitee则需要到Gitee创建一个应用并复制其中的clientId和clientSecret填充到环境变量 GITEE_CLIENT_ID 和 GITEE_CLIENT_SECRET 中。
# 如何创建Gitee应用
按下面图的指示创建
![](../images/gitee/gitee01.png)
![](../images/gitee/gitee02.png)
![](../images/gitee/gitee03.png)
![](../images/gitee/gitee04.png)
创建成功后即可看到client id 和 client secret。

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 793 KiB

After

Width:  |  Height:  |  Size: 368 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 339 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 726 KiB

After

Width:  |  Height:  |  Size: 327 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 360 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 937 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 761 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 195 KiB

View File

@ -1,31 +1,28 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head>
<head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Markdown编辑器-JonyLee的设计导航</title> <title>StackEdit中文版</title>
<link rel="canonical" href="https://md.jonylee.top"> <link rel="canonical" href="https://stackedit.cn/app">
<meta name="description" content="StackEdit中文版免费开源功能全面的Markdown编辑器。"> <meta name="description" content="免费开源功能全面的Markdown编辑器。">
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">
<meta name="mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="apple-mobile-web-app-status-bar-style" content="black">
</head> <script>
var _hmt = _hmt || [];
<body> </script>
</head>
<body>
<div id="app"></div> <div id="app"></div>
<!-- built files will be auto injected --> <!-- built files will be auto injected -->
<!-- baidu统计 -->
<script> <script>
var _hmt = _hmt || []; (function() {
(function() { var hm = document.createElement("script");
var hm = document.createElement("script"); hm.src = "https://hm.baidu.com/hm.js?20a1e7a201b42702c49074c87a1f1035";
hm.src = "https://hm.baidu.com/hm.js?dad4b4383b13eedea1ab45ee323df1c3"; var s = document.getElementsByTagName("script")[0];
var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s);
s.parentNode.insertBefore(hm, s); })();
})();
</script> </script>
<!-- baidu统计结束 --> </body>
</body>
</html> </html>

237
package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "stackedit", "name": "stackedit",
"version": "5.15.21", "version": "5.15.10",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@ -736,8 +736,7 @@
"abab": { "abab": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz",
"integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==", "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w=="
"dev": true
}, },
"abbrev": { "abbrev": {
"version": "1.1.1", "version": "1.1.1",
@ -764,8 +763,7 @@
"acorn": { "acorn": {
"version": "5.3.0", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.3.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.3.0.tgz",
"integrity": "sha512-Yej+zOJ1Dm/IMZzzj78OntP/r3zHEaKcyNoU2lAaxPtrseM6rF0xwqoz5Q5ysAiED9hTjI2hgtvLXitlCN1/Ug==", "integrity": "sha512-Yej+zOJ1Dm/IMZzzj78OntP/r3zHEaKcyNoU2lAaxPtrseM6rF0xwqoz5Q5ysAiED9hTjI2hgtvLXitlCN1/Ug=="
"dev": true
}, },
"acorn-dynamic-import": { "acorn-dynamic-import": {
"version": "2.0.2", "version": "2.0.2",
@ -788,7 +786,6 @@
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.1.0.tgz", "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.1.0.tgz",
"integrity": "sha512-KjZwU26uG3u6eZcfGbTULzFcsoz6pegNKtHPksZPOUsiKo5bUmiBPa38FuHZ/Eun+XYh/JCCkS9AS3Lu4McQOQ==", "integrity": "sha512-KjZwU26uG3u6eZcfGbTULzFcsoz6pegNKtHPksZPOUsiKo5bUmiBPa38FuHZ/Eun+XYh/JCCkS9AS3Lu4McQOQ==",
"dev": true,
"requires": { "requires": {
"acorn": "^5.0.0" "acorn": "^5.0.0"
} }
@ -1024,8 +1021,7 @@
"array-equal": { "array-equal": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz",
"integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM="
"dev": true
}, },
"array-filter": { "array-filter": {
"version": "0.0.1", "version": "0.0.1",
@ -1241,8 +1237,7 @@
"async-limiter": { "async-limiter": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
"integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
"dev": true
}, },
"async-settle": { "async-settle": {
"version": "1.0.0", "version": "1.0.0",
@ -1278,6 +1273,38 @@
"postcss-value-parser": "^3.2.3" "postcss-value-parser": "^3.2.3"
} }
}, },
"aws-sdk": {
"version": "2.317.0",
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.317.0.tgz",
"integrity": "sha512-X2Cd1Gb9Cf9WVgGOiBSW4TK6q5Mb6AYiGmEA9XikCgur4H8E4TgmgWbBWJnTzxssugclVLVoWQfw3RshNKJksg==",
"requires": {
"buffer": "4.9.1",
"events": "1.1.1",
"ieee754": "1.1.8",
"jmespath": "0.15.0",
"querystring": "0.2.0",
"sax": "1.2.1",
"url": "0.10.3",
"uuid": "3.1.0",
"xml2js": "0.4.19"
},
"dependencies": {
"xml2js": {
"version": "0.4.19",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
"integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
"requires": {
"sax": ">=0.6.0",
"xmlbuilder": "~9.0.1"
}
},
"xmlbuilder": {
"version": "9.0.7",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
"integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0="
}
}
},
"aws-sign2": { "aws-sign2": {
"version": "0.7.0", "version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
@ -2319,8 +2346,7 @@
"base64-js": { "base64-js": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz",
"integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==", "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw=="
"dev": true
}, },
"bcrypt-pbkdf": { "bcrypt-pbkdf": {
"version": "1.0.2", "version": "1.0.2",
@ -2455,8 +2481,7 @@
"browser-process-hrtime": { "browser-process-hrtime": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.2.tgz", "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.2.tgz",
"integrity": "sha1-Ql1opY00R/AqBKqJQYf86K+Le44=", "integrity": "sha1-Ql1opY00R/AqBKqJQYf86K+Le44="
"dev": true
}, },
"browser-resolve": { "browser-resolve": {
"version": "1.11.3", "version": "1.11.3",
@ -2568,7 +2593,6 @@
"version": "4.9.1", "version": "4.9.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
"integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
"dev": true,
"requires": { "requires": {
"base64-js": "^1.0.2", "base64-js": "^1.0.2",
"ieee754": "^1.1.4", "ieee754": "^1.1.4",
@ -3880,14 +3904,12 @@
"cssom": { "cssom": {
"version": "0.3.4", "version": "0.3.4",
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.4.tgz", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.4.tgz",
"integrity": "sha512-+7prCSORpXNeR4/fUP3rL+TzqtiFfhMvTd7uEqMdgPvLPt4+uzFUeufx5RHjGTACCargg/DiEt/moMQmvnfkog==", "integrity": "sha512-+7prCSORpXNeR4/fUP3rL+TzqtiFfhMvTd7uEqMdgPvLPt4+uzFUeufx5RHjGTACCargg/DiEt/moMQmvnfkog=="
"dev": true
}, },
"cssstyle": { "cssstyle": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.1.1.tgz", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.1.1.tgz",
"integrity": "sha512-364AI1l/M5TYcFH83JnOH/pSqgaNnKmYgKrm0didZMGKWjQB60dymwWy1rKUgL3J1ffdq9xVi2yGLHdSjjSNog==", "integrity": "sha512-364AI1l/M5TYcFH83JnOH/pSqgaNnKmYgKrm0didZMGKWjQB60dymwWy1rKUgL3J1ffdq9xVi2yGLHdSjjSNog==",
"dev": true,
"requires": { "requires": {
"cssom": "0.3.x" "cssom": "0.3.x"
} }
@ -3988,7 +4010,7 @@
}, },
"d3-collection": { "d3-collection": {
"version": "1.0.7", "version": "1.0.7",
"resolved": "https://registry.npmmirror.com/d3-collection/-/d3-collection-1.0.7.tgz", "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz",
"integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==" "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A=="
}, },
"d3-color": { "d3-color": {
@ -4166,7 +4188,7 @@
}, },
"d3-voronoi": { "d3-voronoi": {
"version": "1.1.4", "version": "1.1.4",
"resolved": "https://registry.npmmirror.com/d3-voronoi/-/d3-voronoi-1.1.4.tgz", "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz",
"integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==" "integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg=="
}, },
"d3-zoom": { "d3-zoom": {
@ -4183,7 +4205,7 @@
}, },
"dagre": { "dagre": {
"version": "0.8.5", "version": "0.8.5",
"resolved": "https://registry.npmmirror.com/dagre/-/dagre-0.8.5.tgz", "resolved": "https://registry.npmjs.org/dagre/-/dagre-0.8.5.tgz",
"integrity": "sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==", "integrity": "sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==",
"requires": { "requires": {
"graphlib": "^2.1.8", "graphlib": "^2.1.8",
@ -4192,14 +4214,14 @@
"dependencies": { "dependencies": {
"lodash": { "lodash": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
} }
} }
}, },
"dagre-d3": { "dagre-d3": {
"version": "0.6.4", "version": "0.6.4",
"resolved": "https://registry.npmmirror.com/dagre-d3/-/dagre-d3-0.6.4.tgz", "resolved": "https://registry.npmjs.org/dagre-d3/-/dagre-d3-0.6.4.tgz",
"integrity": "sha512-e/6jXeCP7/ptlAM48clmX4xTZc5Ek6T6kagS7Oz2HrYSdqcLZFLqpAfh7ldbZRFfxCZVyh61NEPR08UQRVxJzQ==", "integrity": "sha512-e/6jXeCP7/ptlAM48clmX4xTZc5Ek6T6kagS7Oz2HrYSdqcLZFLqpAfh7ldbZRFfxCZVyh61NEPR08UQRVxJzQ==",
"requires": { "requires": {
"d3": "^5.14", "d3": "^5.14",
@ -4210,7 +4232,7 @@
"dependencies": { "dependencies": {
"lodash": { "lodash": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
} }
} }
@ -4227,7 +4249,6 @@
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.0.1.tgz", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.0.1.tgz",
"integrity": "sha512-0HdcMZzK6ubMUnsMmQmG0AcLQPvbvb47R0+7CCZQCYgcd8OUWG91CG7sM6GoXgjz+WLl4ArFzHtBMy/QqSF4eg==", "integrity": "sha512-0HdcMZzK6ubMUnsMmQmG0AcLQPvbvb47R0+7CCZQCYgcd8OUWG91CG7sM6GoXgjz+WLl4ArFzHtBMy/QqSF4eg==",
"dev": true,
"requires": { "requires": {
"abab": "^2.0.0", "abab": "^2.0.0",
"whatwg-mimetype": "^2.1.0", "whatwg-mimetype": "^2.1.0",
@ -4238,7 +4259,6 @@
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz",
"integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==",
"dev": true,
"requires": { "requires": {
"lodash.sortby": "^4.7.0", "lodash.sortby": "^4.7.0",
"tr46": "^1.0.1", "tr46": "^1.0.1",
@ -4297,8 +4317,7 @@
"deep-is": { "deep-is": {
"version": "0.1.3", "version": "0.1.3",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
"integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ="
"dev": true
}, },
"default-compare": { "default-compare": {
"version": "1.0.0", "version": "1.0.0",
@ -4560,7 +4579,6 @@
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz",
"integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==",
"dev": true,
"requires": { "requires": {
"webidl-conversions": "^4.0.2" "webidl-conversions": "^4.0.2"
} }
@ -4574,11 +4592,6 @@
"domelementtype": "1" "domelementtype": "1"
} }
}, },
"domino": {
"version": "2.1.6",
"resolved": "https://registry.npmmirror.com/domino/-/domino-2.1.6.tgz",
"integrity": "sha512-3VdM/SXBZX2omc9JF9nOPCtDaYQ67BGp5CoLpIQlO2KCAPETs8TcDHacF26jXadGbvUteZzRTeos2fhID5+ucQ=="
},
"domutils": { "domutils": {
"version": "1.6.2", "version": "1.6.2",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.6.2.tgz", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.6.2.tgz",
@ -4746,7 +4759,7 @@
}, },
"entity-decode": { "entity-decode": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmmirror.com/entity-decode/-/entity-decode-2.0.2.tgz", "resolved": "https://registry.npmjs.org/entity-decode/-/entity-decode-2.0.2.tgz",
"integrity": "sha512-5CCY/3ci4MC1m2jlumNjWd7VBFt4VfFnmSqSNmVcXq4gxM3Vmarxtt+SvmBnzwLS669MWdVuXboNVj1qN2esVg==", "integrity": "sha512-5CCY/3ci4MC1m2jlumNjWd7VBFt4VfFnmSqSNmVcXq4gxM3Vmarxtt+SvmBnzwLS669MWdVuXboNVj1qN2esVg==",
"requires": { "requires": {
"he": "^1.1.1" "he": "^1.1.1"
@ -4868,7 +4881,6 @@
"version": "1.11.0", "version": "1.11.0",
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.0.tgz", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.0.tgz",
"integrity": "sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw==", "integrity": "sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw==",
"dev": true,
"requires": { "requires": {
"esprima": "^3.1.3", "esprima": "^3.1.3",
"estraverse": "^4.2.0", "estraverse": "^4.2.0",
@ -4880,14 +4892,12 @@
"esprima": { "esprima": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
"integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM="
"dev": true
}, },
"source-map": { "source-map": {
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"optional": true "optional": true
} }
} }
@ -5339,14 +5349,12 @@
"estraverse": { "estraverse": {
"version": "4.2.0", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
"integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM="
"dev": true
}, },
"esutils": { "esutils": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs="
"dev": true
}, },
"etag": { "etag": {
"version": "1.8.1", "version": "1.8.1",
@ -5362,8 +5370,7 @@
"events": { "events": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
"integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ="
"dev": true
}, },
"eventsource-polyfill": { "eventsource-polyfill": {
"version": "0.9.6", "version": "0.9.6",
@ -6053,8 +6060,7 @@
"fast-levenshtein": { "fast-levenshtein": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
"dev": true
}, },
"fastparse": { "fastparse": {
"version": "1.1.1", "version": "1.1.1",
@ -8636,6 +8642,16 @@
"delegate": "^3.1.2" "delegate": "^3.1.2"
} }
}, },
"google-id-token-verifier": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/google-id-token-verifier/-/google-id-token-verifier-0.2.3.tgz",
"integrity": "sha1-nmt41FieLQUNqBYT+4kK26MKTqg=",
"requires": {
"request": "^2.65.0",
"rsa-pem-from-mod-exp": "^0.8.4",
"underscore": "^1.8.3"
}
},
"graceful-fs": { "graceful-fs": {
"version": "4.1.11", "version": "4.1.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
@ -8644,7 +8660,7 @@
}, },
"graphlib": { "graphlib": {
"version": "2.1.8", "version": "2.1.8",
"resolved": "https://registry.npmmirror.com/graphlib/-/graphlib-2.1.8.tgz", "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz",
"integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==", "integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==",
"requires": { "requires": {
"lodash": "^4.17.15" "lodash": "^4.17.15"
@ -8652,7 +8668,7 @@
"dependencies": { "dependencies": {
"lodash": { "lodash": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
} }
} }
@ -9053,7 +9069,6 @@
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz",
"integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==",
"dev": true,
"requires": { "requires": {
"whatwg-encoding": "^1.0.1" "whatwg-encoding": "^1.0.1"
} }
@ -9624,8 +9639,7 @@
"ieee754": { "ieee754": {
"version": "1.1.8", "version": "1.1.8",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz",
"integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=", "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q="
"dev": true
}, },
"iferr": { "iferr": {
"version": "0.1.5", "version": "0.1.5",
@ -11224,6 +11238,11 @@
"url-regex": "^3.0.0" "url-regex": "^3.0.0"
} }
}, },
"jmespath": {
"version": "0.15.0",
"resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz",
"integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc="
},
"jpeg-js": { "jpeg-js": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.2.0.tgz", "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.2.0.tgz",
@ -11279,7 +11298,6 @@
"version": "11.12.0", "version": "11.12.0",
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz",
"integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==",
"dev": true,
"requires": { "requires": {
"abab": "^2.0.0", "abab": "^2.0.0",
"acorn": "^5.5.3", "acorn": "^5.5.3",
@ -11312,20 +11330,17 @@
"acorn": { "acorn": {
"version": "5.7.1", "version": "5.7.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz",
"integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ==", "integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ=="
"dev": true
}, },
"sax": { "sax": {
"version": "1.2.4", "version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
"dev": true
}, },
"whatwg-url": { "whatwg-url": {
"version": "6.5.0", "version": "6.5.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz",
"integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==",
"dev": true,
"requires": { "requires": {
"lodash.sortby": "^4.7.0", "lodash.sortby": "^4.7.0",
"tr46": "^1.0.1", "tr46": "^1.0.1",
@ -11336,7 +11351,6 @@
"version": "5.2.2", "version": "5.2.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz",
"integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==",
"dev": true,
"requires": { "requires": {
"async-limiter": "~1.0.0" "async-limiter": "~1.0.0"
} }
@ -11436,17 +11450,17 @@
"dev": true "dev": true
}, },
"katex": { "katex": {
"version": "0.16.2", "version": "0.13.0",
"resolved": "https://registry.npmmirror.com/katex/-/katex-0.16.2.tgz", "resolved": "https://registry.npmjs.org/katex/-/katex-0.13.0.tgz",
"integrity": "sha512-70DJdQAyh9EMsthw3AaQlDyFf54X7nWEUIa5W+rq8XOpEk//w5Th7/8SqFqpvi/KZ2t6MHUj4f9wLmztBmAYQA==", "integrity": "sha512-6cHbzbegYgS9vvVGuH8UA+o97X+ZshtboSqJJCdq7trBYzuD75JNwr7Ef606xkUjecPPhFnyB+afx1dVafielg==",
"requires": { "requires": {
"commander": "^8.0.0" "commander": "^6.0.0"
}, },
"dependencies": { "dependencies": {
"commander": { "commander": {
"version": "8.3.0", "version": "6.2.1",
"resolved": "https://registry.npmmirror.com/commander/-/commander-8.3.0.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
"integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==" "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA=="
} }
} }
}, },
@ -11535,8 +11549,7 @@
"left-pad": { "left-pad": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz",
"integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA=="
"dev": true
}, },
"leven": { "leven": {
"version": "2.1.0", "version": "2.1.0",
@ -11548,7 +11561,6 @@
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
"integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
"dev": true,
"requires": { "requires": {
"prelude-ls": "~1.1.2", "prelude-ls": "~1.1.2",
"type-check": "~0.3.2" "type-check": "~0.3.2"
@ -12501,7 +12513,7 @@
}, },
"minify": { "minify": {
"version": "4.1.3", "version": "4.1.3",
"resolved": "https://registry.npmmirror.com/minify/-/minify-4.1.3.tgz", "resolved": "https://registry.npmjs.org/minify/-/minify-4.1.3.tgz",
"integrity": "sha512-ykuscavxivSmVpcCzsXmsVTukWYLUUtPhHj0w2ILvHDGqC+hsuTCihBn9+PJBd58JNvWTNg9132J9nrrI2anzA==", "integrity": "sha512-ykuscavxivSmVpcCzsXmsVTukWYLUUtPhHj0w2ILvHDGqC+hsuTCihBn9+PJBd58JNvWTNg9132J9nrrI2anzA==",
"requires": { "requires": {
"clean-css": "^4.1.6", "clean-css": "^4.1.6",
@ -12515,7 +12527,7 @@
"dependencies": { "dependencies": {
"commander": { "commander": {
"version": "2.20.3", "version": "2.20.3",
"resolved": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
}, },
"debug": { "debug": {
@ -12528,12 +12540,12 @@
}, },
"he": { "he": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmmirror.com/he/-/he-1.2.0.tgz", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
}, },
"html-minifier": { "html-minifier": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmmirror.com/html-minifier/-/html-minifier-4.0.0.tgz", "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz",
"integrity": "sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==", "integrity": "sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==",
"requires": { "requires": {
"camel-case": "^3.0.0", "camel-case": "^3.0.0",
@ -12547,7 +12559,7 @@
}, },
"ms": { "ms": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}, },
"uglify-js": { "uglify-js": {
@ -13330,8 +13342,7 @@
"nwsapi": { "nwsapi": {
"version": "2.0.8", "version": "2.0.8",
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.0.8.tgz", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.0.8.tgz",
"integrity": "sha512-7RZ+qbFGiVc6v14Y8DSZjPN1wZPOaMbiiP4tzf5eNuyOITAeOIA3cMhjuKUypVIqBgCSg1KaSyAv8Ocq/0ZJ1A==", "integrity": "sha512-7RZ+qbFGiVc6v14Y8DSZjPN1wZPOaMbiiP4tzf5eNuyOITAeOIA3cMhjuKUypVIqBgCSg1KaSyAv8Ocq/0ZJ1A=="
"dev": true
}, },
"oauth-sign": { "oauth-sign": {
"version": "0.9.0", "version": "0.9.0",
@ -13666,7 +13677,6 @@
"version": "0.8.2", "version": "0.8.2",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
"integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
"dev": true,
"requires": { "requires": {
"deep-is": "~0.1.3", "deep-is": "~0.1.3",
"fast-levenshtein": "~2.0.4", "fast-levenshtein": "~2.0.4",
@ -13679,8 +13689,7 @@
"wordwrap": { "wordwrap": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
"integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
"dev": true
} }
} }
}, },
@ -13998,8 +14007,7 @@
"parse5": { "parse5": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz",
"integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA=="
"dev": true
}, },
"parseurl": { "parseurl": {
"version": "1.3.2", "version": "1.3.2",
@ -14200,8 +14208,7 @@
"pn": { "pn": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz",
"integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA=="
"dev": true
}, },
"pngjs": { "pngjs": {
"version": "3.3.3", "version": "3.3.3",
@ -15104,8 +15111,7 @@
"prelude-ls": { "prelude-ls": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
"integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ="
"dev": true
}, },
"prepend-http": { "prepend-http": {
"version": "1.0.4", "version": "1.0.4",
@ -15269,8 +15275,7 @@
"punycode": { "punycode": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
"dev": true
}, },
"q": { "q": {
"version": "1.5.1", "version": "1.5.1",
@ -15296,8 +15301,7 @@
"querystring": { "querystring": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
"dev": true
}, },
"querystring-es3": { "querystring-es3": {
"version": "0.2.1", "version": "0.2.1",
@ -16246,7 +16250,6 @@
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz",
"integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=",
"dev": true,
"requires": { "requires": {
"lodash": "^4.13.1" "lodash": "^4.13.1"
} }
@ -16255,7 +16258,6 @@
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.5.tgz", "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.5.tgz",
"integrity": "sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU=", "integrity": "sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU=",
"dev": true,
"requires": { "requires": {
"request-promise-core": "1.1.1", "request-promise-core": "1.1.1",
"stealthy-require": "^1.1.0", "stealthy-require": "^1.1.0",
@ -16417,6 +16419,11 @@
"inherits": "^2.0.1" "inherits": "^2.0.1"
} }
}, },
"rsa-pem-from-mod-exp": {
"version": "0.8.4",
"resolved": "https://registry.npmjs.org/rsa-pem-from-mod-exp/-/rsa-pem-from-mod-exp-0.8.4.tgz",
"integrity": "sha1-NipCxtMEBW1JOz8SvOq7LGV2ptQ="
},
"rsvp": { "rsvp": {
"version": "3.6.2", "version": "3.6.2",
"resolved": "https://registry.npmjs.org/rsvp/-/rsvp-3.6.2.tgz", "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-3.6.2.tgz",
@ -17707,8 +17714,7 @@
"sax": { "sax": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
"integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=", "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o="
"dev": true
}, },
"schema-utils": { "schema-utils": {
"version": "0.3.0", "version": "0.3.0",
@ -18442,8 +18448,7 @@
"stealthy-require": { "stealthy-require": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
"integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks="
"dev": true
}, },
"stream-browserify": { "stream-browserify": {
"version": "2.0.1", "version": "2.0.1",
@ -19602,8 +19607,7 @@
"symbol-tree": { "symbol-tree": {
"version": "3.2.2", "version": "3.2.2",
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz",
"integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY="
"dev": true
}, },
"table": { "table": {
"version": "4.0.2", "version": "4.0.2",
@ -20413,12 +20417,12 @@
}, },
"try-catch": { "try-catch": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmmirror.com/try-catch/-/try-catch-2.0.1.tgz", "resolved": "https://registry.npmjs.org/try-catch/-/try-catch-2.0.1.tgz",
"integrity": "sha512-LsOrmObN/2WdM+y2xG+t16vhYrQsnV8wftXIcIOWZhQcBJvKGYuamJGwnU98A7Jxs2oZNkJztXlphEOoA0DWqg==" "integrity": "sha512-LsOrmObN/2WdM+y2xG+t16vhYrQsnV8wftXIcIOWZhQcBJvKGYuamJGwnU98A7Jxs2oZNkJztXlphEOoA0DWqg=="
}, },
"try-to-catch": { "try-to-catch": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmmirror.com/try-to-catch/-/try-to-catch-1.1.1.tgz", "resolved": "https://registry.npmjs.org/try-to-catch/-/try-to-catch-1.1.1.tgz",
"integrity": "sha512-ikUlS+/BcImLhNYyIgZcEmq4byc31QpC+46/6Jm5ECWkVFhf8SM2Fp/0pMVXPX6vk45SMCwrP4Taxucne8I0VA==" "integrity": "sha512-ikUlS+/BcImLhNYyIgZcEmq4byc31QpC+46/6Jm5ECWkVFhf8SM2Fp/0pMVXPX6vk45SMCwrP4Taxucne8I0VA=="
}, },
"tryer": { "tryer": {
@ -20462,11 +20466,11 @@
} }
}, },
"turndown": { "turndown": {
"version": "7.1.1", "version": "4.0.2",
"resolved": "https://registry.npmmirror.com/turndown/-/turndown-7.1.1.tgz", "resolved": "https://registry.npmjs.org/turndown/-/turndown-4.0.2.tgz",
"integrity": "sha512-BEkXaWH7Wh7e9bd2QumhfAXk5g34+6QUmmWx+0q6ThaVOLuLUqsnkq35HQ5SBHSaxjSfSM7US5o4lhJNH7B9MA==", "integrity": "sha512-pqZ6WrHFGnxXC9q2xJ3Qa7EoLAwrojgFRajWZjxTKwbz9vnNnyi8lLjiD5h86UTPOcMlEyHjm6NMhjEDdlc25A==",
"requires": { "requires": {
"domino": "^2.1.6" "jsdom": "^11.9.0"
} }
}, },
"tweetnacl": { "tweetnacl": {
@ -20485,7 +20489,6 @@
"version": "0.3.2", "version": "0.3.2",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
"integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
"dev": true,
"requires": { "requires": {
"prelude-ls": "~1.1.2" "prelude-ls": "~1.1.2"
} }
@ -20565,8 +20568,7 @@
"underscore": { "underscore": {
"version": "1.8.3", "version": "1.8.3",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz",
"integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=", "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI="
"dev": true
}, },
"undertaker": { "undertaker": {
"version": "1.3.0", "version": "1.3.0",
@ -20841,6 +20843,15 @@
"integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
"dev": true "dev": true
}, },
"url": {
"version": "0.10.3",
"resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
"integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=",
"requires": {
"punycode": "1.3.2",
"querystring": "0.2.0"
}
},
"url-loader": { "url-loader": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.1.1.tgz", "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.1.1.tgz",
@ -20965,6 +20976,11 @@
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
"dev": true "dev": true
}, },
"uuid": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz",
"integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g=="
},
"v8flags": { "v8flags": {
"version": "3.2.0", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz",
@ -21320,7 +21336,6 @@
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz",
"integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=",
"dev": true,
"requires": { "requires": {
"browser-process-hrtime": "^0.1.2" "browser-process-hrtime": "^0.1.2"
} }
@ -21742,7 +21757,6 @@
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.4.tgz", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.4.tgz",
"integrity": "sha512-vM9KWN6MP2mIHZ86ytcyIv7e8Cj3KTfO2nd2c8PFDqcI4bxFmQp83ibq4wadq7rL9l9sZV6o9B0LTt8ygGAAXg==", "integrity": "sha512-vM9KWN6MP2mIHZ86ytcyIv7e8Cj3KTfO2nd2c8PFDqcI4bxFmQp83ibq4wadq7rL9l9sZV6o9B0LTt8ygGAAXg==",
"dev": true,
"requires": { "requires": {
"iconv-lite": "0.4.23" "iconv-lite": "0.4.23"
}, },
@ -21751,7 +21765,6 @@
"version": "0.4.23", "version": "0.4.23",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
"integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
"dev": true,
"requires": { "requires": {
"safer-buffer": ">= 2.1.2 < 3" "safer-buffer": ">= 2.1.2 < 3"
} }
@ -21761,8 +21774,7 @@
"whatwg-mimetype": { "whatwg-mimetype": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.1.0.tgz", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.1.0.tgz",
"integrity": "sha512-FKxhYLytBQiUKjkYteN71fAUA3g6KpNXoho1isLiLSB3N1G4F35Q5vUxWfKFhBwi5IWF27VE6WxhrnnC+m0Mew==", "integrity": "sha512-FKxhYLytBQiUKjkYteN71fAUA3g6KpNXoho1isLiLSB3N1G4F35Q5vUxWfKFhBwi5IWF27VE6WxhrnnC+m0Mew=="
"dev": true
}, },
"whatwg-url": { "whatwg-url": {
"version": "6.4.0", "version": "6.4.0",
@ -21941,8 +21953,7 @@
"xml-name-validator": { "xml-name-validator": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
"integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw=="
"dev": true
}, },
"xml-parse-from-string": { "xml-parse-from-string": {
"version": "1.0.1", "version": "1.0.1",

View File

@ -1,6 +1,6 @@
{ {
"name": "stackedit", "name": "stackedit",
"version": "5.15.21", "version": "5.15.10",
"description": "免费, 开源, 功能齐全的 Markdown 编辑器", "description": "免费, 开源, 功能齐全的 Markdown 编辑器",
"author": "Benoit Schweblin, 豆萁", "author": "Benoit Schweblin, 豆萁",
"license": "Apache-2.0", "license": "Apache-2.0",
@ -27,6 +27,7 @@
"dependencies": { "dependencies": {
"@vue/test-utils": "^1.0.0-beta.16", "@vue/test-utils": "^1.0.0-beta.16",
"abcjs": "^5.2.0", "abcjs": "^5.2.0",
"aws-sdk": "^2.317.0",
"babel-runtime": "^6.26.0", "babel-runtime": "^6.26.0",
"bezier-easing": "^1.1.0", "bezier-easing": "^1.1.0",
"body-parser": "^1.18.2", "body-parser": "^1.18.2",
@ -34,10 +35,11 @@
"compression": "^1.7.0", "compression": "^1.7.0",
"diff-match-patch": "^1.0.0", "diff-match-patch": "^1.0.0",
"file-saver": "^1.3.8", "file-saver": "^1.3.8",
"google-id-token-verifier": "^0.2.3",
"handlebars": "^4.0.10", "handlebars": "^4.0.10",
"indexeddbshim": "^3.6.2", "indexeddbshim": "^3.6.2",
"js-yaml": "^3.11.0", "js-yaml": "^3.11.0",
"katex": "^0.16.2", "katex": "^0.13.0",
"markdown-it": "^8.4.1", "markdown-it": "^8.4.1",
"markdown-it-abbr": "^1.0.4", "markdown-it-abbr": "^1.0.4",
"markdown-it-deflist": "^2.0.2", "markdown-it-deflist": "^2.0.2",
@ -55,7 +57,7 @@
"request": "^2.85.0", "request": "^2.85.0",
"serve-static": "^1.13.2", "serve-static": "^1.13.2",
"tmp": "^0.0.33", "tmp": "^0.0.33",
"turndown": "^7.1.1", "turndown": "^4.0.2",
"vue": "^2.5.16", "vue": "^2.5.16",
"vuex": "^3.0.1" "vuex": "^3.0.1"
}, },

View File

@ -1,5 +1,7 @@
const pandocPath = process.env.PANDOC_PATH || 'pandoc'; const pandocPath = process.env.PANDOC_PATH || 'pandoc';
const wkhtmltopdfPath = process.env.WKHTMLTOPDF_PATH || 'wkhtmltopdf'; const wkhtmltopdfPath = process.env.WKHTMLTOPDF_PATH || 'wkhtmltopdf';
const userBucketName = process.env.USER_BUCKET_NAME || 'stackedit-users';
const paypalUri = process.env.PAYPAL_URI || 'https://www.paypal.com/cgi-bin/webscr';
const paypalReceiverEmail = process.env.PAYPAL_RECEIVER_EMAIL; const paypalReceiverEmail = process.env.PAYPAL_RECEIVER_EMAIL;
const dropboxAppKey = process.env.DROPBOX_APP_KEY; const dropboxAppKey = process.env.DROPBOX_APP_KEY;
@ -11,16 +13,12 @@ const giteeClientSecret = process.env.GITEE_CLIENT_SECRET;
const googleClientId = process.env.GOOGLE_CLIENT_ID; const googleClientId = process.env.GOOGLE_CLIENT_ID;
const googleApiKey = process.env.GOOGLE_API_KEY; const googleApiKey = process.env.GOOGLE_API_KEY;
const wordpressClientId = process.env.WORDPRESS_CLIENT_ID; const wordpressClientId = process.env.WORDPRESS_CLIENT_ID;
const giteaClientId = process.env.GITEA_CLIENT_ID;
const giteaClientSecret = process.env.GITEA_CLIENT_SECRET;
const giteaUrl = process.env.GITEA_URL;
const gitlabClientId = process.env.GITLAB_CLIENT_ID;
const gitlabClientSecret = process.env.GITLAB_CLIENT_SECRET;
const gitlabUrl = process.env.GITLAB_URL;
exports.values = { exports.values = {
pandocPath, pandocPath,
wkhtmltopdfPath, wkhtmltopdfPath,
userBucketName,
paypalUri,
paypalReceiverEmail, paypalReceiverEmail,
dropboxAppKey, dropboxAppKey,
dropboxAppKeyFull, dropboxAppKeyFull,
@ -31,12 +29,6 @@ exports.values = {
googleClientId, googleClientId,
googleApiKey, googleApiKey,
wordpressClientId, wordpressClientId,
giteaClientId,
giteaClientSecret,
giteaUrl,
gitlabClientId,
gitlabClientSecret,
gitlabUrl,
}; };
exports.publicValues = { exports.publicValues = {
@ -47,8 +39,4 @@ exports.publicValues = {
googleApiKey, googleApiKey,
wordpressClientId, wordpressClientId,
allowSponsorship: !!paypalReceiverEmail, allowSponsorship: !!paypalReceiverEmail,
giteaClientId,
giteaUrl,
gitlabClientId,
gitlabUrl,
}; };

View File

@ -1,40 +0,0 @@
const request = require('request');
const conf = require('./conf');
function giteaToken(queryParam) {
return new Promise((resolve, reject) => {
request({
method: 'POST',
url: `${conf.values.giteaUrl}/login/oauth/access_token`,
headers: {
'content-type': 'application/json',
},
json: true,
body: {
...queryParam,
client_id: conf.values.giteaClientId,
client_secret: conf.values.giteaClientSecret,
},
}, (err, res, body) => {
if (err) {
reject(err);
}
const token = body.access_token;
if (token) {
resolve(body);
} else {
reject(res.statusCode + ',body:' + JSON.stringify(body));
}
});
});
}
exports.giteaToken = (req, res) => {
giteaToken(req.query)
.then(
tokenBody => res.send(tokenBody),
err => res
.status(400)
.send(err ? err.message || err.toString() : 'bad_code'),
);
};

View File

@ -1,40 +0,0 @@
const request = require('request');
const conf = require('./conf');
function gitlabToken(queryParam) {
return new Promise((resolve, reject) => {
request({
method: 'POST',
url: `${conf.values.gitlabUrl}/oauth/token`,
headers: {
'content-type': 'application/json',
},
json: true,
qs: {
...queryParam,
client_id: conf.values.gitlabClientId,
client_secret: conf.values.gitlabClientSecret,
},
}, (err, res, body) => {
if (err) {
reject(err);
}
const token = body.access_token;
if (token) {
resolve(body);
} else {
reject(res.statusCode + ',body:' + JSON.stringify(body));
}
});
});
}
exports.gitlabToken = (req, res) => {
gitlabToken(req.query)
.then(
tokenBody => res.send(tokenBody),
err => res
.status(400)
.send(err ? err.message || err.toString() : 'bad_code'),
);
};

View File

@ -2,10 +2,9 @@ const compression = require('compression');
const serveStatic = require('serve-static'); const serveStatic = require('serve-static');
const bodyParser = require('body-parser'); const bodyParser = require('body-parser');
const path = require('path'); const path = require('path');
const user = require('./user');
const github = require('./github'); const github = require('./github');
const gitee = require('./gitee'); const gitee = require('./gitee');
const gitea = require('./gitea');
const gitlab = require('./gitlab');
const pdf = require('./pdf'); const pdf = require('./pdf');
const pandoc = require('./pandoc'); const pandoc = require('./pandoc');
const conf = require('./conf'); const conf = require('./conf');
@ -28,11 +27,13 @@ module.exports = (app) => {
app.get('/oauth2/githubToken', github.githubToken); app.get('/oauth2/githubToken', github.githubToken);
app.get('/oauth2/giteeToken', gitee.giteeToken); app.get('/oauth2/giteeToken', gitee.giteeToken);
app.get('/oauth2/giteaToken', gitea.giteaToken);
app.get('/oauth2/gitlabToken', gitlab.gitlabToken);
app.get('/conf', (req, res) => res.send(conf.publicValues)); app.get('/conf', (req, res) => res.send(conf.publicValues));
app.get('/userInfo', user.userInfo);
app.post('/pdfExport', pdf.generate); app.post('/pdfExport', pdf.generate);
app.post('/pandocExport', pandoc.generate); app.post('/pandocExport', pandoc.generate);
app.post('/paypalIpn', bodyParser.urlencoded({
extended: false,
}), user.paypalIpn);
app.get('/giteeClientId', (req, res) => { app.get('/giteeClientId', (req, res) => {
const giteeClientIds = conf.values.giteeClientId.split(','); const giteeClientIds = conf.values.giteeClientId.split(',');
// 仅一个 则直接返回 // 仅一个 则直接返回
@ -61,24 +62,17 @@ module.exports = (app) => {
// Google Drive action receiver // Google Drive action receiver
app.get('/googleDriveAction', (req, res) => app.get('/googleDriveAction', (req, res) =>
res.redirect(`./app#providerId=googleDrive&state=${encodeURIComponent(req.query.state)}`)); res.redirect(`./app#providerId=googleDrive&state=${encodeURIComponent(req.query.state)}`));
// Serve the static folder with 30 day max-age
app.use('/themes', serveStatic(resolvePath('static/themes'), {
maxAge: '5d',
}));
// Serve style.css with 1 day max-age
app.get('/style.css', (req, res) => res.sendFile(resolvePath('dist/style.css'), {
maxAge: '1d',
}));
// Serve share.html
app.get('/share.html', (req, res) => res.sendFile(resolvePath('static/landing/share.html')));
app.get('/gistshare.html', (req, res) => res.sendFile(resolvePath('static/landing/gistshare.html')));
// Serve static resources // Serve static resources
if (process.env.NODE_ENV === 'production') { if (process.env.NODE_ENV === 'production') {
// Serve index.html in /app // Serve index.html in /app
app.get('/app', (req, res) => res.sendFile(resolvePath('dist/index.html'))); app.get('/app', (req, res) => res.sendFile(resolvePath('dist/index.html')));
// Serve style.css with 1 day max-age
app.get('/style.css', (req, res) => res.sendFile(resolvePath('dist/style.css'), {
maxAge: '1d',
}));
// Serve the static folder with 1 year max-age // Serve the static folder with 1 year max-age
app.use('/static', serveStatic(resolvePath('dist/static'), { app.use('/static', serveStatic(resolvePath('dist/static'), {
maxAge: '1y', maxAge: '1y',

116
server/user.js Normal file
View File

@ -0,0 +1,116 @@
const request = require('request');
const AWS = require('aws-sdk');
const verifier = require('google-id-token-verifier');
const conf = require('./conf');
const s3Client = new AWS.S3();
const cb = (resolve, reject) => (err, res) => {
if (err) {
reject(err);
} else {
resolve(res);
}
};
exports.getUser = id => new Promise((resolve, reject) => {
s3Client.getObject({
Bucket: conf.values.userBucketName,
Key: id,
}, cb(resolve, reject));
})
.then(
res => JSON.parse(`${res.Body}`),
(err) => {
if (err.code !== 'NoSuchKey') {
throw err;
}
},
);
exports.putUser = (id, user) => new Promise((resolve, reject) => {
s3Client.putObject({
Bucket: conf.values.userBucketName,
Key: id,
Body: JSON.stringify(user),
}, cb(resolve, reject));
});
exports.removeUser = id => new Promise((resolve, reject) => {
s3Client.deleteObject({
Bucket: conf.values.userBucketName,
Key: id,
}, cb(resolve, reject));
});
exports.getUserFromToken = idToken => new Promise((resolve, reject) => verifier
.verify(idToken, conf.values.googleClientId, cb(resolve, reject)))
.then(tokenInfo => exports.getUser(tokenInfo.sub));
exports.userInfo = (req, res) => exports.getUserFromToken(req.query.idToken)
.then(
user => res.send(Object.assign({
sponsorUntil: 0,
}, user)),
err => res
.status(400)
.send(err ? err.message || err.toString() : 'invalid_token'),
);
exports.paypalIpn = (req, res, next) => Promise.resolve()
.then(() => {
const userId = req.body.custom;
const paypalEmail = req.body.payer_email;
const gross = parseFloat(req.body.mc_gross);
let sponsorUntil;
if (gross === 5) {
sponsorUntil = Date.now() + (3 * 31 * 24 * 60 * 60 * 1000); // 3 months
} else if (gross === 15) {
sponsorUntil = Date.now() + (366 * 24 * 60 * 60 * 1000); // 1 year
} else if (gross === 25) {
sponsorUntil = Date.now() + (2 * 366 * 24 * 60 * 60 * 1000); // 2 years
} else if (gross === 50) {
sponsorUntil = Date.now() + (5 * 366 * 24 * 60 * 60 * 1000); // 5 years
}
if (
req.body.receiver_email !== conf.values.paypalReceiverEmail ||
req.body.payment_status !== 'Completed' ||
req.body.mc_currency !== 'USD' ||
(req.body.txn_type !== 'web_accept' && req.body.txn_type !== 'subscr_payment') ||
!userId || !sponsorUntil
) {
// Ignoring PayPal IPN
return res.end();
}
// Processing PayPal IPN
req.body.cmd = '_notify-validate';
return new Promise((resolve, reject) => request.post({
uri: conf.values.paypalUri,
form: req.body,
}, (err, response, body) => {
if (err) {
reject(err);
} else if (body !== 'VERIFIED') {
reject(new Error('PayPal IPN unverified'));
} else {
resolve();
}
}))
.then(() => exports.putUser(userId, {
paypalEmail,
sponsorUntil,
}))
.then(() => res.end());
})
.catch(next);
exports.checkSponsor = (idToken) => {
if (!conf.publicValues.allowSponsorship) {
return Promise.resolve(true);
}
if (!idToken) {
return Promise.resolve(false);
}
return exports.getUserFromToken(idToken)
.then(userInfo => userInfo && userInfo.sponsorUntil > Date.now(), () => false);
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -1,3 +1,4 @@
<svg <svg
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 58"> xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 58">
<g fill="none" fill-rule="evenodd"> <g fill="none" fill-rule="evenodd">

Before

Width:  |  Height:  |  Size: 1010 B

After

Width:  |  Height:  |  Size: 1011 B

View File

@ -1,6 +0,0 @@
<svg
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 58">
<g fill="none" fill-rule="evenodd">
<path d="m1324.62 140c-16.355 0-29.616 13.219-29.616 29.527 0 13.04 8.485 24.11 20.256 28.01 1.482.27 2.02-.642 2.02-1.425 0-.7-.025-2.557-.04-5.02-8.238 1.784-9.976-3.958-9.976-3.958-1.347-3.411-3.289-4.317-3.289-4.317-2.689-1.832.204-1.796.204-1.796 2.973.21 4.536 3.043 4.536 3.043 2.642 4.511 6.931 3.208 8.62 2.454.269-1.909 1.033-3.21 1.88-3.948-6.576-.745-13.491-3.279-13.491-14.592 0-3.223 1.155-5.858 3.049-7.922-.305-.747-1.322-3.748.289-7.814 0 0 2.487-.794 8.145 3.03 2.362-.656 4.896-.982 7.415-.995 2.515.013 5.05.339 7.415.995 5.655-3.821 8.136-3.03 8.136-3.03 1.616 4.065.6 7.07.295 7.814 1.898 2.064 3.045 4.7 3.045 7.922 0 11.343-6.925 13.838-13.524 14.569 1.064.912 2.01 2.713 2.01 5.468 0 3.946-.036 7.13-.036 8.098 0 .79.533 1.709 2.036 1.421 11.758-3.913 20.238-14.971 20.238-28.01 0-16.309-13.262-29.527-29.62-29.527" transform="translate(-1295-140)" fill="#fff"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1007 B

View File

@ -22,8 +22,6 @@ import networkSvc from '../services/networkSvc';
import tempFileSvc from '../services/tempFileSvc'; import tempFileSvc from '../services/tempFileSvc';
import store from '../store'; import store from '../store';
import './common/vueGlobals'; import './common/vueGlobals';
import utils from '../services/utils';
import providerRegistry from '../services/providers/common/providerRegistry';
const themeClasses = { const themeClasses = {
light: ['app--light'], light: ['app--light'],
@ -51,49 +49,11 @@ export default {
close() { close() {
tempFileSvc.close(); tempFileSvc.close();
}, },
//
viewFileByPath(path) {
// md
if (!path) {
return;
}
const currDirNode = store.getters['explorer/selectedNodeFolder'];
if (path.slice(-3) === '.md') {
const rootNode = store.getters['explorer/rootNode'];
const node = utils.findNodeByPath(rootNode, currDirNode, path);
if (!node) {
return;
}
store.commit('explorer/setSelectedId', node.item.id);
// Prevent from freezing the UI while loading the file
setTimeout(() => {
store.commit('file/setCurrentId', node.item.id);
}, 10);
} else {
const workspace = store.getters['workspace/currentWorkspace'];
const provider = providerRegistry.providersById[workspace.providerId];
if (provider == null) {
return;
}
const absolutePath = utils.getAbsoluteFilePath(currDirNode, path);
const url = provider.getFilePathUrl(absolutePath);
if (url) {
window.open(url, '_blank');
}
}
},
}, },
async created() { async created() {
window.viewFileByPath = this.viewFileByPath;
try { try {
await syncSvc.init(); await syncSvc.init();
await networkSvc.init(); await networkSvc.init();
// store
const editTheme = localStorage.getItem('theme/currEditTheme');
store.dispatch('theme/setEditTheme', editTheme || 'default');
// store
const previewTheme = localStorage.getItem('theme/currPreviewTheme');
store.dispatch('theme/setPreviewTheme', previewTheme || 'default');
this.ready = true; this.ready = true;
tempFileSvc.setReady(); tempFileSvc.setReady();
} catch (err) { } catch (err) {

View File

@ -7,12 +7,11 @@ import Prism from 'prismjs';
import cledit from '../services/editor/cledit'; import cledit from '../services/editor/cledit';
export default { export default {
props: ['value', 'lang', 'disabled', 'scrollClass'], props: ['value', 'lang', 'disabled'],
mounted() { mounted() {
const preElt = this.$el; const preElt = this.$el;
let scrollElt = preElt; let scrollElt = preElt;
const scrollCls = this.scrollClass || 'modal'; while (scrollElt && !scrollElt.classList.contains('modal')) {
while (scrollElt && !scrollElt.classList.contains(scrollCls)) {
scrollElt = scrollElt.parentNode; scrollElt = scrollElt.parentNode;
} }
if (scrollElt) { if (scrollElt) {
@ -44,10 +43,6 @@ export default {
overflow: auto; overflow: auto;
padding: 0.2em 0.4em; padding: 0.2em 0.4em;
.app--dark & {
caret-color: $editor-color-dark-low;
}
* { * {
line-height: $line-height-base; line-height: $line-height-base;
font-size: inherit !important; font-size: inherit !important;

View File

@ -14,8 +14,6 @@ import CommentList from './gutters/CommentList';
import EditorNewDiscussionButton from './gutters/EditorNewDiscussionButton'; import EditorNewDiscussionButton from './gutters/EditorNewDiscussionButton';
import store from '../store'; import store from '../store';
import editorSvc from '../services/editorSvc'; import editorSvc from '../services/editorSvc';
import imageSvc from '../services/imageSvc';
import utils from '../services/utils';
export default { export default {
components: { components: {
@ -34,7 +32,7 @@ export default {
]), ]),
}, },
methods: { methods: {
async processUpload(items) { setImgAndDoClick(items) {
let file = null; let file = null;
if (!items || items.length === 0) { if (!items || items.length === 0) {
return; return;
@ -48,23 +46,8 @@ export default {
if (!file) { if (!file) {
return; return;
} }
const imgId = utils.uid(); store.dispatch('img/setImg', file);
store.dispatch('img/setCurrImgId', imgId); editorSvc.pagedownEditor.uiManager.doClick('image');
editorSvc.pagedownEditor.uiManager.doClick('imageUploading');
try {
const { url, error } = await imageSvc.updateImg(file);
//
if (error) {
editorSvc.clEditor.replaceAll(`[图片上传中...(image-${imgId})]`, `[图片上传失败...(image-${imgId})]`);
store.dispatch('notification/error', error);
return;
}
editorSvc.clEditor.replaceAll(`[图片上传中...(image-${imgId})]`, `![输入图片说明](${url})`);
} catch (err) {
console.error(err); // eslint-disable-line no-console
editorSvc.clEditor.replaceAll(`[图片上传中...(image-${imgId})]`, `[图片上传失败...(image-${imgId})]`);
store.dispatch('notification/error', err);
}
}, },
}, },
mounted() { mounted() {
@ -73,11 +56,6 @@ export default {
if (currImgStorageStr) { if (currImgStorageStr) {
store.commit('img/changeCheckedStorage', JSON.parse(currImgStorageStr)); store.commit('img/changeCheckedStorage', JSON.parse(currImgStorageStr));
} }
//
const workspaceImgPath = localStorage.getItem('img/workspaceImgPath');
if (workspaceImgPath) {
store.commit('img/setWorkspaceImgPath', JSON.parse(workspaceImgPath));
}
const editorElt = this.$el.querySelector('.editor__inner'); const editorElt = this.$el.querySelector('.editor__inner');
const onDiscussionEvt = cb => (evt) => { const onDiscussionEvt = cb => (evt) => {
let elt = evt.target; let elt = evt.target;
@ -105,11 +83,11 @@ export default {
editorElt.addEventListener('drop', (event) => { editorElt.addEventListener('drop', (event) => {
const transItems = event.dataTransfer.items; const transItems = event.dataTransfer.items;
this.processUpload(transItems); this.setImgAndDoClick(transItems);
}); });
editorElt.addEventListener('paste', (event) => { editorElt.addEventListener('paste', (event) => {
const pasteItems = (event.clipboardData || window.clipboardData).items; const pasteItems = (event.clipboardData || window.clipboardData).items;
this.processUpload(pasteItems); this.setImgAndDoClick(pasteItems);
}); });
this.$watch( this.$watch(

View File

@ -1,197 +0,0 @@
<template>
<div class="editor-in-page-buttons">
<ul>
<li :title="`查找 ${mod}+F`">
<a @click="showFind"><icon-search></icon-search></a>
</li>
<li :title="`替换 ${mod}+Alt+F`">
<a @click="showFindReplace"><icon-find-replace></icon-find-replace></a>
</li>
<li title="切换编辑主题">
<dropdown-menu :selected="selectedTheme" :options="allThemes" :closeOnItemClick="false" @change="changeTheme">
<icon-select-theme></icon-select-theme>
</dropdown-menu>
</li>
<li class="after">
<icon-ellipsis></icon-ellipsis>
</li>
</ul>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
import store from '../store';
import editorSvc from '../services/editorSvc';
import DropdownMenu from './common/DropdownMenu';
const mod = /Mac|iPod|iPhone|iPad/.test(navigator.platform) ? 'Meta' : 'Ctrl';
export default {
components: {
DropdownMenu,
},
data: () => ({
mod,
allThemes: [{
name: '默认主题',
value: 'default',
}, {
name: '天蓝黑',
value: 'azure',
}, {
name: '冰山黑',
value: 'iceberg_contrast',
}, {
name: '黎明白',
value: 'dawn',
}, {
name: '孔雀黑',
value: 'peacock',
}, {
name: '薄荷黑',
value: 'mintchoc',
}, {
name: '薄荷绿',
value: 'spearmint',
}, {
name: '暗蓝黑',
value: 'slate',
}, {
name: '文墨黑',
value: 'carbonight',
}, {
name: '日光白',
value: 'solarized_light',
}, {
name: '咖啡黑',
value: 'espresso_libre',
}, {
name: '薰衣草黑',
value: 'lavender',
}, {
name: '耀斑黑',
value: 'solarflare',
}, {
name: 'Clouds白',
value: 'clouds',
}, {
name: 'Clouds黑',
value: 'clouds_midnight',
}, {
name: 'GitHub白',
value: 'github',
}, {
name: '自定义',
value: 'custom',
}],
}),
computed: {
...mapGetters('theme', [
'currEditTheme',
'customEditThemeStyle',
]),
selectedTheme() {
return {
value: this.currEditTheme || 'default',
};
},
},
methods: {
...mapActions('data', [
'toggleSideBar',
]),
showFind() {
store.dispatch('findReplace/open', {
type: 'find',
findText: editorSvc.clEditor.selectionMgr.hasFocus() &&
editorSvc.clEditor.selectionMgr.getSelectedText(),
});
},
showFindReplace() {
store.dispatch('findReplace/open', {
type: 'replace',
findText: editorSvc.clEditor.selectionMgr.hasFocus() &&
editorSvc.clEditor.selectionMgr.getSelectedText(),
});
},
async changeTheme(item) {
await store.dispatch('theme/setEditTheme', item.value);
//
if (item.value === 'custom' && !this.customEditThemeStyle) {
this.toggleSideBar(true);
store.dispatch('data/setSideBarPanel', 'editTheme');
}
},
},
};
</script>
<style lang="scss">
@import '../styles/variables.scss';
.editor-in-page-buttons {
position: absolute;
top: 0;
left: -108px;
height: 34px;
padding: 5px;
background-color: rgba(84, 96, 114, 0.4);
border-radius: $border-radius-base;
transition: 0.5s;
display: flex;
.dropdown-menu {
display: none;
.dropdown-menu-items {
right: unset;
left: 0;
}
}
&:active,
&:focus,
&:hover {
left: 0;
transition: 0.5s;
background-color: #546072;
.dropdown-menu {
display: block;
}
}
ul {
padding: 0;
margin-left: 10px;
line-height: 20px;
li {
width: 16px;
display: inline-block;
vertical-align: middle;
list-style: none;
cursor: pointer;
font-size: 14px;
margin-right: 10px;
}
}
.icon {
color: #dea731;
opacity: 0.7;
&:active,
&:focus,
&:hover {
opacity: 1;
}
}
.after {
margin-left: 0;
margin-right: -6px;
}
}
</style>

View File

@ -15,7 +15,7 @@
<icon-pen></icon-pen> <icon-pen></icon-pen>
</button> </button>
<button class="side-title__button side-title__button--search button" @click="toSearch()" v-title="'搜索文件'"> <button class="side-title__button side-title__button--search button" @click="toSearch()" v-title="'搜索文件'">
<icon-file-search></icon-file-search> <icon-search></icon-search>
</button> </button>
</div> </div>
<div class="flex flex--row" v-else> <div class="flex flex--row" v-else>
@ -34,7 +34,7 @@
<explorer-node :node="rootNode" :depth="0"></explorer-node> <explorer-node :node="rootNode" :depth="0"></explorer-node>
</div> </div>
<div class="explorer__search" tabindex="0" v-if="!light && showSearch"> <div class="explorer__search" tabindex="0" v-if="!light && showSearch">
<input type="text" v-model="searchText" class="text-input" placeholder="请输入关键字回车" @keyup.enter="search" /> <input type="text" v-model="searchText" class="text-input" placeholder="请输入关键字回车" @keyup.enter="search"></input>
<div class="explorer__search-list"> <div class="explorer__search-list">
<div class="search-tips" v-if="searching">正在查询中...</div> <div class="search-tips" v-if="searching">正在查询中...</div>
<a class="menu-entry button flex flex--row flex--align-center" :class="{'search-node--selected': currentFileId === fileItem.id}" <a class="menu-entry button flex flex--row flex--align-center" :class="{'search-node--selected': currentFileId === fileItem.id}"
@ -54,7 +54,6 @@ import explorerSvc from '../services/explorerSvc';
import store from '../store'; import store from '../store';
import MenuEntry from './menus/common/MenuEntry'; import MenuEntry from './menus/common/MenuEntry';
import localDbSvc from '../services/localDbSvc'; import localDbSvc from '../services/localDbSvc';
import badgeSvc from '../services/badgeSvc';
export default { export default {
components: { components: {
@ -136,19 +135,12 @@ export default {
} }
}); });
this.searching = false; this.searching = false;
badgeSvc.addBadge('searchFile');
}); });
}, },
clickSearch(item) { clickSearch(item) {
const node = store.getters['explorer/nodeMap'][item.id];
if (!node) {
return;
}
store.commit('explorer/setSelectedId', item.id); store.commit('explorer/setSelectedId', item.id);
// Prevent from freezing the UI while loading the file store.commit('file/setCurrentId', item.id);
setTimeout(() => { this.showSearch = false;
store.commit('file/setCurrentId', item.id);
}, 10);
}, },
}, },
created() { created() {
@ -215,11 +207,6 @@ export default {
.app--dark & { .app--dark & {
background-color: rgba(0, 0, 0, 0.4); background-color: rgba(0, 0, 0, 0.4);
} }
&:focus {
background-color: #39f;
color: #fff;
}
} }
} }
</style> </style>

View File

@ -14,7 +14,6 @@
</div> </div>
<explorer-node v-for="node in node.files" :key="node.item.id" :node="node" :depth="depth + 1"></explorer-node> <explorer-node v-for="node in node.files" :key="node.item.id" :node="node" :depth="depth + 1"></explorer-node>
</div> </div>
<button ref="copyId" v-clipboard="copyPath()" @click="info('路径已复制到剪切板!')" style="display: none;"></button>
</div> </div>
</template> </template>
@ -24,7 +23,6 @@ import workspaceSvc from '../services/workspaceSvc';
import explorerSvc from '../services/explorerSvc'; import explorerSvc from '../services/explorerSvc';
import store from '../store'; import store from '../store';
import badgeSvc from '../services/badgeSvc'; import badgeSvc from '../services/badgeSvc';
import utils from '../services/utils';
export default { export default {
name: 'explorer-node', // Required for recursivity name: 'explorer-node', // Required for recursivity
@ -82,9 +80,6 @@ export default {
...mapActions('explorer', [ ...mapActions('explorer', [
'setDragTarget', 'setDragTarget',
]), ]),
...mapActions('notification', [
'info',
]),
select(id = this.node.item.id, doOpen = true) { select(id = this.node.item.id, doOpen = true) {
const node = store.getters['explorer/nodeMap'][id]; const node = store.getters['explorer/nodeMap'][id];
if (!node) { if (!node) {
@ -149,11 +144,6 @@ export default {
// See https://stackoverflow.com/a/3977637/1333165 // See https://stackoverflow.com/a/3977637/1333165
evt.dataTransfer.setData('Text', ''); evt.dataTransfer.setData('Text', '');
}, },
copyPath() {
let path = utils.getAbsoluteDir(this.node).replaceAll(' ', '%20');
path = path.indexOf('/') === 0 ? path : `/${path}`;
return this.node.isFolder ? path : `${path}.md`;
},
onDrop() { onDrop() {
const sourceNode = store.getters['explorer/dragSourceNode']; const sourceNode = store.getters['explorer/dragSourceNode'];
const targetNode = store.getters['explorer/dragTargetNodeFolder']; const targetNode = store.getters['explorer/dragTargetNodeFolder'];
@ -179,26 +169,22 @@ export default {
top: evt.clientY, top: evt.clientY,
}, },
items: [{ items: [{
name: '新建文件', name: 'New file',
disabled: !this.node.isFolder || this.node.isTrash, disabled: !this.node.isFolder || this.node.isTrash,
perform: () => explorerSvc.newItem(false), perform: () => explorerSvc.newItem(false),
}, { }, {
name: '新建文件夹', name: 'New folder',
disabled: !this.node.isFolder || this.node.isTrash || this.node.isTemp, disabled: !this.node.isFolder || this.node.isTrash || this.node.isTemp,
perform: () => explorerSvc.newItem(true), perform: () => explorerSvc.newItem(true),
}, { }, {
type: 'separator', type: 'separator',
}, { }, {
name: '重命名', name: 'Rename',
disabled: this.node.isTrash || this.node.isTemp, disabled: this.node.isTrash || this.node.isTemp,
perform: () => this.setEditingId(this.node.item.id), perform: () => this.setEditingId(this.node.item.id),
}, { }, {
name: '删除', name: 'Delete',
perform: () => explorerSvc.deleteItem(), perform: () => explorerSvc.deleteItem(),
}, {
name: '复制路径',
disabled: this.node.isTrash || this.node.isTemp,
perform: () => this.$refs.copyId.click(),
}], }],
}); });
if (item) { if (item) {

View File

@ -383,10 +383,6 @@ export default {
.find-replace-highlighting { .find-replace-highlighting {
background-color: $highlighting-color; background-color: $highlighting-color;
color: $editor-color-light !important; color: $editor-color-light !important;
.app--dark & {
background-color: $dark-highlighting-color;
}
} }
.find-replace-selection { .find-replace-selection {

View File

@ -9,12 +9,11 @@
<navigation-bar></navigation-bar> <navigation-bar></navigation-bar>
</div> </div>
<div class="layout__panel flex flex--row" :style="{height: styles.innerHeight + 'px'}"> <div class="layout__panel flex flex--row" :style="{height: styles.innerHeight + 'px'}">
<div class="layout__panel layout__panel--editor" :class="editTheme" v-show="styles.showEditor" :style="{width: (styles.editorWidth + styles.editorGutterWidth) + 'px', fontSize: styles.fontSize + 'px'}"> <div class="layout__panel layout__panel--editor" v-show="styles.showEditor" :style="{width: (styles.editorWidth + styles.editorGutterWidth) + 'px', fontSize: styles.fontSize + 'px'}">
<div class="gutter" :style="{left: styles.editorGutterLeft + 'px'}"> <div class="gutter" :style="{left: styles.editorGutterLeft + 'px'}">
<div class="gutter__background" v-if="styles.editorGutterWidth" :style="{width: styles.editorGutterWidth + 'px'}"></div> <div class="gutter__background" v-if="styles.editorGutterWidth" :style="{width: styles.editorGutterWidth + 'px'}"></div>
</div> </div>
<editor></editor> <editor></editor>
<editor-in-page-buttons v-if="editorShowInPageButtons"></editor-in-page-buttons>
<div class="gutter" :style="{left: styles.editorGutterLeft + 'px'}"> <div class="gutter" :style="{left: styles.editorGutterLeft + 'px'}">
<sticky-comment v-if="styles.editorGutterWidth && stickyComment === 'top'"></sticky-comment> <sticky-comment v-if="styles.editorGutterWidth && stickyComment === 'top'"></sticky-comment>
<current-discussion v-if="styles.editorGutterWidth"></current-discussion> <current-discussion v-if="styles.editorGutterWidth"></current-discussion>
@ -28,7 +27,6 @@
<div class="gutter__background" v-if="styles.previewGutterWidth" :style="{width: styles.previewGutterWidth + 'px'}"></div> <div class="gutter__background" v-if="styles.previewGutterWidth" :style="{width: styles.previewGutterWidth + 'px'}"></div>
</div> </div>
<preview></preview> <preview></preview>
<preview-in-page-buttons></preview-in-page-buttons>
<div class="gutter" :style="{left: styles.previewGutterLeft + 'px'}"> <div class="gutter" :style="{left: styles.previewGutterLeft + 'px'}">
<sticky-comment v-if="styles.previewGutterWidth && stickyComment === 'top'"></sticky-comment> <sticky-comment v-if="styles.previewGutterWidth && stickyComment === 'top'"></sticky-comment>
<current-discussion v-if="styles.previewGutterWidth"></current-discussion> <current-discussion v-if="styles.previewGutterWidth"></current-discussion>
@ -60,8 +58,6 @@ import SideBar from './SideBar';
import Editor from './Editor'; import Editor from './Editor';
import Preview from './Preview'; import Preview from './Preview';
import Tour from './Tour'; import Tour from './Tour';
import EditorInPageButtons from './EditorInPageButtons';
import PreviewInPageButtons from './PreviewInPageButtons';
import StickyComment from './gutters/StickyComment'; import StickyComment from './gutters/StickyComment';
import CurrentDiscussion from './gutters/CurrentDiscussion'; import CurrentDiscussion from './gutters/CurrentDiscussion';
import FindReplace from './FindReplace'; import FindReplace from './FindReplace';
@ -79,8 +75,6 @@ export default {
Editor, Editor,
Preview, Preview,
Tour, Tour,
EditorInPageButtons,
PreviewInPageButtons,
StickyComment, StickyComment,
CurrentDiscussion, CurrentDiscussion,
FindReplace, FindReplace,
@ -102,18 +96,9 @@ export default {
...mapGetters('data', [ ...mapGetters('data', [
'layoutSettings', 'layoutSettings',
]), ]),
...mapGetters('theme', [
'currEditTheme',
]),
editTheme() {
return `edit-theme--${this.currEditTheme || 'default'}`;
},
showFindReplace() { showFindReplace() {
return !!store.state.findReplace.type; return !!store.state.findReplace.type;
}, },
editorShowInPageButtons() {
return store.getters['data/computedSettings'].editor.showInPageButtons;
},
}, },
methods: { methods: {
...mapActions('layout', [ ...mapActions('layout', [

View File

@ -10,7 +10,6 @@
<div class="modal__button-bar"> <div class="modal__button-bar">
<button class="button" v-if="simpleModal.rejectText" @click="config.reject()">{{simpleModal.rejectText}}</button> <button class="button" v-if="simpleModal.rejectText" @click="config.reject()">{{simpleModal.rejectText}}</button>
<button class="button button--resolve" v-if="simpleModal.resolveText" @click="config.resolve()">{{simpleModal.resolveText}}</button> <button class="button button--resolve" v-if="simpleModal.resolveText" @click="config.resolve()">{{simpleModal.resolveText}}</button>
<button v-for="(item, idx) in (simpleModal.resolveArray || [])" class="button button--resolve" @click="config.resolve(item.value)">{{item.text}}</button>
</div> </div>
</modal-inner> </modal-inner>
</div> </div>
@ -40,9 +39,6 @@ import WorkspaceManagementModal from './modals/WorkspaceManagementModal';
import AccountManagementModal from './modals/AccountManagementModal'; import AccountManagementModal from './modals/AccountManagementModal';
import BadgeManagementModal from './modals/BadgeManagementModal'; import BadgeManagementModal from './modals/BadgeManagementModal';
import SponsorModal from './modals/SponsorModal'; import SponsorModal from './modals/SponsorModal';
import CommitMessageModal from './modals/CommitMessageModal';
import WorkspaceImgPathModal from './modals/WorkspaceImgPathModal';
import ChatGptModal from './modals/ChatGptModal';
// Providers // Providers
import GooglePhotoModal from './modals/providers/GooglePhotoModal'; import GooglePhotoModal from './modals/providers/GooglePhotoModal';
@ -66,8 +62,6 @@ import GiteeOpenModal from './modals/providers/GiteeOpenModal';
import GiteeSaveModal from './modals/providers/GiteeSaveModal'; import GiteeSaveModal from './modals/providers/GiteeSaveModal';
import GiteeWorkspaceModal from './modals/providers/GiteeWorkspaceModal'; import GiteeWorkspaceModal from './modals/providers/GiteeWorkspaceModal';
import GiteePublishModal from './modals/providers/GiteePublishModal'; import GiteePublishModal from './modals/providers/GiteePublishModal';
import GiteeGistSyncModal from './modals/providers/GiteeGistSyncModal';
import GiteeGistPublishModal from './modals/providers/GiteeGistPublishModal';
import GitlabAccountModal from './modals/providers/GitlabAccountModal'; import GitlabAccountModal from './modals/providers/GitlabAccountModal';
import GitlabOpenModal from './modals/providers/GitlabOpenModal'; import GitlabOpenModal from './modals/providers/GitlabOpenModal';
import GitlabPublishModal from './modals/providers/GitlabPublishModal'; import GitlabPublishModal from './modals/providers/GitlabPublishModal';
@ -111,9 +105,6 @@ export default {
AccountManagementModal, AccountManagementModal,
BadgeManagementModal, BadgeManagementModal,
SponsorModal, SponsorModal,
CommitMessageModal,
WorkspaceImgPathModal,
ChatGptModal,
// Providers // Providers
GooglePhotoModal, GooglePhotoModal,
GoogleDriveAccountModal, GoogleDriveAccountModal,
@ -136,8 +127,6 @@ export default {
GiteeSaveModal, GiteeSaveModal,
GiteeWorkspaceModal, GiteeWorkspaceModal,
GiteePublishModal, GiteePublishModal,
GiteeGistSyncModal,
GiteeGistPublishModal,
GitlabAccountModal, GitlabAccountModal,
GitlabOpenModal, GitlabOpenModal,
GitlabPublishModal, GitlabPublishModal,
@ -188,7 +177,6 @@ export default {
// User has to sign in // User has to sign in
await store.dispatch('modal/open', 'signInForSponsorship'); await store.dispatch('modal/open', 'signInForSponsorship');
await giteeHelper.signin(); await giteeHelper.signin();
await syncSvc.afterSignIn();
syncSvc.requestSync(); syncSvc.requestSync();
} }
if (!store.getters.isSponsor) { if (!store.getters.isSponsor) {

View File

@ -114,8 +114,7 @@ export default {
publishLocations: 'current', publishLocations: 'current',
}), }),
pagedownButtons() { pagedownButtons() {
const buttonShowObj = store.getters['data/computedSettings'].editor.headButtons; return pagedownButtons.map(button => ({
return pagedownButtons.filter(it => buttonShowObj[it.method]).map(button => ({
...button, ...button,
titleWithShortcut: `${button.title}${getShortcut(button.method)}`, titleWithShortcut: `${button.title}${getShortcut(button.method)}`,
iconClass: `icon-${button.icon}`, iconClass: `icon-${button.icon}`,

View File

@ -10,10 +10,10 @@
{{item.content}} {{item.content}}
</div> </div>
<button class="notification__button button" v-if="item.type === 'confirm'" @click="item.reject"> <button class="notification__button button" v-if="item.type === 'confirm'" @click="item.reject">
No
</button> </button>
<button class="notification__button button" v-if="item.type === 'confirm'" @click="item.resolve"> <button class="notification__button button" v-if="item.type === 'confirm'" @click="item.resolve">
Yes
</button> </button>
</div> </div>
</div> </div>

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="preview"> <div class="preview">
<div class="preview__inner-1" @click="onClick" @scroll="onScroll"> <div class="preview__inner-1" @click="onClick" @scroll="onScroll">
<div class="preview__inner-2" :class="previewTheme" :style="{padding: styles.previewPadding}"> <div class="preview__inner-2" :style="{padding: styles.previewPadding}">
</div> </div>
<div class="gutter" :style="{left: styles.previewGutterLeft + 'px'}"> <div class="gutter" :style="{left: styles.previewGutterLeft + 'px'}">
<comment-list v-if="styles.previewGutterWidth"></comment-list> <comment-list v-if="styles.previewGutterWidth"></comment-list>
@ -37,15 +37,9 @@ export default {
...mapGetters('file', [ ...mapGetters('file', [
'isCurrentTemp', 'isCurrentTemp',
]), ]),
...mapGetters('theme', [
'currPreviewTheme',
]),
...mapGetters('layout', [ ...mapGetters('layout', [
'styles', 'styles',
]), ]),
previewTheme() {
return `preview-theme--${this.currPreviewTheme || 'default'}`;
},
}, },
methods: { methods: {
...mapActions('data', [ ...mapActions('data', [

View File

@ -1,212 +0,0 @@
<template>
<div class="preview-in-page-buttons">
<ul>
<li class="before">
<icon-ellipsis></icon-ellipsis>
</li>
<li title="分享">
<a href="javascript:void(0)" @click="share"><icon-share></icon-share></a>
</li>
<li title="切换预览主题">
<dropdown-menu :selected="selectedTheme" :options="allThemes" :closeOnItemClick="false" @change="changeTheme">
<icon-select-theme></icon-select-theme>
</dropdown-menu>
</li>
<li title="Markdown语法帮助">
<a href="javascript:void(0)" @click="showHelp"><icon-help-circle></icon-help-circle></a>
</li>
</ul>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
// import juice from 'juice';
import store from '../store';
import DropdownMenu from './common/DropdownMenu';
import publishSvc from '../services/publishSvc';
import giteeGistProvider from '../services/providers/giteeGistProvider';
import gistProvider from '../services/providers/gistProvider';
export default {
components: {
DropdownMenu,
},
data: () => ({
allThemes: [{
name: '默认主题',
value: 'default',
}, {
name: '凝夜紫',
value: 'ningyezi',
}, {
name: '草原绿',
value: 'caoyuangreen',
}, {
name: '雁栖湖',
value: 'yanqihu',
}, {
name: '灵动蓝',
value: 'activeblue',
}, {
name: '极客黑',
value: 'jikebrack',
}, {
name: '极简黑',
value: 'simplebrack',
}, {
name: '全栈蓝',
value: 'allblue',
}, {
name: '自定义',
value: 'custom',
}],
baseCss: '',
sharing: false,
}),
computed: {
...mapGetters('theme', [
'currPreviewTheme',
'customPreviewThemeStyle',
]),
...mapGetters('publishLocation', {
publishLocations: 'current',
}),
selectedTheme() {
return {
value: this.currPreviewTheme || 'default',
};
},
},
methods: {
...mapActions('data', [
'toggleSideBar',
]),
async changeTheme(item) {
await store.dispatch('theme/setPreviewTheme', item.value);
//
if (item.value === 'custom' && !this.customPreviewThemeStyle) {
this.toggleSideBar(true);
store.dispatch('data/setSideBarPanel', 'previewTheme');
}
},
showHelp() {
this.toggleSideBar(true);
store.dispatch('data/setSideBarPanel', 'help');
},
async share() {
if (this.sharing) {
store.dispatch('notification/info', '分享链接创建中...请稍后再试');
return;
}
try {
const currentFile = store.getters['file/current'];
await store.dispatch('modal/open', { type: 'shareHtmlPre', name: currentFile.name });
this.sharing = true;
const mainToken = store.getters['workspace/mainWorkspaceToken'];
if (!mainToken) {
store.dispatch('notification/info', '登录主文档空间之后才可使用分享功能!');
return;
}
let tempGistId = null;
const isGithub = mainToken.providerId === 'githubAppData';
const gistProviderId = isGithub ? 'gist' : 'giteegist';
const filterLocations = this.publishLocations.filter(it => it.providerId === gistProviderId
&& it.url && it.gistId);
if (filterLocations.length > 0) {
tempGistId = filterLocations[0].gistId;
}
const location = (isGithub ? gistProvider : giteeGistProvider).makeLocation(
mainToken,
`分享-${currentFile.name}`,
true,
null,
);
location.templateId = 'styledHtmlWithTheme';
location.fileId = currentFile.id;
location.gistId = tempGistId;
const { gistId } = await publishSvc.publishLocationAndStore(location);
const sharePage = mainToken.providerId === 'githubAppData' ? 'gistshare.html' : 'share.html';
const url = `${window.location.protocol}//${window.location.host}/${sharePage}?id=${gistId}`;
await store.dispatch('modal/open', { type: 'shareHtml', name: currentFile.name, url });
} catch (err) {
if (err) {
store.dispatch('notification/error', err);
}
} finally {
this.sharing = false;
}
},
},
};
</script>
<style lang="scss">
@import '../styles/variables.scss';
.preview-in-page-buttons {
position: absolute;
bottom: 10px;
right: -98px;
height: 34px;
padding: 5px;
background-color: rgba(84, 96, 114, 0.4);
border-radius: $border-radius-base;
transition: 0.5s;
display: flex;
.dropdown-menu {
display: none;
}
&:active,
&:focus,
&:hover {
right: 0;
transition: 0.5s;
background-color: #546072;
.dropdown-menu {
display: block;
}
}
.dropdown-menu-items {
bottom: 100%;
top: unset;
}
ul {
padding: 0;
margin-left: 10px;
line-height: 20px;
li {
line-height: 16px;
width: 16px;
display: inline-block;
vertical-align: middle;
list-style: none;
cursor: pointer;
font-size: 14px;
margin-right: 10px;
.icon {
color: #fff;
opacity: 0.7;
&:active,
&:focus,
&:hover {
opacity: 1;
}
}
}
.before {
margin-left: -16px;
margin-right: 0;
}
}
}
</style>

View File

@ -23,8 +23,6 @@
<div v-else-if="panel === 'help'" class="side-bar__panel side-bar__panel--help"> <div v-else-if="panel === 'help'" class="side-bar__panel side-bar__panel--help">
<pre class="markdown-highlighting" v-html="markdownSample"></pre> <pre class="markdown-highlighting" v-html="markdownSample"></pre>
</div> </div>
<edit-theme-menu v-else-if="panel === 'editTheme'"></edit-theme-menu>
<preview-theme-menu v-else-if="panel === 'previewTheme'"></preview-theme-menu>
<div class="side-bar__panel side-bar__panel--toc" :class="{'side-bar__panel--hidden': panel !== 'toc'}"> <div class="side-bar__panel side-bar__panel--toc" :class="{'side-bar__panel--hidden': panel !== 'toc'}">
<toc> <toc>
</toc> </toc>
@ -43,8 +41,6 @@ import PublishMenu from './menus/PublishMenu';
import HistoryMenu from './menus/HistoryMenu'; import HistoryMenu from './menus/HistoryMenu';
import ImportExportMenu from './menus/ImportExportMenu'; import ImportExportMenu from './menus/ImportExportMenu';
import WorkspaceBackupMenu from './menus/WorkspaceBackupMenu'; import WorkspaceBackupMenu from './menus/WorkspaceBackupMenu';
import EditThemeMenu from './menus/EditThemeMenu';
import PreviewThemeMenu from './menus/PreviewThemeMenu';
import markdownSample from '../data/markdownSample.md'; import markdownSample from '../data/markdownSample.md';
import markdownConversionSvc from '../services/markdownConversionSvc'; import markdownConversionSvc from '../services/markdownConversionSvc';
import store from '../store'; import store from '../store';
@ -59,8 +55,6 @@ const panelNames = {
history: '文件历史', history: '文件历史',
importExport: '导入/导出', importExport: '导入/导出',
workspaceBackups: '文档空间备份', workspaceBackups: '文档空间备份',
editTheme: '编辑区主题',
previewTheme: '预览区主题',
}; };
export default { export default {
@ -73,8 +67,6 @@ export default {
HistoryMenu, HistoryMenu,
ImportExportMenu, ImportExportMenu,
WorkspaceBackupMenu, WorkspaceBackupMenu,
EditThemeMenu,
PreviewThemeMenu,
}, },
data: () => ({ data: () => ({
markdownSample: markdownConversionSvc.highlight(markdownSample), markdownSample: markdownConversionSvc.highlight(markdownSample),

View File

@ -1,137 +0,0 @@
<template>
<span class="dropdown-menu">
<span ref="slotInfo" @click="toggleMenu()" class="dropdown-toggle">
<slot></slot>
</span>
<ul class="dropdown-menu-items" :style="dropdownStyle" v-if="showMenu">
<li v-for="(option, idx) in options" :key="idx">
<a href="javascript:void(0)" :class="{selected: option.value === selectedOption.value}" @click="updateOption(option)">
{{ option.name }}
</a>
</li>
</ul>
</span>
</template>
<script>
import store from '../../store';
export default {
data: () => ({
selectedOption: {
value: '',
name: '',
},
showMenu: false,
}),
props: {
options: {
type: [Array, Object],
},
selected: {},
closeOnOutsideClick: {
type: [Boolean],
default: true,
},
closeOnItemClick: {
type: [Boolean],
default: true,
},
},
mounted() {
this.selectedOption = this.selected;
if (this.closeOnOutsideClick) {
document.addEventListener('click', this.clickHandler);
}
},
beforeDestroy() {
document.removeEventListener('click', this.clickHandler);
},
computed: {
dropdownStyle() {
const height = store.state.layout.bodyHeight;
return `max-height: ${height * 0.7}px;`;
},
},
methods: {
updateOption(option) {
this.selectedOption = option;
if (this.closeOnItemClick) {
this.showMenu = false;
}
this.$emit('change', option);
},
toggleMenu() {
this.showMenu = !this.showMenu;
},
clickHandler(event) {
const { target } = event;
const { $el } = this;
if (!$el.contains(target)) {
this.showMenu = false;
}
},
},
watch: {
selected(val) {
this.selectedOption = val;
},
},
};
</script>
<style lang="scss">
.dropdown-menu {
.dropdown-menu-items {
position: absolute;
top: 100%;
right: 0;
float: left;
min-width: 160px;
max-height: 450px;
overflow-y: scroll;
padding: 5px 0;
margin: 0;
list-style: none;
font-size: 15px;
background-color: #666;
border: 1px solid #666;
border-radius: 4px;
-webkit-box-shadow: 0 6px 12px rgb(0, 0, 0 / 18%);
box-shadow: 0 6px 12px rgb(0, 0, 0 / 18%);
background-clip: padding-box;
li {
width: 100%;
display: list-item;
margin: 0;
text-align: -webkit-match-parent;
a {
display: block;
clear: both;
font-weight: normal;
line-height: 1.45;
white-space: nowrap;
color: #eee;
padding: 5px 20px;
border-top: 1px solid transparent;
border-bottom: 1px solid transparent;
text-decoration: none;
&:active,
&:focus,
&:hover {
background-color: rgb(82, 82, 82);
}
}
a.selected {
background: #74b936 !important;
color: #fff !important;
}
}
}
}
</style>

View File

@ -1,116 +0,0 @@
<template>
<div class="edit-theme side-bar__panel side-bar__panel--menu">
<div class="side-bar__info">
<div class="menu-entry menu-entry--info flex flex--row flex--align-center">
<span v-if="currEditTheme==='custom'">
下面的自定义主题样式可编辑可参考其他主题样式填入自己喜欢的编辑样式<br>
主题class为edit-theme--custom
</span>
<span v-else>
下面的主题样式不可编辑
</span>
</div>
</div>
<div class="side-bar__content">
<template v-if="currEditTheme === 'default'">
默认主题无额外样式请选择其他主题
</template>
<template v-else>
<code-editor v-for="(value, index) in styleEles" :key="index"
v-if="value.id === `edit-theme-${currEditTheme}`" lang="css" :value="value.innerHTML"
:disabled="value.id!=='edit-theme-custom'" @changed="changeText" scrollClass="side-bar__inner"></code-editor>
</template>
</div>
<div class="flex flex--row flex--end" v-if="currEditTheme==='custom'">
<button class="edit-theme__button button" @click="saveStyleText">保存</button>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import MenuEntry from './common/MenuEntry';
import CodeEditor from '../CodeEditor';
import store from '../../store';
export default {
components: {
MenuEntry,
CodeEditor,
},
data: () => ({
themeStyleText: '',
styleEles: [],
}),
computed: {
...mapGetters('theme', [
'currEditTheme',
]),
},
methods: {
saveStyleText() {
const typeEle = this.findByTheme(this.currEditTheme);
if (!typeEle || !this.themeStyleText) {
return;
}
typeEle.innerHTML = this.themeStyleText;
store.dispatch('theme/setCustomEditThemeStyle', this.themeStyleText);
store.dispatch('notification/info', '保存自定义主题样式成功!');
},
findByTheme(theme) {
const findEles = this.styleEles.filter(it => it.id === `edit-theme-${theme}`);
return findEles.length ? findEles[0] : null;
},
changeText(text) {
this.themeStyleText = text;
},
close() {
store.dispatch('data/setSideBarPanel', 'menu');
},
initStyle(theme) {
if (theme === 'default') {
return;
}
const value = theme || this.currEditTheme;
if (this.findByTheme(value)) {
return;
}
const styleId = `edit-theme-${value}`;
const styleEle = document.getElementById(styleId);
if (!styleEle) {
setTimeout(() => this.initStyle(value), 1000);
return;
}
this.styleEles.push(styleEle);
},
},
watch: {
currEditTheme: {
immediate: true,
handler(val) {
this.initStyle(val);
},
},
},
created() {
this.initStyle();
},
};
</script>
<style lang="scss">
.side-bar__panel--menu {
.side-bar__content {
.code-editor {
min-height: 400px !important;
max-height: 100%;
}
}
.edit-theme__button {
font-size: 14px;
margin-top: 0.5em;
}
}
</style>

View File

@ -8,7 +8,7 @@
</option> </option>
</select> </select>
</p> </p>
<p v-if="!historyContext">同步 <b>{{currentFileName}}</b> 以启用修订历史 或者 <a href="javascript:void(0)" @click="signin">登录 Gitee</a> <a href="javascript:void(0)" @click="signinWithGithub">登录 GitHub</a> 以同步您的主文档空间</p> <p v-if="!historyContext">同步 <b>{{currentFileName}}</b> 以启用修订历史 或者 <a href="javascript:void(0)" @click="signin">登录 Gitee</a> 以同步您的主文档空间</p>
<p v-else-if="loading">历史版本加载中</p> <p v-else-if="loading">历史版本加载中</p>
<p v-else-if="!revisionsWithSpacer.length"><b>{{currentFileName}}</b> 没有历史版本.</p> <p v-else-if="!revisionsWithSpacer.length"><b>{{currentFileName}}</b> 没有历史版本.</p>
<div class="menu-entry menu-entry--info flex flex--row flex--align-center" v-else> <div class="menu-entry menu-entry--info flex flex--row flex--align-center" v-else>
@ -33,7 +33,6 @@
<div class="revision__header flex flex--column"> <div class="revision__header flex flex--column">
<user-name :user-id="revision.sub"></user-name> <user-name :user-id="revision.sub"></user-name>
<div class="revision__created">{{revision.created | formatTime}}</div> <div class="revision__created">{{revision.created | formatTime}}</div>
<div class="revision__msg">{{revision.message}}</div>
</div> </div>
</a> </a>
</div> </div>
@ -55,7 +54,6 @@ import EditorClassApplier from '../common/EditorClassApplier';
import PreviewClassApplier from '../common/PreviewClassApplier'; import PreviewClassApplier from '../common/PreviewClassApplier';
import utils from '../../services/utils'; import utils from '../../services/utils';
import giteeHelper from '../../services/providers/helpers/giteeHelper'; import giteeHelper from '../../services/providers/helpers/giteeHelper';
import githubHelper from '../../services/providers/helpers/githubHelper';
import syncSvc from '../../services/syncSvc'; import syncSvc from '../../services/syncSvc';
import store from '../../store'; import store from '../../store';
import badgeSvc from '../../services/badgeSvc'; import badgeSvc from '../../services/badgeSvc';
@ -169,16 +167,6 @@ export default {
async signin() { async signin() {
try { try {
await giteeHelper.signin(); await giteeHelper.signin();
await syncSvc.afterSignIn();
syncSvc.requestSync();
} catch (e) {
// Cancel
}
},
async signinWithGithub() {
try {
await githubHelper.signin();
await syncSvc.afterSignIn();
syncSvc.requestSync(); syncSvc.requestSync();
} catch (e) { } catch (e) {
// Cancel // Cancel
@ -424,14 +412,6 @@ export default {
opacity: 0.6; opacity: 0.6;
} }
.revision__msg {
font-size: 0.75em;
opacity: 0.6;
white-space: pre-wrap;
word-break: break-word;
word-wrap: break-word;
}
.layout--revision { .layout--revision {
.cledit-section *, .cledit-section *,
.cl-preview-section * { .cl-preview-section * {

View File

@ -14,9 +14,6 @@
<span v-if="currentWorkspace.providerId === 'giteeAppData'"> <span v-if="currentWorkspace.providerId === 'giteeAppData'">
<b>{{currentWorkspace.name}}</b> 与您的 Gitee 默认文档空间仓库同步 <b>{{currentWorkspace.name}}</b> 与您的 Gitee 默认文档空间仓库同步
</span> </span>
<span v-else-if="currentWorkspace.providerId === 'githubAppData'">
<b>{{currentWorkspace.name}}</b> 与您的 GitHub 默认文档空间仓库同步
</span>
<span v-else-if="currentWorkspace.providerId === 'googleDriveWorkspace'"> <span v-else-if="currentWorkspace.providerId === 'googleDriveWorkspace'">
<b>{{currentWorkspace.name}}</b> <a :href="workspaceLocationUrl" target="_blank">Google Drive 文件夹</a>同步 <b>{{currentWorkspace.name}}</b> <a :href="workspaceLocationUrl" target="_blank">Google Drive 文件夹</a>同步
</span> </span>
@ -24,10 +21,10 @@
<b>{{currentWorkspace.name}}</b> <a :href="workspaceLocationUrl" target="_blank">CouchDB 数据库</a>同步 <b>{{currentWorkspace.name}}</b> <a :href="workspaceLocationUrl" target="_blank">CouchDB 数据库</a>同步
</span> </span>
<span v-else-if="currentWorkspace.providerId === 'githubWorkspace'"> <span v-else-if="currentWorkspace.providerId === 'githubWorkspace'">
<b>{{currentWorkspace.name}}</b> <a :href="workspaceLocationUrl" target="_blank">GitHub 仓库</a> 同步 <b>{{currentWorkspace.name}}</b> <a :href="workspaceLocationUrl" target="_blank">GitHub repo</a> 同步
</span> </span>
<span v-else-if="currentWorkspace.providerId === 'giteeWorkspace'"> <span v-else-if="currentWorkspace.providerId === 'giteeWorkspace'">
<b>{{currentWorkspace.name}}</b> <a :href="workspaceLocationUrl" target="_blank">Gitee 仓库</a> 同步 <b>{{currentWorkspace.name}}</b> <a :href="workspaceLocationUrl" target="_blank">Gitee repo</a> 同步
</span> </span>
<span v-else-if="currentWorkspace.providerId === 'gitlabWorkspace'"> <span v-else-if="currentWorkspace.providerId === 'gitlabWorkspace'">
<b>{{currentWorkspace.name}}</b> <a :href="workspaceLocationUrl" target="_blank">GitLab 项目</a>同步 <b>{{currentWorkspace.name}}</b> <a :href="workspaceLocationUrl" target="_blank">GitLab 项目</a>同步
@ -48,11 +45,6 @@
<div>使用 Gitee 登录</div> <div>使用 Gitee 登录</div>
<span>同步您的主文档空间并解锁功能</span> <span>同步您的主文档空间并解锁功能</span>
</menu-entry> </menu-entry>
<menu-entry v-if="!loginToken" @click.native="signinWithGithub">
<icon-login slot="icon"></icon-login>
<div>使用 GitHub 登录</div>
<span>同步您的主文档空间并解锁功能</span>
</menu-entry>
<menu-entry @click.native="setPanel('workspaces')"> <menu-entry @click.native="setPanel('workspaces')">
<icon-database slot="icon"></icon-database> <icon-database slot="icon"></icon-database>
<div><div class="menu-entry__label menu-entry__label--count" v-if="workspaceCount">{{workspaceCount}}</div> 文档空间</div> <div><div class="menu-entry__label menu-entry__label--count" v-if="workspaceCount">{{workspaceCount}}</div> 文档空间</div>
@ -113,16 +105,6 @@
<div><div class="menu-entry__label menu-entry__label--count">{{templateCount}}</div> 模板</div> <div><div class="menu-entry__label menu-entry__label--count">{{templateCount}}</div> 模板</div>
<span>为您的导出配置 Handlebars 模板</span> <span>为您的导出配置 Handlebars 模板</span>
</menu-entry> </menu-entry>
<menu-entry @click.native="setPanel('editTheme')">
<icon-select-theme slot="icon"></icon-select-theme>
编辑区主题
<span>编辑区主题样式(自定义主题可编辑)</span>
</menu-entry>
<menu-entry @click.native="setPanel('previewTheme')">
<icon-select-theme slot="icon"></icon-select-theme>
预览区主题
<span>预览区主题样式(自定义主题可编辑)</span>
</menu-entry>
<menu-entry @click.native="settings"> <menu-entry @click.native="settings">
<icon-settings slot="icon"></icon-settings> <icon-settings slot="icon"></icon-settings>
<div>配置</div> <div>配置</div>
@ -150,7 +132,6 @@ import MenuEntry from './common/MenuEntry';
import providerRegistry from '../../services/providers/common/providerRegistry'; import providerRegistry from '../../services/providers/common/providerRegistry';
import UserImage from '../UserImage'; import UserImage from '../UserImage';
import giteeHelper from '../../services/providers/helpers/giteeHelper'; import giteeHelper from '../../services/providers/helpers/giteeHelper';
import githubHelper from '../../services/providers/helpers/githubHelper';
import syncSvc from '../../services/syncSvc'; import syncSvc from '../../services/syncSvc';
import userSvc from '../../services/userSvc'; import userSvc from '../../services/userSvc';
import store from '../../store'; import store from '../../store';
@ -203,16 +184,6 @@ export default {
async signin() { async signin() {
try { try {
await giteeHelper.signin(); await giteeHelper.signin();
await syncSvc.afterSignIn();
syncSvc.requestSync();
} catch (e) {
// Cancel
}
},
async signinWithGithub() {
try {
await githubHelper.signin();
await syncSvc.afterSignIn();
syncSvc.requestSync(); syncSvc.requestSync();
} catch (e) { } catch (e) {
// Cancel // Cancel

View File

@ -1,116 +0,0 @@
<template>
<div class="preview-theme side-bar__panel side-bar__panel--menu">
<div class="side-bar__info">
<div class="menu-entry menu-entry--info flex flex--row flex--align-center">
<span v-if="currPreviewTheme==='custom'">
下面的自定义主题样式可编辑可参考其他主题样式填入自己喜欢的预览样式<br>
主题class为preview-theme--custom
</span>
<span v-else>
下面的主题样式不可编辑
</span>
</div>
</div>
<div class="side-bar__content">
<template v-if="currPreviewTheme === 'default'">
默认主题无额外样式请选择其他主题
</template>
<template v-else>
<code-editor v-for="(value, index) in styleEles" :key="index"
v-if="value.id === `preview-theme-${currPreviewTheme}`" lang="css" :value="value.innerHTML"
:disabled="value.id!=='preview-theme-custom'" @changed="changeText" scrollClass="side-bar__inner"></code-editor>
</template>
</div>
<div class="flex flex--row flex--end" v-if="currPreviewTheme==='custom'">
<button class="preview-theme__button button" @click="saveStyleText">保存</button>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import MenuEntry from './common/MenuEntry';
import CodeEditor from '../CodeEditor';
import store from '../../store';
export default {
components: {
MenuEntry,
CodeEditor,
},
data: () => ({
themeStyleText: '',
styleEles: [],
}),
computed: {
...mapGetters('theme', [
'currPreviewTheme',
]),
},
methods: {
saveStyleText() {
const typeEle = this.findByTheme(this.currPreviewTheme);
if (!typeEle || !this.themeStyleText) {
return;
}
typeEle.innerHTML = this.themeStyleText;
store.dispatch('theme/setCustomPreviewThemeStyle', this.themeStyleText);
store.dispatch('notification/info', '保存自定义主题样式成功!');
},
findByTheme(theme) {
const findEles = this.styleEles.filter(it => it.id === `preview-theme-${theme}`);
return findEles.length ? findEles[0] : null;
},
changeText(text) {
this.themeStyleText = text;
},
close() {
store.dispatch('data/setSideBarPanel', 'menu');
},
initStyle(theme) {
if (theme === 'default') {
return;
}
const value = theme || this.currPreviewTheme;
if (this.findByTheme(value)) {
return;
}
const styleId = `preview-theme-${value}`;
const styleEle = document.getElementById(styleId);
if (!styleEle) {
setTimeout(() => this.initStyle(value), 1000);
return;
}
this.styleEles.push(styleEle);
},
},
watch: {
currPreviewTheme: {
immediate: true,
handler(val) {
this.initStyle(val);
},
},
},
created() {
this.initStyle();
},
};
</script>
<style lang="scss">
.side-bar__panel--menu {
.side-bar__content {
.code-editor {
min-height: 400px !important;
max-height: 100%;
}
}
.preview-theme__button {
font-size: 14px;
margin-top: 0.5em;
}
}
</style>

View File

@ -43,7 +43,7 @@
<div v-for="token in githubTokens" :key="token.sub"> <div v-for="token in githubTokens" :key="token.sub">
<menu-entry @click.native="publishGist(token)"> <menu-entry @click.native="publishGist(token)">
<icon-provider slot="icon" provider-id="gist"></icon-provider> <icon-provider slot="icon" provider-id="gist"></icon-provider>
<div>发布到 GitHubGist</div> <div>发布到 Gist</div>
<span>{{token.name}}</span> <span>{{token.name}}</span>
</menu-entry> </menu-entry>
<menu-entry @click.native="publishGithub(token)"> <menu-entry @click.native="publishGithub(token)">
@ -53,11 +53,6 @@
</menu-entry> </menu-entry>
</div> </div>
<div v-for="token in giteeTokens" :key="token.sub"> <div v-for="token in giteeTokens" :key="token.sub">
<menu-entry @click.native="publishGiteeGist(token)">
<icon-provider slot="icon" provider-id="giteegist"></icon-provider>
<div>发布到 GiteeGist</div>
<span>{{token.name}}</span>
</menu-entry>
<menu-entry @click.native="publishGitee(token)"> <menu-entry @click.native="publishGitee(token)">
<icon-provider slot="icon" provider-id="gitee"></icon-provider> <icon-provider slot="icon" provider-id="gitee"></icon-provider>
<div>发布到 Gitee</div> <div>发布到 Gitee</div>
@ -263,8 +258,8 @@ export default {
}, },
async addGitlabAccount() { async addGitlabAccount() {
try { try {
const { serverUrl, applicationId, applicationSecret } = await store.dispatch('modal/open', { type: 'gitlabAccount' }); const { serverUrl, applicationId } = await store.dispatch('modal/open', { type: 'gitlabAccount' });
await gitlabHelper.addAccount(serverUrl, applicationId, applicationSecret); await gitlabHelper.addAccount(serverUrl, applicationId);
} catch (e) { /* cancel */ } } catch (e) { /* cancel */ }
}, },
async addGiteaAccount() { async addGiteaAccount() {
@ -294,9 +289,8 @@ export default {
publishBloggerPage: publishModalOpener('bloggerPagePublish', 'publishToBloggerPage'), publishBloggerPage: publishModalOpener('bloggerPagePublish', 'publishToBloggerPage'),
publishDropbox: publishModalOpener('dropboxPublish', 'publishToDropbox'), publishDropbox: publishModalOpener('dropboxPublish', 'publishToDropbox'),
publishGithub: publishModalOpener('githubPublish', 'publishToGithub'), publishGithub: publishModalOpener('githubPublish', 'publishToGithub'),
publishGist: publishModalOpener('gistPublish', 'publishToGist'),
publishGitee: publishModalOpener('giteePublish', 'publishToGitee'), publishGitee: publishModalOpener('giteePublish', 'publishToGitee'),
publishGiteeGist: publishModalOpener('giteeGistPublish', 'publishGiteeGist'), publishGist: publishModalOpener('gistPublish', 'publishToGist'),
publishGitlab: publishModalOpener('gitlabPublish', 'publishToGitlab'), publishGitlab: publishModalOpener('gitlabPublish', 'publishToGitlab'),
publishGitea: publishModalOpener('giteaPublish', 'publishToGitea'), publishGitea: publishModalOpener('giteaPublish', 'publishToGitea'),
publishGoogleDrive: publishModalOpener('googleDrivePublish', 'publishToGoogleDrive'), publishGoogleDrive: publishModalOpener('googleDrivePublish', 'publishToGoogleDrive'),

View File

@ -46,7 +46,7 @@
</menu-entry> </menu-entry>
<menu-entry @click.native="saveGist(token)"> <menu-entry @click.native="saveGist(token)">
<icon-provider slot="icon" provider-id="gist"></icon-provider> <icon-provider slot="icon" provider-id="gist"></icon-provider>
<div>在GitHubGist上保存</div> <div>在Gist上保存</div>
<span>{{token.name}}</span> <span>{{token.name}}</span>
</menu-entry> </menu-entry>
</div> </div>
@ -61,11 +61,6 @@
<div>在Gitee上保存</div> <div>在Gitee上保存</div>
<span>{{token.name}}</span> <span>{{token.name}}</span>
</menu-entry> </menu-entry>
<menu-entry @click.native="saveGiteeGist(token)">
<icon-provider slot="icon" provider-id="giteegist"></icon-provider>
<div>在GiteeGist上保存</div>
<span>{{token.name}}</span>
</menu-entry>
</div> </div>
<div v-for="token in gitlabTokens" :key="token.sub"> <div v-for="token in gitlabTokens" :key="token.sub">
<menu-entry @click.native="openGitlab(token)"> <menu-entry @click.native="openGitlab(token)">
@ -239,8 +234,8 @@ export default {
}, },
async addGitlabAccount() { async addGitlabAccount() {
try { try {
const { serverUrl, applicationId, applicationSecret } = await store.dispatch('modal/open', { type: 'gitlabAccount' }); const { serverUrl, applicationId } = await store.dispatch('modal/open', { type: 'gitlabAccount' });
await gitlabHelper.addAccount(serverUrl, applicationId, applicationSecret); await gitlabHelper.addAccount(serverUrl, applicationId);
} catch (e) { /* cancel */ } } catch (e) { /* cancel */ }
}, },
async addGiteaAccount() { async addGiteaAccount() {
@ -335,12 +330,6 @@ export default {
badgeSvc.addBadge('saveOnGist'); badgeSvc.addBadge('saveOnGist');
} catch (e) { /* cancel */ } } catch (e) { /* cancel */ }
}, },
async saveGiteeGist(token) {
try {
await openSyncModal(token, 'giteeGistSync');
badgeSvc.addBadge('saveOnGiteeGist');
} catch (e) { /* cancel */ }
},
async openGitlab(token) { async openGitlab(token) {
try { try {
const syncLocation = await store.dispatch('modal/open', { const syncLocation = await store.dispatch('modal/open', {

View File

@ -8,8 +8,7 @@
<hr> <hr>
<div class="workspace" v-for="(workspace, id) in workspacesById" :key="id"> <div class="workspace" v-for="(workspace, id) in workspacesById" :key="id">
<menu-entry :href="workspace.url" target="_blank"> <menu-entry :href="workspace.url" target="_blank">
<icon-provider v-if="id === 'main' && !workspace.sub" slot="icon" :provider-id="'stackedit'"></icon-provider> <icon-provider slot="icon" :provider-id="workspace.providerId"></icon-provider>
<icon-provider v-else slot="icon" :provider-id="workspace.providerId"></icon-provider>
<div class="workspace__name"><div class="menu-entry__label" v-if="currentWorkspace === workspace">当前</div>{{workspace.name}}</div> <div class="workspace__name"><div class="menu-entry__label" v-if="currentWorkspace === workspace">当前</div>{{workspace.name}}</div>
</menu-entry> </menu-entry>
</div> </div>
@ -86,8 +85,8 @@ export default {
}, },
async addGitlabWorkspace() { async addGitlabWorkspace() {
try { try {
const { serverUrl, applicationId, applicationSecret } = await store.dispatch('modal/open', { type: 'gitlabAccount' }); const { serverUrl, applicationId } = await store.dispatch('modal/open', { type: 'gitlabAccount' });
const token = await gitlabHelper.addAccount(serverUrl, applicationId, applicationSecret); const token = await gitlabHelper.addAccount(serverUrl, applicationId);
store.dispatch('modal/open', { store.dispatch('modal/open', {
type: 'gitlabWorkspace', type: 'gitlabWorkspace',
token, token,

View File

@ -248,8 +248,8 @@ export default {
}, },
async addGitlabAccount() { async addGitlabAccount() {
try { try {
const { serverUrl, applicationId, applicationSecret } = await store.dispatch('modal/open', { type: 'gitlabAccount' }); const { serverUrl, applicationId } = await store.dispatch('modal/open', { type: 'gitlabAccount' });
await gitlabHelper.addAccount(serverUrl, applicationId, applicationSecret); await gitlabHelper.addAccount(serverUrl, applicationId);
} catch (e) { /* cancel */ } } catch (e) { /* cancel */ }
}, },
async addGiteaAccount() { async addGiteaAccount() {

View File

@ -1,131 +0,0 @@
<template>
<modal-inner class="modal__inner-1--chatgpt" aria-label="chatgpt">
<div class="modal__content">
<div class="modal__image">
<icon-chat-gpt></icon-chat-gpt>
</div>
<p><b>ChatGPT内容生成</b><br>生成时长受ChatGPT服务响应与网络响应时长影响时间可能较长</p>
<form-entry label="生成内容要求详细描述" error="content">
<textarea slot="field" class="text-input" type="text" placeholder="输入内容(支持换行)" v-model.trim="content" :disabled="generating"></textarea>
<div class="form-entry__info">
使用 <a href="https://api35.pxj123.cn/" target="_blank">api35.pxj123.cn</a> 的免费接口生成内容AI模型是GPT-3.5 Turbo
</div>
</form-entry>
<div class="modal__result">
<pre class="result_pre" v-if="generating && !result">(等待生成中...)</pre>
<pre class="result_pre" v-else v-text="result"></pre>
</div>
</div>
<div class="modal__button-bar">
<button class="button" @click="reject()">{{ generating ? '停止' : '关闭' }}</button>
<button class="button button--resolve" @click="generate" v-if="!generating && !!content">{{ !!result ? '重新生成' : '开始生成' }}</button>
<button class="button button--resolve" @click="resolve" v-if="!generating && !!result">确认插入</button>
</div>
</modal-inner>
</template>
<script>
import modalTemplate from './common/modalTemplate';
import chatGptSvc from '../../services/chatGptSvc';
import store from '../../store';
export default modalTemplate({
data: () => ({
generating: false,
content: '',
result: '',
xhr: null,
}),
methods: {
resolve(evt) {
evt.preventDefault();
const { callback } = this.config;
this.config.resolve();
callback(this.result);
},
process({ done, content, error }) {
if (done) {
this.generating = false;
//
} else if (content) {
this.result = this.result + content;
const container = document.querySelector('.result_pre');
container.scrollTo(0, container.scrollHeight); //
} else if (error) {
this.generating = false;
}
},
generate() {
this.generating = true;
this.result = '';
try {
this.xhr = chatGptSvc.chat({
content: `${this.content}\n(使用Markdown方式输出结果)`,
}, this.process);
} catch (err) {
this.generating = false;
store.dispatch('notification/error', err);
}
},
reject() {
if (this.generating) {
if (this.xhr) {
this.xhr.abort();
this.generating = false;
}
return;
}
const { callback } = this.config;
this.config.reject();
callback(null);
},
},
mounted() {
const script = document.createElement('script');
script.src = `https://api35.pxj123.cn/js/chat.js?t=${new Date().getTime()}`;
script.onload = () => {
/* eslint-disable */
console.log('加载外部chatgpt的js成功!');
};
this.$el.appendChild(script);
},
});
</script>
<style lang="scss">
@import '../../styles/variables.scss';
.modal__inner-1.modal__inner-1--chatgpt {
max-width: 560px;
.result_pre {
font-size: 0.9em;
font-variant-ligatures: no-common-ligatures;
line-height: 1.25;
white-space: pre-wrap;
word-break: break-word;
word-wrap: break-word;
height: 300px;
border: 1px solid rgb(126, 126, 126);
border-radius: $border-radius-base;
padding: 10px;
overflow-y: scroll; /* 开启垂直滚动条 */
}
.result_pre::-webkit-scrollbar {
display: none; /* 隐藏滚动条 */
}
.result_pre.scroll-bottom {
scroll-behavior: smooth;
}
.config-warning {
color: #f00;
}
.text-input {
min-height: 60px;
}
}
</style>

View File

@ -1,43 +0,0 @@
<template>
<modal-inner aria-label="提交信息">
<p>自定义 <b>{{ config.name }}</b> 提交信息</p>
<div class="modal__content">
<div class="form-entry">
<label class="form-entry__label">提交信息</label>
<div class="form-entry__field">
<input slot="field" class="textfield" placeholder="提交信息非必填" type="text" v-model.trim="commitMessage" @keydown.enter="resolve()">
</div>
</div>
</div>
<div class="modal__button-bar">
<button class="button" @click="config.reject()">取消</button>
<button class="button button--resolve" @click="resolve()">确认</button>
</div>
</modal-inner>
</template>
<script>
import { mapGetters } from 'vuex';
import ModalInner from './common/ModalInner';
export default {
components: {
ModalInner,
},
data: () => ({
commitMessage: '',
}),
computed: {
...mapGetters('modal', [
'config',
]),
},
methods: {
resolve() {
this.config.resolve({
commitMessage: this.commitMessage,
});
},
},
};
</script>

View File

@ -1,35 +1,18 @@
<template> <template>
<modal-inner aria-label="插入图像"> <modal-inner aria-label="插入图像">
<div class="modal__content"> <div class="modal__content">
<p>请为您的图像提供<b> url </b><span v-if="uploading">(图片上传中...)</span></p> <p v-if="hasFile">
<form-entry label="URL" error="url"> <span v-if="uploading">粘贴/拖拽图片上传中...</span>
<span v-if="!this.uploading && url">
<img :src="url">
</span>
<span v-if="!this.uploading && !url">图片上传失败如未添加图床请先添加并选择之后关闭窗口再重试</span>
</p>
<p v-if="!hasFile">请为您的图像提供<b> url </b></p>
<form-entry v-if="!hasFile" label="URL" error="url">
<input slot="field" class="textfield" type="text" v-model.trim="url" @keydown.enter="resolve"> <input slot="field" class="textfield" type="text" v-model.trim="url" @keydown.enter="resolve">
</form-entry> </form-entry>
</div> <p>添加并选择图床后可实现粘贴/拖拽自动上传图片</p>
<div class="modal__button-bar">
<input class="hidden-file" id="upload-image-file-input" type="file" accept="image/*" :disabled="uploading" @change="uploadImage">
<label for="upload-image-file-input"><a class="button">上传图片</a></label>
<button class="button" @click="reject()">取消</button>
<button class="button button--resolve" @click="resolve" :disabled="uploading">确认</button>
</div>
<div>
<hr />
<p>添加并选择图床后可在编辑区中粘贴/拖拽图片自动上传</p>
<menu-entry @click.native="checkedImgDest(path)" v-for="path in workspaceImgPath" :key="path">
<icon-check-circle v-if="checkedStorage.sub === path" slot="icon"></icon-check-circle>
<icon-check-circle-un v-if="checkedStorage.sub !== path" slot="icon"></icon-check-circle-un>
<menu-item>
<icon-provider slot="icon" :provider-id="currentWorkspace.providerId"></icon-provider>
<div>
当前文档空间图片路径
<button class="menu-item__button button" @click.stop="removeByPath(path)" v-title="'删除'">
<icon-delete></icon-delete>
</button>
</div>
<span>路径{{path}}</span>
</menu-item>
</menu-entry>
<menu-entry @click.native="checkedImgDest(token.sub, token.providerId)" v-for="token in imageTokens" :key="token.sub"> <menu-entry @click.native="checkedImgDest(token.sub, token.providerId)" v-for="token in imageTokens" :key="token.sub">
<icon-check-circle v-if="checkedStorage.sub === token.sub" slot="icon"></icon-check-circle> <icon-check-circle v-if="checkedStorage.sub === token.sub" slot="icon"></icon-check-circle>
<icon-check-circle-un v-if="checkedStorage.sub !== token.sub" slot="icon"></icon-check-circle-un> <icon-check-circle-un v-if="checkedStorage.sub !== token.sub" slot="icon"></icon-check-circle-un>
@ -47,9 +30,9 @@
<span class="line-entry" v-if="token.params">自定义Form参数{{token.params}}</span> <span class="line-entry" v-if="token.params">自定义Form参数{{token.params}}</span>
</menu-item> </menu-item>
</menu-entry> </menu-entry>
<menu-entry @click.native="checkedImgDest(tokenStorage.token.sub, tokenStorage.providerId, tokenStorage.sid)" v-for="tokenStorage in tokensImgStorages" :key="tokenStorage.sid"> <menu-entry @click.native="checkedImgDest(tokenStorage.sid, tokenStorage.providerId)" v-for="tokenStorage in tokensImgStorages" :key="tokenStorage.sid">
<icon-check-circle v-if="checkedStorage.sid === tokenStorage.sid" slot="icon"></icon-check-circle> <icon-check-circle v-if="checkedStorage.sub === tokenStorage.sid" slot="icon"></icon-check-circle>
<icon-check-circle-un v-if="checkedStorage.sid !== tokenStorage.sid" slot="icon"></icon-check-circle-un> <icon-check-circle-un v-if="checkedStorage.sub !== tokenStorage.sid" slot="icon"></icon-check-circle-un>
<menu-item> <menu-item>
<icon-provider slot="icon" :provider-id="tokenStorage.providerId"></icon-provider> <icon-provider slot="icon" :provider-id="tokenStorage.providerId"></icon-provider>
<div>{{tokenStorage.providerName}} <div>{{tokenStorage.providerName}}
@ -60,10 +43,12 @@
<span> {{tokenStorage.uname}}, 仓库URL: {{tokenStorage.repoUrl}}, 路径: {{tokenStorage.path}}, 分支: {{tokenStorage.branch}}</span> <span> {{tokenStorage.uname}}, 仓库URL: {{tokenStorage.repoUrl}}, 路径: {{tokenStorage.path}}, 分支: {{tokenStorage.branch}}</span>
</menu-item> </menu-item>
</menu-entry> </menu-entry>
<menu-entry @click.native="addWorkspaceImgPath"> </div>
<icon-provider slot="icon" :provider-id="currentWorkspace.providerId"></icon-provider> <div class="modal__button-bar">
<span>添加当前文档空间图片路径</span> <button class="button" @click="reject()">取消</button>
</menu-entry> <button class="button button--resolve" @click="resolve" :disabled="uploading">确认</button>
</div>
<div>
<menu-entry @click.native="addSmmsAccount"> <menu-entry @click.native="addSmmsAccount">
<icon-provider slot="icon" provider-id="smms"></icon-provider> <icon-provider slot="icon" provider-id="smms"></icon-provider>
<span>添加SM.MS图床账号</span> <span>添加SM.MS图床账号</span>
@ -85,7 +70,6 @@
</template> </template>
<script> <script>
import { mapGetters } from 'vuex';
import modalTemplate from './common/modalTemplate'; import modalTemplate from './common/modalTemplate';
import MenuEntry from '../menus/common/MenuEntry'; import MenuEntry from '../menus/common/MenuEntry';
import MenuItem from '../menus/common/MenuItem'; import MenuItem from '../menus/common/MenuItem';
@ -95,7 +79,6 @@ import giteaHelper from '../../services/providers/helpers/giteaHelper';
import githubHelper from '../../services/providers/helpers/githubHelper'; import githubHelper from '../../services/providers/helpers/githubHelper';
import customHelper from '../../services/providers/helpers/customHelper'; import customHelper from '../../services/providers/helpers/customHelper';
import utils from '../../services/utils'; import utils from '../../services/utils';
import imageSvc from '../../services/imageSvc';
export default modalTemplate({ export default modalTemplate({
components: { components: {
@ -103,24 +86,14 @@ export default modalTemplate({
MenuItem, MenuItem,
}, },
data: () => ({ data: () => ({
hasFile: false,
uploading: false, uploading: false,
url: '', url: '',
}), }),
computed: { computed: {
...mapGetters('workspace', [
'currentWorkspace',
'currentWorkspaceIsGit',
]),
checkedStorage() { checkedStorage() {
return store.getters['img/getCheckedStorage']; return store.getters['img/getCheckedStorage'];
}, },
workspaceImgPath() {
if (!this.currentWorkspaceIsGit) {
return [];
}
const workspaceImgPath = store.getters['img/getWorkspaceImgPath'];
return Object.keys(workspaceImgPath || {});
},
imageTokens() { imageTokens() {
return [ return [
...Object.values(store.getters['data/smmsTokensBySub']).map(token => ({ ...Object.values(store.getters['data/smmsTokensBySub']).map(token => ({
@ -164,12 +137,92 @@ export default modalTemplate({
uname: it.token.name, uname: it.token.name,
providerId: it.providerId, providerId: it.providerId,
providerName: it.providerName, providerName: it.providerName,
repoUrl: it.providerId === 'gitea' ? `${it.token.serverUrl}/${storage.repoUri}` : `${storage.owner}/${storage.repo}`, repoUrl: it.providerId === 'gitea' ? `${it.serverUrl}/${storage.repoUri}` : `${storage.owner}/${storage.repo}`,
})); }));
}); });
return imgStorages; return imgStorages;
}, },
}, },
async mounted() {
this.hasFile = false;
const imgFile = store.getters['img/getImg'];
if (imgFile) {
this.hasFile = true;
this.uploading = true;
try {
//
// provider smms
const currStorage = this.checkedStorage;
if (!currStorage) {
store.dispatch('notification/info', '暂无已选择的图床,未自动上传图片!请选择图床后重新粘贴/拖拽图片!');
return;
}
if (currStorage.provider === 'smms' || currStorage.provider === 'custom') {
const filterTokens = this.imageTokens.filter(it => it.sub === currStorage.sub);
if (!filterTokens.length) {
store.dispatch('notification/info', '图床已失效,未自动上传图片!请选择图床后重新粘贴/拖拽图片!');
return;
}
const token = filterTokens[0];
const helper = currStorage.provider === 'smms' ? smmsHelper : customHelper;
try {
this.url = await helper.uploadFile({
token,
file: imgFile,
});
} catch (err) {
store.dispatch('notification/error', err);
}
} else if (currStorage.provider === 'gitea' || currStorage.provider === 'github') {
const filterTokenStorages = this.tokensImgStorages
.filter(it => it.sid === currStorage.sub);
if (!filterTokenStorages.length) {
store.dispatch('notification/info', 'Gitea图床已失效未自动上传图片请选择图床后重新粘贴/拖拽图片!');
return;
}
const tokenStorage = filterTokenStorages[0];
const time = new Date();
const date = time.getDate();
const month = time.getMonth() + 1;
const year = time.getFullYear();
let path = tokenStorage.path.replace('{YYYY}', year)
.replace('{MM}', `0${month}`.slice(-2)).replace('{DD}', `0${date}`.slice(-2));
path = `${path}${path.endsWith('/') ? '' : '/'}${utils.uid()}.${imgFile.type.split('/')[1]}`;
try {
if (currStorage.provider === 'gitea') {
const result = await giteaHelper.uploadFile({
token: tokenStorage.token,
projectId: tokenStorage.repoUri,
branch: tokenStorage.branch,
path,
content: imgFile,
isFile: true,
});
this.url = result.content.download_url;
} else if (currStorage.provider === 'github') {
const result = await githubHelper.uploadFile({
token: tokenStorage.token,
owner: tokenStorage.owner,
repo: tokenStorage.repo,
branch: tokenStorage.branch,
path,
content: imgFile,
isFile: true,
});
this.url = result.content.download_url;
}
} catch (err) {
store.dispatch('notification/error', err);
}
} else {
store.dispatch('notification/info', '暂无已选择的图床,未自动上传图片!请选择图床后重新粘贴/拖拽图片!');
}
} finally {
store.dispatch('img/clearImg');
this.uploading = false;
}
}
},
methods: { methods: {
resolve(evt) { resolve(evt) {
evt.preventDefault(); // Fixes https://github.com/mafgwo/stackedit/issues/1503 evt.preventDefault(); // Fixes https://github.com/mafgwo/stackedit/issues/1503
@ -186,27 +239,6 @@ export default modalTemplate({
this.config.reject(); this.config.reject();
callback(null); callback(null);
}, },
async uploadImage(evt) {
if (!evt.target.files || !evt.target.files.length) {
return;
}
const imgFile = evt.target.files[0];
try {
this.uploading = true;
const { url, error } = await imageSvc.updateImg(imgFile);
if (error) {
store.dispatch('notification/error', error);
return;
}
this.url = url;
} catch (err) {
store.dispatch('notification/error', err);
} finally {
this.uploading = false;
//
evt.target.value = '';
}
},
async remove(proivderId, item) { async remove(proivderId, item) {
try { try {
await store.dispatch('modal/open', 'imgStorageDeletion'); await store.dispatch('modal/open', 'imgStorageDeletion');
@ -226,13 +258,6 @@ export default modalTemplate({
// Cancel // Cancel
} }
}, },
async removeByPath(path) {
store.dispatch('img/removeWorkspaceImgPath', path);
},
async addWorkspaceImgPath() {
const { path } = await store.dispatch('modal/open', { type: 'workspaceImgPath' });
store.dispatch('img/addWorkspaceImgPath', path);
},
async addSmmsAccount() { async addSmmsAccount() {
const { proxyUrl, apiSecretToken } = await store.dispatch('modal/open', { type: 'smmsAccount' }); const { proxyUrl, apiSecretToken } = await store.dispatch('modal/open', { type: 'smmsAccount' });
await smmsHelper.addAccount(proxyUrl, apiSecretToken); await smmsHelper.addAccount(proxyUrl, apiSecretToken);
@ -263,19 +288,15 @@ export default modalTemplate({
githubHelper.updateToken(token, imgStorageInfo); githubHelper.updateToken(token, imgStorageInfo);
} catch (e) { /* Cancel */ } } catch (e) { /* Cancel */ }
}, },
async checkedImgDest(sub, provider, sid) { async checkedImgDest(sub, provider) {
let type = 'token'; let type = 'token';
// if (provider === 'gitea' || provider === 'github') {
if (!provider) {
type = 'workspace';
} else if (provider === 'gitea' || provider === 'github') {
type = 'tokenRepo'; type = 'tokenRepo';
} }
store.dispatch('img/changeCheckedStorage', { store.dispatch('img/changeCheckedStorage', {
type, type,
provider, provider,
sub, sub,
sid,
}); });
// const { callback } = this.config; // const { callback } = this.config;
// this.config.reject(); // this.config.reject();

View File

@ -1,7 +1,7 @@
<template> <template>
<modal-inner aria-label="导出到PDF"> <modal-inner aria-label="导出到PDF">
<div class="modal__content"> <div class="modal__content">
<p>请为您的<b> pdf导出</b>选择模板(该导出很消耗服务器资源文档太大或图片太多可能会导出超时失败可参考 <a href="https://gitee.com/mafgwo/stackedit/blob/master/docs/大文档导出PDF方式.md" target="_blank">大文档导出PDF方式</a> 自行导出大文档)</p> <p>请为您的<b> pdf导出</b>选择模板</p>
<form-entry label="模板"> <form-entry label="模板">
<select class="textfield" slot="field" v-model="selectedTemplate" @keydown.enter="resolve()"> <select class="textfield" slot="field" v-model="selectedTemplate" @keydown.enter="resolve()">
<option v-for="(template, id) in allTemplatesById" :key="id" :value="id"> <option v-for="(template, id) in allTemplatesById" :key="id" :value="id">

View File

@ -4,8 +4,8 @@
<div class="modal__image"> <div class="modal__image">
<icon-upload></icon-upload> <icon-upload></icon-upload>
</div> </div>
<p v-if="publishLocations.length"><b>{{currentFileName}}</b> 被发布到了以下位置:</p> <p v-if="publishLocations.length"><b>{{currentFileName}}</b> is published to the following location(s):</p>
<p v-else><b>{{currentFileName}}</b> 还没有被发布.</p> <p v-else><b>{{currentFileName}}</b> is not published yet.</p>
<div> <div>
<div class="publish-entry flex flex--column" v-for="location in publishLocations" :key="location.id"> <div class="publish-entry flex flex--column" v-for="location in publishLocations" :key="location.id">
<div class="publish-entry__header flex flex--row flex--align-center"> <div class="publish-entry__header flex flex--row flex--align-center">
@ -26,7 +26,7 @@
{{location.url}} {{location.url}}
</div> </div>
<div class="publish-entry__buttons flex flex--row flex--center" v-if="location.url"> <div class="publish-entry__buttons flex flex--row flex--center" v-if="location.url">
<button class="publish-entry__button button" v-clipboard="location.url" @click="info('位置URL已复制到剪贴板!')" v-title="'复制URL'"> <button class="publish-entry__button button" v-clipboard="location.url" @click="info('Location URL copied to clipboard!')" v-title="'复制URL'">
<icon-content-copy></icon-content-copy> <icon-content-copy></icon-content-copy>
</button> </button>
<a class="publish-entry__button button" v-if="location.url" :href="location.url" target="_blank" v-title="'打开位置'"> <a class="publish-entry__button button" v-if="location.url" :href="location.url" target="_blank" v-title="'打开位置'">
@ -34,19 +34,6 @@
</a> </a>
</div> </div>
</div> </div>
<div class="publish-entry__row flex flex--row flex--align-center" v-if="shareUrl(location)">
<div class="publish-entry__url">
分享链接: {{shareUrl(location)}}
</div>
<div class="publish-entry__buttons flex flex--row flex--center">
<button class="publish-entry__button button" v-clipboard="shareUrl(location)" @click="info('分享URL已复制到剪贴板!')" v-title="'复制分享URL'">
<icon-content-copy></icon-content-copy>
</button>
<a class="publish-entry__button button" :href="shareUrl(location)" target="_blank" v-title="'打开分享'">
<icon-open-in-new></icon-open-in-new>
</a>
</div>
</div>
</div> </div>
</div> </div>
<div class="modal__info" v-if="publishLocations.length"> <div class="modal__info" v-if="publishLocations.length">
@ -54,7 +41,7 @@
</div> </div>
</div> </div>
<div class="modal__button-bar"> <div class="modal__button-bar">
<button class="button button--resolve" @click="config.resolve()">关闭</button> <button class="button button--resolve" @click="config.resolve()">Close</button>
</div> </div>
</modal-inner> </modal-inner>
</template> </template>
@ -88,16 +75,6 @@ export default {
store.commit('publishLocation/deleteItem', location.id); store.commit('publishLocation/deleteItem', location.id);
badgeSvc.addBadge('removePublishLocation'); badgeSvc.addBadge('removePublishLocation');
}, },
shareUrl(location) {
if (location.providerId !== 'giteegist' && location.providerId !== 'gist') {
return null;
}
if (!location.url || !location.gistId) {
return null;
}
const sharePage = location.providerId === 'gist' ? 'gistshare.html' : 'share.html';
return `${window.location.protocol}//${window.location.host}/${sharePage}?id=${location.gistId}`;
},
}, },
}; };
</script> </script>

View File

@ -1,48 +0,0 @@
<template>
<modal-inner aria-label="文档空间图片路径">
<div class="modal__content">
<div class="modal__image">
<icon-provider :provider-id="currentWorkspace.providerId"></icon-provider>
</div>
<p>在当前文档空间增加图片上传路径</p>
<form-entry label="图片上传路径" error="path">
<input slot="field" class="textfield" type="text" placeholder="如:/imgs/{YYYY}-{MM}-{DD}" v-model.trim="path" @keydown.enter="resolve()">
<div class="form-entry__info">
如果不提供默认为 /imgs/{YYYY}-{MM}-{DD}<br/>
支持相对路径 ./imgs../imgs imgs 都是相对当前编辑中文档的路径<br/>
变量说明{YYYY}为年变量{MM}为月变量{DD}为日变量{MDNAME}为当前文档名称
</div>
</form-entry>
</div>
<div class="modal__button-bar">
<button class="button" @click="config.reject()">取消</button>
<button class="button button--resolve" @click="resolve()">确认</button>
</div>
</modal-inner>
</template>
<script>
import { mapGetters } from 'vuex';
import modalTemplate from './common/modalTemplate';
export default modalTemplate({
computedLocalSettings: {
path: '',
},
computed: {
...mapGetters('workspace', [
'currentWorkspace',
]),
},
methods: {
resolve() {
if (!this.path) {
this.setError('path');
}
this.config.resolve({
path: this.path || '/imgs/{YYYY}-{MM}-{DD}',
});
},
},
});
</script>

View File

@ -9,8 +9,7 @@
<div class="flex flex--column"> <div class="flex flex--column">
<div class="workspace-entry__header flex flex--row flex--align-center"> <div class="workspace-entry__header flex flex--row flex--align-center">
<div class="workspace-entry__icon"> <div class="workspace-entry__icon">
<icon-provider v-if="id === 'main' && !workspace.sub" :provider-id="'stackedit'"></icon-provider> <icon-provider :provider-id="workspace.providerId"></icon-provider>
<icon-provider v-else :provider-id="workspace.providerId"></icon-provider>
</div> </div>
<input class="text-input" type="text" v-if="editedId === id" v-focus @blur="submitEdit()" @keydown.enter="submitEdit()" @keydown.esc.stop="submitEdit(true)" v-model="editingName"> <input class="text-input" type="text" v-if="editedId === id" v-focus @blur="submitEdit()" @keydown.enter="submitEdit()" @keydown.esc.stop="submitEdit(true)" v-model="editingName">
<div class="workspace-entry__name" v-else>{{workspace.name}}</div> <div class="workspace-entry__name" v-else>{{workspace.name}}</div>
@ -18,15 +17,6 @@
<button class="workspace-entry__button button" @click="edit(id)" v-title="'编辑名称'"> <button class="workspace-entry__button button" @click="edit(id)" v-title="'编辑名称'">
<icon-pen></icon-pen> <icon-pen></icon-pen>
</button> </button>
<template v-if="workspace.providerId === 'giteeAppData' || workspace.providerId === 'githubAppData' || workspace.providerId === 'githubWorkspace'
|| workspace.providerId === 'giteeWorkspace' || workspace.providerId === 'gitlabWorkspace' || workspace.providerId === 'giteaWorkspace'">
<button class="workspace-entry__button button" @click="stopAutoSync(id)" v-if="workspace.autoSync == undefined || workspace.autoSync" v-title="'关闭自动同步'">
<icon-sync-auto></icon-sync-auto>
</button>
<button class="workspace-entry__button button" @click="startAutoSync(id)" v-if="workspace.autoSync != undefined && !workspace.autoSync" v-title="'启动自动同步'">
<icon-sync-stop></icon-sync-stop>
</button>
</template>
<button class="workspace-entry__button button" @click="remove(id)" v-title="'删除'"> <button class="workspace-entry__button button" @click="remove(id)" v-title="'删除'">
<icon-delete></icon-delete> <icon-delete></icon-delete>
</button> </button>
@ -132,53 +122,12 @@ export default {
this.info('请先关闭文档空间,然后再将其删除。'); this.info('请先关闭文档空间,然后再将其删除。');
} else { } else {
try { try {
const workspace = this.workspacesById[id]; await store.dispatch('modal/open', 'removeWorkspace');
if (!workspace) {
return;
}
await store.dispatch('modal/open', {
type: 'removeWorkspace',
name: workspace.name,
});
workspaceSvc.removeWorkspace(id); workspaceSvc.removeWorkspace(id);
badgeSvc.addBadge('removeWorkspace'); badgeSvc.addBadge('removeWorkspace');
} catch (e) { /* Cancel */ } } catch (e) { /* Cancel */ }
} }
}, },
async stopAutoSync(id) {
const workspace = this.workspacesById[id];
if (!workspace) {
return;
}
await store.dispatch('modal/open', {
type: 'stopAutoSyncWorkspace',
name: workspace.name,
});
store.dispatch('workspace/patchWorkspacesById', {
[id]: {
...workspace,
autoSync: false,
},
});
badgeSvc.addBadge('stopAutoSyncWorkspace');
},
async startAutoSync(id) {
const workspace = this.workspacesById[id];
if (!workspace) {
return;
}
await store.dispatch('modal/open', {
type: 'autoSyncWorkspace',
name: workspace.name,
});
store.dispatch('workspace/patchWorkspacesById', {
[id]: {
...workspace,
autoSync: true,
},
});
badgeSvc.addBadge('autoSyncWorkspace');
},
}, },
created() { created() {
Object.keys(this.workspacesById).forEach(async (workspaceId) => { Object.keys(this.workspacesById).forEach(async (workspaceId) => {

View File

@ -17,7 +17,7 @@ export default {
uid: utils.uid(), uid: utils.uid(),
}), }),
mounted() { mounted() {
this.$el.querySelector('input,select,textarea').id = this.uid; this.$el.querySelector('input,select').id = this.uid;
}, },
}; };
</script> </script>

View File

@ -1,11 +1,11 @@
<template> <template>
<modal-inner aria-label="发布到GitHubGist"> <modal-inner aria-label="发布到Gist">
<div class="modal__content"> <div class="modal__content">
<div class="modal__image"> <div class="modal__image">
<icon-provider provider-id="gist"></icon-provider> <icon-provider provider-id="gist"></icon-provider>
</div> </div>
<p>发布<b> {{CurrentFileName}} </b><b>GitHubGist</b></p> <p>发布<b> {{CurrentFileName}} </b><b>Gist</b></p>
<form-entry label="文件名" error="filename"> <form-entry label="Filename" error="filename">
<input slot="field" class="textfield" type="text" v-model.trim="filename" @keydown.enter="resolve()"> <input slot="field" class="textfield" type="text" v-model.trim="filename" @keydown.enter="resolve()">
</form-entry> </form-entry>
<div class="form-entry"> <div class="form-entry">
@ -15,10 +15,10 @@
</label> </label>
</div> </div>
</div> </div>
<form-entry label="存在Gist ID" info="可选的"> <form-entry label="Existing Gist ID" info="可选的">
<input slot="field" class="textfield" type="text" v-model.trim="gistId" @keydown.enter="resolve()"> <input slot="field" class="textfield" type="text" v-model.trim="gistId" @keydown.enter="resolve()">
<div class="form-entry__info"> <div class="form-entry__info">
如果文件存在于GitHubGist中则将被覆盖 如果文件存在于Gist中则将被覆盖
</div> </div>
</form-entry> </form-entry>
<form-entry label="Template"> <form-entry label="Template">

View File

@ -1,11 +1,11 @@
<template> <template>
<modal-inner aria-label=" GitHubGist 同步"> <modal-inner aria-label=" Gist 同步">
<div class="modal__content"> <div class="modal__content">
<div class="modal__image"> <div class="modal__image">
<icon-provider provider-id="gist"></icon-provider> <icon-provider provider-id="gist"></icon-provider>
</div> </div>
<p><b> {{currentFileName}} </b>保存到<b>GitHubGist</b>并保持同步</p> <p><b> {{currentFileName}} </b>保存到<b>Gist</b>并保持同步</p>
<form-entry label="文件名" error="filename"> <form-entry label="Filename" error="filename">
<input slot="field" class="textfield" type="text" v-model.trim="filename" @keydown.enter="resolve()"> <input slot="field" class="textfield" type="text" v-model.trim="filename" @keydown.enter="resolve()">
</form-entry> </form-entry>
<div class="form-entry"> <div class="form-entry">
@ -15,10 +15,10 @@
</label> </label>
</div> </div>
</div> </div>
<form-entry label="存在Gist ID" info="可选的"> <form-entry label="Existing Gist ID" info="可选的">
<input slot="field" class="textfield" type="text" v-model.trim="gistId" @keydown.enter="resolve()"> <input slot="field" class="textfield" type="text" v-model.trim="gistId" @keydown.enter="resolve()">
<div class="form-entry__info"> <div class="form-entry__info">
如果文件存在于GitHubGist中则将被覆盖 如果文件存在于Gist中则将被覆盖
</div> </div>
</form-entry> </form-entry>
</div> </div>

View File

@ -5,30 +5,28 @@
<icon-provider provider-id="gitea"></icon-provider> <icon-provider provider-id="gitea"></icon-provider>
</div> </div>
<p>将您的<b>Gitea</b>链接到<b>StackEdit中文版</b></p> <p>将您的<b>Gitea</b>链接到<b>StackEdit中文版</b></p>
<template v-if="!useServerConf"> <form-entry label="Gitea URL" error="serverUrl">
<form-entry label="Gitea URL" error="serverUrl"> <input v-if="config.forceServerUrl" slot="field" class="textfield" type="text" disabled="disabled" v-model="config.forceServerUrl">
<input v-if="config.forceServerUrl" slot="field" class="textfield" type="text" disabled="disabled" v-model="config.forceServerUrl"> <input v-else slot="field" class="textfield" type="text" v-model.trim="serverUrl" @keydown.enter="resolve()">
<input v-else slot="field" class="textfield" type="text" v-model.trim="serverUrl" @keydown.enter="resolve()"> <div class="form-entry__info">
<div class="form-entry__info"> <b>例如:</b> https://gitea.example.com/
<b>例如:</b> https://gitea.example.com/ <span v-if="httpAppUrl">
<span v-if="httpAppUrl"> 非https的URL请跳转到 <a :href="httpAppUrl" target="_blank">HTTP链接</a> 添加Gitea
非https的URL请跳转到 <a :href="httpAppUrl" target="_blank">HTTP链接</a> 添加Gitea </span>
</span> </div>
</div> </form-entry>
</form-entry> <form-entry label="Application ID" error="applicationId">
<form-entry label="Application ID" error="applicationId"> <input slot="field" class="textfield" type="text" v-model.trim="applicationId" @keydown.enter="resolve()">
<input slot="field" class="textfield" type="text" v-model.trim="applicationId" @keydown.enter="resolve()"> </form-entry>
</form-entry> <form-entry label="Application Secret" error="applicationSecret">
<form-entry label="Application Secret" error="applicationSecret"> <input slot="field" class="textfield" type="text" v-model.trim="applicationSecret" @keydown.enter="resolve()">
<input slot="field" class="textfield" type="text" v-model.trim="applicationSecret" @keydown.enter="resolve()"> <div class="form-entry__info">
<div class="form-entry__info"> 您必须使用重定向url <b>{{redirectUrl}}</b>配置OAuth2应用程序
您必须使用重定向url <b>{{redirectUrl}}</b>配置OAuth2应用程序 </div>
</div> <div class="form-entry__actions">
<div class="form-entry__actions"> <a href="https://docs.gitea.io/en-us/oauth2-provider/" target="_blank">更多信息</a>
<a href="https://docs.gitea.io/en-us/oauth2-provider/" target="_blank">更多信息</a> </div>
</div> </form-entry>
</form-entry>
</template>
</div> </div>
<div class="modal__button-bar"> <div class="modal__button-bar">
<button class="button" @click="config.reject()">取消</button> <button class="button" @click="config.reject()">取消</button>
@ -40,8 +38,6 @@
<script> <script>
import modalTemplate from '../common/modalTemplate'; import modalTemplate from '../common/modalTemplate';
import constants from '../../../data/constants'; import constants from '../../../data/constants';
import store from '../../../store';
import networkSvc from '../../../services/networkSvc';
export default modalTemplate({ export default modalTemplate({
data: () => ({ data: () => ({
@ -59,22 +55,9 @@ export default modalTemplate({
} }
return null; return null;
}, },
// 使
useServerConf() {
const confClientId = store.getters['data/serverConf'].giteaClientId;
const confServerUrl = store.getters['data/serverConf'].giteaUrl;
return !!confClientId && !!confServerUrl;
},
},
mounted() {
networkSvc.getServerConf();
}, },
methods: { methods: {
resolve() { resolve() {
if (this.useServerConf) {
this.config.resolve({});
return;
}
const serverUrl = this.config.forceServerUrl || this.serverUrl; const serverUrl = this.config.forceServerUrl || this.serverUrl;
if (!serverUrl) { if (!serverUrl) {
this.setError('serverUrl'); this.setError('serverUrl');

View File

@ -12,10 +12,9 @@
</div> </div>
</form-entry> </form-entry>
<form-entry label="文件夹路径" info="可选的"> <form-entry label="文件夹路径" info="可选的">
<input slot="field" class="textfield" type="text" placeholder="如imgs/{YYYY}-{MM}-{DD}" v-model.trim="path" @keydown.enter="resolve()"> <input slot="field" class="textfield" type="text" placeholder="如imgs/{YYYY}/{MM}" v-model.trim="path" @keydown.enter="resolve()">
<div class="form-entry__info"> <div class="form-entry__info">
如果不提供默认为 imgs/{YYYY}-{MM}-{DD} <br/> 如果不提供默认为 {YYYY}/{MM}/{DD} 其中{YYYY}为年变量{MM}为月变量{DD}为日变量
变量说明{YYYY}为年变量{MM}为月变量{DD}为日变量{MDNAME}为当前文档名称
</div> </div>
</form-entry> </form-entry>
<form-entry label="分支" info="可选的"> <form-entry label="分支" info="可选的">
@ -63,7 +62,7 @@ export default modalTemplate({
const path = this.path && this.path.replace(/^\//, ''); const path = this.path && this.path.replace(/^\//, '');
this.config.resolve({ this.config.resolve({
repoUri: projectPath, repoUri: projectPath,
path: path || 'imgs/{YYYY}-{MM}-{DD}', path: path || '{YYYY}/{MM}/{DD}',
branch: this.branch || 'master', branch: this.branch || 'master',
}); });
} catch (err) { } catch (err) {

View File

@ -4,11 +4,11 @@
<div class="modal__image"> <div class="modal__image">
<icon-provider provider-id="gitea"></icon-provider> <icon-provider provider-id="gitea"></icon-provider>
</div> </div>
<p>向您的<b> Gitea </b>项目发布<b> {{ currentFileName }} </b></p> <p>向您的<b> Gitea </b>项目发布<b> {{CurrentFileName}} </b></p>
<form-entry label="Project URL" error="projectUrl"> <form-entry label="Project URL" error="projectUrl">
<input slot="field" class="textfield" type="text" v-model.trim="projectUrl" @keydown.enter="resolve()"> <input slot="field" class="textfield" type="text" v-model.trim="projectUrl" @keydown.enter="resolve()">
<div class="form-entry__info"> <div class="form-entry__info">
<b>例如:</b> {{ config.token.serverUrl }}/path/to/project <b>例如:</b> {{config.token.serverUrl}}/path/to/project
</div> </div>
</form-entry> </form-entry>
<form-entry label="File path" error="path"> <form-entry label="File path" error="path">

View File

@ -1,79 +0,0 @@
<template>
<modal-inner aria-label="发布到GiteeGist">
<div class="modal__content">
<div class="modal__image">
<icon-provider provider-id="giteegist"></icon-provider>
</div>
<p>发布<b> {{CurrentFileName}} </b><b>GiteeGist</b></p>
<form-entry label="文件名" error="filename">
<input slot="field" class="textfield" type="text" v-model.trim="filename" @keydown.enter="resolve()">
</form-entry>
<div class="form-entry">
<div class="form-entry__checkbox">
<label>
<input type="checkbox" v-model="isPublic"> 公开的
</label>
</div>
</div>
<form-entry label="存在Gist ID" info="可选的">
<input slot="field" class="textfield" type="text" v-model.trim="gistId" @keydown.enter="resolve()">
<div class="form-entry__info">
如果文件存在于GiteeGist中则将被覆盖
</div>
</form-entry>
<form-entry label="Template">
<select slot="field" class="textfield" v-model="selectedTemplate" @keydown.enter="resolve()">
<option v-for="(template, id) in allTemplatesById" :key="id" :value="id">
{{ template.name }}
</option>
</select>
<div class="form-entry__actions">
<a href="javascript:void(0)" @click="configureTemplates">配置模板</a>
</div>
</form-entry>
<div class="modal__info">
<b>ProTip:</b> You can provide a value for <code>title</code> in the <a href="javascript:void(0)" @click="openFileProperties">file properties</a>.
</div>
</div>
<div class="modal__button-bar">
<button class="button" @click="config.reject()">取消</button>
<button class="button button--resolve" @click="resolve()">确认</button>
</div>
</modal-inner>
</template>
<script>
import giteeGistProvider from '../../../services/providers/giteeGistProvider';
import modalTemplate from '../common/modalTemplate';
export default modalTemplate({
data: () => ({
filename: '',
gistId: '',
}),
computedLocalSettings: {
isPublic: 'gistIsPublic',
selectedTemplate: 'gistPublishTemplate',
},
created() {
this.filename = `${this.currentFileName}.md`;
},
methods: {
resolve() {
if (!this.filename) {
this.setError('filename');
} else {
// Return new location
const location = giteeGistProvider.makeLocation(
this.config.token,
this.filename,
this.isPublic,
this.gistId,
);
location.templateId = this.selectedTemplate;
this.config.resolve(location);
}
},
},
});
</script>

View File

@ -1,64 +0,0 @@
<template>
<modal-inner aria-label=" GiteeGist 同步">
<div class="modal__content">
<div class="modal__image">
<icon-provider provider-id="giteegist"></icon-provider>
</div>
<p><b> {{currentFileName}} </b>保存到<b>GiteeGist</b>并保持同步</p>
<form-entry label="文件名" error="filename">
<input slot="field" class="textfield" type="text" v-model.trim="filename" @keydown.enter="resolve()">
</form-entry>
<div class="form-entry">
<div class="form-entry__checkbox">
<label>
<input type="checkbox" v-model="isPublic"> 公开的
</label>
</div>
</div>
<form-entry label="存在Gist ID" info="可选的">
<input slot="field" class="textfield" type="text" v-model.trim="gistId" @keydown.enter="resolve()">
<div class="form-entry__info">
如果文件存在于GiteeGist中则将被覆盖
</div>
</form-entry>
</div>
<div class="modal__button-bar">
<button class="button" @click="config.reject()">取消</button>
<button class="button button--resolve" @click="resolve()">确认</button>
</div>
</modal-inner>
</template>
<script>
import giteeGistProvider from '../../../services/providers/giteeGistProvider';
import modalTemplate from '../common/modalTemplate';
export default modalTemplate({
data: () => ({
filename: '',
gistId: '',
}),
computedLocalSettings: {
isPublic: 'gistIsPublic',
},
created() {
this.filename = `${this.currentFileName}.md`;
},
methods: {
resolve() {
if (!this.filename) {
this.setError('filename');
} else {
// Return new location
const location = giteeGistProvider.makeLocation(
this.config.token,
this.filename,
this.isPublic,
this.gistId,
);
this.config.resolve(location);
}
},
},
});
</script>

View File

@ -4,7 +4,7 @@
<div class="modal__image"> <div class="modal__image">
<icon-provider provider-id="gitee"></icon-provider> <icon-provider provider-id="gitee"></icon-provider>
</div> </div>
<p>发布 <b>{{currentFileName}}</b> 到您的 <b>Gitee</b> 仓库.</p> <p>Publish <b>{{currentFileName}}</b> to your <b>Gitee</b> repository.</p>
<form-entry label="仓库URL" error="repoUrl"> <form-entry label="仓库URL" error="repoUrl">
<input slot="field" class="textfield" type="text" v-model.trim="repoUrl" @keydown.enter="resolve()"> <input slot="field" class="textfield" type="text" v-model.trim="repoUrl" @keydown.enter="resolve()">
<div class="form-entry__info"> <div class="form-entry__info">

View File

@ -4,7 +4,7 @@
<div class="modal__image"> <div class="modal__image">
<icon-provider provider-id="gitee"></icon-provider> <icon-provider provider-id="gitee"></icon-provider>
</div> </div>
<p>保存 <b>{{currentFileName}}</b> 并与您的 <b>Gitee</b> 仓库保持同步.</p> <p>Save <b>{{currentFileName}}</b> to your <b>Gitee</b> repository and keep it synced.</p>
<form-entry label="仓库URL" error="repoUrl"> <form-entry label="仓库URL" error="repoUrl">
<input slot="field" class="textfield" type="text" v-model.trim="repoUrl" @keydown.enter="resolve()"> <input slot="field" class="textfield" type="text" v-model.trim="repoUrl" @keydown.enter="resolve()">
<div class="form-entry__info"> <div class="form-entry__info">

View File

@ -12,10 +12,9 @@
</div> </div>
</form-entry> </form-entry>
<form-entry label="文件夹路径" info="可选的"> <form-entry label="文件夹路径" info="可选的">
<input slot="field" class="textfield" type="text" placeholder="如imgs/{YYYY}-{MM}-{DD}" v-model.trim="path" @keydown.enter="resolve()"> <input slot="field" class="textfield" type="text" placeholder="如imgs/{YYYY}/{MM}" v-model.trim="path" @keydown.enter="resolve()">
<div class="form-entry__info"> <div class="form-entry__info">
如果不提供默认为 imgs/{YYYY}-{MM}-{DD} <br/> 如果不提供默认为 {YYYY}/{MM}/{DD} 其中{YYYY}为年变量{MM}为月变量{DD}为日变量
变量说明{YYYY}为年变量{MM}为月变量{DD}为日变量{MDNAME}为当前文档名称
</div> </div>
</form-entry> </form-entry>
<form-entry label="分支" info="可选的"> <form-entry label="分支" info="可选的">
@ -64,7 +63,7 @@ export default modalTemplate({
this.config.resolve({ this.config.resolve({
owner, owner,
repo, repo,
path: path || 'imgs/{YYYY}-{MM}-{DD}', path: path || '{YYYY}/{MM}/{DD}',
branch: this.branch || 'master', branch: this.branch || 'master',
}); });
} catch (err) { } catch (err) {

View File

@ -4,7 +4,7 @@
<div class="modal__image"> <div class="modal__image">
<icon-provider provider-id="github"></icon-provider> <icon-provider provider-id="github"></icon-provider>
</div> </div>
<p>发布 <b>{{currentFileName}}</b> 到您的 <b>GitHub</b> 仓库.</p> <p>Publish <b>{{currentFileName}}</b> to your <b>GitHub</b> repository.</p>
<form-entry label="仓库URL" error="repoUrl"> <form-entry label="仓库URL" error="repoUrl">
<input slot="field" class="textfield" type="text" v-model.trim="repoUrl" @keydown.enter="resolve()"> <input slot="field" class="textfield" type="text" v-model.trim="repoUrl" @keydown.enter="resolve()">
<div class="form-entry__info"> <div class="form-entry__info">

View File

@ -5,30 +5,22 @@
<icon-provider provider-id="gitlab"></icon-provider> <icon-provider provider-id="gitlab"></icon-provider>
</div> </div>
<p>将您的<b>GitLab</b>链接到<b>StackEdit中文版</b></p> <p>将您的<b>GitLab</b>链接到<b>StackEdit中文版</b></p>
<template v-if="!useServerConf"> <form-entry label="GitLab URL" error="serverUrl">
<form-entry label="GitLab URL" error="serverUrl"> <input v-if="config.forceServerUrl" slot="field" class="textfield" type="text" disabled="disabled" v-model="config.forceServerUrl">
<input v-if="config.forceServerUrl" slot="field" class="textfield" type="text" disabled="disabled" v-model="config.forceServerUrl"> <input v-else slot="field" class="textfield" type="text" v-model.trim="serverUrl" @keydown.enter="resolve()">
<input v-else slot="field" class="textfield" type="text" v-model.trim="serverUrl" @keydown.enter="resolve()"> <div class="form-entry__info">
<div class="form-entry__info"> <b>例如:</b> https://gitlab.example.com/
<b>例如:</b> https://gitlab.example.com/ </div>
<span v-if="httpAppUrl"> </form-entry>
非https的URL请跳转到 <a :href="httpAppUrl" target="_blank">HTTP链接</a> 添加Gitlab <form-entry label="Application ID" error="applicationId">
</span> <input slot="field" class="textfield" type="text" v-model.trim="applicationId" @keydown.enter="resolve()">
</div> <div class="form-entry__info">
</form-entry> 您必须使用重定向url <b>{{redirectUrl}}</b>配置OAuth2应用程序
<form-entry label="Application ID" error="applicationId"> </div>
<input slot="field" class="textfield" type="text" v-model.trim="applicationId" @keydown.enter="resolve()"> <div class="form-entry__actions">
</form-entry> <a href="https://docs.gitlab.com/ee/integration/oauth_provider.html" target="_blank">更多信息</a>
<form-entry label="Application Secret" error="applicationSecret"> </div>
<input slot="field" class="textfield" type="text" v-model.trim="applicationSecret" @keydown.enter="resolve()"> </form-entry>
<div class="form-entry__info">
您必须使用重定向url <b>{{redirectUrl}}</b>配置OAuth2应用程序
</div>
<div class="form-entry__actions">
<a href="https://docs.gitlab.com/ee/integration/oauth_provider.html" target="_blank">更多信息</a>
</div>
</form-entry>
</template>
</div> </div>
<div class="modal__button-bar"> <div class="modal__button-bar">
<button class="button" @click="config.reject()">取消</button> <button class="button" @click="config.reject()">取消</button>
@ -40,8 +32,6 @@
<script> <script>
import modalTemplate from '../common/modalTemplate'; import modalTemplate from '../common/modalTemplate';
import constants from '../../../data/constants'; import constants from '../../../data/constants';
import store from '../../../store';
import networkSvc from '../../../services/networkSvc';
export default modalTemplate({ export default modalTemplate({
data: () => ({ data: () => ({
@ -50,31 +40,9 @@ export default modalTemplate({
computedLocalSettings: { computedLocalSettings: {
serverUrl: 'gitlabServerUrl', serverUrl: 'gitlabServerUrl',
applicationId: 'gitlabApplicationId', applicationId: 'gitlabApplicationId',
applicationSecret: 'gitlabApplicationSecret',
},
computed: {
httpAppUrl() {
if (constants.origin.indexOf('https://') === 0 && this.serverUrl.indexOf('http://') === 0) {
return `${constants.origin.replace('https://', 'http://')}/app`;
}
return null;
},
// 使
useServerConf() {
const confClientId = store.getters['data/serverConf'].gitlabClientId;
const confServerUrl = store.getters['data/serverConf'].gitlabUrl;
return !!confClientId && !!confServerUrl;
},
},
mounted() {
networkSvc.getServerConf();
}, },
methods: { methods: {
resolve() { resolve() {
if (this.useServerConf) {
this.config.resolve({});
return;
}
const serverUrl = this.config.forceServerUrl || this.serverUrl; const serverUrl = this.config.forceServerUrl || this.serverUrl;
if (!serverUrl) { if (!serverUrl) {
this.setError('serverUrl'); this.setError('serverUrl');
@ -82,18 +50,14 @@ export default modalTemplate({
if (!this.applicationId) { if (!this.applicationId) {
this.setError('applicationId'); this.setError('applicationId');
} }
if (!this.applicationSecret) { if (serverUrl && this.applicationId) {
this.setError('applicationSecret'); const parsedUrl = serverUrl.match(/^(https:\/\/[^/]+)/);
}
if (serverUrl && this.applicationId && this.applicationSecret) {
const parsedUrl = serverUrl.match(/^(http[s]?:\/\/[^/]+)/);
if (!parsedUrl) { if (!parsedUrl) {
this.setError('serverUrl'); this.setError('serverUrl');
} else { } else {
this.config.resolve({ this.config.resolve({
serverUrl: parsedUrl[1], serverUrl: parsedUrl[1],
applicationId: this.applicationId, applicationId: this.applicationId,
applicationSecret: this.applicationSecret,
}); });
} }
} }

View File

@ -4,7 +4,7 @@
<div class="modal__image"> <div class="modal__image">
<icon-provider provider-id="gitlab"></icon-provider> <icon-provider provider-id="gitlab"></icon-provider>
</div> </div>
<p>发布 <b>{{currentFileName}}</b> 到您的 <b>GitLab</b> 仓库.</p> <p>Publish <b>{{currentFileName}}</b> to your <b>GitLab</b> project.</p>
<form-entry label="Project URL" error="projectUrl"> <form-entry label="Project URL" error="projectUrl">
<input slot="field" class="textfield" type="text" v-model.trim="projectUrl" @keydown.enter="resolve()"> <input slot="field" class="textfield" type="text" v-model.trim="projectUrl" @keydown.enter="resolve()">
<div class="form-entry__info"> <div class="form-entry__info">

View File

@ -4,7 +4,7 @@
<div class="modal__image"> <div class="modal__image">
<icon-provider provider-id="googleDrive"></icon-provider> <icon-provider provider-id="googleDrive"></icon-provider>
</div> </div>
<p>发布 <b>{{currentFileName}}</b> 到您的 <b>Google Drive</b> 账号.</p> <p>Publish <b>{{currentFileName}}</b> to your <b>Google Drive</b> account.</p>
<form-entry label="Folder ID" info="可选的"> <form-entry label="Folder ID" info="可选的">
<input slot="field" class="textfield" type="text" v-model.trim="folderId" @keydown.enter="resolve()"> <input slot="field" class="textfield" type="text" v-model.trim="folderId" @keydown.enter="resolve()">
<div class="form-entry__info"> <div class="form-entry__info">

View File

@ -22,6 +22,6 @@ export default {
'badgeCreations', 'badgeCreations',
'serverConf', 'serverConf',
], ],
textMaxLength: 10000000, textMaxLength: 250000,
defaultName: 'Untitled', defaultName: 'Untitled',
}; };

View File

@ -24,7 +24,6 @@ export default () => ({
giteePublishTemplate: 'jekyllSite', giteePublishTemplate: 'jekyllSite',
gitlabServerUrl: '', gitlabServerUrl: '',
gitlabApplicationId: '', gitlabApplicationId: '',
gitlabApplicationSecret: '',
gitlabProjectUrl: '', gitlabProjectUrl: '',
gitlabWorkspaceProjectUrl: '', gitlabWorkspaceProjectUrl: '',
gitlabPublishTemplate: 'plainText', gitlabPublishTemplate: 'plainText',

View File

@ -15,36 +15,6 @@ editor:
inlineImages: true inlineImages: true
# Use monospaced font only # Use monospaced font only
monospacedFontOnly: false monospacedFontOnly: false
# 是否显示右上角图标
showInPageButtons: true
# 头部的按钮是否显示独立设置
headButtons:
# 加粗
bold: true
# 斜体
italic: true
# 标题
heading: true
# 删除线
strikethrough: true
# 无序列表
ulist: true
# 有序列表
olist: true
# 可选列表
clist: true
# 块引用
quote: true
# 代码
code: true
# 表格
table: true
# 链接
link: true
# 图片
image: true
# ChatGPT
chatgpt: true
# Keyboard shortcuts # Keyboard shortcuts
# See https://craig.is/killing/mice # See https://craig.is/killing/mice
@ -59,7 +29,6 @@ shortcuts:
mod+shift+h: heading mod+shift+h: heading
mod+shift+r: hr mod+shift+r: hr
mod+shift+g: image mod+shift+g: image
mod+shift+p: chatgpt
mod+shift+i: italic mod+shift+i: italic
mod+shift+l: link mod+shift+l: link
mod+shift+o: olist mod+shift+o: olist
@ -67,9 +36,6 @@ shortcuts:
mod+shift+s: strikethrough mod+shift+s: strikethrough
mod+shift+t: table mod+shift+t: table
mod+shift+u: ulist mod+shift+u: ulist
mod+shift+f: inlineformula
# 切换编辑与预览模式
mod+shift+e: toggleeditor
'= = > space': '= = > space':
method: expand method: expand
params: params:
@ -113,16 +79,16 @@ turndown:
# GitHub/GitLab/Gitee/Gitea commit messages # GitHub/GitLab/Gitee/Gitea commit messages
git: git:
createFileMessage: '{{path}} created from https://md.jonylee.top/' createFileMessage: '{{path}} created from https://stackedit.cn/'
updateFileMessage: '{{path}} updated from https://md.jonylee.top/' updateFileMessage: '{{path}} updated from https://stackedit.cn/'
deleteFileMessage: '{{path}} deleted from https://md.jonylee.top/' deleteFileMessage: '{{path}} deleted from https://stackedit.cn/'
# Default content for new files # Default content for new files
newFileContent: | newFileContent: |
> Written with [Markdown编辑器-StackEdit中文版](https://md.jonylee.top/). > Written with [StackEdit中文版](https://stackedit.cn/).
# Default properties for new files # Default properties for new files
newFileProperties: | newFileProperties: |

View File

@ -101,11 +101,6 @@ export default [
'文件夹删除', '文件夹删除',
'使用文件资源管理器删除文档空间中的文件夹。', '使用文件资源管理器删除文档空间中的文件夹。',
), ),
new Feature(
'searchFile',
'文件搜索',
'使用文件资源管理器搜索文档空间中的文件。',
),
], ],
), ),
new Feature( new Feature(
@ -158,19 +153,7 @@ export default [
new Feature( new Feature(
'sponsor', 'sponsor',
'赞助', '赞助',
'使用 Gitee 登录并赞助 StackEdit 以解锁 PDF 和 Pandoc 导出。(暂不支持赞助)', '使用 Google 登录并赞助 StackEdit 以解锁 PDF 和 Pandoc 导出。(暂不支持赞助)',
),
],
),
new Feature(
'githubSignIn',
'登录',
'使用 Gitee 登录,同步您的主文档空间并解锁功能。',
[
new Feature(
'githubSyncMainWorkspace',
'主文档空间已同步',
'使用 GitHub 登录以将您的主文档空间与您的默认空间stackedit-app-data仓库数据同步。',
), ),
], ],
), ),
@ -219,16 +202,6 @@ export default [
'文档空间删除', '文档空间删除',
'使用“管理文档空间”对话框在本地删除文档空间。', '使用“管理文档空间”对话框在本地删除文档空间。',
), ),
new Feature(
'autoSyncWorkspace',
'文档空间启用自动同步',
'使用“管理文档空间”对话框启用自动同步。',
),
new Feature(
'stopAutoSyncWorkspace',
'文档空间关闭自动同步',
'使用“管理文档空间”对话框关闭自动同步。',
),
], ],
), ),
new Feature( new Feature(
@ -328,11 +301,6 @@ export default [
'GitHub保存', 'GitHub保存',
'使用“同步”菜单将文件保存在GitHub仓库中。', '使用“同步”菜单将文件保存在GitHub仓库中。',
), ),
new Feature(
'saveOnGist',
'GitHubGist保存',
'使用“同步”菜单将文件保存在GitHubGist中。',
),
new Feature( new Feature(
'openFromGitee', 'openFromGitee',
'Gitee阅读器', 'Gitee阅读器',
@ -344,9 +312,9 @@ export default [
'使用“同步”菜单将文件保存在Gitee仓库中。', '使用“同步”菜单将文件保存在Gitee仓库中。',
), ),
new Feature( new Feature(
'saveOnGiteeGist', 'saveOnGist',
'GiteeGist保存', 'Gist保存',
'使用“同步”菜单将文件保存在GiteeGist中。', '使用“同步”菜单将文件保存在GIST中。',
), ),
new Feature( new Feature(
'openFromGitlab', 'openFromGitlab',
@ -422,19 +390,14 @@ export default [
), ),
new Feature( new Feature(
'publishToGist', 'publishToGist',
'GitHubGist发布', 'Gist发布',
'使用“发布”菜单将文件发布到GitHubGist。', '使用“发布”菜单将文件发布到GIST。',
), ),
new Feature( new Feature(
'publishToGitee', 'publishToGitee',
'Gitee发布', 'Gitee发布',
'使用“发布”菜单将文件发布到Gitee仓库。', '使用“发布”菜单将文件发布到Gitee仓库。',
), ),
new Feature(
'publishToGiteeGist',
'GiteeGist发布',
'使用“发布”菜单将文件发布到GiteeGist。',
),
new Feature( new Feature(
'publishToGitlab', 'publishToGitlab',
'GitLab发布', 'GitLab发布',

Some files were not shown because too many files have changed in this diff Show More