From f721ac269ce151441f4d7a98b027f755642132f2 Mon Sep 17 00:00:00 2001 From: Pig Fang Date: Mon, 17 Feb 2020 18:27:56 +0800 Subject: [PATCH] add bsh --- app/Http/View/Composers/UserMenuComposer.php | 3 +- package.json | 5 +- public/.htaccess | 2 + resources/assets/src/index.tsx | 8 +++ resources/assets/src/scripts/cli.ts | 60 +++++++++++++++++++ resources/assets/src/styles/_breakpoints.scss | 2 +- resources/assets/src/styles/terminal.scss | 25 ++++++++ resources/views/shared/user-menu.twig | 5 ++ yarn.lock | 15 +++++ 9 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 resources/assets/src/scripts/cli.ts create mode 100644 resources/assets/src/styles/terminal.scss diff --git a/app/Http/View/Composers/UserMenuComposer.php b/app/Http/View/Composers/UserMenuComposer.php index 3ad58ae0..f3d17bec 100644 --- a/app/Http/View/Composers/UserMenuComposer.php +++ b/app/Http/View/Composers/UserMenuComposer.php @@ -25,7 +25,8 @@ class UserMenuComposer $user = auth()->user(); $avatarUrl = route('avatar.user', ['uid' => $user->uid, 'size' => 25]); $avatar = $this->filter->apply('user_avatar', $avatarUrl, [$user]); + $cli = $this->request->is('admin', 'admin/*'); - $view->with(compact('user', 'avatar')); + $view->with(compact('user', 'avatar', 'cli')); } } diff --git a/package.json b/package.json index 8cd68a35..572ae1b2 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@hot-loader/react-dom": "^16.11.0", "@tweenjs/tween.js": "^18.4.2", "admin-lte": "^3.0.1", + "blessing-skin-shell": "^0.1.0", "echarts": "^4.6.0", "jquery": "^3.4.1", "lodash.debounce": "^4.0.8", @@ -35,7 +36,9 @@ "vue-good-table": "^2.18.1", "vue-recaptcha": "^1.2.0", "vue-upload-component": "^2.8.20", - "vuejs-paginate": "^2.1.0" + "vuejs-paginate": "^2.1.0", + "xterm": "^4.4.0", + "xterm-addon-fit": "^0.3.0" }, "devDependencies": { "@babel/core": "^7.8.0", diff --git a/public/.htaccess b/public/.htaccess index 62dcb4d6..d92ef5c7 100644 --- a/public/.htaccess +++ b/public/.htaccess @@ -24,3 +24,5 @@ RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^ index.php [L] + +AddType application/wasm .wasm diff --git a/resources/assets/src/index.tsx b/resources/assets/src/index.tsx index 4a3d822a..67fc4a0e 100644 --- a/resources/assets/src/index.tsx +++ b/resources/assets/src/index.tsx @@ -10,6 +10,14 @@ Vue.config.productionTip = false loadModules() function loadModules() { + if (blessing.route.startsWith('admin')) { + const entry = document.querySelector('#launch-cli') + entry?.addEventListener('click', async () => { + const { launch } = await import('./scripts/cli') + launch() + }) + } + const route = routes.find( // eslint-disable-next-line no-shadow route => new RegExp(`^${route.path}$`, 'i').test(blessing.route), diff --git a/resources/assets/src/scripts/cli.ts b/resources/assets/src/scripts/cli.ts new file mode 100644 index 00000000..224925df --- /dev/null +++ b/resources/assets/src/scripts/cli.ts @@ -0,0 +1,60 @@ +import { Terminal } from 'xterm' +import { FitAddon } from 'xterm-addon-fit' +import { Shell } from 'blessing-skin-shell' +import 'xterm/css/xterm.css' +import styles from '@/styles/terminal.scss' + +let launched = false + +export function launch() { + if (launched) { + return + } + + const card = document.createElement('div') + card.className = `card ${styles.terminal}` + const header = document.createElement('div') + header.className = 'card-header' + const headerStuff = document.createElement('div') + headerStuff.className = 'd-flex justify-content-between' + const title = document.createElement('h4') + title.className = 'card-title mt-1' + title.textContent = 'Blessing Skin Shell' + headerStuff.appendChild(title) + const btnClose = document.createElement('button') + btnClose.innerHTML = '×' + btnClose.className = 'btn btn-default' + headerStuff.appendChild(btnClose) + header.appendChild(headerStuff) + card.appendChild(header) + + const body = document.createElement('div') + body.className = 'card-body p-2' + body.style.backgroundColor = '#000' + const container = document.createElement('div') + body.appendChild(container) + card.appendChild(body) + document.body.appendChild(card) + + const terminal = new Terminal() + const fitAddon = new FitAddon() + terminal.loadAddon(fitAddon) + terminal.setOption('fontFamily', 'Monaco, Consolas, "Roboto Mono", "Noto Sans", "Droid Sans Mono"') + terminal.open(container) + fitAddon.fit() + + const shell = new Shell(terminal) + const unbind = terminal.onData(e => shell.input(e)) + launched = true + + const handleClose = () => { + unbind.dispose() + shell.free() + fitAddon.dispose() + terminal.dispose() + btnClose.removeEventListener('click', handleClose) + card.remove() + launched = false + } + btnClose.addEventListener('click', handleClose) +} diff --git a/resources/assets/src/styles/_breakpoints.scss b/resources/assets/src/styles/_breakpoints.scss index 0be643f5..5d60a40d 100644 --- a/resources/assets/src/styles/_breakpoints.scss +++ b/resources/assets/src/styles/_breakpoints.scss @@ -15,7 +15,7 @@ $breakpoints: ( } @mixin between($down, $up) { - @media (max-width: map-get($breakpoints, $down)) and (min-width: map-get($breakpoints, $up)) { + @media (min-width: map-get($breakpoints, $down)) and (max-width: map-get($breakpoints, $up)) { @content; } } diff --git a/resources/assets/src/styles/terminal.scss b/resources/assets/src/styles/terminal.scss new file mode 100644 index 00000000..cf74bc90 --- /dev/null +++ b/resources/assets/src/styles/terminal.scss @@ -0,0 +1,25 @@ +@use '../styles/breakpoints'; + +.terminal { + z-index: 1060; + position: fixed; + bottom: 0; + + @include breakpoints.greater-than('xl') { + left: 25vw; + width: 50vw; + height: 50vh; + } + + @include breakpoints.between('md', 'xl') { + left: 5vw; + width: 90vw; + height: 40vh; + } + + @include breakpoints.less-than('md') { + left: 1vw; + width: 98vw; + height: 35vh; + } +} diff --git a/resources/views/shared/user-menu.twig b/resources/views/shared/user-menu.twig index 422f991d..952425b8 100644 --- a/resources/views/shared/user-menu.twig +++ b/resources/views/shared/user-menu.twig @@ -24,6 +24,11 @@ {{ trans('general.report-manage') }} + {% if cli %} + + Web CLI + + {% endif %} {% endif %} diff --git a/yarn.lock b/yarn.lock index 599b3123..6aa5372f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2265,6 +2265,11 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c" integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow== +blessing-skin-shell@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/blessing-skin-shell/-/blessing-skin-shell-0.1.0.tgz#6251b154d2e5b225d65f1b6e8c7489bb2eb67303" + integrity sha512-6c7RwPqLS1mXsrLd6QU7xUjr/gfmDgAY5L+CqOMWwwjLfjuohw6SK6cnGyfWxeK8KoDj5zX3QyoYuCKapXlkUg== + bluebird@^3.1.1, bluebird@^3.5.5: version "3.5.5" resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f" @@ -10702,6 +10707,16 @@ xtend@^4.0.1: resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68= +xterm-addon-fit@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/xterm-addon-fit/-/xterm-addon-fit-0.3.0.tgz#341710741027de9d648a9f84415a01ddfdbbe715" + integrity sha512-kvkiqHVrnMXgyCH9Xn0BOBJ7XaWC/4BgpSWQy3SueqximgW630t/QOankgqkvk11iTOCwWdAY9DTyQBXUMN3lw== + +xterm@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.4.0.tgz#5915d3c4c8800fadbcf555a0a603c672ab9df589" + integrity sha512-JGIpigWM3EBWvnS3rtBuefkiToIILSK1HYMXy4BCsUpO+O4UeeV+/U1AdAXgCB6qJrnPNb7yLgBsVCQUNMteig== + "y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"