feat: 🎉 Add Terminal Debugging

This commit is contained in:
viarotel 2023-11-13 18:35:44 +08:00
parent 4cc6617b9f
commit fdf40c70e8
11 changed files with 310 additions and 0 deletions

View File

@ -43,12 +43,16 @@
"husky": "^8.0.0",
"lodash-es": "^4.17.21",
"pinia": "^2.1.7",
"postcss": "^8.4.31",
"postcss-nested": "^6.0.1",
"postcss-scss": "^4.0.9",
"typescript": "^5.2.2",
"vite": "^4.5.0",
"vite-plugin-electron": "^0.14.1",
"vite-plugin-electron-renderer": "^0.14.5",
"vite-plugin-eslint": "^1.8.1",
"vite-svg-loader": "^4.0.0",
"vue-command": "^35.2.1",
"vue-i18n": "^9.5.0",
"which": "^4.0.0"
}

7
postcss.config.mjs Normal file
View File

@ -0,0 +1,7 @@
import nested from 'postcss-nested'
import postcssScss from 'postcss-scss'
export default {
parser: postcssScss,
plugins: [nested],
}

View File

@ -0,0 +1,24 @@
import { createStderr, createStdout } from 'vue-command'
const $adb = window.adbkit
export function useAdb({ loading }) {
const adb = async (args) => {
loading.value = true
const command = args.slice(1).join(' ')
const { stderr, stdout } = await $adb.shell(command || 'help')
if (stderr) {
return createStderr(stderr)
}
loading.value = false
return createStdout(stdout)
}
return {
adb,
}
}

View File

@ -0,0 +1,50 @@
import { debounce } from 'lodash-es'
import { createStderr, createStdout, textFormatter } from 'vue-command'
const $gnirehtet = window.gnirehtet
const fixCursor = (history) => {
const length = history.value.length
if (history.value[length - 1]?.__name === 'VueCommandQuery') {
history.value.splice(length - 1, 1, textFormatter('Waiting...'))
}
}
export function useGnirehtet({ vShell, history, loading } = {}) {
const gnirehtet = async (args) => {
loading.value = true
const command = args.slice(1).join(' ')
const appendToHistory = debounce(vShell.value.appendToHistory, 500)
let stdoutText = ''
let stderrText = ''
$gnirehtet.shell(command, {
stdout(text) {
loading.value = false
stdoutText += text
fixCursor(history)
appendToHistory(createStdout(stdoutText))
},
stderr(text) {
loading.value = false
stderrText += text
fixCursor(history)
appendToHistory(createStderr(stderrText))
},
})
return textFormatter('Loading...')
}
return {
gnirehtet,
}
}

View File

@ -0,0 +1,50 @@
import { debounce } from 'lodash-es'
import { createStderr, createStdout, textFormatter } from 'vue-command'
const $scrcpy = window.scrcpy
const fixCursor = (history) => {
const length = history.value.length
if (history.value[length - 1]?.__name === 'VueCommandQuery') {
history.value.splice(length - 1, 1, textFormatter('Waiting...'))
}
}
export function useScrcpy({ vShell, history, loading } = {}) {
const scrcpy = async (args) => {
loading.value = true
const command = args.slice(1).join(' ')
const appendToHistory = debounce(vShell.value.appendToHistory, 500)
let stdoutText = ''
let stderrText = ''
$scrcpy.shell(command, {
stdout(text) {
loading.value = false
stdoutText += text
fixCursor(history)
appendToHistory(createStdout(stdoutText))
},
stderr(text) {
loading.value = false
stderrText += text
fixCursor(history)
appendToHistory(createStderr(stderrText))
},
})
return textFormatter('Loading...')
}
return {
scrcpy,
}
}

View File

