Compare commits
No commits in common. "master" and "v3.1.5" have entirely different histories.
14
.babelrc
@ -1,14 +0,0 @@
|
||||
{
|
||||
"presets": [
|
||||
["env", { "modules": false }],
|
||||
"stage-2"
|
||||
],
|
||||
"plugins": ["transform-runtime"],
|
||||
"comments": false,
|
||||
"env": {
|
||||
"test": {
|
||||
"presets": ["env", "stage-2"],
|
||||
"plugins": ["transform-es2015-modules-commonjs", "dynamic-import-node"]
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
node_modules
|
||||
.git
|
||||
dist
|
||||
.history
|
||||
images
|
||||
docs
|
||||
Dockerfile
|
||||
README.md
|
||||
build.sh
|
@ -1,9 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
@ -1,3 +0,0 @@
|
||||
build/*.js
|
||||
config/*.js
|
||||
src/libs/*.js
|
44
.eslintrc.js
@ -1,44 +0,0 @@
|
||||
// http://eslint.org/docs/user-guide/configuring
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
parser: 'babel-eslint',
|
||||
parserOptions: {
|
||||
sourceType: 'module'
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
},
|
||||
extends: 'airbnb-base',
|
||||
// required to lint *.vue files
|
||||
plugins: [
|
||||
'html'
|
||||
],
|
||||
globals: {
|
||||
"NODE_ENV": false,
|
||||
"VERSION": false
|
||||
},
|
||||
// check if imports actually resolve
|
||||
'settings': {
|
||||
'import/resolver': {
|
||||
'webpack': {
|
||||
'config': 'build/webpack.base.conf.js'
|
||||
}
|
||||
}
|
||||
},
|
||||
// add your custom rules here
|
||||
'rules': {
|
||||
'no-param-reassign': [2, { 'props': false }],
|
||||
// don't require .vue extension when importing
|
||||
'import/extensions': ['error', 'always', {
|
||||
'js': 'never',
|
||||
'vue': 'never'
|
||||
}],
|
||||
// allow optionalDependencies
|
||||
'import/no-extraneous-dependencies': ['error', {
|
||||
'optionalDependencies': ['test/unit/index.js']
|
||||
}],
|
||||
// allow debugger during development
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
|
||||
}
|
||||
}
|
15
.gitignore
vendored
@ -1,10 +1,5 @@
|
||||
.DS_Store
|
||||
node_modules/
|
||||
dist/
|
||||
.history
|
||||
.idea
|
||||
npm-debug.log*
|
||||
.vscode
|
||||
stackedit_v4
|
||||
chrome-app/*.zip
|
||||
/test/unit/coverage/
|
||||
.project
|
||||
.settings
|
||||
node_modules
|
||||
Thumbs.db
|
||||
public/res/bower-libs
|
||||
|
@ -1,8 +0,0 @@
|
||||
// https://github.com/michael-ciniawsky/postcss-load-config
|
||||
|
||||
module.exports = {
|
||||
"plugins": {
|
||||
// to edit target browsers: use "browserlist" field in package.json
|
||||
"autoprefixer": {}
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
{
|
||||
"processors": ["stylelint-processor-html"],
|
||||
"extends": "stylelint-config-standard",
|
||||
"rules": {
|
||||
"no-empty-source": null
|
||||
}
|
||||
}
|
22
.travis.yml
@ -1,22 +0,0 @@
|
||||
language: node_js
|
||||
|
||||
node_js:
|
||||
- "12"
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
before_deploy:
|
||||
# Run docker build
|
||||
- docker build -t benweet/stackedit .
|
||||
# Install Helm
|
||||
- curl -SL -o /tmp/get_helm.sh https://git.io/get_helm.sh
|
||||
- chmod 700 /tmp/get_helm.sh
|
||||
- /tmp/get_helm.sh
|
||||
- helm init --client-only
|
||||
|
||||
deploy:
|
||||
provider: script
|
||||
script: bash build/deploy.sh
|
||||
on:
|
||||
tags: true
|
21
Dockerfile
@ -1,16 +1,15 @@
|
||||
FROM mafgwo/wkhtmltopdf-nodejs:11.15.0
|
||||
# Dockerfile for StackEdit
|
||||
|
||||
WORKDIR /opt/stackedit
|
||||
FROM shykes/nodejs
|
||||
|
||||
COPY package*json /opt/stackedit/
|
||||
COPY gulpfile.js /opt/stackedit/
|
||||
RUN apt-get update
|
||||
RUN apt-get upgrade
|
||||
|
||||
RUN npm install --unsafe-perm \
|
||||
&& npm cache clean --force
|
||||
COPY . /opt/stackedit
|
||||
ENV NODE_ENV production
|
||||
RUN npm run build
|
||||
RUN apt-get install -y git-core
|
||||
|
||||
EXPOSE 8080
|
||||
RUN git clone https://github.com/benweet/stackedit.git
|
||||
|
||||
CMD [ "node", "." ]
|
||||
RUN (cd /stackedit/ && npm install)
|
||||
EXPOSE 3000
|
||||
|
||||
CMD (cd /stackedit/ && node server.js)
|
||||
|
275
Gruntfile.js
Normal file
@ -0,0 +1,275 @@
|
||||
module.exports = function(grunt) {
|
||||
|
||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||
grunt.loadNpmTasks('grunt-contrib-requirejs');
|
||||
grunt.loadNpmTasks('grunt-contrib-less');
|
||||
grunt.loadNpmTasks('grunt-string-replace');
|
||||
grunt.loadNpmTasks('grunt-contrib-copy');
|
||||
grunt.loadNpmTasks('grunt-bower-requirejs');
|
||||
grunt.loadNpmTasks('grunt-bump');
|
||||
|
||||
/***************************************************************************
|
||||
* Configuration
|
||||
*/
|
||||
grunt.initConfig({
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
jshint: {
|
||||
options: {
|
||||
curly: true,
|
||||
browser: true,
|
||||
devel: true,
|
||||
indent: 4,
|
||||
latedef: true,
|
||||
undef: true,
|
||||
unused: true,
|
||||
expr: true,
|
||||
globals: {
|
||||
"define": false,
|
||||
"require": false,
|
||||
},
|
||||
ignores: [
|
||||
'node_modules/**/*.js',
|
||||
'public/libs/**/*.js',
|
||||
'public/res/libs/**/*.js',
|
||||
'public/res/bower-libs/**/*.js',
|
||||
'public/res-min/**/*.js'
|
||||
]
|
||||
},
|
||||
client: ['public/**/*.js'],
|
||||
},
|
||||
requirejs: {
|
||||
compile: {
|
||||
options: {
|
||||
baseUrl: "public/res",
|
||||
name: "main",
|
||||
out: "public/res-min/main.js",
|
||||
mainConfigFile: 'public/res/main.js',
|
||||
optimize: "uglify2",
|
||||
inlineText: true,
|
||||
uglify2: {
|
||||
output: {
|
||||
beautify: true,
|
||||
indent_level: 1,
|
||||
},
|
||||
},
|
||||
excludeShallow: [
|
||||
'css/css-builder',
|
||||
'less/lessc-server',
|
||||
'less/lessc'
|
||||
],
|
||||
}
|
||||
}
|
||||
},
|
||||
less: {
|
||||
compile: {
|
||||
options: {
|
||||
compress: true,
|
||||
},
|
||||
files: [
|
||||
{
|
||||
expand: true,
|
||||
cwd: 'public/res/themes',
|
||||
src: [
|
||||
'*.less'
|
||||
],
|
||||
dest: 'public/res-min/themes',
|
||||
ext: '.css',
|
||||
},
|
||||
{
|
||||
src: 'public/res/styles/base.less',
|
||||
dest: 'public/res-min/themes/base.css',
|
||||
}
|
||||
],
|
||||
},
|
||||
},
|
||||
'string-replace': {
|
||||
'font-parameters': {
|
||||
files: {
|
||||
'./': 'public/res-min/themes/*.css',
|
||||
},
|
||||
options: {
|
||||
replacements: [
|
||||
{
|
||||
pattern: /(font\/fontello\.\w+)\?\w+/g,
|
||||
replacement: '$1'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
'constants': {
|
||||
files: {
|
||||
'public/res/constants.js': 'public/res/constants.js'
|
||||
},
|
||||
options: {
|
||||
replacements: [
|
||||
{
|
||||
pattern: /constants\.VERSION = .*/,
|
||||
replacement: 'constants.VERSION = "<%= pkg.version %>";'
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
'cache-manifest': {
|
||||
files: {
|
||||
'public/cache.manifest': 'public/cache.manifest'
|
||||
},
|
||||
options: {
|
||||
replacements: [
|
||||
{
|
||||
pattern: /(#Date ).*/,
|
||||
replacement: '$1<%= grunt.template.today() %>'
|
||||
},
|
||||
{
|
||||
pattern: /(#DynamicResourcesBegin\n)[\s\S]*(\n#DynamicResourcesEnd)/,
|
||||
replacement: '$1<%= resources %>$2'
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
},
|
||||
copy: {
|
||||
resources: {
|
||||
files: [
|
||||
// Fonts
|
||||
{
|
||||
expand: true,
|
||||
cwd: 'public/res/font',
|
||||
src: [
|
||||
'**'
|
||||
],
|
||||
dest: 'public/res-min/font/'
|
||||
},
|
||||
// Images
|
||||
{
|
||||
expand: true,
|
||||
cwd: 'public/res/img',
|
||||
src: [
|
||||
'**'
|
||||
],
|
||||
dest: 'public/res-min/img/'
|
||||
},
|
||||
// Libraries
|
||||
{
|
||||
expand: true,
|
||||
cwd: 'public/res/bower-libs/requirejs',
|
||||
src: [
|
||||
'require.js'
|
||||
],
|
||||
dest: 'public/res-min/'
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
// Inject bower dependencies into RequireJS configuration
|
||||
bower: {
|
||||
target: {
|
||||
rjsConfig: 'public/res/main.js'
|
||||
}
|
||||
},
|
||||
bump: {
|
||||
options: {
|
||||
files: [
|
||||
'package.json',
|
||||
'bower.json'
|
||||
],
|
||||
updateConfigs: [
|
||||
'pkg'
|
||||
],
|
||||
commitFiles: [
|
||||
'-a'
|
||||
],
|
||||
pushTo: 'origin'
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
/***************************************************************************
|
||||
* Clean
|
||||
*/
|
||||
grunt.registerTask('clean', function() {
|
||||
|
||||
// Remove public/res-min folder
|
||||
grunt.file['delete']('public/res-min');
|
||||
|
||||
});
|
||||
|
||||
/***************************************************************************
|
||||
* Build JavaScript
|
||||
*/
|
||||
grunt.registerTask('build-js', function() {
|
||||
|
||||
// JSHint validation
|
||||
grunt.task.run('jshint');
|
||||
|
||||
// Run r.js optimization
|
||||
grunt.task.run('requirejs');
|
||||
|
||||
});
|
||||
|
||||
/***************************************************************************
|
||||
* Build CSS
|
||||
*/
|
||||
grunt.registerTask('build-css', function() {
|
||||
|
||||
// First compile less files
|
||||
grunt.task.run('less:compile');
|
||||
// Remove fontello checksum arguments
|
||||
grunt.task.run('string-replace:font-parameters');
|
||||
|
||||
});
|
||||
|
||||
/***************************************************************************
|
||||
* Resources
|
||||
*/
|
||||
grunt.registerTask('build-res', function() {
|
||||
|
||||
// Copy some resources (images, fonts...)
|
||||
grunt.task.run('copy:resources');
|
||||
|
||||
// List resources and inject them in cache.manifest
|
||||
var resFolderList = [
|
||||
'public/res-min',
|
||||
'public/libs/dictionaries',
|
||||
'public/libs/MathJax/extensions',
|
||||
'public/libs/MathJax/fonts/HTML-CSS/TeX/woff',
|
||||
'public/libs/MathJax/jax/element',
|
||||
'public/libs/MathJax/jax/output/HTML-CSS/autoload',
|
||||
'public/libs/MathJax/jax/output/HTML-CSS/fonts/TeX',
|
||||
'public/libs/MathJax/jax/output/HTML-CSS/fonts/STIX'
|
||||
];
|
||||
grunt.task.run('list-res:' + resFolderList.join(':'));
|
||||
grunt.task.run('string-replace:cache-manifest');
|
||||
|
||||
});
|
||||
|
||||
grunt.registerTask('list-res', function() {
|
||||
var resourceList = [];
|
||||
grunt.util.recurse(arguments, function(arg) {
|
||||
grunt.log.writeln('Listing resources: ' + arg);
|
||||
grunt.file.recurse(arg, function(abspath) {
|
||||
resourceList.push(abspath.replace(/^public\//, ''));
|
||||
});
|
||||
});
|
||||
grunt.config.set('resources', resourceList.join('\n'));
|
||||
});
|
||||
|
||||
/***************************************************************************
|
||||
* Default task
|
||||
*/
|
||||
grunt.registerTask('default', function() {
|
||||
grunt.task.run('clean');
|
||||
grunt.task.run('build-js');
|
||||
grunt.task.run('build-css');
|
||||
grunt.task.run('build-res');
|
||||
});
|
||||
|
||||
/***************************************************************************
|
||||
* Tag task
|
||||
*/
|
||||
grunt.registerTask('tag', function(versionType) {
|
||||
grunt.task.run('bump-only:' + (versionType || 'patch'));
|
||||
grunt.task.run('string-replace:constants');
|
||||
grunt.task.run('default');
|
||||
grunt.task.run('bump-commit');
|
||||
});
|
||||
};
|
4
LICENSE.txt
Normal file
@ -0,0 +1,4 @@
|
||||
StackEdit - The Markdown editor powered by PageDown.
|
||||
|
||||
Copyright 2013 Benoit Schweblin (http://www.benoitschweblin.com)
|
||||
Licensed under an Apache License (http://www.apache.org/licenses/LICENSE-2.0)
|
218
README.md
@ -1,191 +1,55 @@
|
||||
<h1 align="center" style="text-align:center;">
|
||||
<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
|
||||
=========
|
||||
|
||||
## 说明
|
||||
StackEdit is a full-featured, open-source Markdown editor based on PageDown, the Markdown library used by Stack Overflow and the other Stack Exchange sites.
|
||||
|
||||
本项目为本人clone修改自用,如果你也喜欢,请至原作者处获取及交流。
|
||||
Main showcase: https://stackedit.io/.
|
||||
|
||||
## 截图
|
||||
> **NOTE:**
|
||||
>
|
||||
> - Documents are stored in the [browser's local storage][1], which means they are not shared between different browsers/computers. Clearing your browser's data may delete all your local documents.
|
||||
> - Full access to Dropbox or Google Drive is required to be able to import any document in StackEdit. Imported documents are downloaded in your browser and are not transmitted to a server.
|
||||
|
||||
**亮暗主题切换、编辑主题切换**
|
||||

|
||||
### StackEdit can:
|
||||
|
||||
**支持的文档空间**
|
||||

|
||||
- Manage multiple Markdown documents online or offline
|
||||
- Export your documents in Markdown, HTML or PDF and format it using a template
|
||||
- Synchronize your Markdown documents in the Cloud
|
||||
- Edit existing Markdown documents from Google Drive, Dropbox and your local hard drive
|
||||
- Post your Markdown document on Blogger/Blogspot, WordPress, Tumblr
|
||||
- Publish your Markdown document on GitHub, Gist, Google Drive, Dropbox or any SSH server
|
||||
- Share a link to a Markdown document that renders it in a nice viewer
|
||||
- Show statistics about your document
|
||||
- Convert HTML to Markdown
|
||||
|
||||
**拖拽粘贴上传图片**
|
||||

|
||||
### Features:
|
||||
|
||||
**支持文档搜索**
|
||||

|
||||
- Real-time HTML preview with Scroll Link feature to bind editor and preview scrollbars
|
||||
- Markdown Extra/GitHub Flavored Markdown support and Prettify/Highlight.js syntax highlighting
|
||||
- LaTeX mathematical expressions using MathJax
|
||||
- WYSIWYG control buttons
|
||||
- Configurable layout
|
||||
- Theming support with different themes available
|
||||
- A la carte extensions
|
||||
- Offline editing
|
||||
- Online synchronization using Google Drive (multi-accounts) and Dropbox
|
||||
- One click publish on Blogger, Dropbox, Gist, GitHub, Google Drive, SSH server, Tumblr, WordPress
|
||||
|
||||
**ChatGPT集成协助写作**
|
||||

|
||||
### Documentation:
|
||||
|
||||
## 相比国外开源版本的区别:
|
||||
- [Welcome document][2]
|
||||
- [Developer guide][3]
|
||||
- [Theming guide][4]
|
||||
|
||||
- 修复了Github授权登录问题
|
||||
- 支持了Gitee仓库(2022-05-25)
|
||||
- 支持了Gitea仓库(2022-05-25)
|
||||
- 汉化(2022-06-01)
|
||||
- 主文档空间从GoogleDrive切换为Gitee(2022-06-04)
|
||||
- 支持SM.MS图床粘贴/拖拽图片自动上传(2022-07-01)
|
||||
- 支持Gitea图床粘贴/拖拽图片自动上传(2022-07-02)
|
||||
- 支持自定义图床粘贴/拖拽图片自动上传(2022-07-04)
|
||||
- 支持GitHub图床粘贴/拖拽图片自动上传(2022-07-31)
|
||||
- 支持了右上角一键切换主题,补全了深色主题的样式(2022-08-07)
|
||||
- 编辑与预览区域样式优化(2022-08-10)
|
||||
- 左边栏文件资源管理支持搜索文件(2022-08-17)
|
||||
- 支持[TOC]目录(2022-09-04)
|
||||
- 发布支持填写提交信息[针对Gitee、GitHub、Gitea、Gitlab](2022-09-10)
|
||||
- 支持文档空间关闭自动同步[针对Gitee、GitHub、Gitea、Gitlab],关闭后可自定义提交信息(2022-09-23)
|
||||
- Gitea支持后端配置指定应用ID和Secret(2022-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)
|
||||
### Support StackEdit:
|
||||
|
||||
## 国外开源版本弊端:
|
||||
[](https://www.gittip.com/stackedit/ "Fund me on Gittip")
|
||||
|
||||
- 作者已经不维护了或很少维护了
|
||||
- 不支持国内常用Gitee
|
||||
- 强依赖GoogleDrive,而Google Drive在国内不能正常访问
|
||||
> **NOTE:** This page has been written and published with [StackEdit][5].
|
||||
|
||||
## 部署说明
|
||||
|
||||
> 建议docker-compose方式部署,其他部署方式如遇到问题欢迎提issue。
|
||||
|
||||
docker官方仓库下载太慢可以使用阿里云的镜像仓库,镜像仓库地址:registry.cn-hangzhou.aliyuncs.com/mafgwo/stackedit:【版本号】
|
||||
|
||||
`docker-compose.yml`如下:
|
||||
|
||||
```yaml
|
||||
version: "3.7"
|
||||
services:
|
||||
stackedit:
|
||||
image: mafgwo/stackedit:【docker中央仓库找到最新版本】
|
||||
container_name: stackedit
|
||||
environment:
|
||||
- LISTENING_PORT=8080
|
||||
- ROOT_URL=/
|
||||
- USER_BUCKET_NAME=root
|
||||
- DROPBOX_APP_KEY=【不需要支持则删掉】
|
||||
- DROPBOX_APP_KEY_FULL=【不需要支持则删掉】
|
||||
- GITHUB_CLIENT_ID=【不需要支持则删掉】
|
||||
- GITHUB_CLIENT_SECRET=【不需要支持则删掉】
|
||||
- GITEE_CLIENT_ID=【不需要支持则删掉】
|
||||
- GITEE_CLIENT_SECRET=【不需要支持则删掉】
|
||||
- GOOGLE_CLIENT_ID=【不需要支持则删掉】
|
||||
- GOOGLE_API_KEY=【不需要支持则删掉】
|
||||
- GITEA_CLIENT_ID=【不需要支持则删掉】
|
||||
- GITEA_CLIENT_SECRET=【不需要支持则删掉】
|
||||
- GITEA_URL=【不需要支持则删掉】
|
||||
- GITLAB_CLIENT_ID=【不需要支持则删掉】
|
||||
- GITLAB_CLIENT_SECRET=【不需要支持则删掉】
|
||||
- GITLAB_URL=【不需要支持则删掉】
|
||||
ports:
|
||||
- 8080:8080/tcp
|
||||
network_mode: bridge
|
||||
restart: always
|
||||
```
|
||||
|
||||
docker-compose方式的启动或停止命令
|
||||
|
||||
```bash
|
||||
# 在 docker-compose.yml 文件目录下 启动命令
|
||||
docker-compose up -d
|
||||
# 在 docker-compose.yml 文件目录下 停止命令
|
||||
docker-compose down
|
||||
# 更新镜像只需要修改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版本
|
||||
|
||||
```bash
|
||||
# 安装依赖
|
||||
npm install
|
||||
|
||||
# serve with hot reload at localhost:8080
|
||||
npm start
|
||||
|
||||
# build for production with minification
|
||||
npm run build
|
||||
|
||||
# build for production and view the bundle analyzer report
|
||||
npm run build --report
|
||||
```
|
||||
[1]: https://developer.mozilla.org/en-US/docs/Web/Guide/DOM/Storage#localStorage
|
||||
[2]: https://github.com/benweet/stackedit/blob/master/public/res/WELCOME.md#welcome-to-stackedit---welcome "Welcome document"
|
||||
[3]: https://github.com/benweet/stackedit/blob/master/doc/developer-guide.md#developer-guide "Developer guide"
|
||||
[4]: https://github.com/benweet/stackedit/blob/master/doc/theming.md#stackedit-theming-guide "Theming guide"
|
||||
[5]: https://stackedit.io/ "StackEdit"
|
35
bower.json
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "stackedit",
|
||||
"version": "3.1.5",
|
||||
"description": "StackEdit is a free, open-source Markdown editor based on PageDown, the Markdown library used by Stack Overflow and the other Stack Exchange sites.",
|
||||
"dependencies": {
|
||||
"bootstrap": "v3.0.0",
|
||||
"jquery": "2.0.3",
|
||||
"underscore": "1.5.1",
|
||||
"requirejs": "~2.1.8",
|
||||
"require-css": "0.1.0",
|
||||
"require-less": "0.1.0",
|
||||
"mousetrap": "~1.4.4",
|
||||
"jgrowl": "~1.2.10",
|
||||
"google-code-prettify": "~1.0.0",
|
||||
"jquery-ui": "~1.10.3",
|
||||
"FileSaver": "*",
|
||||
"stacktrace": "~0.5.3",
|
||||
"requirejs-text": "~2.0.10",
|
||||
"bootstrap-tour": "~0.7.1",
|
||||
"ace": "4bbe5346f2ae5ad35c0c47defa244ab27aedd451",
|
||||
"pagedown-ace": "git@github.com:benweet/pagedown-ace.git#84d5e1b7ff233a1c8cafa9716e825228d275120c",
|
||||
"pagedown-extra": "git@github.com:jmcmanus/pagedown-extra.git#f22ee6af21c0048d80a5d2328fd79745c802acda",
|
||||
"crel": "git@github.com:KoryNunn/crel.git#8dbda04b129fc0aec01a2a080d1cab26816e11c1",
|
||||
"waitForImages": "git@github.com:alexanderdickson/waitForImages.git#~1.4.2",
|
||||
"to-markdown": "git@github.com:benweet/to-markdown.git#jquery",
|
||||
"Typo.js": "git@github.com:cfinke/Typo.js.git",
|
||||
"xregexp": "d06eff50f87d81d2dd3afc1e854784c38b17bcc4",
|
||||
"yaml.js": "git@github.com:jeremyfa/yaml.js.git#~0.1.4",
|
||||
"lz-string": "git@github.com:pieroxy/lz-string.git"
|
||||
},
|
||||
"resolutions": {
|
||||
"jquery": "2.0.3",
|
||||
"bootstrap": "v3.0.0"
|
||||
}
|
||||
}
|
37
build.sh
@ -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 "操作完成"
|
@ -1,35 +0,0 @@
|
||||
require('./check-versions')()
|
||||
|
||||
process.env.NODE_ENV = 'production'
|
||||
|
||||
var ora = require('ora')
|
||||
var rm = require('rimraf')
|
||||
var path = require('path')
|
||||
var chalk = require('chalk')
|
||||
var webpack = require('webpack')
|
||||
var config = require('../config')
|
||||
var webpackConfig = require('./webpack.prod.conf')
|
||||
|
||||
var spinner = ora('building for production...')
|
||||
spinner.start()
|
||||
|
||||
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
|
||||
if (err) throw err
|
||||
webpack(webpackConfig, function (err, stats) {
|
||||
spinner.stop()
|
||||
if (err) throw err
|
||||
process.stdout.write(stats.toString({
|
||||
colors: true,
|
||||
modules: false,
|
||||
children: false,
|
||||
chunks: false,
|
||||
chunkModules: false
|
||||
}) + '\n\n')
|
||||
|
||||
console.log(chalk.cyan(' Build complete.\n'))
|
||||
console.log(chalk.yellow(
|
||||
' Tip: built files are meant to be served over an HTTP server.\n' +
|
||||
' Opening index.html over file:// won\'t work.\n'
|
||||
))
|
||||
})
|
||||
})
|
@ -1,48 +0,0 @@
|
||||
var chalk = require('chalk')
|
||||
var semver = require('semver')
|
||||
var packageConfig = require('../package.json')
|
||||
var shell = require('shelljs')
|
||||
function exec (cmd) {
|
||||
return require('child_process').execSync(cmd).toString().trim()
|
||||
}
|
||||
|
||||
var versionRequirements = [
|
||||
{
|
||||
name: 'node',
|
||||
currentVersion: semver.clean(process.version),
|
||||
versionRequirement: packageConfig.engines.node
|
||||
},
|
||||
]
|
||||
|
||||
if (shell.which('npm')) {
|
||||
versionRequirements.push({
|
||||
name: 'npm',
|
||||
currentVersion: exec('npm --version'),
|
||||
versionRequirement: packageConfig.engines.npm
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = function () {
|
||||
var warnings = []
|
||||
for (var i = 0; i < versionRequirements.length; i++) {
|
||||
var mod = versionRequirements[i]
|
||||
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
|
||||
warnings.push(mod.name + ': ' +
|
||||
chalk.red(mod.currentVersion) + ' should be ' +
|
||||
chalk.green(mod.versionRequirement)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (warnings.length) {
|
||||
console.log('')
|
||||
console.log(chalk.yellow('To use this template, you must update following to modules:'))
|
||||
console.log()
|
||||
for (var i = 0; i < warnings.length; i++) {
|
||||
var warning = warnings[i]
|
||||
console.log(' ' + warning)
|
||||
}
|
||||
console.log()
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Tag and push docker image
|
||||
docker login -u benweet -p "$DOCKER_PASSWORD"
|
||||
docker tag benweet/stackedit "benweet/stackedit:$TRAVIS_TAG"
|
||||
docker push benweet/stackedit:$TRAVIS_TAG
|
||||
docker tag benweet/stackedit:$TRAVIS_TAG benweet/stackedit:latest
|
||||
docker push benweet/stackedit:latest
|
||||
|
||||
# Build the chart
|
||||
cd "$TRAVIS_BUILD_DIR"
|
||||
npm run chart
|
||||
|
||||
# Add chart to helm repository
|
||||
git clone --branch master "https://benweet:$GITHUB_TOKEN@github.com/benweet/stackedit-charts.git" /tmp/charts
|
||||
cd /tmp/charts
|
||||
helm package "$TRAVIS_BUILD_DIR/dist/stackedit"
|
||||
helm repo index --url https://benweet.github.io/stackedit-charts/ .
|
||||
git config user.name "Benoit Schweblin"
|
||||
git config user.email "benoit.schweblin@gmail.com"
|
||||
git add .
|
||||
git commit -m "Added $TRAVIS_TAG"
|
||||
git push origin master
|
@ -1,9 +0,0 @@
|
||||
/* eslint-disable */
|
||||
require('eventsource-polyfill')
|
||||
var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
|
||||
|
||||
hotClient.subscribe(function (event) {
|
||||
if (event.action === 'reload') {
|
||||
window.location.reload()
|
||||
}
|
||||
})
|
@ -1,94 +0,0 @@
|
||||
require('./check-versions')()
|
||||
|
||||
var config = require('../config')
|
||||
Object.keys(config.dev.env).forEach((key) => {
|
||||
if (!process.env[key]) {
|
||||
process.env[key] = JSON.parse(config.dev.env[key]);
|
||||
}
|
||||
});
|
||||
|
||||
var opn = require('opn')
|
||||
var path = require('path')
|
||||
var express = require('express')
|
||||
var webpack = require('webpack')
|
||||
var proxyMiddleware = require('http-proxy-middleware')
|
||||
var webpackConfig = require('./webpack.dev.conf')
|
||||
|
||||
// default port where dev server listens for incoming traffic
|
||||
var port = process.env.PORT || config.dev.port
|
||||
// automatically open browser, if not set will be false
|
||||
var autoOpenBrowser = !!config.dev.autoOpenBrowser
|
||||
// Define HTTP proxies to your custom API backend
|
||||
// https://github.com/chimurai/http-proxy-middleware
|
||||
var proxyTable = config.dev.proxyTable
|
||||
|
||||
var app = express()
|
||||
var compiler = webpack(webpackConfig)
|
||||
|
||||
// StackEdit custom middlewares
|
||||
require('../server')(app);
|
||||
|
||||
var devMiddleware = require('webpack-dev-middleware')(compiler, {
|
||||
publicPath: webpackConfig.output.publicPath,
|
||||
quiet: true
|
||||
})
|
||||
|
||||
var hotMiddleware = require('webpack-hot-middleware')(compiler, {
|
||||
log: () => {}
|
||||
})
|
||||
// force page reload when html-webpack-plugin template changes
|
||||
compiler.plugin('compilation', function (compilation) {
|
||||
compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
|
||||
hotMiddleware.publish({ action: 'reload' })
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
// proxy api requests
|
||||
Object.keys(proxyTable).forEach(function (context) {
|
||||
var options = proxyTable[context]
|
||||
if (typeof options === 'string') {
|
||||
options = { target: options }
|
||||
}
|
||||
app.use(proxyMiddleware(options.filter || context, options))
|
||||
})
|
||||
|
||||
// handle fallback for HTML5 history API
|
||||
app.use(require('connect-history-api-fallback')())
|
||||
|
||||
// serve webpack bundle output
|
||||
app.use(devMiddleware)
|
||||
|
||||
// enable hot-reload and state-preserving
|
||||
// compilation error display
|
||||
app.use(hotMiddleware)
|
||||
|
||||
// serve pure static assets
|
||||
var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
|
||||
app.use(staticPath, express.static('./static'))
|
||||
|
||||
var uri = 'http://localhost:' + port
|
||||
|
||||
var _resolve
|
||||
var readyPromise = new Promise(resolve => {
|
||||
_resolve = resolve
|
||||
})
|
||||
|
||||
console.log('> Starting dev server...')
|
||||
devMiddleware.waitUntilValid(() => {
|
||||
console.log('> Listening at ' + uri + '\n')
|
||||
// when env is testing, don't need open it
|
||||
if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
|
||||
opn(uri)
|
||||
}
|
||||
_resolve()
|
||||
})
|
||||
|
||||
var server = app.listen(port)
|
||||
|
||||
module.exports = {
|
||||
ready: readyPromise,
|
||||
close: () => {
|
||||
server.close()
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
var path = require('path')
|
||||
var config = require('../config')
|
||||
var ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||
|
||||
exports.assetsPath = function (_path) {
|
||||
var assetsSubDirectory = process.env.NODE_ENV === 'production'
|
||||
? config.build.assetsSubDirectory
|
||||
: config.dev.assetsSubDirectory
|
||||
return path.posix.join(assetsSubDirectory, _path)
|
||||
}
|
||||
|
||||
exports.cssLoaders = function (options) {
|
||||
options = options || {}
|
||||
|
||||
var cssLoader = {
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
minimize: process.env.NODE_ENV === 'production',
|
||||
sourceMap: options.sourceMap
|
||||
}
|
||||
}
|
||||
|
||||
// generate loader string to be used with extract text plugin
|
||||
function generateLoaders (loader, loaderOptions) {
|
||||
var loaders = [cssLoader]
|
||||
if (loader) {
|
||||
loaders.push({
|
||||
loader: loader + '-loader',
|
||||
options: Object.assign({}, loaderOptions, {
|
||||
sourceMap: options.sourceMap
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Extract CSS when that option is specified
|
||||
// (which is the case during production build)
|
||||
if (options.extract) {
|
||||
return ExtractTextPlugin.extract({
|
||||
use: loaders,
|
||||
fallback: 'vue-style-loader'
|
||||
})
|
||||
} else {
|
||||
return ['vue-style-loader'].concat(loaders)
|
||||
}
|
||||
}
|
||||
|
||||
// https://vue-loader.vuejs.org/en/configurations/extract-css.html
|
||||
return {
|
||||
css: generateLoaders(),
|
||||
postcss: generateLoaders(),
|
||||
less: generateLoaders('less'),
|
||||
sass: generateLoaders('sass', { indentedSyntax: true }),
|
||||
scss: generateLoaders('sass'),
|
||||
stylus: generateLoaders('stylus'),
|
||||
styl: generateLoaders('stylus')
|
||||
}
|
||||
}
|
||||
|
||||
// Generate loaders for standalone style files (outside of .vue)
|
||||
exports.styleLoaders = function (options) {
|
||||
var output = []
|
||||
var loaders = exports.cssLoaders(options)
|
||||
for (var extension in loaders) {
|
||||
var loader = loaders[extension]
|
||||
output.push({
|
||||
test: new RegExp('\\.' + extension + '$'),
|
||||
use: loader
|
||||
})
|
||||
}
|
||||
return output
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
var utils = require('./utils')
|
||||
var config = require('../config')
|
||||
var isProduction = process.env.NODE_ENV === 'production'
|
||||
|
||||
module.exports = {
|
||||
loaders: utils.cssLoaders({
|
||||
sourceMap: isProduction
|
||||
? config.build.productionSourceMap
|
||||
: config.dev.cssSourceMap,
|
||||
extract: isProduction
|
||||
})
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
var path = require('path')
|
||||
var webpack = require('webpack')
|
||||
var utils = require('./utils')
|
||||
var config = require('../config')
|
||||
var VueLoaderPlugin = require('vue-loader/lib/plugin')
|
||||
var vueLoaderConfig = require('./vue-loader.conf')
|
||||
var StylelintPlugin = require('stylelint-webpack-plugin')
|
||||
|
||||
function resolve (dir) {
|
||||
return path.join(__dirname, '..', dir)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
app: './src/'
|
||||
},
|
||||
node: {
|
||||
// For mermaid
|
||||
fs: 'empty' // jison generated code requires 'fs'
|
||||
},
|
||||
output: {
|
||||
path: config.build.assetsRoot,
|
||||
filename: '[name].js',
|
||||
publicPath: process.env.NODE_ENV === 'production'
|
||||
? config.build.assetsPublicPath
|
||||
: config.dev.assetsPublicPath
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js', '.vue', '.json'],
|
||||
alias: {
|
||||
'@': resolve('src')
|
||||
}
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(js|vue)$/,
|
||||
loader: 'eslint-loader',
|
||||
enforce: 'pre',
|
||||
include: [resolve('src'), resolve('test')],
|
||||
options: {
|
||||
formatter: require('eslint-friendly-formatter')
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.vue$/,
|
||||
loader: 'vue-loader',
|
||||
options: vueLoaderConfig
|
||||
},
|
||||
// We can't pass graphlibrary to babel
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'string-replace-loader',
|
||||
include: [
|
||||
resolve('node_modules/graphlibrary')
|
||||
],
|
||||
options: {
|
||||
search: '^\\s*(?:let|const) ',
|
||||
replace: 'var ',
|
||||
flags: 'gm'
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader',
|
||||
include: [
|
||||
resolve('src'),
|
||||
resolve('test'),
|
||||
resolve('node_modules/mermaid')
|
||||
],
|
||||
exclude: [
|
||||
resolve('node_modules/mermaid/src/diagrams/class/parser'),
|
||||
resolve('node_modules/mermaid/src/diagrams/flowchart/parser'),
|
||||
resolve('node_modules/mermaid/src/diagrams/gantt/parser'),
|
||||
resolve('node_modules/mermaid/src/diagrams/git/parser'),
|
||||
resolve('node_modules/mermaid/src/diagrams/sequence/parser')
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 10000,
|
||||
name: utils.assetsPath('img/[name].[hash:7].[ext]')
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.(ttf|eot|otf|woff2?)(\?.*)?$/,
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.(md|yml|html)$/,
|
||||
loader: 'raw-loader'
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new VueLoaderPlugin(),
|
||||
new StylelintPlugin({
|
||||
files: ['**/*.vue', '**/*.scss']
|
||||
}),
|
||||
new webpack.DefinePlugin({
|
||||
VERSION: JSON.stringify(require('../package.json').version)
|
||||
})
|
||||
]
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
var utils = require('./utils')
|
||||
var webpack = require('webpack')
|
||||
var config = require('../config')
|
||||
var merge = require('webpack-merge')
|
||||
var baseWebpackConfig = require('./webpack.base.conf')
|
||||
var HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
|
||||
|
||||
// add hot-reload related code to entry chunks
|
||||
Object.keys(baseWebpackConfig.entry).forEach(function (name) {
|
||||
baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
|
||||
})
|
||||
|
||||
module.exports = merge(baseWebpackConfig, {
|
||||
module: {
|
||||
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })
|
||||
},
|
||||
// cheap-module-eval-source-map is faster for development
|
||||
devtool: 'source-map',
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
NODE_ENV: config.dev.env.NODE_ENV
|
||||
}),
|
||||
// https://github.com/glenjamin/webpack-hot-middleware#installation--usage
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
new webpack.NoEmitOnErrorsPlugin(),
|
||||
// https://github.com/ampedandwired/html-webpack-plugin
|
||||
new HtmlWebpackPlugin({
|
||||
filename: 'index.html',
|
||||
template: 'index.html',
|
||||
inject: true
|
||||
}),
|
||||
new FriendlyErrorsPlugin()
|
||||
]
|
||||
})
|
@ -1,154 +0,0 @@
|
||||
var path = require('path')
|
||||
var utils = require('./utils')
|
||||
var webpack = require('webpack')
|
||||
var config = require('../config')
|
||||
var merge = require('webpack-merge')
|
||||
var baseWebpackConfig = require('./webpack.base.conf')
|
||||
var CopyWebpackPlugin = require('copy-webpack-plugin')
|
||||
var HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
var ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||
var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
|
||||
var OfflinePlugin = require('offline-plugin');
|
||||
var WebpackPwaManifest = require('webpack-pwa-manifest')
|
||||
var FaviconsWebpackPlugin = require('favicons-webpack-plugin')
|
||||
|
||||
function resolve (dir) {
|
||||
return path.join(__dirname, '..', dir)
|
||||
}
|
||||
|
||||
var env = config.build.env
|
||||
|
||||
var webpackConfig = merge(baseWebpackConfig, {
|
||||
module: {
|
||||
rules: utils.styleLoaders({
|
||||
sourceMap: config.build.productionSourceMap,
|
||||
extract: true
|
||||
})
|
||||
},
|
||||
devtool: config.build.productionSourceMap ? '#source-map' : false,
|
||||
output: {
|
||||
path: config.build.assetsRoot,
|
||||
filename: utils.assetsPath('js/[name].[chunkhash].js'),
|
||||
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
|
||||
},
|
||||
plugins: [
|
||||
// http://vuejs.github.io/vue-loader/en/workflow/production.html
|
||||
new webpack.DefinePlugin({
|
||||
NODE_ENV: env.NODE_ENV,
|
||||
GOOGLE_CLIENT_ID: env.GOOGLE_CLIENT_ID,
|
||||
GITHUB_CLIENT_ID: env.GITHUB_CLIENT_ID
|
||||
}),
|
||||
new webpack.optimize.UglifyJsPlugin({
|
||||
compress: {
|
||||
warnings: false
|
||||
},
|
||||
sourceMap: true
|
||||
}),
|
||||
// extract css into its own file
|
||||
new ExtractTextPlugin({
|
||||
filename: utils.assetsPath('css/[name].[contenthash].css')
|
||||
}),
|
||||
// Compress extracted CSS. We are using this plugin so that possible
|
||||
// duplicated CSS from different components can be deduped.
|
||||
new OptimizeCSSPlugin({
|
||||
cssProcessorOptions: {
|
||||
safe: true
|
||||
}
|
||||
}),
|
||||
// generate dist index.html with correct asset hash for caching.
|
||||
// you can customize output by editing /index.html
|
||||
// see https://github.com/ampedandwired/html-webpack-plugin
|
||||
new HtmlWebpackPlugin({
|
||||
filename: config.build.index,
|
||||
template: 'index.html',
|
||||
inject: true,
|
||||
minify: {
|
||||
removeComments: true,
|
||||
collapseWhitespace: true,
|
||||
removeAttributeQuotes: true
|
||||
// more options:
|
||||
// https://github.com/kangax/html-minifier#options-quick-reference
|
||||
},
|
||||
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
|
||||
chunksSortMode: 'dependency'
|
||||
}),
|
||||
// split vendor js into its own file
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: 'vendor',
|
||||
minChunks: function (module, count) {
|
||||
// any required modules inside node_modules are extracted to vendor
|
||||
return (
|
||||
module.resource &&
|
||||
/\.js$/.test(module.resource) &&
|
||||
module.resource.indexOf(
|
||||
path.join(__dirname, '../node_modules')
|
||||
) === 0
|
||||
)
|
||||
}
|
||||
}),
|
||||
// extract webpack runtime and module manifest to its own file in order to
|
||||
// prevent vendor hash from being updated whenever app bundle is updated
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: 'manifest',
|
||||
chunks: ['vendor']
|
||||
}),
|
||||
// copy custom static assets
|
||||
new CopyWebpackPlugin([
|
||||
{
|
||||
from: path.resolve(__dirname, '../static'),
|
||||
to: config.build.assetsSubDirectory,
|
||||
ignore: ['.*']
|
||||
}
|
||||
]),
|
||||
new FaviconsWebpackPlugin({
|
||||
logo: resolve('src/assets/favicon.png'),
|
||||
title: 'StackEdit',
|
||||
}),
|
||||
new WebpackPwaManifest({
|
||||
name: 'StackEdit',
|
||||
description: 'Full-featured, open-source Markdown editor',
|
||||
display: 'standalone',
|
||||
orientation: 'any',
|
||||
start_url: 'app',
|
||||
background_color: '#ffffff',
|
||||
crossorigin: 'use-credentials',
|
||||
icons: [{
|
||||
src: resolve('src/assets/favicon.png'),
|
||||
sizes: [96, 128, 192, 256, 384, 512]
|
||||
}]
|
||||
}),
|
||||
new OfflinePlugin({
|
||||
ServiceWorker: {
|
||||
events: true
|
||||
},
|
||||
AppCache: true,
|
||||
excludes: ['**/.*', '**/*.map', '**/index.html', '**/static/oauth2/callback.html', '**/icons-*/*.png', '**/static/fonts/KaTeX_*'],
|
||||
externals: ['/', '/app', '/oauth2/callback']
|
||||
}),
|
||||
]
|
||||
})
|
||||
|
||||
if (config.build.productionGzip) {
|
||||
var CompressionWebpackPlugin = require('compression-webpack-plugin')
|
||||
|
||||
webpackConfig.plugins.push(
|
||||
new CompressionWebpackPlugin({
|
||||
asset: '[path].gz[query]',
|
||||
algorithm: 'gzip',
|
||||
test: new RegExp(
|
||||
'\\.(' +
|
||||
config.build.productionGzipExtensions.join('|') +
|
||||
')$'
|
||||
),
|
||||
threshold: 10240,
|
||||
minRatio: 0.8
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (config.build.bundleAnalyzerReport) {
|
||||
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
|
||||
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
|
||||
}
|
||||
|
||||
module.exports = webpackConfig
|
@ -1,56 +0,0 @@
|
||||
var path = require('path')
|
||||
var utils = require('./utils')
|
||||
var webpack = require('webpack')
|
||||
var utils = require('./utils')
|
||||
var config = require('../config')
|
||||
var vueLoaderConfig = require('./vue-loader.conf')
|
||||
var StylelintPlugin = require('stylelint-webpack-plugin')
|
||||
var ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||
var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
|
||||
|
||||
function resolve (dir) {
|
||||
return path.join(__dirname, '..', dir)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
style: './src/styles/'
|
||||
},
|
||||
module: {
|
||||
rules: [{
|
||||
test: /\.(ttf|eot|otf|woff2?)(\?.*)?$/,
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
|
||||
}
|
||||
}]
|
||||
.concat(utils.styleLoaders({
|
||||
sourceMap: config.build.productionSourceMap,
|
||||
extract: true
|
||||
})),
|
||||
},
|
||||
output: {
|
||||
path: config.build.assetsRoot,
|
||||
filename: '[name].js',
|
||||
publicPath: config.build.assetsPublicPath
|
||||
},
|
||||
plugins: [
|
||||
new webpack.optimize.UglifyJsPlugin({
|
||||
compress: {
|
||||
warnings: false
|
||||
},
|
||||
sourceMap: true
|
||||
}),
|
||||
// extract css into its own file
|
||||
new ExtractTextPlugin({
|
||||
filename: '[name].css',
|
||||
}),
|
||||
// Compress extracted CSS. We are using this plugin so that possible
|
||||
// duplicated CSS from different components can be deduped.
|
||||
new OptimizeCSSPlugin({
|
||||
cssProcessorOptions: {
|
||||
safe: true
|
||||
}
|
||||
}),
|
||||
]
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
# Patterns to ignore when building packages.
|
||||
# This supports shell glob matching, relative path matching, and
|
||||
# negation (prefixed with !). Only one pattern per line.
|
||||
.DS_Store
|
||||
# Common VCS dirs
|
||||
.git/
|
||||
.gitignore
|
||||
.bzr/
|
||||
.bzrignore
|
||||
.hg/
|
||||
.hgignore
|
||||
.svn/
|
||||
# Common backup files
|
||||
*.swp
|
||||
*.bak
|
||||
*.tmp
|
||||
*~
|
||||
# Various IDEs
|
||||
.project
|
||||
.idea/
|
||||
*.tmproj
|
||||
.vscode/
|
@ -1,5 +0,0 @@
|
||||
apiVersion: v1
|
||||
appVersion: vSTACKEDIT_VERSION
|
||||
description: In-browser Markdown editor
|
||||
name: stackedit
|
||||
version: STACKEDIT_VERSION
|
@ -1,21 +0,0 @@
|
||||
1. Get the application URL by running these commands:
|
||||
{{- if .Values.ingress.enabled }}
|
||||
{{- range $host := .Values.ingress.hosts }}
|
||||
{{- range .paths }}
|
||||
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- else if contains "NodePort" .Values.service.type }}
|
||||
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "stackedit.fullname" . }})
|
||||
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
|
||||
echo http://$NODE_IP:$NODE_PORT
|
||||
{{- else if contains "LoadBalancer" .Values.service.type }}
|
||||
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
|
||||
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "stackedit.fullname" . }}'
|
||||
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "stackedit.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
|
||||
echo http://$SERVICE_IP:{{ .Values.service.port }}
|
||||
{{- else if contains "ClusterIP" .Values.service.type }}
|
||||
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "stackedit.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
|
||||
echo "Visit http://127.0.0.1:8080 to use your application"
|
||||
kubectl port-forward $POD_NAME 8080:80
|
||||
{{- end }}
|
@ -1,45 +0,0 @@
|
||||
{{/* vim: set filetype=mustache: */}}
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "stackedit.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||
If release name contains chart name it will be used as a full name.
|
||||
*/}}
|
||||
{{- define "stackedit.fullname" -}}
|
||||
{{- if .Values.fullnameOverride -}}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
|
||||
{{- else -}}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride -}}
|
||||
{{- if contains $name .Release.Name -}}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
|
||||
{{- else -}}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Create chart name and version as used by the chart label.
|
||||
*/}}
|
||||
{{- define "stackedit.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "stackedit.labels" -}}
|
||||
app.kubernetes.io/name: {{ include "stackedit.name" . }}
|
||||
helm.sh/chart: {{ include "stackedit.chart" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- end -}}
|
@ -1,87 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "stackedit.fullname" . }}
|
||||
labels:
|
||||
{{ include "stackedit.labels" . | indent 4 }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: {{ include "stackedit.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ include "stackedit.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
spec:
|
||||
{{- with .Values.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
volumeMounts:
|
||||
- mountPath: /run
|
||||
name: run-volume
|
||||
- mountPath: /tmp
|
||||
name: tmp-volume
|
||||
env:
|
||||
- name: PORT
|
||||
value: "80"
|
||||
- name: PAYPAL_RECEIVER_EMAIL
|
||||
value: {{ .Values.paypalReceiverEmail }}
|
||||
- name: AWS_ACCESS_KEY_ID
|
||||
value: {{ .Values.awsAccessKeyId }}
|
||||
- name: AWS_SECRET_ACCESS_KEY
|
||||
value: {{ .Values.awsSecretAccessKey }}
|
||||
- name: DROPBOX_APP_KEY
|
||||
value: {{ .Values.dropboxAppKey }}
|
||||
- name: DROPBOX_APP_KEY_FULL
|
||||
value: {{ .Values.dropboxAppKeyFull }}
|
||||
- name: GOOGLE_CLIENT_ID
|
||||
value: {{ .Values.googleClientId }}
|
||||
- name: GOOGLE_API_KEY
|
||||
value: {{ .Values.googleApiKey }}
|
||||
- name: GITHUB_CLIENT_ID
|
||||
value: {{ .Values.githubClientId }}
|
||||
- name: GITHUB_CLIENT_SECRET
|
||||
value: {{ .Values.githubClientSecret }}
|
||||
- name: WORDPRESS_CLIENT_ID
|
||||
value: {{ .Values.wordpressClientId }}
|
||||
- name: WORDPRESS_SECRET
|
||||
value: {{ .Values.wordpressSecret }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
volumes:
|
||||
- name: run-volume
|
||||
emptyDir: {}
|
||||
- name: tmp-volume
|
||||
emptyDir: {}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
@ -1,39 +0,0 @@
|
||||
{{- if .Values.ingress.enabled -}}
|
||||
{{- $fullName := include "stackedit.fullname" . -}}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ $fullName }}
|
||||
labels:
|
||||
{{ include "stackedit.labels" . | indent 4 }}
|
||||
{{- with .Values.ingress.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if .Values.ingress.tls }}
|
||||
tls:
|
||||
{{- range .Values.ingress.tls }}
|
||||
- hosts:
|
||||
{{- range .hosts }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
secretName: {{ .secretName }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
rules:
|
||||
{{- range .Values.ingress.hosts }}
|
||||
- host: {{ .host | quote }}
|
||||
http:
|
||||
paths:
|
||||
{{- range .paths }}
|
||||
- path: {{ . }}
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: {{ $fullName }}
|
||||
port:
|
||||
name: http
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
@ -1,16 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "stackedit.fullname" . }}
|
||||
labels:
|
||||
{{ include "stackedit.labels" . | indent 4 }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.service.port }}
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app.kubernetes.io/name: {{ include "stackedit.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
@ -1,15 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: "{{ include "stackedit.fullname" . }}-test-connection"
|
||||
labels:
|
||||
{{ include "stackedit.labels" . | indent 4 }}
|
||||
annotations:
|
||||
"helm.sh/hook": test-success
|
||||
spec:
|
||||
containers:
|
||||
- name: wget
|
||||
image: busybox
|
||||
command: ['wget']
|
||||
args: ['{{ include "stackedit.fullname" . }}:{{ .Values.service.port }}']
|
||||
restartPolicy: Never
|
@ -1,71 +0,0 @@
|
||||
# Default values for stackedit.
|
||||
# This is a YAML-formatted file.
|
||||
# Declare variables to be passed into your templates.
|
||||
|
||||
dropboxAppKey: ""
|
||||
dropboxAppKeyFull: ""
|
||||
googleClientId: ""
|
||||
googleApiKey: ""
|
||||
githubClientId: ""
|
||||
githubClientSecret: ""
|
||||
giteeClientId: ""
|
||||
giteeClientSecret: ""
|
||||
wordpressClientId: ""
|
||||
wordpressSecret: ""
|
||||
paypalReceiverEmail: ""
|
||||
awsAccessKeyId: ""
|
||||
awsSecretAccessKey: ""
|
||||
giteaClientId: ""
|
||||
giteaClientSecret: ""
|
||||
giteaUrl: ""
|
||||
gitlabClientId: ""
|
||||
gitlabUrl: ""
|
||||
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: benweet/stackedit
|
||||
tag: vSTACKEDIT_VERSION
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
imagePullSecrets: []
|
||||
nameOverride: ""
|
||||
fullnameOverride: ""
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 80
|
||||
|
||||
ingress:
|
||||
enabled: false
|
||||
annotations:
|
||||
# kubernetes.io/ingress.class: nginx
|
||||
# certmanager.k8s.io/issuer: letsencrypt-prod
|
||||
# certmanager.k8s.io/acme-challenge-type: http01
|
||||
hosts: []
|
||||
# - host: stackedit.example.com
|
||||
# paths:
|
||||
# - /
|
||||
|
||||
tls: []
|
||||
# - secretName: stackedit-tls
|
||||
# hosts:
|
||||
# - stackedit.example.com
|
||||
|
||||
resources: {}
|
||||
# We usually recommend not to specify default resources and to leave this as a conscious
|
||||
# choice for the user. This also increases chances charts run on environments with little
|
||||
# resources, such as Minikube. If you do want to specify resources, uncomment the following
|
||||
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
|
||||
# limits:
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
# requests:
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
|
||||
nodeSelector: {}
|
||||
|
||||
tolerations: []
|
||||
|
||||
affinity: {}
|
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 7.6 KiB |
BIN
chrome-app/logo-128.png
Normal file
After Width: | Height: | Size: 8.9 KiB |
@ -1,28 +1,23 @@
|
||||
{
|
||||
"name": "StackEdit中文版",
|
||||
"description": "支持Gitee仓库/粘贴图片自动上传的浏览器内 Markdown 编辑器",
|
||||
"version": "5.15.17",
|
||||
"manifest_version": 2,
|
||||
"container": "GITEE",
|
||||
"api_console_project_id": "241271498917",
|
||||
"icons": {
|
||||
"16": "icon-16.png",
|
||||
"32": "icon-32.png",
|
||||
"64": "icon-64.png",
|
||||
"128": "icon-128.png",
|
||||
"256": "icon-256.png",
|
||||
"512": "icon-512.png"
|
||||
},
|
||||
"app": {
|
||||
"urls": [
|
||||
"https://md.jonylee.top/"
|
||||
],
|
||||
"launch": {
|
||||
"web_url": "https://md.jonylee.top/app"
|
||||
}
|
||||
},
|
||||
"offline_enabled": true,
|
||||
"permissions": [
|
||||
"unlimitedStorage"
|
||||
]
|
||||
"name": "StackEdit",
|
||||
"description": "The Markdown editor powered by PageDown",
|
||||
"version": "1.0.8",
|
||||
"manifest_version": 2,
|
||||
"container" : "GOOGLE_DRIVE",
|
||||
"api_console_project_id" : "241271498917",
|
||||
"icons": {
|
||||
"128": "logo-128.png"
|
||||
},
|
||||
"app": {
|
||||
"urls": [
|
||||
"https://stackedit.io/"
|
||||
],
|
||||
"launch": {
|
||||
"web_url": "https://stackedit.io/"
|
||||
}
|
||||
},
|
||||
"offline_enabled": true,
|
||||
"permissions": [
|
||||
"unlimitedStorage"
|
||||
]
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
var merge = require('webpack-merge')
|
||||
var prodEnv = require('./prod.env')
|
||||
|
||||
module.exports = merge(prodEnv, {
|
||||
NODE_ENV: '"development"',
|
||||
// 以下配置是开发临时用的配置 随时可能失效 请替换为自己的
|
||||
GITHUB_CLIENT_ID: '"845b8f75df48f2ee0563"',
|
||||
GITHUB_CLIENT_SECRET: '"80df676597abded1450926861965cc3f9bead6a0"',
|
||||
GITEE_CLIENT_ID: '"925ba7c78b85dec984f7877e4aca5cab10ae333c6d68e761bdb0b9dfb8f55672"',
|
||||
GITEE_CLIENT_SECRET: '"f05731066e42d307339dc8ebbb037a103881dafc7207a359a393b87749f1c562"',
|
||||
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"',
|
||||
})
|
@ -1,39 +0,0 @@
|
||||
// see http://vuejs-templates.github.io/webpack for documentation.
|
||||
var path = require('path')
|
||||
|
||||
module.exports = {
|
||||
build: {
|
||||
env: require('./prod.env'),
|
||||
index: path.resolve(__dirname, '../dist/index.html'),
|
||||
assetsRoot: path.resolve(__dirname, '../dist'),
|
||||
assetsSubDirectory: 'static',
|
||||
assetsPublicPath: '/',
|
||||
productionSourceMap: true,
|
||||
// Gzip off by default as many popular static hosts such as
|
||||
// Surge or Netlify already gzip all static assets for you.
|
||||
// Before setting to `true`, make sure to:
|
||||
// npm install --save-dev compression-webpack-plugin
|
||||
productionGzip: false,
|
||||
productionGzipExtensions: ['js', 'css'],
|
||||
// Run the build command with an extra argument to
|
||||
// View the bundle analyzer report after build finishes:
|
||||
// `npm run build --report`
|
||||
// Set to `true` or `false` to always turn it on or off
|
||||
bundleAnalyzerReport: process.env.npm_config_report
|
||||
},
|
||||
dev: {
|
||||
env: require('./dev.env'),
|
||||
port: 80,
|
||||
autoOpenBrowser: false,
|
||||
assetsSubDirectory: 'static',
|
||||
assetsPublicPath: '/',
|
||||
proxyTable: {},
|
||||
// CSS Sourcemaps off by default because relative paths are "buggy"
|
||||
// with this option, according to the CSS-Loader README
|
||||
// (https://github.com/webpack/css-loader#sourcemaps)
|
||||
// In our experience, they generally work as expected,
|
||||
// just be aware of this issue when enabling this option.
|
||||
// cssSourceMap: false
|
||||
cssSourceMap: true
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
NODE_ENV: '"production"'
|
||||
}
|
662
doc/developer-guide.md
Normal file
@ -0,0 +1,662 @@
|
||||
Developer guide
|
||||
===============
|
||||
|
||||
Getting started
|
||||
---------------
|
||||
|
||||
### Pre-requisites
|
||||
|
||||
- [Git][1]
|
||||
- [node.js/npm][2]
|
||||
- [Grunt][3]
|
||||
- [Bower][4]
|
||||
|
||||
### Before debugging
|
||||
|
||||
- Download development tools:
|
||||
|
||||
npm install
|
||||
|
||||
- Download dependencies:
|
||||
|
||||
bower install
|
||||
|
||||
- Serve **StackEdit** at `http://localhost/`:
|
||||
|
||||
(export PORT=80 && node server.js)
|
||||
|
||||
> **NOTE:** StackEdit project itself has no back end. It can run on any apache server.
|
||||
|
||||
- Run Chrome without application cache:
|
||||
|
||||
chrome --disable-application-cache
|
||||
|
||||
- Run **StackEdit** in debug mode (serve original files instead of minified):
|
||||
|
||||
http://localhost/?debug
|
||||
|
||||
### Add new dependencies
|
||||
|
||||
> **NOTE:** StackEdit uses [RequireJS][5] for asynchronous module definition ([AMD][6]).
|
||||
|
||||
- Install new dependencies using [Bower][7]:
|
||||
|
||||
bower install <library> --save
|
||||
|
||||
- Add the new dependency to [RequireJS][8] configuration file (`main.js`):
|
||||
|
||||
grunt bower
|
||||
|
||||
### Build/minify
|
||||
|
||||
grunt
|
||||
|
||||
### Deploy
|
||||
|
||||
- on Heroku:
|
||||
|
||||
heroku create my-stackedit-instance
|
||||
git push heroku master
|
||||
|
||||
- in a Docker container:
|
||||
|
||||
docker build -t my-stackedit-image .
|
||||
docker run -p 3000 my-stackedit-image
|
||||
|
||||
> **NOTE:** OAuth authorizations work out of the box for address `http://localhost/` except for WordPress. To allow another address, you have to add specific keys at the end of `constants.js` and eventually to set up specific proxies with the corresponding key/secret pairs ([WordPress Proxy][9], [Tumblr Proxy][10] and [Gatekeeper][11]).
|
||||
|
||||
|
||||
Architecture
|
||||
------------
|
||||
|
||||
![Architecture diagram][12]
|
||||
|
||||
The modules are loaded by RequireJS in the following order:
|
||||
|
||||
1. The 3rd party libraries (jQuery, underscore.js...)
|
||||
2. The `Extension` modules
|
||||
3. The `eventMgr` module
|
||||
4. The `core` module
|
||||
5. The `fileMgr` module and the helpers modules
|
||||
6. The `Provider` modules
|
||||
7. The `publisher` and `synchronizer` modules
|
||||
|
||||
This is important to notice in order to avoid circular dependencies. For instance, if an `Extension` is declared with the `core` module as a dependency, RequireJS will inject `undefined` instead of the actual module.
|
||||
|
||||
Any module though can access any dependencies by implementing the proper [injection listener][13] provided by the `eventMgr`.
|
||||
|
||||
----------
|
||||
|
||||
|
||||
### core
|
||||
|
||||
The `core` module is responsible for:
|
||||
|
||||
- creating the [UI Layout][14], the [ACE][15] editor and the [PageDown][16] editor,
|
||||
- loading/saving the settings,
|
||||
- running periodic tasks,
|
||||
- detecting the user activity,
|
||||
- checking the offline status.
|
||||
|
||||
**Attributes:**
|
||||
|
||||
- `isOffline`: indicates the offline status of the application.
|
||||
|
||||
**Methods:**
|
||||
|
||||
- `onReady(callback)`: sets a callback to be called when all modules have been loaded and the DOM is ready.
|
||||
> **NOTE:** This is preferred over [jQuery's `.ready()`][17] because it ensures that all AMD modules are loaded by [RequireJS][18].
|
||||
|
||||
- `runPeriodically(callback)`: sets a callback to be called every second.
|
||||
> **NOTE:** The callback will not run if the user is inactive or in StackEdit Viewer. User is considered inactive after 5 minutes of inactivity (mouse or keyboard).
|
||||
|
||||
- `setOffline()`: can be called by any other modules when a network timeout occurs for instance.
|
||||
> **NOTE:** the offline status is also set by detecting the window `offline` event. `core.isOffline` is automatically set to `false` when the network is recovered.
|
||||
|
||||
- `initEditor(fileDesc)`: creates or refreshes the [PageDown][19] editor with a given [`FileDescriptor`][20] object.
|
||||
|
||||
|
||||
----------
|
||||
|
||||
|
||||
### fileMgr
|
||||
|
||||
The `fileMgr` module is responsible for:
|
||||
|
||||
- creating and deleting local files,
|
||||
- switching from one file to another.
|
||||
|
||||
**Attributes:**
|
||||
|
||||
- `currentFile`: the [`FileDescriptor`][21] object that is currently edited.
|
||||
|
||||
**Methods:**
|
||||
|
||||
- `createFile(title, content)`: creates a [`FileDescriptor`][22] object, add it in the [`fileSystem`][23] map and returns it.
|
||||
- `deleteFile(fileDesc)`: deletes a [`FileDescriptor`][24] object from the [`fileSystem`][25] map.
|
||||
- `selectFile(fileDesc)`: selects a [`FileDescriptor`][26] object for editing.
|
||||
|
||||
|
||||
#### FileDescriptor
|
||||
|
||||
The `FileDescriptor` class represents a local file. A `FileDescriptor` object has the following properties:
|
||||
|
||||
- `fileIndex`: the unique string index of the file in the file system.
|
||||
- `title`: the title of the document.
|
||||
- `content`: the content of the document.
|
||||
- `syncLocations`: a map containing all the associated [`syncAttributes`][27] objects with their `syncIndex` as a key.
|
||||
- `publishLocations`: a map containing all the associated [`publishAttributes`][28] objects with their `publishIndex` as a key.
|
||||
|
||||
And the following methods:
|
||||
|
||||
- `addSyncLocation(syncAttributes)`: associates a [`syncAttributes`][29] object with the file.
|
||||
- `removeSyncLocation(syncAttributes)`: unassociates a [`syncAttributes`][30] object with the file.
|
||||
- `addPublishLocation(publishAttributes)`: associates a [`publishAttributes`][31] object with the file.
|
||||
- `removePublishLocation(publishAttributes)`: unassociates a [`publishAttributes`][32] object with the file.
|
||||
|
||||
#### fileSystem
|
||||
|
||||
The `fileSystem` module is a map containing all the [`FileDescriptor`][33] objects with their `fileIndex` as a key.
|
||||
|
||||
|
||||
----------
|
||||
|
||||
|
||||
### synchronizer
|
||||
|
||||
The `synchronizer` module is responsible for:
|
||||
|
||||
- creating a new local file from a sync location (import).
|
||||
- creating a new sync location from a local file (export).
|
||||
- running 2 ways synchronization (upload and download) for all sync locations.
|
||||
|
||||
#### synchronizer's providers
|
||||
|
||||
A [`provider`][34] module can be associated with the `synchronizer` module if it implements the following functions:
|
||||
|
||||
- `importFiles()`: downloads one or multiple files and create local files associated with the sync locations.
|
||||
- `exportFile()`: uploads a local file to a new sync location.
|
||||
- `syncDown()`: performs a download of all the changes operated on all sync locations.
|
||||
- `syncUp()`: performs an upload of a change to a sync location.
|
||||
|
||||
#### syncAttributes
|
||||
|
||||
A `syncAttributes` object is an object that describes a sync location. Attributes differ from one provider to another except for the following:
|
||||
|
||||
- `syncIndex`: the unique string index of the publish location.
|
||||
- `provider`: the [`provider`][35] module that handles the sync location.
|
||||
|
||||
|
||||
----------
|
||||
|
||||
|
||||
### publisher
|
||||
|
||||
The `publisher` module is responsible for:
|
||||
|
||||
- creating new publish locations,
|
||||
- updating existing publish locations.
|
||||
|
||||
#### publisher's providers
|
||||
|
||||
A [`provider`][36] module can be associated with the `publisher` module if it implements the following functions:
|
||||
|
||||
- `newPublishAttributes()`: returns a new [`publishAttributes`][37] object in order to create a new publish location.
|
||||
- `publish()`: performs publishing of one publish location.
|
||||
|
||||
#### publishAttributes
|
||||
|
||||
A `publishAttributes` object is an object that describes a publish location. Attributes differ from one provider to another except for the following:
|
||||
|
||||
- `publishIndex`: the unique string index of the publish location.
|
||||
- `provider`: the [`provider`][38] module that handles the publish location.
|
||||
- `format`: the publishing format for the publish location. It can be:
|
||||
- `markdown` for Markdown format.
|
||||
- `html` for HTML format.
|
||||
- `template` for template format.
|
||||
|
||||
|
||||
----------
|
||||
|
||||
|
||||
### eventMgr
|
||||
|
||||
The `eventMgr` module is responsible for receiving and dispatching events. Below is the list of all events signatures.
|
||||
|
||||
Most events (those that are not triggered by the `eventMgr` module) can be triggered by calling methods of the same name in the `eventMgr` module. For example:
|
||||
|
||||
```js
|
||||
eventMgr.onMessage('StackEdit is awesome!');
|
||||
```
|
||||
|
||||
The method `addListener(eventName, callback)` of the `eventMgr` module can be used to listen to these events (except those that can only be handled by `Extension` modules). For example:
|
||||
|
||||
```js
|
||||
eventMgr.addListener('onMessage', function(message) {
|
||||
alert(message);
|
||||
});
|
||||
```
|
||||
|
||||
`Extension` modules have the possibility to listen to those events by implementing methods of the same name. For example:
|
||||
|
||||
```js
|
||||
myExtension.onMessage = function(message) {
|
||||
alert(message);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
----------
|
||||
|
||||
#### Core events
|
||||
|
||||
- **`onReady()`**
|
||||
|
||||
All the modules are loaded and the DOM is ready.
|
||||
|
||||
> Triggered by the `core` module.
|
||||
|
||||
> This is preferred over [jQuery's `.ready()`][39] because it ensures that all modules have been loaded by RequireJS.
|
||||
|
||||
- **`onMessage(message)`**
|
||||
|
||||
A message destined to the user has been produced.
|
||||
- `message`: the text string of the message.
|
||||
|
||||
- **`onError(error)`**
|
||||
|
||||
An error has been thrown.
|
||||
- `error`: an error object or a string.
|
||||
|
||||
- **`onOfflineChanged(isOffline)`**
|
||||
|
||||
The off-line status has changed.
|
||||
- `isOffline`: the off-line status.
|
||||
|
||||
> Triggered by the `core` module.
|
||||
|
||||
- **`onUserActive()`**
|
||||
|
||||
The user has just moved the mouse or pressed the keyboard.
|
||||
|
||||
> Triggered by the `core` module.
|
||||
|
||||
- **`onAsyncRunning(isRunning)`**
|
||||
|
||||
Some asynchronous tasks have just started or stopped.
|
||||
- `isRunning`: true if started, false if stopped.
|
||||
|
||||
> Triggered by the `AsyncTask` module.
|
||||
|
||||
- **`onPeriodicRun()`**
|
||||
|
||||
A hook that is called periodically (every 1 second if user is active).
|
||||
|
||||
> Triggered by the `core` module.
|
||||
|
||||
- **`onLoadSettings()`**
|
||||
|
||||
A hook that is called when the settings dialog has to be refreshed. Every `Extension` module that has configuration inputs in the settings dialog has to implement a listener for this event.
|
||||
|
||||
> Triggered by the `core` module. Only `Extension` modules can handle this event.
|
||||
|
||||
- **`onSaveSettings(newConfig, event)`**
|
||||
|
||||
A hook that is called when the settings dialog has to be validated. Every `Extension` module that has configuration inputs in the settings dialog has to implement a listener for this event.
|
||||
- `newConfig`: the new configuration object, deduced from the settings dialog inputs.
|
||||
- `event`: the submit event object. `stopPropagation` has to be called in case of an error when parsing settings dialog inputs.
|
||||
|
||||
> Triggered by the `core` module. Only `Extension` modules can handle this event.
|
||||
|
||||
- **`onInit()`**
|
||||
|
||||
A hook allowing enabled extensions to initialize.
|
||||
|
||||
> Triggered by the `eventMgr` module. Only `Extension` modules can handle this event.
|
||||
|
||||
> This event is triggered before `onReady` event and just after the `config` and `enabled` extensions properties have been set by the `eventMgr`.
|
||||
|
||||
|
||||
----------
|
||||
|
||||
#### Module injection events
|
||||
|
||||
- **`onFileMgrCreated(fileMgr)`**
|
||||
|
||||
The `fileMgr` module has been created.
|
||||
- `fileMgr`: the `fileMgr` module.
|
||||
|
||||
> Triggered by the `fileMgr` module.
|
||||
|
||||
|
||||
- **`onSynchronizerCreated(synchronizer)`**
|
||||
|
||||
The `synchronizer` module has been created.
|
||||
- `synchronizer`: the `synchronizer` module.
|
||||
|
||||
> Triggered by the `synchronizer` module.
|
||||
|
||||
- **`onPublisherCreated(publisher)`**
|
||||
|
||||
The `publisher` module has been created.
|
||||
- `publisher`: the `publisher` module.
|
||||
|
||||
> Triggered by the `publisher` module.
|
||||
|
||||
- **`onEventMgrCreated()`**
|
||||
|
||||
The `eventMgr` module has been created.
|
||||
- `eventMgr`: the `eventMgr` module.
|
||||
|
||||
> Triggered by the `eventMgr` module.
|
||||
|
||||
|
||||
----------
|
||||
|
||||
#### file operation events
|
||||
|
||||
- **`onFileCreated(fileDesc)`**
|
||||
|
||||
A [`FileDescriptor`][41] object has been created.
|
||||
- `fileDesc`: the [`FileDescriptor`][42] object.
|
||||
|
||||
> Triggered by the `fileMgr` module.
|
||||
|
||||
- **`onFileDeleted(fileDesc)`**
|
||||
|
||||
A [`FileDescriptor`][43] object has been removed from the `fileSystem` module.
|
||||
- `fileDesc`: the [`FileDescriptor`][44] object.
|
||||
|
||||
> Triggered by the `fileMgr` module.
|
||||
|
||||
- **`onFileSelected(fileDesc)`**
|
||||
|
||||
A [`FileDescriptor`][45] object has been selected.
|
||||
- `fileDesc`: the [`FileDescriptor`][46] object.
|
||||
|
||||
> Triggered by the `fileMgr` module. This event is triggered before `onFileClosed` (if another document is open) and `onFileOpen` events.
|
||||
|
||||
- **`onFileClosed(fileDesc)`**
|
||||
|
||||
The current [`FileDescriptor`][47] object is about to be detached from the editor.
|
||||
- `fileDesc`: the [`FileDescriptor`][48] object.
|
||||
|
||||
> Triggered by the `fileMgr` module. This event is triggered after `onFileSelected` event and before `onFileClosed` event.
|
||||
|
||||
- **`onFileOpen(fileDesc)`**
|
||||
|
||||
The selected [`FileDescriptor`][49] object has been attached to the editor.
|
||||
- `fileDesc`: the [`FileDescriptor`][50] object.
|
||||
|
||||
> Triggered by the `fileMgr` module. This event is triggered after `onFileSelected` and `onFileClosed` (if another document is open) events.
|
||||
|
||||
- **`onContentChanged(fileDesc)`**
|
||||
|
||||
The content of a [`FileDescriptor`][51] object has been modified.
|
||||
- `fileDesc`: the [`FileDescriptor`][52] object.
|
||||
|
||||
- **`onTitleChanged(fileDesc)`**
|
||||
|
||||
The content of a [`FileDescriptor`][53] object has been modified.
|
||||
- `fileDesc`: the [`FileDescriptor`][54] object.
|
||||
|
||||
- **`onFoldersChanged()`**
|
||||
|
||||
The folders structure has changed.
|
||||
|
||||
|
||||
----------
|
||||
|
||||
#### Sync events
|
||||
|
||||
- **`onSyncRunning(isRunning)`**
|
||||
|
||||
A synchronization job has just started or stopped.
|
||||
- `isRunning`: true if started, false if stopped.
|
||||
|
||||
> Triggered by the `synchronizer` module.
|
||||
|
||||
> A synchronization job is the action to download and upload all detected changes for all sync locations of all documents.
|
||||
|
||||
- **`onSyncSuccess()`**
|
||||
|
||||
A synchronization job has successfully finished.
|
||||
|
||||
> Triggered by the `synchronizer` module.
|
||||
|
||||
> A synchronization job is the action to download and upload all detected changes for all sync locations of all documents.
|
||||
|
||||
- **`onSyncImportSuccess(fileDescList, provider)`**
|
||||
|
||||
The import of documents has successfully finished.
|
||||
- `fileDescList`: the list of [`FileDescriptor`][55] objects that have been created.
|
||||
- `provider`: the [`provider`][56] module that handled the import.
|
||||
|
||||
> Triggered by the [`provider`][57] module that handled the import.
|
||||
|
||||
> An import is the action to download multiple files and to create, for each, one [`FileDescriptor`][55] objects with one sync location.
|
||||
|
||||
- **`onSyncExportSuccess(fileDesc, syncAttributes)`**
|
||||
|
||||
The export of one document has successfully finished.
|
||||
- `fileDesc`: the [`FileDescriptor`][58] object that has been exported.
|
||||
- `syncAttributes`: the descriptor object of the new sync location.
|
||||
|
||||
> Triggered by the `synchronizer` module.
|
||||
|
||||
> An export is the action to upload one file and to create one new sync location associated with one existing `FileDescriptor`][55] object.
|
||||
|
||||
- **`onSyncRemoved(fileDesc, syncAttributes)`**
|
||||
|
||||
A sync location has been removed from a [`FileDescriptor`][59] object.
|
||||
- `fileDesc`: the [`FileDescriptor`][60] object.
|
||||
- `syncAttributes`: the descriptor object of the removed sync location.
|
||||
|
||||
|
||||
----------
|
||||
|
||||
#### Publish events
|
||||
|
||||
- **`onPublishRunning(isRunning)`**
|
||||
|
||||
A document publication job has just started or stopped.
|
||||
- `isRunning`: true if started, false if stopped.
|
||||
|
||||
> Triggered by the `publisher` module.
|
||||
|
||||
> A publication job is the action to upload changes on multiple publish locations associated with one `FileDescriptor`][55] object.
|
||||
|
||||
- **`onPublishSuccess(fileDesc)`**
|
||||
|
||||
A document publication job has successfully finished.
|
||||
- `fileDesc`: the [`FileDescriptor`][60] object that has been published.
|
||||
|
||||
> Triggered by the `publisher` module.
|
||||
|
||||
> A publication job is the action to upload changes on multiple publish locations associated with one `FileDescriptor`][55] object.
|
||||
|
||||
- **`onNewPublishSuccess(fileDesc, publishAttributes)`**
|
||||
|
||||
A new publish location has been successfully created.
|
||||
- `fileDesc`: the [`FileDescriptor`][60] object that has been published.
|
||||
- `publishAttributes`: the descriptor object of the new publish location.
|
||||
|
||||
> Triggered by the `publisher` module.
|
||||
|
||||
- **`onPublishRemoved(fileDesc, publishAttributes)`**
|
||||
|
||||
A publish location has been removed from a [`FileDescriptor`][59] object.
|
||||
- `fileDesc`: the [`FileDescriptor`][60] object.
|
||||
- `publishAttributes`: the descriptor object of the removed publish location.
|
||||
|
||||
> Triggered by the `publisher` module.
|
||||
|
||||
|
||||
----------
|
||||
|
||||
#### UI Layout events
|
||||
|
||||
- **`onLayoutConfigure(layoutConfig)`**
|
||||
|
||||
The layout is about to be configured.
|
||||
- `layoutConfig`: the configuration object of the UI Layout library.
|
||||
|
||||
> Triggered by the `core` module.
|
||||
|
||||
- **`onLayoutCreated(layout)`**
|
||||
|
||||
The layout has just been created.
|
||||
- `layout`: the layout object of the UI Layout library.
|
||||
|
||||
> Triggered by the `core` module.
|
||||
|
||||
- **`onLayoutResize(paneName)`**
|
||||
|
||||
One pane of the layout has been resized.
|
||||
- `paneName`: the name of the resized layout pane.
|
||||
|
||||
> Triggered by the `core` module.
|
||||
|
||||
- **`onCreateButton()`**
|
||||
|
||||
Allows extensions to add their own buttons in the navigation bar. Implemented listeners have to return an HTML button element. For example:
|
||||
|
||||
myExtension.onCreateButton = function() {
|
||||
var button = $('<button class="btn btn-success"><i class="icon-rocket"></i></button>');
|
||||
button.click(function() {
|
||||
eventMgr.onMessage('Booom!');
|
||||
});
|
||||
return button[0];
|
||||
};
|
||||
|
||||
> Triggered by the `eventMgr` module. Only `Extension` modules can handle this event.
|
||||
|
||||
- **`onCreateEditorButton()`**
|
||||
|
||||
Allows extensions to add their own buttons in the side bar. Implemented listeners have to return an HTML button element. See `onCreateButton` for a concrete example.
|
||||
|
||||
> Triggered by the `eventMgr` module. Only `Extension` modules can handle this event.
|
||||
|
||||
- **`onCreatePreviewButton()`**
|
||||
|
||||
Allows extensions to add their own buttons over the preview. Implemented listeners have to return an HTML button element. See `onCreateButton` for a concrete example.
|
||||
|
||||
> Triggered by the `eventMgr` module. Only `Extension` modules can handle this event.
|
||||
|
||||
|
||||
----------
|
||||
|
||||
#### PageDown events
|
||||
|
||||
- **`onPagedownConfigure(editor)`**
|
||||
|
||||
The Pagedown editor is about to be created.
|
||||
- `editor`: the Pagedown editor object before `run` has been called.
|
||||
|
||||
> Triggered by the `core` module.
|
||||
|
||||
- **`onAsyncPreview(callback)`**
|
||||
|
||||
Called after Pagedown's synchronous rendering to trigger extra asynchronous rendering (such as MathJax). Implemented listeners have to call the callback parameter after processing in order other `onAsyncPreview` listeners to run.
|
||||
- `callback`: the callback to call at the end of the asynchronous processing.
|
||||
|
||||
> Triggered by the `eventMgr` module. Only `Extension` modules can handle this event.
|
||||
|
||||
- **`onPreviewFinished(html)`**
|
||||
|
||||
Called after every `onAsyncPreview` listeners have been called.
|
||||
- `html`: the finally rendered HTML.
|
||||
|
||||
- **`onSectionsCreated(sectionList)`**
|
||||
|
||||
The Markdown has been split into sections before rendering.
|
||||
- `sectionList`: the list of section objects. Each section object contains:
|
||||
- `text`: the markdown substring contained in the section.
|
||||
- `textWithDelimiter`: the text with an added delimiter.
|
||||
|
||||
> Triggered by the `markdownSectionParser` extension.
|
||||
|
||||
- **`onMarkdownTrim(offset)`**
|
||||
|
||||
The Markdown has been left trimmed by a certain number of character.
|
||||
- `offset`: the number of characters that have been removed.
|
||||
|
||||
> Triggered by the `yamlFrontMatterParser` extension.
|
||||
|
||||
|
||||
----------
|
||||
|
||||
#### ACE events
|
||||
|
||||
- **`onAceCreated(aceEditor)`**
|
||||
|
||||
The ACE editor has just been created.
|
||||
- `aceEditor`: the ACE editor object.
|
||||
|
||||
> Triggered by the `core` module.
|
||||
|
||||
|
||||
|
||||
> Written with [StackEdit](https://stackedit.io/).
|
||||
|
||||
|
||||
[1]: http://git-scm.com/
|
||||
[2]: http://nodejs.org/
|
||||
[3]: http://gruntjs.com/
|
||||
[4]: http://bower.io/
|
||||
[5]: http://requirejs.org/ "RequireJS"
|
||||
[6]: http://en.wikipedia.org/wiki/Asynchronous_module_definition "Asynchronous module definition"
|
||||
[7]: http://bower.io/
|
||||
[8]: http://requirejs.org/ "RequireJS"
|
||||
[9]: https://github.com/benweet/stackedit-wordpress-proxy
|
||||
[10]: https://github.com/benweet/stackedit-tumblr-proxy
|
||||
[11]: https://github.com/prose/gatekeeper
|
||||
[12]: https://lh6.googleusercontent.com/-sr6zRtyaoUk/Un5qSakOzPI/AAAAAAAAFC0/oI5If5fI9Gw/s0/StackEdit%252520architecture%252520-%252520New%252520Page%252520%2525283%252529.png "StackEdit architecture"
|
||||
[13]: #module-injection
|
||||
[14]: http://layout.jquery-dev.net/ "UI Layout"
|
||||
[15]: http://ace.c9.io
|
||||
[16]: https://code.google.com/p/pagedown/ "PageDown"
|
||||
[17]: http://api.jquery.com/ready/
|
||||
[18]: http://requirejs.org/ "RequireJS"
|
||||
[19]: https://code.google.com/p/pagedown/ "PageDown"
|
||||
[20]: #filedescriptor
|
||||
[21]: #filedescriptor
|
||||
[22]: #filedescriptor
|
||||
[23]: #filesystem
|
||||
[24]: #filedescriptor
|
||||
[25]: #filesystem
|
||||
[26]: #filedescriptor
|
||||
[27]: #syncattributes
|
||||
[28]: #publishattributes
|
||||
[29]: #syncattributes
|
||||
[30]: #syncattributes
|
||||
[31]: #publishattributes
|
||||
[32]: #publishattributes
|
||||
[33]: #filedescriptor
|
||||
[34]: #provider
|
||||
[35]: #provider
|
||||
[36]: #provider
|
||||
[37]: #publishattributes
|
||||
[38]: #provider
|
||||
[39]: http://api.jquery.com/ready/
|
||||
[40]: http://requirejs.org/ "RequireJS"
|
||||
[41]: #filedescriptor
|
||||
[42]: #filedescriptor
|
||||
[43]: #filedescriptor
|
||||
[44]: #filedescriptor
|
||||
[45]: #filedescriptor
|
||||
[46]: #filedescriptor
|
||||
[47]: #filedescriptor
|
||||
[48]: #filedescriptor
|
||||
[49]: #filedescriptor
|
||||
[50]: #filedescriptor
|
||||
[51]: #filedescriptor
|
||||
[52]: #filedescriptor
|
||||
[53]: #filedescriptor
|
||||
[54]: #filedescriptor
|
||||
[55]: #filedescriptor
|
||||
[56]: #provider
|
||||
[57]: #provider
|
||||
[58]: #filedescriptor
|
||||
[59]: #filedescriptor
|
||||
[60]: #filedescriptor
|
BIN
doc/img/architecture.png
Normal file
After Width: | Height: | Size: 30 KiB |
34
doc/theming.md
Normal file
@ -0,0 +1,34 @@
|
||||
StackEdit theming guide
|
||||
=======================
|
||||
|
||||
In **StackEdit**, a theme is pretty much a [LESS][1] file that overrides the default look and feel.
|
||||
|
||||
### Create your special theme very quickly by following these steps
|
||||
|
||||
1. Fork **StackEdit** on [GitHub][2] and clone the repository localy.
|
||||
|
||||
2. Install the development tools as described in the [Developer guide][3].
|
||||
|
||||
3. In `res/themes`, create a LESS file, just like the other themes.
|
||||
|
||||
> You can put images in `res/img`.
|
||||
|
||||
4. Add an entry in `THEME_LIST` at the end of `public/res/constants.js` with the filename as a key and the name of your theme as a value.
|
||||
|
||||
> **Example:** `"cool": "The coolest ever"`
|
||||
|
||||
5. Run the application on your machine using the `?debug` flag. Basically:
|
||||
|
||||
http://localhost/stackedit/?debug
|
||||
|
||||
6. Go to `Settings -> Editor -> Theme` and select your theme. Check that everything is fine.
|
||||
|
||||
7. Commit, push, create a pull request and wait for publishing.
|
||||
|
||||
|
||||
> Written with [StackEdit](http://benweet.github.io/stackedit/).
|
||||
|
||||
|
||||
[1]: http://lesscss.org/
|
||||
[2]: https://github.com/benweet/stackedit
|
||||
[3]: https://github.com/benweet/stackedit/blob/master/doc/developer-guide.md#getting-started
|
@ -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` 查看帮助文档。
|
||||
|
||||
|
||||
|
@ -1,20 +0,0 @@
|
||||
# GitHub应用配置说明
|
||||
|
||||
> StackEdit中文版部署如果需要支持GitHub,则需要到GitHub创建一个应用,并复制其中的clientId和clientSecret填充到环境变量 GITHUB_CLIENT_ID 和 GITHUB_CLIENT_SECRET 中。
|
||||
|
||||
|
||||
# 如何创建GitHub应用
|
||||
|
||||
按下面图的指示创建
|
||||
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
@ -1,35 +0,0 @@
|
||||
# Gitea应用配置说明
|
||||
|
||||
> StackEdit中文版支持Gitea,则需要到Gitea创建一个应用,在StackEdit中文版绑定Gitea账号的时候填入。
|
||||
|
||||
|
||||
# 如何创建Gitea应用
|
||||
|
||||
按下面图的指示创建
|
||||
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
创建成功后即可看到应用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;
|
||||
}
|
||||
```
|
@ -1,19 +0,0 @@
|
||||
# Gitee应用配置说明
|
||||
|
||||
> StackEdit中文版部署如果需要支持Gitee,则需要到Gitee创建一个应用,并复制其中的clientId和clientSecret填充到环境变量 GITEE_CLIENT_ID 和 GITEE_CLIENT_SECRET 中。
|
||||
|
||||
|
||||
# 如何创建Gitee应用
|
||||
|
||||
按下面图的指示创建
|
||||
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
创建成功后即可看到client id 和 client secret。
|
20
gulpfile.js
@ -1,20 +0,0 @@
|
||||
const path = require('path');
|
||||
const gulp = require('gulp');
|
||||
const concat = require('gulp-concat');
|
||||
|
||||
const prismScripts = [
|
||||
'prismjs/components/prism-core',
|
||||
'prismjs/components/prism-markup',
|
||||
'prismjs/components/prism-clike',
|
||||
'prismjs/components/prism-c',
|
||||
'prismjs/components/prism-javascript',
|
||||
'prismjs/components/prism-css',
|
||||
'prismjs/components/prism-ruby',
|
||||
'prismjs/components/prism-cpp',
|
||||
].map(require.resolve);
|
||||
prismScripts.push(
|
||||
path.join(path.dirname(require.resolve('prismjs/components/prism-core')), 'prism-!(*.min).js'));
|
||||
|
||||
gulp.task('build-prism', () => gulp.src(prismScripts)
|
||||
.pipe(concat('prism.js'))
|
||||
.pipe(gulp.dest(path.dirname(require.resolve('prismjs')))));
|
Before Width: | Height: | Size: 175 KiB |
BIN
images/dark.png
Before Width: | Height: | Size: 793 KiB |
Before Width: | Height: | Size: 102 KiB |
Before Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 121 KiB |
Before Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 81 KiB |
Before Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 86 KiB |
Before Width: | Height: | Size: 109 KiB |
Before Width: | Height: | Size: 339 KiB |
BIN
images/light.png
Before Width: | Height: | Size: 726 KiB |
BIN
images/qq.jpeg
Before Width: | Height: | Size: 87 KiB |
Before Width: | Height: | Size: 360 KiB |
BIN
images/theme.gif
Before Width: | Height: | Size: 937 KiB |
Before Width: | Height: | Size: 761 KiB |
Before Width: | Height: | Size: 195 KiB |
BIN
img/icon-16.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
img/icon-32.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
img/icon.ico
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
img/icon.idraw
Normal file
BIN
img/logo-128.png
Normal file
After Width: | Height: | Size: 8.9 KiB |
BIN
img/logo-256.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
img/logo-32.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
img/logo-512.png
Normal file
After Width: | Height: | Size: 47 KiB |
BIN
img/logo-64.png
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
img/logo-page-1280.png
Normal file
After Width: | Height: | Size: 75 KiB |
BIN
img/logo-page.idraw
Normal file
BIN
img/logo-promo-128.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
img/logo-promo-24.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
img/logo-promo-chrome-280.idraw
Normal file
BIN
img/logo-promo-chrome-280.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
img/logo-promo-chrome-560.idraw
Normal file
BIN
img/logo-promo-chrome-560.png
Normal file
After Width: | Height: | Size: 182 KiB |
BIN
img/logo-promo-chrome-680.idraw
Normal file
BIN
img/logo-promo-chrome-680.png
Normal file
After Width: | Height: | Size: 152 KiB |