forked from platypush/platypush
[Extensions UI] Install extensions deps from the UI.
- Added `Install` button - Added interactive output panel
This commit is contained in:
parent
033317e0a7
commit
a652bd9df8
5 changed files with 171 additions and 9 deletions
|
@ -45,5 +45,6 @@ export default {
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,9 +1,43 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="install-container">
|
<div class="install-container">
|
||||||
<div class="install-cmd-container">
|
<section class="top">
|
||||||
|
<header>
|
||||||
|
<h2>Dependencies</h2>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="body">
|
||||||
|
<div class="container install-cmd-container">
|
||||||
<CopyButton :text="installCmd" />
|
<CopyButton :text="installCmd" />
|
||||||
<pre><code v-html="highlightedInstallCmd" /></pre>
|
<pre><code v-html="highlightedInstallCmd" /></pre>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="buttons install-btn" v-if="installCmd?.length">
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-default"
|
||||||
|
:disabled="installRunning"
|
||||||
|
@click="installExtension">
|
||||||
|
<i class="fas fa-download" /> Install
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="bottom" v-if="installRunning || installOutput">
|
||||||
|
<header>
|
||||||
|
<h2>Output</h2>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="body">
|
||||||
|
<div class="container install-output" ref="installOutput">
|
||||||
|
<CopyButton :text="installOutput" />
|
||||||
|
<pre><code v-text="installOutput" /><div
|
||||||
|
class="loading-container"
|
||||||
|
v-if="installRunning">
|
||||||
|
<Loading />
|
||||||
|
</div></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -12,14 +46,18 @@ import 'highlight.js/lib/common'
|
||||||
import 'highlight.js/styles/stackoverflow-dark.min.css'
|
import 'highlight.js/styles/stackoverflow-dark.min.css'
|
||||||
import hljs from "highlight.js"
|
import hljs from "highlight.js"
|
||||||
import CopyButton from "@/components/elements/CopyButton"
|
import CopyButton from "@/components/elements/CopyButton"
|
||||||
|
import Loading from "@/components/Loading"
|
||||||
import Utils from "@/Utils"
|
import Utils from "@/Utils"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Install",
|
name: "Install",
|
||||||
mixins: [Utils],
|
mixins: [Utils],
|
||||||
|
emit: ['install-start', 'install-end'],
|
||||||
components: {
|
components: {
|
||||||
CopyButton,
|
CopyButton,
|
||||||
|
Loading,
|
||||||
},
|
},
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
extension: {
|
extension: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -27,6 +65,14 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
installRunning: false,
|
||||||
|
installOutput: null,
|
||||||
|
pendingCommands: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
installCmd() {
|
installCmd() {
|
||||||
const cmd = this.extension.deps.install_cmd.join('\n').trim()
|
const cmd = this.extension.deps.install_cmd.join('\n').trim()
|
||||||
|
@ -47,29 +93,142 @@ export default {
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
wsProcess(path) {
|
||||||
|
try {
|
||||||
|
const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws'
|
||||||
|
const url = `${protocol}://${location.host}${path}`
|
||||||
|
const ws = new WebSocket(url)
|
||||||
|
|
||||||
|
ws.onmessage = this.onMessage
|
||||||
|
ws.onerror = this.onError
|
||||||
|
ws.onclose = this.onClose
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Websocket initialization error') // TODO notify
|
||||||
|
console.error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onMessage(msg) {
|
||||||
|
if (!this.installOutput)
|
||||||
|
this.installOutput = ''
|
||||||
|
|
||||||
|
this.installOutput += msg.data
|
||||||
|
},
|
||||||
|
|
||||||
|
onClose() {
|
||||||
|
this.installRunning = false
|
||||||
|
this.$emit('install-end', this.extension)
|
||||||
|
},
|
||||||
|
|
||||||
|
onError(error) {
|
||||||
|
console.error('Websocket error') // TODO notify
|
||||||
|
console.error(error)
|
||||||
|
},
|
||||||
|
|
||||||
|
installExtension() {
|
||||||
|
this.installRunning = true
|
||||||
|
this.installOutput = ''
|
||||||
|
this.$emit('install-start', this.extension)
|
||||||
|
|
||||||
|
const cmd = (this.extension.deps.install_cmd || []).join(';\n')
|
||||||
|
this.request('shell.exec', {
|
||||||
|
cmd: cmd,
|
||||||
|
ws: true,
|
||||||
|
}).then((output) => {
|
||||||
|
this.wsProcess(output.ws_path)
|
||||||
|
}).catch((err) => {
|
||||||
|
console.error(err) // TODO notify
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.$watch('installOutput', () => {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.installOutput.focus()
|
||||||
|
this.$refs.installOutput.scrollTop = this.$refs.installOutput.scrollHeight
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "common.scss";
|
@import "common.scss";
|
||||||
|
|
||||||
|
$header-height: 3.5em;
|
||||||
|
|
||||||
.install-container {
|
.install-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
.install-cmd-container {
|
section {
|
||||||
|
&.top {
|
||||||
|
height: 33.3333% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.bottom {
|
||||||
|
height: 66.6666% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
height: $header-height;
|
||||||
|
padding-left: 0.5em;
|
||||||
|
border-top: 1px solid $border-color-1;
|
||||||
|
border-bottom: 1px solid $border-color-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body {
|
||||||
|
height: calc(100% - #{$header-height});
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.3em;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex-grow: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
height: 50%;
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
border-radius: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.install-btn {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 1em;
|
||||||
|
display: flex;
|
||||||
|
justify-content: right;
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 0.5em;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-container {
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
:deep(.loading) {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -3,6 +3,7 @@ pre {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
background: $code-dark-bg;
|
background: $code-dark-bg;
|
||||||
color: $code-dark-fg;
|
color: $code-dark-fg;
|
||||||
|
font-size: 0.9em;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,7 +166,7 @@ $scrollbar-thumb-bg: #a5a2a2 !default;
|
||||||
$row-shadow: 0 0 1px 0.5px #cfcfcf !default;
|
$row-shadow: 0 0 1px 0.5px #cfcfcf !default;
|
||||||
|
|
||||||
//// Code blocks
|
//// Code blocks
|
||||||
$code-dark-bg: #090909 !default;
|
$code-dark-bg: rgb(11, 11, 13) !default;
|
||||||
$code-dark-fg: #f0f0f0 !default;
|
$code-dark-fg: rgb(243, 243, 250) !default;
|
||||||
$code-light-bg: #f2f0e9 !default;
|
$code-light-bg: #f2f0e9 !default;
|
||||||
$code-light-fg: #090909 !default;
|
$code-light-fg: #090909 !default;
|
||||||
|
|
|
@ -36,6 +36,7 @@ module.exports = {
|
||||||
proxy: {
|
proxy: {
|
||||||
'^/ws/events': wsProxy,
|
'^/ws/events': wsProxy,
|
||||||
'^/ws/requests': wsProxy,
|
'^/ws/requests': wsProxy,
|
||||||
|
'^/ws/shell': wsProxy,
|
||||||
'^/execute': httpProxy,
|
'^/execute': httpProxy,
|
||||||
'^/auth': httpProxy,
|
'^/auth': httpProxy,
|
||||||
'^/camera/': httpProxy,
|
'^/camera/': httpProxy,
|
||||||
|
|
Loading…
Reference in a new issue