@ -0,0 +1,113 @@
<template>
<el-dialog
v-model="visible"
width="80%"
:close-on-click-modal="false"
:close-on-press-escape="false"
class="overflow-hidden rounded-xl el-dialog-headless"
@open="onOpen"
>
<el-icon
class="cursor-pointer absolute top-2 right-2 w-8 h-8 flex items-center justify-center text-gray-200 hover:bg-gray-700 !active:bg-red-600 rounded"
@click="hide"
>
<CloseBold />
</el-icon>
<VueCommand
v-if="visible"
:ref="(value) => (vShell = value)"
v-model:history="history"
:commands="commands"
hide-bar
show-help
help-text="Type in help"
:help-timeout="3500"
class=""
:dispatched-queries="dispatchedQueries"
>
<template #prompt>
<div class="flex items-center pr-2">
<span class="">escrcpy~$</span>
</div>
</template>
</VueCommand>
</el-dialog>
</template>
<script>
import { ref } from 'vue'
import VueCommand, {
createQuery,
createStdout,
listFormatter,
} from 'vue-command'
import 'vue-command/dist/vue-command.css'
import { useAdb } from './composables/adb.js'
import { useScrcpy } from './composables/scrcpy.js'
import { useGnirehtet } from './composables/gnirehtet.js'
export default {
components: {
VueCommand,
},
setup() {
const vShell = ref(null)
const history = ref([createQuery()])
const loading = ref(false)
const dispatchedQueries = ref(new Set([]))
const { adb } = useAdb({ vShell, history, loading })
const { scrcpy } = useScrcpy({ vShell, history, loading })
const { gnirehtet } = useGnirehtet({ vShell, history, loading })
const commands = ref({
adb,
scrcpy,
gnirehtet,
clear() {
history.value = []
return createQuery()
},
})
commands.value.help = () => {
const commandList = Object.keys(commands.value)
return createStdout(listFormatter('Supported Commands:', ...commandList))
}
dispatchedQueries.value = new Set(Object.keys(commands.value))
return {
vShell,
loading,
history,
commands,
dispatchedQueries,
}
},
data() {
return {
visible: false,
}
},
methods: {
show() {
this.visible = true
},
hide() {
this.visible = false
},
async onOpen() {
console.log('vShell', this.vShell)
this.vShell.signals.off('SIGINT')
this.vShell.signals.on('SIGINT', () => {
console.log('vShell.signals.on.SIGINT')
this.$gnirehtet.shell('stop')
})
},
},
}
</script>

View File

@ -0,0 +1,23 @@
<template>
<div class="">
<slot :show="show" />
<TerminalDialog ref="terminalDialog" />
</div>
</template>
<script>
import TerminalDialog from './components/TerminalDialog/index.vue'
export default {
components: {
TerminalDialog,
},
methods: {
show() {
this.$refs.terminalDialog.show()
},
},
}
</script>
<style></style>

View File

@ -19,6 +19,14 @@
<el-button icon="View" @click="handleLog">
{{ $t("device.log.name") }}
</el-button>
<Terminal>
<template #default="{ show }">
<el-button icon="View" @click="show">
{{ $t("device.terminal.name") }}
</el-button>
</template>
</Terminal>
</div>
<div class="pt-4 flex-1 h-0 overflow-hidden">
<el-table
@ -157,6 +165,7 @@
import ControlBar from './components/ControlBar/index.vue'
import Remark from './components/Remark/index.vue'
import Wireless from './components/Wireless/index.vue'
import Terminal from './components/Terminal/index.vue'
import { isIPWithPort, sleep } from '@/utils/index.js'
export default {
@ -164,6 +173,7 @@ export default {
Wireless,
ControlBar,
Remark,
Terminal,
},
data() {
return {

View File

@ -24,6 +24,7 @@
"device.name": "设备名称",
"device.remark": "备注",
"device.permission.error": "设备可能未授权成功请重新插拔设备并点击允许USB调试",
"device.terminal.name": "终端调试",
"device.wireless.name": "无线",
"device.wireless.mode": "无线模式",

View File

@ -36,3 +36,26 @@
@apply h-full overflow-auto;
}
}
.el-dialog-flex {
height: calc(100% - 20vh - 8px) !important;
@apply flex flex-col !my-[10vh];
.el-dialog__header,
.el-dialog__footer {
@apply flex-none;
}
.el-dialog__body {
@apply flex-1 !h-0;
}
}
.el-dialog-headless {
.el-dialog__header,
.el-dialog__footer {
@apply !hidden;
}
.el-dialog__body {
@apply !p-0;
}
}

View File

@ -10,6 +10,8 @@ import useUnoCSS from 'unocss/vite'
import useSvg from 'vite-svg-loader'
import useI18n from '@intlify/unplugin-vue-i18n/vite'
import postcssConfig from './postcss.config.mjs'
const merge = (config, { command = '' } = {}) =>
mergeConfig(
{
@ -58,5 +60,8 @@ export default params =>
]),
useRenderer(),
],
css: {
postcss: postcssConfig,
},
}),
)