mirror of
https://github.com/viarotel-org/escrcpy.git
synced 2025-01-19 01:24:12 +01:00
feat: 🎉 Add Terminal Debugging
This commit is contained in:
parent
4cc6617b9f
commit
fdf40c70e8
@ -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
7
postcss.config.mjs
Normal file
@ -0,0 +1,7 @@
|
||||
import nested from 'postcss-nested'
|
||||
import postcssScss from 'postcss-scss'
|
||||
|
||||
export default {
|
||||
parser: postcssScss,
|
||||
plugins: [nested],
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
@ -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>
|
23
src/components/Device/components/Terminal/index.vue
Normal file
23
src/components/Device/components/Terminal/index.vue
Normal 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>
|
@ -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 {
|
||||
|
@ -24,6 +24,7 @@
|
||||
"device.name": "设备名称",
|
||||
"device.remark": "备注",
|
||||
"device.permission.error": "设备可能未授权成功,请重新插拔设备并点击允许USB调试",
|
||||
"device.terminal.name": "终端调试",
|
||||
|
||||
"device.wireless.name": "无线",
|
||||
"device.wireless.mode": "无线模式",
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user