perf: 🚀 优化页面查找工具性能

This commit is contained in:
viarotel 2024-05-14 19:16:46 +08:00
parent fcf8269e0e
commit 8587977627
49 changed files with 189 additions and 190 deletions

3
components.d.ts vendored
View File

@ -28,8 +28,7 @@ declare module 'vue' {
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElIconArrowDownBold: typeof import('@element-plus/icons-vue')['ArrowDownBold']
ElIconArrowUpBold: typeof import('@element-plus/icons-vue')['ArrowUpBold']
ElIconSearch: typeof import('@element-plus/icons-vue')['Search']
ElInput: typeof import('element-plus/es')['ElInput']
ElLink: typeof import('element-plus/es')['ElLink']
ElOption: typeof import('element-plus/es')['ElOption']

View File

@ -2,7 +2,7 @@
<div class="flex flex-col absolute inset-0 h-full overflow-hidden">
<div class="py-4 px-4 flex items-center flex-none">
<a class="block" :href="escrcpyURL" target="_blank">
<img src="@electron/resources/build/logo.png" class="h-9" alt="" />
<img src="#electron/resources/build/logo.png" class="h-9" alt="" />
</a>
<div class="pl-2 text-sm">
Escrcpy Copilot

View File

@ -25,10 +25,10 @@
</template>
<script>
import logoURL from '@electron/resources/build/logo.png'
import userURL from '@/assets/user.png'
import mobileURL from '@/assets/mobile.png'
import computerURL from '@/assets/computer.png'
import logoURL from '#electron/resources/build/logo.png'
import userURL from '#/assets/user.png'
import mobileURL from '#/assets/mobile.png'
import computerURL from '#/assets/computer.png'
export default {
props: {

View File

@ -2,14 +2,14 @@ import { createApp, toRaw } from 'vue'
import App from './App.vue'
import { mockAPI } from './utils/index.js'
import ws from './utils/ws.js'
import { i18n, t } from '@/locales/index.js'
import plugins from '@/plugins/index.js'
import icons from '@/icons/index.js'
import { i18n, t } from '#/locales/index.js'
import plugins from '#/plugins/index.js'
import icons from '#/icons/index.js'
import { replaceIP, restoreIP } from '@/utils/index.js'
import { replaceIP, restoreIP } from '#/utils/index.js'
import 'virtual:uno.css'
import '@/styles/index.js'
import '#/styles/index.js'
const app = createApp(App)

View File

@ -1,4 +1,4 @@
import { extraResolve } from '@electron/helpers/index.js'
import { extraResolve } from '#electron/helpers/index.js'
export const getAdbPath = () => {
switch (process.platform) {

View File

@ -1,5 +1,5 @@
import { extraResolve } from '@electron/helpers/index.js'
import which from 'which'
import { extraResolve } from '#electron/helpers/index.js'
export const getGnirehtetPath = () => {
switch (process.platform) {

View File

@ -1,6 +1,6 @@
import { resolve } from 'node:path'
import { buildResolve, extraResolve } from '@electron/helpers/index.js'
import { buildResolve, extraResolve } from '#electron/helpers/index.js'
export { adbPath } from './android-platform-tools/index.js'

View File

@ -1,5 +1,5 @@
import { extraResolve } from '@electron/helpers/index.js'
import which from 'which'
import { extraResolve } from '#electron/helpers/index.js'
export const getScrcpyPath = () => {
switch (process.platform) {

View File

@ -1,10 +1,7 @@
import fs from 'fs-extra'
import { dialog, ipcMain, shell } from 'electron'
import themeHandles from './theme/index.js'
export default (mainWindow) => {
themeHandles(mainWindow)
ipcMain.handle(
'show-open-dialog',
async (_, { preset = '', ...options } = {}) => {

View File

@ -1,9 +1,10 @@
import { app, ipcMain } from 'electron'
import shortcuts from './shortcuts/index.js'
import updater from './updater/index.js'
import handles from './handles/index.js'
import tray from './tray/index.js'
// import search from './search/index.js'
import theme from './theme/index.js'
export default (mainWindow) => {
ipcMain.on('restart-app', () => {
@ -15,5 +16,6 @@ export default (mainWindow) => {
handles(mainWindow)
updater(mainWindow)
tray(mainWindow)
// search(mainWindow)
theme(mainWindow)
shortcuts(mainWindow)
}

View File

@ -1,29 +0,0 @@
import { globalShortcut, ipcMain } from 'electron'
import { FindInPageManager } from './helper.js'
export default (mainWindow) => {
mainWindow.on('focus', () => {
globalShortcut.register('CommandOrControl+F', (event) => {
mainWindow.webContents.send('focus-on-search')
})
})
mainWindow.on('blur', () => {
globalShortcut.unregister('CommandOrControl+F')
})
const findInPageManager = new FindInPageManager(mainWindow.webContents)
ipcMain.handle('findInPageStart', async (event, args = {}) => {
return findInPageManager.start(args)
})
ipcMain.handle('findInPageNext', async (event, args = {}) => {
return findInPageManager.next(args)
})
ipcMain.handle('findInPagePrev', async (event, args = {}) => {
return findInPageManager.prev(args)
})
ipcMain.handle('findInPageStop', async (event, args = {}) => {
return findInPageManager.stop(args)
})
}

View File

@ -0,0 +1,13 @@
import { globalShortcut } from 'electron'
export default (mainWindow) => {
mainWindow.on('focus', () => {
globalShortcut.register('CommandOrControl+F', (event) => {
mainWindow.webContents.send('focus-on-search')
})
})
mainWindow.on('blur', () => {
globalShortcut.unregister('CommandOrControl+F')
})
}

View File

@ -1,7 +1,7 @@
import { Menu, Tray, app, dialog } from 'electron'
import { trayPath } from '@electron/configs/index.js'
import appStore from '@electron/helpers/store.js'
import { executeI18n } from '@electron/helpers/index.js'
import { trayPath } from '#electron/configs/index.js'
import appStore from '#electron/helpers/store.js'
import { executeI18n } from '#electron/helpers/index.js'
export default (mainWindow) => {
const t = value => executeI18n(mainWindow, value)

View File

@ -1,7 +1,7 @@
import { app, ipcMain } from 'electron'
import { is } from '@electron-toolkit/utils'
import electronUpdater from 'electron-updater'
import { devPublishPath } from '@electron/configs/index.js'
import { devPublishPath } from '#electron/configs/index.js'
const { autoUpdater } = electronUpdater

View File

@ -4,9 +4,9 @@ import path from 'node:path'
import fs from 'node:fs'
import dayjs from 'dayjs'
import { Adb } from '@devicefarmer/adbkit'
import appStore from '@electron/helpers/store.js'
import { adbPath } from '@electron/configs/index.js'
import { uniq } from 'lodash-es'
import appStore from '#electron/helpers/store.js'
import { adbPath } from '#electron/configs/index.js'
const exec = util.promisify(_exec)

View File

@ -1,10 +1,10 @@
import { spawn } from 'node:child_process'
import appStore from '@electron/helpers/store.js'
import appStore from '#electron/helpers/store.js'
import {
adbPath,
gnirehtetApkPath,
gnirehtetPath,
} from '@electron/configs/index.js'
} from '#electron/configs/index.js'
const appDebug = appStore.get('common.debug') || false

View File

@ -1,15 +1,14 @@
import path from 'node:path'
import appLog from '@electron/helpers/log.js'
import '@electron/helpers/console.js'
import store from '@electron/helpers/store.js'
import * as configs from '@electron/configs/index.js'
import '#electron/helpers/console.js'
import electron from './electron/index.js'
import adbkit from './adbkit/index.js'
import scrcpy from './scrcpy/index.js'
import gnirehtet from './gnirehtet/index.js'
import search from './search/index.js'
import * as configs from '#electron/configs/index.js'
import store from '#electron/helpers/store.js'
import appLog from '#electron/helpers/log.js'
export default {
init(expose) {
@ -31,5 +30,7 @@ export default {
expose('scrcpy', scrcpy({ adbkit: adbkitExecute }))
expose('gnirehtet', gnirehtet({ adbkit: adbkitExecute }))
expose('findInPageModal', search())
},
}

View File

@ -1,5 +1,5 @@
import log from 'electron-log/main.js'
import { createProxy } from '@electron/helpers/index'
import { createProxy } from '#electron/helpers/index'
const levels = Object.keys(log.functions)

View File

@ -1,8 +1,8 @@
import util from 'node:util'
import { exec as _exec, spawn } from 'node:child_process'
import appStore from '@electron/helpers/store.js'
import { adbPath, scrcpyPath } from '@electron/configs/index.js'
import { replaceIP, sleep } from '@renderer/utils/index.js'
import { replaceIP, sleep } from '#renderer/utils/index.js'
import appStore from '#electron/helpers/store.js'
import { adbPath, scrcpyPath } from '#electron/configs/index.js'
let adbkit

View File

@ -0,0 +1,57 @@
import remote from '@electron/remote'
import { FindInPage } from 'electron-find-in-page'
import { primaryColor } from '#renderer/configs/index.js'
export default () => {
const theme = {
isDark: false,
}
let findInPage = create()
async function open({ isDark = false } = {}) {
if (theme.isDark !== isDark) {
theme.isDark = isDark
await update()
}
return findInPage.openFindWindow()
}
function close() {
return findInPage.closeFindWindow()
}
async function update() {
try {
await findInPage.destroy()
}
catch (error) {
console.warn('error', error.message)
}
findInPage = create()
}
function create() {
return new FindInPage(remote.getCurrentWebContents(), {
preload: true,
inputFocusColor: primaryColor,
...(theme.isDark
? {
boxShadowColor: '#4C4D4F',
boxBgColor: '#262727',
inputColor: '#CFD3DC',
inputBgColor: '#141414',
textColor: '#CFD3DC',
textHoverBgColor: '#4C4D4F',
}
: {}),
})
}
return {
open,
close,
}
}

View File

@ -1,7 +1,6 @@
import log from '@electron/helpers/log.js'
import appStore from './store.js'
import { createProxy } from './index.js'
import log from '#electron/helpers/log.js'
const debug = appStore.get('common.debug') || false

View File

@ -1,6 +1,6 @@
import { shell } from 'electron'
import log from 'electron-log/main.js'
import { createProxy } from '@electron/helpers/index'
import { createProxy } from '#electron/helpers/index'
log.transports.console.level = false

View File

@ -5,6 +5,7 @@ import { fileURLToPath } from 'node:url'
import { BrowserWindow, app, shell } from 'electron'
import { electronApp, optimizer } from '@electron-toolkit/utils'
import contextMenu from 'electron-context-menu'
import remote from '@electron/remote/main'
// process.js 必须位于非依赖项的顶部
import './helpers/process.js'
@ -91,6 +92,9 @@ function createWindow() {
backgroundColor: 'white',
})
remote.enable(mainWindow.webContents)
remote.initialize()
mainWindow.on('ready-to-show', () => {
mainWindow.show()
})

View File

@ -2,11 +2,11 @@
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@copilot/*": ["copilot/*"],
"@root/*": ["*"],
"@electron/*": ["electron/*"],
"@renderer/*": ["src/*"]
"#/*": ["src/*"],
"#copilot/*": ["copilot/*"],
"#root/*": ["*"],
"#electron/*": ["electron/*"],
"#renderer/*": ["src/*"]
}
},
"exclude": ["node_modules", "dist", "dist-electron", "dist-release"],

View File

@ -21,7 +21,6 @@
"prepare": "husky install"
},
"dependencies": {
"electron-find-in-page": "^1.0.8",
"electron-in-page-search": "^1.3.2",
"vue": "^3.4.26"
},
@ -30,6 +29,7 @@
"@devicefarmer/adbkit": "^3.2.6",
"@electron-toolkit/preload": "^3.0.1",
"@electron-toolkit/utils": "^3.0.0",
"@electron/remote": "^2.1.2",
"@hono/node-server": "^1.11.1",
"@intlify/unplugin-vue-i18n": "^4.0.0",
"@unocss/reset": "^0.59.4",
@ -40,6 +40,7 @@
"electron": "^30.0.3",
"electron-builder": "^24.13.3",
"electron-context-menu": "^4.0.0",
"electron-find-in-page": "^1.0.8",
"electron-log": "^5.1.2",
"electron-store": "^9.0.0",
"electron-updater": "^6.1.8",

View File

@ -3,9 +3,9 @@
<el-tabs
v-model="activeTab"
class="el-tabs-flex"
addable
@tab-change="onTabChange"
>
<!-- addable -->
<template #add-icon>
<AppSearch />
</template>
@ -35,8 +35,8 @@ import Preference from './components/Preference/index.vue'
import About from './components/About/index.vue'
import AppSearch from './components/AppSearch/index.vue'
import { useThemeStore } from '@/store/theme/index.js'
import { usePreferenceStore } from '@/store/preference/index.js'
import { useThemeStore } from '#/store/theme/index.js'
import { usePreferenceStore } from '#/store/preference/index.js'
const tabsModel = ref([
{
@ -57,6 +57,8 @@ const tabsModel = ref([
])
const activeTab = ref('Device')
provide('activeTab', activeTab)
const renderTab = ref('')
const rendered = ref(true)
const renderSign = ref(false)
@ -78,10 +80,10 @@ async function showTips() {
ElMessageBox.alert(
`<div>
${window.t('dependencies.lack.content', {
name: '<a class="hover:underline text-primary-500" href="https://github.com/Genymobile/scrcpy" target="_blank">scrcpy</a>',
})}
<div>`,
${window.t('dependencies.lack.content', {
name: '<a class="hover:underline text-primary-500" href="https://github.com/Genymobile/scrcpy" target="_blank">scrcpy</a>',
})}
<div>`,
window.t('dependencies.lack.title'),
{
dangerouslyUseHTMLString: true,
@ -138,5 +140,9 @@ defineExpose({
.el-tabs__new-tab {
@apply !absolute !inset-y-0 !right-0 !border-none;
}
.el-tabs__nav-wrap:after {
@apply !h-px;
}
}
</style>

View File

@ -1,7 +1,7 @@
<template>
<div class="flex flex-col items-center justify-center h-full -mt-8">
<a class="block" :href="escrcpyURL" target="_blank">
<img src="@electron/resources/build/logo.png" class="h-48" alt="" />
<img src="#electron/resources/build/logo.png" class="h-48" alt="" />
</a>
<div class="pt-4 text-xl text-center italic text-gray-700 dark:text-white">

View File

@ -1,93 +1,40 @@
<template>
<div class="absolute -bottom-[5px] right-0 z-10">
<el-input
ref="elInputRef"
v-model="keyword"
prefix-icon="Search"
:placeholder="$t('common.search')"
clearable
class="transition-all overflow-hidden"
:class="activated || keyword ? '!w-96' : '!w-26'"
@focus="onFocus"
@blur="onBlur"
@change="onChange"
@clear="onClear"
<div class="relative z-10">
<el-button
class=""
circle
size="small"
:title="$t('common.search')"
@click="openPageModal"
>
<template v-if="keyword" #append>
<div class="flex flex-col size-full absolute inset-0 justify-center">
<div
class="flex-1 h-0 flex items-end justify-center apply-search-button"
@click="handlePrev"
>
<el-icon size="12">
<ElIconArrowUpBold />
</el-icon>
</div>
<div class="h-px w-full bg-gray-200 dark:bg-gray-600"></div>
<div
class="flex-1 h-0 flex items-start justify-center apply-search-button"
@click="handleNext"
>
<el-icon size="12">
<ElIconArrowDownBold />
</el-icon>
</div>
</div>
</template>
</el-input>
<el-icon size="12">
<ElIconSearch />
</el-icon>
</el-button>
</div>
</template>
<script setup>
const elInputRef = ref(null)
import { useThemeStore } from '#/store'
const keyword = ref('')
const activated = ref(false)
const themeStore = useThemeStore()
function handleFocus() {
elInputRef.value.focus()
}
const activeTab = inject('activeTab')
async function onFocus() {
activated.value = true
}
function onBlur() {
activated.value = false
}
function onClear() {
window.electron.ipcRenderer.invoke('findInPageStop')
}
async function onChange(value) {
if (!value) {
onClear()
return false
}
window.electron.ipcRenderer.invoke('findInPageStart', {
text: value,
})
}
function handlePrev() {
window.electron.ipcRenderer.invoke('findInPagePrev')
}
function handleNext() {
window.electron.ipcRenderer.invoke('findInPageNext')
}
watch([() => themeStore.value, () => activeTab.value], () => {
closePageModal()
})
window.electron.ipcRenderer.on('focus-on-search', (event, ret) => {
handleFocus()
openPageModal()
})
function openPageModal() {
window.findInPageModal.open({ isDark: themeStore.isDark })
}
function closePageModal() {
window.findInPageModal.close()
}
</script>
<style lang="postcss">
.apply-search-button {
@apply hover:bg-gray-200 dark:hover:bg-gray-700 !active:bg-gray-300 !dark:active:bg-gray-600 !active:text-primary-500;
}
</style>
<style lang="postcss"></style>

View File

@ -5,7 +5,7 @@
</template>
<script>
import LoadingIcon from '@/components/Device/components/LoadingIcon/index.vue'
import LoadingIcon from '#/components/Device/components/LoadingIcon/index.vue'
export default {
props: {

View File

@ -15,7 +15,7 @@
</template>
<script>
import { sleep } from '@/utils'
import { sleep } from '#/utils'
export default {
props: {

View File

@ -21,7 +21,7 @@
</template>
<script>
import { sleep } from '@/utils'
import { sleep } from '#/utils'
export default {
props: {

View File

@ -18,7 +18,7 @@
</template>
<script>
import { sleep } from '@/utils'
import { sleep } from '#/utils'
export default {
props: {

View File

@ -5,7 +5,7 @@
</template>
<script>
import LoadingIcon from '@/components/Device/components/LoadingIcon/index.vue'
import LoadingIcon from '#/components/Device/components/LoadingIcon/index.vue'
export default {
props: {

View File

@ -12,7 +12,7 @@
</template>
<script>
import { sleep } from '@/utils'
import { sleep } from '#/utils'
export default {
props: {

View File

@ -3,7 +3,7 @@
</template>
<script>
import { sleep } from '@/utils'
import { sleep } from '#/utils'
export default {
props: {

View File

@ -3,7 +3,7 @@
</template>
<script>
import { sleep } from '@/utils'
import { sleep } from '#/utils'
export default {
props: {

View File

@ -3,7 +3,7 @@
</template>
<script>
import { sleep } from '@/utils'
import { sleep } from '#/utils'
export default {
props: {

View File

@ -30,7 +30,7 @@
</template>
<script>
import { sleep } from '@/utils'
import { sleep } from '#/utils'
export default {
inheritAttrs: false,

View File

@ -120,7 +120,7 @@ import MirrorAction from './components/MirrorAction/index.vue'
import MoreDropdown from './components/MoreDropdown/index.vue'
import WirelessAction from './components/WirelessAction/index.vue'
import { isIPWithPort, sleep } from '@/utils/index.js'
import { isIPWithPort, sleep } from '#/utils/index.js'
export default {
components: {

View File

@ -18,7 +18,7 @@
</template>
<script>
import { i18n } from '@/locales/index.js'
import { i18n } from '#/locales/index.js'
export default {
props: {

View File

@ -207,8 +207,8 @@ import AudioCodecSelect from './components/AudioCodecSelect/index.vue'
import DisplaySelect from './components/DisplaySelect/index.vue'
import KeyboardInjectSelect from './components/KeyboardInjectSelect/index.vue'
import { usePreferenceStore } from '@/store/index.js'
import LoadingIcon from '@/components/Device/components/LoadingIcon/index.vue'
import { usePreferenceStore } from '#/store/index.js'
import LoadingIcon from '#/components/Device/components/LoadingIcon/index.vue'
export default {
components: {

1
src/configs/index.js Normal file
View File

@ -0,0 +1 @@
export const primaryColor = '#028d71'

View File

@ -1,4 +1,4 @@
import '@/utils/console.js'
import '#/utils/console.js'
import { createApp, toRaw } from 'vue'
import App from './App.vue'
@ -10,7 +10,7 @@ import icons from './icons/index.js'
import { i18n, t } from './locales/index.js'
import { replaceIP, restoreIP } from '@/utils/index.js'
import { replaceIP, restoreIP } from '#/utils/index.js'
import 'virtual:uno.css'
import './styles/index.js'

View File

@ -1,8 +1,8 @@
import { defineStore } from 'pinia'
import dayjs from 'dayjs'
import { t } from '@/locales/index.js'
import { isIPWithPort, replaceIP } from '@/utils/index.js'
import { t } from '#/locales/index.js'
import { isIPWithPort, replaceIP } from '#/utils/index.js'
const $appStore = window.appStore

View File

@ -11,7 +11,7 @@ import {
setStoreData,
} from './helpers/index.js'
import { replaceIP, restoreIP } from '@/utils/index.js'
import { replaceIP, restoreIP } from '#/utils/index.js'
const { adbPath, scrcpyPath, gnirehtetPath } = window.electron?.configs || {}

View File

@ -1,10 +1,11 @@
import { defineConfig, presetWind } from 'unocss'
import transformerDirectives from '@unocss/transformer-directives'
import { presetShades } from '@viarotel-org/unocss-preset-shades'
import { primaryColor } from './src/configs/index.js'
const presetMain = presetWind()
const presets = [presetMain, presetShades('#028d71')]
const presets = [presetMain, presetShades(primaryColor)]
export default defineConfig({
// @ts-expect-error

View File

@ -18,9 +18,9 @@ const merge = (config, { command = '' } = {}) =>
{
resolve: {
alias: {
'@root': resolve(),
'@electron': resolve('electron'),
'@renderer': resolve('src'),
'#root': resolve(),
'#electron': resolve('electron'),
'#renderer': resolve('src'),
},
},
plugins: [...(command === 'serve' ? [notBundle()] : [])],
@ -41,9 +41,9 @@ export default args =>
},
resolve: {
alias: {
'@': resolve('src'),
'@copilot': resolve('copilot'),
'@electron': resolve('electron'),
'#': resolve('src'),
'#copilot': resolve('copilot'),
'#electron': resolve('electron'),
},
},
plugins: [