perf: ♻️ Optimize terminal performance

This commit is contained in:
viarotel 2023-11-14 10:53:58 +08:00
parent 5b6b8d1150
commit d3afc4ba62
9 changed files with 191 additions and 43 deletions

View File

@ -1,5 +1,5 @@
import util from 'node:util'
import child_process from 'node:child_process'
import { exec as _exec, spawn } from 'node:child_process'
import path from 'node:path'
import fs from 'node:fs'
import dayjs from 'dayjs'
@ -8,7 +8,7 @@ import appStore from '@electron/helpers/store.js'
import { adbPath } from '@electron/configs/index.js'
import { uniq } from 'lodash-es'
const exec = util.promisify(child_process.exec)
const exec = util.promisify(_exec)
let client = null
@ -37,7 +37,66 @@ appStore.onDidChange('common.adbPath', async (value, oldValue) => {
client = Adb.createClient({ bin: value || adbPath })
})
const shell = async command => exec(`${adbPath} ${command}`)
const shell = async (command) => {
const execPath = appStore.get('common.adbPath') || adbPath
return exec(`${execPath} ${command}`, {
env: { ...process.env },
shell: true,
})
}
const spawnShell = async (command, { stdout, stderr } = {}) => {
const spawnPath = appStore.get('common.adbPath') || adbPath
const args = command.split(' ')
console.log('adb.spawnShell.spawnPath', spawnPath)
console.log('adb.spawnShell.args', args)
const spawnProcess = spawn(`"${spawnPath}"`, args, {
env: { ...process.env },
shell: true,
encoding: 'utf8',
})
spawnProcess.stdout.on('data', (data) => {
const stringData = data.toString()
console.log('spawnProcess.stdout.data:', stringData)
if (stdout) {
stdout(stringData, spawnProcess)
}
})
let lastStderr = ''
spawnProcess.stderr.on('data', (data) => {
const stringData = data.toString()
lastStderr = stringData
console.error('spawnProcess.stderr.data:', stringData)
if (stderr) {
stderr(stringData, spawnProcess)
}
})
return new Promise((resolve, reject) => {
spawnProcess.on('close', (code) => {
if (code === 0) {
resolve()
}
else {
reject(new Error(lastStderr || `Command failed with code ${code}`))
}
})
spawnProcess.on('error', (err) => {
reject(err)
})
})
}
const getDevices = async () => client.listDevicesWithPaths()
const deviceShell = async (id, command) => {
const res = await client.getDevice(id).shell(command).then(Adb.util.readAll)
@ -193,6 +252,7 @@ export default () => {
return {
shell,
spawnShell,
getDevices,
deviceShell,
kill,

View File

@ -46,9 +46,12 @@ const shell = async (command, { debug = false, stdout, stderr } = {}) => {
}
})
let lastStderr = ''
gnirehtetProcess.stderr.on('data', (data) => {
const stringData = data.toString()
lastStderr = stringData
if (debug) {
console.error(`${command}.gnirehtet.process.stderr.data:`, stringData)
}

View File

@ -33,9 +33,12 @@ const shell = async (command, { stdout, stderr } = {}) => {
}
})
let lastStderr = ''
scrcpyProcess.stderr.on('data', (data) => {
const stringData = data.toString()
lastStderr = stringData
console.error('scrcpyProcess.stderr.data:', stringData)
if (stderr) {
@ -49,7 +52,7 @@ const shell = async (command, { stdout, stderr } = {}) => {
resolve()
}
else {
reject(new Error(`Command failed with code ${code}`))
reject(new Error(lastStderr || `Command failed with code ${code}`))
}
})

View File

@ -0,0 +1,44 @@
import { debounce } from 'lodash-es'
import { createStderr, createStdout, textFormatter } from 'vue-command'
import { useFixCursor } from './helper.js'
const $adb = window.adbkit
export function useAdb({ vShell, history, loading } = {}) {
const adb = async (args) => {
loading.value = true
const command = args.slice(1).join(' ')
const appendToHistory = debounce(vShell.value.appendToHistory, 500)
let stdoutText = ''
let stderrText = ''
$adb.spawnShell(command, {
stdout(text) {
loading.value = false
stdoutText += text
useFixCursor(history)
appendToHistory(createStdout(stdoutText))
},
stderr(text) {
loading.value = false
stderrText += text
useFixCursor(history)
appendToHistory(createStderr(stderrText))
},
})
return textFormatter('Waiting...')
}
return {
adb,
}
}

View File

@ -1,15 +1,9 @@
import { debounce } from 'lodash-es'
import { createStderr, createStdout, textFormatter } from 'vue-command'
import { useFixCursor } from './helper.js'
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
@ -26,7 +20,7 @@ export function useGnirehtet({ vShell, history, loading } = {}) {
stdoutText += text
fixCursor(history)
useFixCursor(history)
appendToHistory(createStdout(stdoutText))
},
@ -35,13 +29,13 @@ export function useGnirehtet({ vShell, history, loading } = {}) {
stderrText += text
fixCursor(history)
useFixCursor(history)
appendToHistory(createStderr(stderrText))
},
})
return textFormatter('Loading...')
return textFormatter('Waiting...')
}
return {

View File

@ -0,0 +1,8 @@
import { textFormatter } from 'vue-command'
export function useFixCursor(history) {
const length = history.value.length
if (history.value[length - 1]?.__name === 'VueCommandQuery') {
history.value.splice(length - 1, 1, textFormatter('Waiting...'))
}
}

View File

@ -1,15 +1,9 @@
import { debounce } from 'lodash-es'
import { createStderr, createStdout, textFormatter } from 'vue-command'
import { useFixCursor } from './helper.js'
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
@ -22,11 +16,12 @@ export function useScrcpy({ vShell, history, loading } = {}) {
let stderrText = ''
$scrcpy.shell(command, {
stdout(text) {
console.log('history', history)
loading.value = false
stdoutText += text
fixCursor(history)
useFixCursor(history)
appendToHistory(createStdout(stdoutText))
},
@ -35,13 +30,13 @@ export function useScrcpy({ vShell, history, loading } = {}) {
stderrText += text
fixCursor(history)
useFixCursor(history)
appendToHistory(createStderr(stderrText))
},
})
return textFormatter('Loading...')
return textFormatter('Waiting...')
}
return {

View File

@ -3,32 +3,34 @@
v-model="visible"
width="80%"
:close-on-click-modal="false"
:close-on-press-escape="false"
class="overflow-hidden rounded-xl el-dialog-headless"
:close-on-press-escape="true"
class="overflow-hidden rounded-md el-dialog-headless dark:border dark:border-gray-700"
@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"
class="cursor-pointer absolute top-2 right-2 w-8 h-8 flex items-center justify-center hover:bg-gray-200 dark:text-gray-200 dark:hover:bg-gray-700 !active:bg-red-600 !active:text-gray-200 rounded-md"
@click="hide"
>
<CloseBold />
</el-icon>
<VueCommand
v-if="visible"
v-if="renderShell"
:ref="(value) => (vShell = value)"
v-model:history="history"
:dispatched-queries="dispatchedQueries"
:commands="commands"
hide-bar
show-help
help-text="Type in help"
:help-timeout="3500"
:help-timeout="3000"
:invert="invert"
class=""
:dispatched-queries="dispatchedQueries"
@update:dispatched-queries="onDispatchedQueriesUpdate"
>
<template #prompt>
<div class="flex items-center pr-2">
<span class="">escrcpy~$</span>
<span class="">Escrcpy~$</span>
</div>
</template>
</VueCommand>
@ -36,7 +38,7 @@
</template>
<script>
import { ref } from 'vue'
import { ref, shallowRef } from 'vue'
import VueCommand, {
createQuery,
createStdout,
@ -53,8 +55,9 @@ export default {
},
setup() {
const vShell = ref(null)
const history = ref([createQuery()])
const history = shallowRef([createQuery()])
const loading = ref(false)
const renderShell = ref(false)
const dispatchedQueries = ref(new Set([]))
const { adb } = useAdb({ vShell, history, loading })
@ -76,7 +79,17 @@ export default {
return createStdout(listFormatter('Supported Commands:', ...commandList))
}
dispatchedQueries.value = new Set(Object.keys(commands.value))
dispatchedQueries.value = new Set([
...(window.appStore.get('terminal.dispatchedQueries') || []),
...Object.keys(commands.value),
])
const onOpen = () => {
console.log('vShell.value', vShell.value)
console.log('history.value', history.value)
renderShell.value = true
}
return {
vShell,
@ -84,6 +97,8 @@ export default {
history,
commands,
dispatchedQueries,
onOpen,
renderShell,
}
},
data() {
@ -91,22 +106,44 @@ export default {
visible: false,
}
},
computed: {
invert() {
return this.$store.theme.value !== 'dark'
},
},
watch: {
'vShell.signals': {
handler(value) {
console.log('vShell.signals.value', value)
value.off('SIGINT')
value.on('SIGINT', () => {
console.log('vShell.signals.on.SIGINT')
this.onCtrlC()
})
},
},
},
methods: {
show() {
this.visible = true
},
hide() {
this.visible = false
},
async onOpen() {
console.log('vShell', this.vShell)
this.vShell.signals.off('SIGINT')
onDispatchedQueriesUpdate(value) {
console.log('onDispatchedQueriesUpdate.value', value)
this.vShell.signals.on('SIGINT', () => {
console.log('vShell.signals.on.SIGINT')
this.$gnirehtet.shell('stop')
})
this.$appStore.set('terminal.dispatchedQueries', Array.from(value))
this.dispatchedQueries = value
},
onCtrlC() {
window.gnirehtet.shell('stop')
},
},
}

View File

@ -282,15 +282,21 @@ export default {
args: this.scrcpyArgs(row.id, { isRecord: true }),
stdout: this.onStdout,
})
this.handleRecordSuccess(savePath)
}
catch (error) {
console.warn(error)
if (error.message) {
this.$message.warning(error.message)
}
this.handleReset()
}
row.$recordLoading = false
},
async handleRecordSuccess(savePath) {
try {
await this.$confirm(
this.$t('device.record.success.message'),
@ -311,8 +317,6 @@ export default {
catch (error) {
console.warn(error)
}
row.$recordLoading = false
},
async handleMirror(row) {
row.$loading = true