增加内置终端

This commit is contained in:
purocean
2019-02-13 20:35:17 +08:00
parent af6477d3b9
commit 91e861c108
13 changed files with 580 additions and 33 deletions
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

+4
View File
@@ -32,6 +32,7 @@
+ 重命名文件/目录:`Ctrl + 右键文件/目录`
+ 切换文档预览显示:`Alt + V` 或点击状态栏 `切换预览` 按钮
+ 切换编辑器自动换行:`Alt + W` 或点击状态栏 `切换换行` 按钮
+ 切换内置终端展示:`Alt + O` 或点击状态栏 `切换终端` 按钮
+ 将外链或 BASE64 图片转换为本地图片:`Ctrl + Shift + 单击图片`
+ 默认文件存储在 `./data` 目录下,添加更多仓库请编辑 `./config.json` 文件
+ 默认快捷键参考 [vscode](https://code.visualstudio.com/)
@@ -88,6 +89,7 @@
+ katex 公式解析 [markdown-it-katex](https://github.com/waylonflinn/markdown-it-katex)
+ 元素属性书写 [markdown-it-attrs](https://github.com/arve0/markdown-it-attrs)
+ 表格解析增强,支持表格标题多行文本,列表等特性 [markdown-it-multimd-table](https://github.com/RedBug312/markdown-it-multimd-table)
+ 内置终端 [XTERM.JS](https://xtermjs.org/) [node-pty](https://github.com/Microsoft/node-pty)
## 开发
+ 后端: `cd backend; node main.js`
@@ -124,6 +126,7 @@
+ [x] 支持多根目录
+ [x] 状态栏
+ [x] toc 生成
+ [x] 内置终端
+ [ ] 代码块渲染增加行号
+ [ ] 信息流,定时任务
+ [ ] 标签支持
@@ -141,3 +144,4 @@
![截图](./4.png)
![截图](./5.gif)
![截图](./6.png)
![截图](./7.png)
+19 -1
View File
@@ -4,6 +4,7 @@ const bodyParser = require('koa-body')
const static = require('koa-static')
const mime = require('mime')
const request = require('request')
const pty = require('node-pty')
const file = require('./file')
const dataRepository = require('./repository')
@@ -171,6 +172,23 @@ app.use(async (ctx, next) => await wrapper(ctx, next, repository))
app.use(async (ctx, next) => await wrapper(ctx, next, proxy))
const port = 3000
app.listen(port)
const server = require('http').createServer(app.callback())
const io = require('socket.io')(server, {path: '/ws'})
io.on('connection', socket => {
const ptyProcess = pty.spawn('bash', ['--login'], {
name: 'xterm-color',
cols: 80,
rows: 24,
cwd: path.join(__dirname, '../'),
env: process.env
})
ptyProcess.on('data', data => socket.emit('output', data))
socket.on('input', data => ptyProcess.write(data))
socket.on('resize', size => ptyProcess.resize(size[0], size[1]))
})
server.listen(port)
console.log(`访问地址:http://localhost:${port}`)
+3 -1
View File
@@ -7,8 +7,10 @@
"mime": "^2.2.0",
"natural-orderby": "^1.0.2",
"node-plantuml": "^0.6.2",
"node-pty": "^0.8.1",
"opn": "^5.3.0",
"request": "^2.88.0",
"safe-buffer": "^5.1.2"
"safe-buffer": "^5.1.2",
"socket.io": "^2.2.0"
}
}
+223 -1
View File
@@ -9,6 +9,17 @@ accepts@^1.2.2:
mime-types "~2.1.16"
negotiator "0.6.1"
accepts@~1.3.4:
version "1.3.5"
resolved "http://registry.npm.taobao.org/accepts/download/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2"
dependencies:
mime-types "~2.1.18"
negotiator "0.6.1"
after@0.8.2:
version "0.8.2"
resolved "http://registry.npm.taobao.org/after/download/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f"
ajv@^5.3.0:
version "5.5.2"
resolved "http://registry.npm.taobao.org/ajv/download/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
@@ -22,6 +33,10 @@ any-promise@^1.0.0, any-promise@^1.1.0:
version "1.3.0"
resolved "http://registry.npm.taobao.org/any-promise/download/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
arraybuffer.slice@~0.0.7:
version "0.0.7"
resolved "http://registry.npm.taobao.org/arraybuffer.slice/download/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675"
asn1@~0.2.3:
version "0.2.4"
resolved "http://registry.npm.taobao.org/asn1/download/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
@@ -32,6 +47,10 @@ assert-plus@1.0.0, assert-plus@^1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/assert-plus/download/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
async-limiter@~1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/async-limiter/download/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8"
asynckit@^0.4.0:
version "0.4.0"
resolved "http://registry.npm.taobao.org/asynckit/download/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
@@ -44,16 +63,42 @@ aws4@^1.8.0:
version "1.8.0"
resolved "http://registry.npm.taobao.org/aws4/download/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
backo2@1.0.2:
version "1.0.2"
resolved "http://registry.npm.taobao.org/backo2/download/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947"
base64-arraybuffer@0.1.5:
version "0.1.5"
resolved "http://registry.npm.taobao.org/base64-arraybuffer/download/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8"
base64id@1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/base64id/download/base64id-1.0.0.tgz#47688cb99bb6804f0e06d3e763b1c32e57d8e6b6"
bcrypt-pbkdf@^1.0.0:
version "1.0.2"
resolved "http://registry.npm.taobao.org/bcrypt-pbkdf/download/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
dependencies:
tweetnacl "^0.14.3"
better-assert@~1.0.0:
version "1.0.2"
resolved "http://registry.npm.taobao.org/better-assert/download/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522"
dependencies:
callsite "1.0.0"
blob@0.0.5:
version "0.0.5"
resolved "http://registry.npm.taobao.org/blob/download/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683"
bytes@3.0.0:
version "3.0.0"
resolved "http://registry.npm.taobao.org/bytes/download/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
callsite@1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/callsite/download/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20"
caseless@~0.12.0:
version "0.12.0"
resolved "http://registry.npm.taobao.org/caseless/download/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
@@ -81,6 +126,18 @@ commander@^2.8.1:
version "2.16.0"
resolved "http://registry.npm.taobao.org/commander/download/commander-2.16.0.tgz#f16390593996ceb4f3eeb020b31d78528f7f8a50"
component-bind@1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/component-bind/download/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1"
component-emitter@1.2.1:
version "1.2.1"
resolved "http://registry.npm.taobao.org/component-emitter/download/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
component-inherit@0.0.3:
version "0.0.3"
resolved "http://registry.npm.taobao.org/component-inherit/download/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143"
content-disposition@~0.5.0:
version "0.5.2"
resolved "http://registry.npm.taobao.org/content-disposition/download/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4"
@@ -89,6 +146,10 @@ content-type@^1.0.0:
version "1.0.4"
resolved "http://registry.npm.taobao.org/content-type/download/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
cookie@0.3.1:
version "0.3.1"
resolved "http://registry.npm.taobao.org/cookie/download/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb"
cookies@~0.7.0:
version "0.7.1"
resolved "http://registry.npm.taobao.org/cookies/download/cookies-0.7.1.tgz#7c8a615f5481c61ab9f16c833731bcb8f663b99b"
@@ -106,7 +167,7 @@ dashdash@^1.12.0:
dependencies:
assert-plus "^1.0.0"
debug@*:
debug@*, debug@~3.1.0:
version "3.1.0"
resolved "http://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
dependencies:
@@ -118,6 +179,12 @@ debug@^2.6.3, debug@^2.6.8:
dependencies:
ms "2.0.0"
debug@~4.1.0:
version "4.1.1"
resolved "http://registry.npm.taobao.org/debug/download/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
dependencies:
ms "^2.1.1"
deep-equal@~1.0.1:
version "1.0.1"
resolved "http://registry.npm.taobao.org/deep-equal/download/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
@@ -153,6 +220,43 @@ ee-first@1.1.1:
version "1.1.1"
resolved "http://registry.npm.taobao.org/ee-first/download/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
engine.io-client@~3.3.1:
version "3.3.2"
resolved "http://registry.npm.taobao.org/engine.io-client/download/engine.io-client-3.3.2.tgz#04e068798d75beda14375a264bb3d742d7bc33aa"
dependencies:
component-emitter "1.2.1"
component-inherit "0.0.3"
debug "~3.1.0"
engine.io-parser "~2.1.1"
has-cors "1.1.0"
indexof "0.0.1"
parseqs "0.0.5"
parseuri "0.0.5"
ws "~6.1.0"
xmlhttprequest-ssl "~1.5.4"
yeast "0.1.2"
engine.io-parser@~2.1.0, engine.io-parser@~2.1.1:
version "2.1.3"
resolved "http://registry.npm.taobao.org/engine.io-parser/download/engine.io-parser-2.1.3.tgz#757ab970fbf2dfb32c7b74b033216d5739ef79a6"
dependencies:
after "0.8.2"
arraybuffer.slice "~0.0.7"
base64-arraybuffer "0.1.5"
blob "0.0.5"
has-binary2 "~1.0.2"
engine.io@~3.3.1:
version "3.3.2"
resolved "http://registry.npm.taobao.org/engine.io/download/engine.io-3.3.2.tgz#18cbc8b6f36e9461c5c0f81df2b830de16058a59"
dependencies:
accepts "~1.3.4"
base64id "1.0.0"
cookie "0.3.1"
debug "~3.1.0"
engine.io-parser "~2.1.0"
ws "~6.1.0"
error-inject@~1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/error-inject/download/error-inject-1.0.0.tgz#e2b3d91b54aed672f309d950d154850fa11d4f37"
@@ -230,6 +334,16 @@ har-validator@~5.1.0:
ajv "^5.3.0"
har-schema "^2.0.0"
has-binary2@~1.0.2:
version "1.0.3"
resolved "http://registry.npm.taobao.org/has-binary2/download/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d"
dependencies:
isarray "2.0.1"
has-cors@1.1.0:
version "1.1.0"
resolved "http://registry.npm.taobao.org/has-cors/download/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39"
http-assert@^1.1.0:
version "1.3.0"
resolved "http://registry.npm.taobao.org/http-assert/download/http-assert-1.3.0.tgz#a31a5cf88c873ecbb5796907d4d6f132e8c01e4a"
@@ -258,6 +372,10 @@ iconv-lite@0.4.19:
version "0.4.19"
resolved "http://registry.npm.taobao.org/iconv-lite/download/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
indexof@0.0.1:
version "0.0.1"
resolved "http://registry.npm.taobao.org/indexof/download/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d"
inflation@^2.0.0:
version "2.0.0"
resolved "http://registry.npm.taobao.org/inflation/download/inflation-2.0.0.tgz#8b417e47c28f925a45133d914ca1fd389107f30f"
@@ -278,6 +396,10 @@ is-wsl@^1.1.0:
version "1.1.0"
resolved "http://registry.npm.taobao.org/is-wsl/download/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d"
isarray@2.0.1:
version "2.0.1"
resolved "http://registry.npm.taobao.org/isarray/download/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e"
isstream@~0.1.2:
version "0.1.2"
resolved "http://registry.npm.taobao.org/isstream/download/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
@@ -402,6 +524,10 @@ mime-db@~1.35.0:
version "1.35.0"
resolved "http://registry.npm.taobao.org/mime-db/download/mime-db-1.35.0.tgz#0569d657466491283709663ad379a99b90d9ab47"
mime-db@~1.37.0:
version "1.37.0"
resolved "http://registry.npm.taobao.org/mime-db/download/mime-db-1.37.0.tgz#0b6a0ce6fdbe9576e25f1f2d2fde8830dc0ad0d8"
mime-types@^2.0.7, mime-types@~2.1.15, mime-types@~2.1.16:
version "2.1.17"
resolved "http://registry.npm.taobao.org/mime-types/download/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a"
@@ -414,6 +540,12 @@ mime-types@^2.1.12, mime-types@~2.1.19:
dependencies:
mime-db "~1.35.0"
mime-types@~2.1.18:
version "2.1.21"
resolved "http://registry.npm.taobao.org/mime-types/download/mime-types-2.1.21.tgz#28995aa1ecb770742fe6ae7e58f9181c744b3f96"
dependencies:
mime-db "~1.37.0"
mime@^2.2.0:
version "2.2.0"
resolved "http://registry.npm.taobao.org/mime/download/mime-2.2.0.tgz#161e541965551d3b549fa1114391e3a3d55b923b"
@@ -422,6 +554,10 @@ ms@2.0.0:
version "2.0.0"
resolved "http://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
ms@^2.1.1:
version "2.1.1"
resolved "http://registry.npm.taobao.org/ms/download/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
mz@^2.6.0:
version "2.7.0"
resolved "http://registry.npm.taobao.org/mz/download/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32"
@@ -430,6 +566,10 @@ mz@^2.6.0:
object-assign "^4.0.1"
thenify-all "^1.0.0"
nan@2.12.1:
version "2.12.1"
resolved "http://registry.npm.taobao.org/nan/download/nan-2.12.1.tgz#7b1aa193e9aa86057e3c7bbd0ac448e770925552"
natural-orderby@^1.0.2:
version "1.0.2"
resolved "http://registry.npm.taobao.org/natural-orderby/download/natural-orderby-1.0.2.tgz#287a4778217fbf6af0d704129d55ae90450f7a88"
@@ -459,6 +599,12 @@ node-plantuml@^0.6.2:
node-nailgun-server "^0.1.3"
plantuml-encoder "^1.2.5"
node-pty@^0.8.1:
version "0.8.1"
resolved "http://registry.npm.taobao.org/node-pty/download/node-pty-0.8.1.tgz#94b457bec013e7a09b8d9141f63b0787fa25c23f"
dependencies:
nan "2.12.1"
oauth-sign@~0.9.0:
version "0.9.0"
resolved "http://registry.npm.taobao.org/oauth-sign/download/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
@@ -467,6 +613,10 @@ object-assign@^4.0.1:
version "4.1.1"
resolved "http://registry.npm.taobao.org/object-assign/download/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
object-component@0.0.3:
version "0.0.3"
resolved "http://registry.npm.taobao.org/object-component/download/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291"
on-finished@^2.1.0:
version "2.3.0"
resolved "http://registry.npm.taobao.org/on-finished/download/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
@@ -487,6 +637,18 @@ pako@1.0.3:
version "1.0.3"
resolved "http://registry.npm.taobao.org/pako/download/pako-1.0.3.tgz#5f515b0c6722e1982920ae8005eacb0b7ca73ccf"
parseqs@0.0.5:
version "0.0.5"
resolved "http://registry.npm.taobao.org/parseqs/download/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d"
dependencies:
better-assert "~1.0.0"
parseuri@0.0.5:
version "0.0.5"
resolved "http://registry.npm.taobao.org/parseuri/download/parseuri-0.0.5.tgz#80204a50d4dbb779bfdc6ebe2778d90e4bce320a"
dependencies:
better-assert "~1.0.0"
parseurl@^1.3.0:
version "1.3.2"
resolved "http://registry.npm.taobao.org/parseurl/download/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3"
@@ -575,6 +737,48 @@ setprototypeof@1.0.3:
version "1.0.3"
resolved "http://registry.npm.taobao.org/setprototypeof/download/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04"
socket.io-adapter@~1.1.0:
version "1.1.1"
resolved "http://registry.npm.taobao.org/socket.io-adapter/download/socket.io-adapter-1.1.1.tgz#2a805e8a14d6372124dd9159ad4502f8cb07f06b"
socket.io-client@2.2.0:
version "2.2.0"
resolved "http://registry.npm.taobao.org/socket.io-client/download/socket.io-client-2.2.0.tgz#84e73ee3c43d5020ccc1a258faeeb9aec2723af7"
dependencies:
backo2 "1.0.2"
base64-arraybuffer "0.1.5"
component-bind "1.0.0"
component-emitter "1.2.1"
debug "~3.1.0"
engine.io-client "~3.3.1"
has-binary2 "~1.0.2"
has-cors "1.1.0"
indexof "0.0.1"
object-component "0.0.3"
parseqs "0.0.5"
parseuri "0.0.5"
socket.io-parser "~3.3.0"
to-array "0.1.4"
socket.io-parser@~3.3.0:
version "3.3.0"
resolved "http://registry.npm.taobao.org/socket.io-parser/download/socket.io-parser-3.3.0.tgz#2b52a96a509fdf31440ba40fed6094c7d4f1262f"
dependencies:
component-emitter "1.2.1"
debug "~3.1.0"
isarray "2.0.1"
socket.io@^2.2.0:
version "2.2.0"
resolved "http://registry.npm.taobao.org/socket.io/download/socket.io-2.2.0.tgz#f0f633161ef6712c972b307598ecd08c9b1b4d5b"
dependencies:
debug "~4.1.0"
engine.io "~3.3.1"
has-binary2 "~1.0.2"
socket.io-adapter "~1.1.0"
socket.io-client "2.2.0"
socket.io-parser "~3.3.0"
sshpk@^1.7.0:
version "1.14.2"
resolved "http://registry.npm.taobao.org/sshpk/download/sshpk-1.14.2.tgz#c6fc61648a3d9c4e764fd3fcdf4ea105e492ba98"
@@ -606,6 +810,10 @@ thenify-all@^1.0.0:
dependencies:
any-promise "^1.0.0"
to-array@0.1.4:
version "0.1.4"
resolved "http://registry.npm.taobao.org/to-array/download/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890"
tough-cookie@~2.4.3:
version "2.4.3"
resolved "http://registry.npm.taobao.org/tough-cookie/download/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781"
@@ -657,3 +865,17 @@ verror@1.10.0:
assert-plus "^1.0.0"
core-util-is "1.0.2"
extsprintf "^1.2.0"
ws@~6.1.0:
version "6.1.3"
resolved "http://registry.npm.taobao.org/ws/download/ws-6.1.3.tgz#d2d2e5f0e3c700ef2de89080ebc0ac6e1bf3a72d"
dependencies:
async-limiter "~1.0.0"
xmlhttprequest-ssl@~1.5.4:
version "1.5.5"
resolved "http://registry.npm.taobao.org/xmlhttprequest-ssl/download/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e"
yeast@0.1.2:
version "0.1.2"
resolved "http://registry.npm.taobao.org/yeast/download/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"
+4
View File
@@ -13,6 +13,10 @@ module.exports = {
proxyTable: {
'/api': {
target: 'http://localhost:3000'
},
'/ws': {
target: 'http://localhost:3000',
ws: true
}
},
+3 -1
View File
@@ -26,9 +26,11 @@
"mermaid": "^7.1.2",
"monaco-editor": "^0.13.1",
"normalize.css": "^7.0.0",
"socket.io-client": "^2.2.0",
"vue": "^2.5.2",
"vue-bus": "^1.1.0",
"vue-router": "^3.0.1"
"vue-router": "^3.0.1",
"xterm": "^3.11.0"
},
"devDependencies": {
"autoprefixer": "^7.1.2",
+7 -1
View File
@@ -43,9 +43,15 @@ const RunPlugin = (md) => {
resultDiv.innerText = e.message
})
`
const runInXterm = `
var code = '${encodeURIComponent(code).replace(/'/g, '\\\'')}'
code = decodeURIComponent(code)
runInXterm('${token.info}', code, !arguments[0].ctrlKey)
`
const result = localStorage[`${cachePrefix}${hash}`] || ''
const resultDiv = `<div id="run-result-${hash}" style="position: relative;border-top: dashed 1px #888; padding: .5em 0; margin-top: 1.5em;"><div style="padding: .5em 0">${result}</div><button style="position: absolute; top: -.7em; height: 0; width: 0; border-left: .7em #b7b3b3 solid; border-top: .6em #dddddd00 solid; border-bottom: .6em #dddddd00 solid; border-right: 0; background: rgba(0, 0, 0, 0); cursor: pointer; outline: none" onclick="${click}"></button></div>`
const resultDiv = `<div id="run-result-${hash}" style="position: relative;border-top: dashed 1px #888; padding: .5em 0; margin-top: 1.5em;"><div style="padding: .5em 0">${result}</div><button title="运行代码" style="position: absolute; top: -.7em; height: 0; width: 0; border-left: .7em #b7b3b3 solid; border-top: .6em #dddddd00 solid; border-bottom: .6em #dddddd00 solid; border-right: 0; background: rgba(0, 0, 0, 0); cursor: pointer; outline: none" onclick="${click}"></button><button title="在终端中运行代码,Ctrl + 单击不退出解释器" style="position: absolute; top: -.25em; right: -0.4em; height: 0; width: 0; border-left: .7em #b7b3b3 solid; border-top: .6em #dddddd00 solid; border-bottom: .6em #dddddd00 solid; border-right: 0; background: rgba(0, 0, 0, 0); cursor: pointer; outline: none;transform: rotate(90deg);" onclick="${runInXterm}"></button></div>`
const codeContent = temp(tokens, idx, options, env, slf).trim().replace(/<\/pre>$/, '')
+12 -2
View File
@@ -2,8 +2,9 @@
<div class="status-bar">
<RepositorySwitch class="left"></RepositorySwitch>
<div class="view-control right" @click="toggleView">切换预览</div>
<div class="wrap-control right" @click="toggleWrap">切换换行</div>
<div class="view-control right" @click="toggleView" title="Alt + v">切换预览</div>
<div class="wrap-control right" @click="toggleWrap" title="Alt + w">切换换行</div>
<div class="wrap-control right" @click="toggleXterm" title="Alt + o">切换终端</div>
<div class="document-info right">
<span>行:{{documentInfo.line}}</span>
<span>列:{{documentInfo.column}}</span>
@@ -50,6 +51,9 @@ export default {
toggleView () {
this.$bus.emit('toggle-view')
},
toggleXterm () {
this.$bus.emit('toggle-xterm')
},
toggleWrap () {
this.$bus.emit('editor-toggle-wrap')
},
@@ -60,6 +64,12 @@ export default {
e.stopPropagation()
}
if (e.key === 'o' && e.altKey) {
this.toggleXterm()
e.preventDefault()
e.stopPropagation()
}
if (e.key === 'w' && e.altKey) {
this.toggleWrap()
e.preventDefault()
+7 -2
View File
@@ -29,7 +29,7 @@
</div>
</div>
</div>
<div :class="{'scroll-to-top': true, 'hide': scrollTop < 30}" @click="scrollToTop">TOP</div>
<div :class="{'scroll-to-top': true, 'show-xterm': showXterm, 'hide': scrollTop < 30}" @click="scrollToTop">TOP</div>
<article ref="view" class="markdown-body" @click="handleClick"></article>
</div>
</template>
@@ -61,7 +61,8 @@ export default {
value: String,
fileRepo: String,
fileName: String,
filePath: String
filePath: String,
showXterm: Boolean
},
data () {
return {
@@ -393,6 +394,10 @@ button:hover {
font-size: 14px;
}
.scroll-to-top.show-xterm {
bottom: 45vh;
}
.scroll-to-top {
user-select: none;
position: fixed;
+113
View File
@@ -0,0 +1,113 @@
<template>
<div class="xterm">
<div ref="xterm" style="height: 100%"></div>
</div>
</template>
<script>
import io from 'socket.io-client'
import { Terminal } from 'xterm'
import * as fit from 'xterm/lib/addons/fit/fit'
import 'xterm/src/xterm.css'
Terminal.applyAddon(fit)
let xterm = null
let socket = null
export default {
name: 'xterm',
created () {
window.runInXterm = this.runInXterm
},
methods: {
init () {
if (!xterm) {
xterm = new Terminal({
cols: 80,
rows: 24,
fontSize: 18,
cursorStyle: 'underline',
fontFamily: 'Consolas',
fontWeightBold: '500',
theme: {
background: 'rgb(30, 31, 32)'
}
})
xterm.open(this.$refs.xterm)
xterm.fit()
window.addEventListener('resize', this.fitXterm)
socket = io({ path: '/ws' })
xterm.on('resize', size => socket.emit('resize', [size.cols, size.rows]))
xterm.on('data', data => this.input(data))
socket.on('output', arrayBuffer => xterm.write(arrayBuffer))
}
this.focus()
},
fitXterm () {
xterm.fit()
},
input (data) {
socket.emit('input', data)
},
focus () {
xterm.focus()
},
runInXterm (language, code, exit = true) {
this.$bus.emit('toggle-xterm', true)
this.$nextTick(() => {
this.init()
const map = {
bat: {start: 'cmd.exe', exit: 'exit', eol: '\r\n'},
bash: {start: 'bash', exit: 'exit', eol: '\n'},
php: {start: 'php -a', exit: 'exit', eol: '\n'},
python: {start: 'python', exit: 'exit()', eol: '\n'},
py: {start: 'python', exit: 'exit()', eol: '\n'},
js: {start: 'node', exit: '.exit', eol: '\n'}
}
if (map[language]) {
this.input(map[language].start)
this.input(map[language].eol)
// 延迟一下等待子进程启动
setTimeout(() => {
code.split('\n').forEach((x, i) => {
this.input(x.trim())
this.input(map[language].eol)
})
if (exit) {
this.input(map[language].exit)
this.input(map[language].eol)
}
}, 400)
} else {
xterm.write(`不支持 ${language} 语言\n`)
}
})
}
},
beforeDestroy () {
window.removeEventListener('resize', this.fitXterm)
window.runInXterm = null
}
}
</script>
<style scoped>
.xterm {
box-sizing: border-box;
padding: 5px;
padding-bottom: 20px;
height: 40vh;
background: rgb(29, 31, 33);
border: 1px solid rgb(92, 91, 100);
flex: 0 0 auto;
}
</style>
+40 -22
View File
@@ -14,24 +14,30 @@
</header>
<div style="display: flex; justify-content: space-between;" :class="{'show-view': showView}">
<Tree ref="tree" class="tree" v-model="file"></Tree>
<Editor
ref="editor"
class="editor"
v-model="value"
@ready="editorReady"
@scroll-line="syncScrollView"
@paste-img="pasteImg"
@upload-file="uploadFile"
@save="saveFile"></Editor>
<XView
ref="view"
class="view"
:value="value"
:file-name="fileName"
:file-path="filePath"
:file-repo="fileRepo"
@sync-scroll="syncScrollEditor"
@switch-todo="switchTodoEditor"></XView>
<div style="display: flex;flex-direction: column; height: 95vh">
<div style="display: flex;height: 100%">
<Editor
ref="editor"
class="editor"
v-model="value"
@ready="editorReady"
@scroll-line="syncScrollView"
@paste-img="pasteImg"
@upload-file="uploadFile"
@save="saveFile"></Editor>
<XView
ref="view"
class="view"
:value="value"
:file-name="fileName"
:file-path="filePath"
:file-repo="fileRepo"
:show-xterm="showXterm"
@sync-scroll="syncScrollEditor"
@switch-todo="switchTodoEditor"></XView>
</div>
<Xterm ref="xterm" v-show="showXterm"></Xterm>
</div>
</div>
<div class="status-bar">
<StatusBar></StatusBar>
@@ -43,6 +49,7 @@
import dayjs from 'dayjs'
import Editor from '../components/Editor'
import XView from '../components/View'
import Xterm from '../components/Xterm'
import Tree from '../components/Tree'
import StatusBar from '../components/StatusBar'
import RunPlugin from '../components/RunPlugin'
@@ -50,7 +57,7 @@ import File from '../file'
export default {
name: 'home',
components: { XView, Editor, Tree, StatusBar },
components: { XView, Editor, Tree, StatusBar, Xterm },
data () {
return {
status: '请选择文件',
@@ -59,7 +66,8 @@ export default {
file: null,
oldHash: null,
timer: null,
showView: true
showView: true,
showXterm: false
}
},
mounted () {
@@ -67,6 +75,7 @@ export default {
this.restartTimer()
this.$bus.on('toggle-view', this.toggleView)
this.$bus.on('toggle-xterm', this.toggleXterm)
window.onbeforeunload = () => {
return this.unsaved || null
@@ -74,12 +83,20 @@ export default {
},
beforeDestroy () {
this.$bus.off('toggle-view', this.toggleView)
this.$bus.off('toggle-xterm', this.toggleXterm)
this.clearTimer()
},
methods: {
toggleView () {
this.showView = !this.showView
},
toggleXterm (flag) {
this.showXterm = flag === undefined ? !this.showXterm : !!flag
this.$nextTick(() => {
this.$refs.editor.resize()
this.$refs.xterm.init()
})
},
editorReady () {
this.$bus.emit('editor-ready')
},
@@ -208,6 +225,7 @@ export default {
padding-bottom: 20px;
box-sizing: border-box;
overflow: auto;
flex: 0 0 auto;
}
.show-view .editor {
@@ -215,7 +233,7 @@ export default {
}
.editor {
height: 95vh;
/* height: 95vh; */
width: 83vw;
overflow: hidden;
}
@@ -232,7 +250,7 @@ export default {
margin: 0 auto;
padding: 45px;
width: 43vw;
height: 95vh;
/* height: 95vh; */
overflow: auto;
box-sizing: border-box;
display: none;
+145 -2
View File
@@ -89,6 +89,10 @@ acorn@^5.0.0, acorn@^5.1.1, acorn@^5.2.1:
version "5.3.0"
resolved "http://registry.npm.taobao.org/acorn/download/acorn-5.3.0.tgz#7446d39459c54fb49a80e6ee6478149b940ec822"
after@0.8.2:
version "0.8.2"
resolved "http://registry.npm.taobao.org/after/download/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f"
ajv-keywords@^2.0.0, ajv-keywords@^2.1.0:
version "2.1.1"
resolved "http://registry.npm.taobao.org/ajv-keywords/download/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762"
@@ -239,6 +243,10 @@ array-unique@^0.3.2:
version "0.3.2"
resolved "http://registry.npm.taobao.org/array-unique/download/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
arraybuffer.slice@~0.0.7:
version "0.0.7"
resolved "http://registry.npm.taobao.org/arraybuffer.slice/download/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675"
arrify@^1.0.0, arrify@^1.0.1:
version "1.0.1"
resolved "http://registry.npm.taobao.org/arrify/download/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
@@ -920,6 +928,10 @@ babylon@^6.18.0:
version "6.18.0"
resolved "http://registry.npm.taobao.org/babylon/download/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3"
backo2@1.0.2:
version "1.0.2"
resolved "http://registry.npm.taobao.org/backo2/download/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947"
balanced-match@^0.4.2:
version "0.4.2"
resolved "http://registry.npm.taobao.org/balanced-match/download/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838"
@@ -928,6 +940,10 @@ balanced-match@^1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/balanced-match/download/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
base64-arraybuffer@0.1.5:
version "0.1.5"
resolved "http://registry.npm.taobao.org/base64-arraybuffer/download/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8"
base64-js@^1.0.2:
version "1.2.1"
resolved "http://registry.npm.taobao.org/base64-js/download/base64-js-1.2.1.tgz#a91947da1f4a516ea38e5b4ec0ec3773675e0886"
@@ -954,6 +970,12 @@ bcrypt-pbkdf@^1.0.0:
dependencies:
tweetnacl "^0.14.3"
better-assert@~1.0.0:
version "1.0.2"
resolved "http://registry.npm.taobao.org/better-assert/download/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522"
dependencies:
callsite "1.0.0"
big.js@^3.1.3:
version "3.2.0"
resolved "http://registry.npm.taobao.org/big.js/download/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e"
@@ -962,6 +984,10 @@ binary-extensions@^1.0.0:
version "1.11.0"
resolved "http://registry.npm.taobao.org/binary-extensions/download/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205"
blob@0.0.5:
version "0.0.5"
resolved "http://registry.npm.taobao.org/blob/download/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683"
block-stream@*:
version "0.0.9"
resolved "http://registry.npm.taobao.org/block-stream/download/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a"
@@ -1183,6 +1209,10 @@ caller-path@^0.1.0:
dependencies:
callsites "^0.2.0"
callsite@1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/callsite/download/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20"
callsites@^0.2.0:
version "0.2.0"
resolved "http://registry.npm.taobao.org/callsites/download/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca"
@@ -1448,10 +1478,18 @@ commondir@^1.0.1:
version "1.0.1"
resolved "http://registry.npm.taobao.org/commondir/download/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
component-emitter@^1.2.1:
component-bind@1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/component-bind/download/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1"
component-emitter@1.2.1, component-emitter@^1.2.1:
version "1.2.1"
resolved "http://registry.npm.taobao.org/component-emitter/download/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
component-inherit@0.0.3:
version "0.0.3"
resolved "http://registry.npm.taobao.org/component-inherit/download/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143"
compressible@~2.0.11:
version "2.0.12"
resolved "http://registry.npm.taobao.org/compressible/download/compressible-2.0.12.tgz#c59a5c99db76767e9876500e271ef63b3493bd66"
@@ -1806,7 +1844,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.6, debug@^2.6.8, debug@^2.6.
dependencies:
ms "2.0.0"
debug@^3.0.1, debug@^3.1.0:
debug@^3.0.1, debug@^3.1.0, debug@~3.1.0:
version "3.1.0"
resolved "http://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
dependencies:
@@ -2065,6 +2103,32 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0:
dependencies:
once "^1.4.0"
engine.io-client@~3.3.1:
version "3.3.2"
resolved "http://registry.npm.taobao.org/engine.io-client/download/engine.io-client-3.3.2.tgz#04e068798d75beda14375a264bb3d742d7bc33aa"
dependencies:
component-emitter "1.2.1"
component-inherit "0.0.3"
debug "~3.1.0"
engine.io-parser "~2.1.1"
has-cors "1.1.0"
indexof "0.0.1"
parseqs "0.0.5"
parseuri "0.0.5"
ws "~6.1.0"
xmlhttprequest-ssl "~1.5.4"
yeast "0.1.2"
engine.io-parser@~2.1.1:
version "2.1.3"
resolved "http://registry.npm.taobao.org/engine.io-parser/download/engine.io-parser-2.1.3.tgz#757ab970fbf2dfb32c7b74b033216d5739ef79a6"
dependencies:
after "0.8.2"
arraybuffer.slice "~0.0.7"
base64-arraybuffer "0.1.5"
blob "0.0.5"
has-binary2 "~1.0.2"
enhanced-resolve@^3.4.0:
version "3.4.1"
resolved "http://registry.npm.taobao.org/enhanced-resolve/download/enhanced-resolve-3.4.1.tgz#0421e339fd71419b3da13d129b3979040230476e"
@@ -2899,6 +2963,16 @@ has-ansi@^2.0.0:
dependencies:
ansi-regex "^2.0.0"
has-binary2@~1.0.2:
version "1.0.3"
resolved "http://registry.npm.taobao.org/has-binary2/download/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d"
dependencies:
isarray "2.0.1"
has-cors@1.1.0:
version "1.1.0"
resolved "http://registry.npm.taobao.org/has-cors/download/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39"
has-flag@^1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
@@ -3458,6 +3532,10 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/isarray/download/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
isarray@2.0.1:
version "2.0.1"
resolved "http://registry.npm.taobao.org/isarray/download/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e"
isexe@^2.0.0:
version "2.0.0"
resolved "http://registry.npm.taobao.org/isexe/download/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
@@ -4224,6 +4302,10 @@ object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
version "4.1.1"
resolved "http://registry.npm.taobao.org/object-assign/download/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
object-component@0.0.3:
version "0.0.3"
resolved "http://registry.npm.taobao.org/object-component/download/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291"
object-copy@^0.1.0:
version "0.1.0"
resolved "http://registry.npm.taobao.org/object-copy/download/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c"
@@ -4428,6 +4510,18 @@ parse-json@^2.2.0:
dependencies:
error-ex "^1.2.0"
parseqs@0.0.5:
version "0.0.5"
resolved "http://registry.npm.taobao.org/parseqs/download/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d"
dependencies:
better-assert "~1.0.0"
parseuri@0.0.5:
version "0.0.5"
resolved "http://registry.npm.taobao.org/parseuri/download/parseuri-0.0.5.tgz#80204a50d4dbb779bfdc6ebe2778d90e4bce320a"
dependencies:
better-assert "~1.0.0"
parseurl@~1.3.2:
version "1.3.2"
resolved "http://registry.npm.taobao.org/parseurl/download/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3"
@@ -5535,6 +5629,33 @@ sntp@1.x.x:
dependencies:
hoek "2.x.x"
socket.io-client@^2.2.0:
version "2.2.0"
resolved "http://registry.npm.taobao.org/socket.io-client/download/socket.io-client-2.2.0.tgz#84e73ee3c43d5020ccc1a258faeeb9aec2723af7"
dependencies:
backo2 "1.0.2"
base64-arraybuffer "0.1.5"
component-bind "1.0.0"
component-emitter "1.2.1"
debug "~3.1.0"
engine.io-client "~3.3.1"
has-binary2 "~1.0.2"
has-cors "1.1.0"
indexof "0.0.1"
object-component "0.0.3"
parseqs "0.0.5"
parseuri "0.0.5"
socket.io-parser "~3.3.0"
to-array "0.1.4"
socket.io-parser@~3.3.0:
version "3.3.0"
resolved "http://registry.npm.taobao.org/socket.io-parser/download/socket.io-parser-3.3.0.tgz#2b52a96a509fdf31440ba40fed6094c7d4f1262f"
dependencies:
component-emitter "1.2.1"
debug "~3.1.0"
isarray "2.0.1"
sockjs-client@1.1.4:
version "1.1.4"
resolved "http://registry.npm.taobao.org/sockjs-client/download/sockjs-client-1.1.4.tgz#5babe386b775e4cf14e7520911452654016c8b12"
@@ -5891,6 +6012,10 @@ tmp@^0.0.33:
dependencies:
os-tmpdir "~1.0.2"
to-array@0.1.4:
version "0.1.4"
resolved "http://registry.npm.taobao.org/to-array/download/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890"
to-arraybuffer@^1.0.0:
version "1.0.1"
resolved "http://registry.npm.taobao.org/to-arraybuffer/download/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
@@ -6405,14 +6530,28 @@ ws@^4.0.0:
safe-buffer "~5.1.0"
ultron "~1.1.0"
ws@~6.1.0:
version "6.1.3"
resolved "http://registry.npm.taobao.org/ws/download/ws-6.1.3.tgz#d2d2e5f0e3c700ef2de89080ebc0ac6e1bf3a72d"
dependencies:
async-limiter "~1.0.0"
xml-char-classes@^1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/xml-char-classes/download/xml-char-classes-1.0.0.tgz#64657848a20ffc5df583a42ad8a277b4512bbc4d"
xmlhttprequest-ssl@~1.5.4:
version "1.5.5"
resolved "http://registry.npm.taobao.org/xmlhttprequest-ssl/download/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e"
xtend@^4.0.0, xtend@~4.0.1:
version "4.0.1"
resolved "http://registry.npm.taobao.org/xtend/download/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
xterm@^3.11.0:
version "3.11.0"
resolved "http://registry.npm.taobao.org/xterm/download/xterm-3.11.0.tgz#2c10da3e965db9170430aa7b68f8965de3ff004d"
xxhashjs@^0.2.1:
version "0.2.2"
resolved "http://registry.npm.taobao.org/xxhashjs/download/xxhashjs-0.2.2.tgz#8a6251567621a1c46a5ae204da0249c7f8caa9d8"
@@ -6487,3 +6626,7 @@ yargs@~3.10.0:
cliui "^2.1.0"
decamelize "^1.0.0"
window-size "0.1.0"
yeast@0.1.2:
version "0.1.2"
resolved "http://registry.npm.taobao.org/yeast/download/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"