Merge branch 'next'
@ -1,9 +0,0 @@
|
|||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
charset = utf-8
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
end_of_line = lf
|
|
||||||
insert_final_newline = true
|
|
||||||
trim_trailing_whitespace = true
|
|
@ -1,12 +1,10 @@
|
|||||||
/* eslint-env node */
|
|
||||||
require('@rushstack/eslint-patch/modern-module-resolution')
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
extends: ['@electron-toolkit', '@viarotel-org'],
|
extends: ['@viarotel-org'],
|
||||||
rules: {
|
rules: {
|
||||||
'no-unused-vars': 'off',
|
'no-unused-vars': 'off',
|
||||||
'eqeqeq': 'off',
|
'eqeqeq': 'off',
|
||||||
'prefer-promise-reject-errors': 'off',
|
'prefer-promise-reject-errors': 'off',
|
||||||
'antfu/top-level-function': 'off',
|
'antfu/top-level-function': 'off',
|
||||||
|
'import/default': 'off',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
23
.github/workflows/release-assets.yml
vendored
@ -48,16 +48,17 @@ jobs:
|
|||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
with:
|
with:
|
||||||
draft: true
|
draft: true
|
||||||
|
prerelease: true
|
||||||
files: |
|
files: |
|
||||||
dist/*.exe
|
dist-release/*.exe
|
||||||
dist/*.zip
|
dist-release/*.zip
|
||||||
dist/*.dmg
|
dist-release/*.dmg
|
||||||
dist/*.AppImage
|
dist-release/*.AppImage
|
||||||
dist/*.snap
|
dist-release/*.snap
|
||||||
dist/*.deb
|
dist-release/*.deb
|
||||||
dist/*.rpm
|
dist-release/*.rpm
|
||||||
dist/*.tar.gz
|
dist-release/*.tar.gz
|
||||||
dist/*.yml
|
dist-release/*.yml
|
||||||
dist/*.blockmap
|
dist-release/*.blockmap
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
|
26
.gitignore
vendored
@ -1,4 +1,26 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
node_modules
|
node_modules
|
||||||
dist
|
dist
|
||||||
out
|
dist-ssr
|
||||||
*.log*
|
dist-electron
|
||||||
|
dist-release
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
2
.npmrc
@ -1,4 +1,4 @@
|
|||||||
registry=https://registry.npmmirror.com/
|
registry=https://registry.npmmirror.com/
|
||||||
ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/
|
ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/
|
||||||
ELECTRON_BUILDER_BINARIES_MIRROR=https://npmmirror.com/mirrors/electron-builder-binaries/
|
ELECTRON_BUILDER_BINARIES_MIRROR=https://npmmirror.com/mirrors/electron-builder-binaries/
|
||||||
shamefully-hoist=true
|
shamefully-hoist=true
|
@ -1,6 +0,0 @@
|
|||||||
out
|
|
||||||
dist
|
|
||||||
pnpm-lock.yaml
|
|
||||||
LICENSE.md
|
|
||||||
jsconfig.json
|
|
||||||
jsconfig.*.json
|
|
@ -1,4 +0,0 @@
|
|||||||
singleQuote: true
|
|
||||||
semi: false
|
|
||||||
printWidth: 100
|
|
||||||
trailingComma: none
|
|
2
.vscode/extensions.json
vendored
@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"recommendations": ["dbaeumer.vscode-eslint"]
|
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
|
||||||
}
|
}
|
||||||
|
39
.vscode/launch.json
vendored
@ -1,39 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"name": "Debug Main Process",
|
|
||||||
"type": "node",
|
|
||||||
"request": "launch",
|
|
||||||
"cwd": "${workspaceRoot}",
|
|
||||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite",
|
|
||||||
"windows": {
|
|
||||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite.cmd"
|
|
||||||
},
|
|
||||||
"runtimeArgs": ["--sourcemap"],
|
|
||||||
"env": {
|
|
||||||
"REMOTE_DEBUGGING_PORT": "9222"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Debug Renderer Process",
|
|
||||||
"port": 9222,
|
|
||||||
"request": "attach",
|
|
||||||
"type": "chrome",
|
|
||||||
"webRoot": "${workspaceFolder}/src/renderer",
|
|
||||||
"timeout": 60000,
|
|
||||||
"presentation": {
|
|
||||||
"hidden": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"compounds": [
|
|
||||||
{
|
|
||||||
"name": "Debug All",
|
|
||||||
"configurations": ["Debug Main Process", "Debug Renderer Process"],
|
|
||||||
"presentation": {
|
|
||||||
"order": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
51
README.md
@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
📱 使用图形界面的 Scrcpy 显示和控制您的 Android 设备,由 Electron 驱动
|
📱 使用图形界面的 Scrcpy 显示和控制您的 Android 设备,由 Electron 驱动
|
||||||
|
|
||||||
📱 Use Scrcpy with a graphical interface to display and control your Android device, driven by Electron
|
|
||||||
|
|
||||||
<div style="display:flex;">
|
<div style="display:flex;">
|
||||||
<img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/570065a5683b4cf7af9cfa9743c06ab4~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1360&h=693&s=140693&e=jpg&b=ffffff" alt="viarotel-escrcpy" style="width: 100%;">
|
<img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/570065a5683b4cf7af9cfa9743c06ab4~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1360&h=693&s=140693&e=jpg&b=ffffff" alt="viarotel-escrcpy" style="width: 100%;">
|
||||||
</div>
|
</div>
|
||||||
@ -15,7 +13,7 @@
|
|||||||
- ⚡️ 性能:30~120 帧每秒,取决于设备
|
- ⚡️ 性能:30~120 帧每秒,取决于设备
|
||||||
- 🌟 质量:1920×1080 或更高
|
- 🌟 质量:1920×1080 或更高
|
||||||
- 🕒 低延迟:35~70 毫秒
|
- 🕒 低延迟:35~70 毫秒
|
||||||
- 🚀 快速启动:显示第一张图片仅需约1秒钟
|
- 🚀 快速启动:显示第一张图片仅需约 1 秒钟
|
||||||
- 🙅♂️ 非侵入性:不会在安卓设备上留下任何安装文件
|
- 🙅♂️ 非侵入性:不会在安卓设备上留下任何安装文件
|
||||||
- 🤩 用户收益:无需账户、无广告、无需互联网连接
|
- 🤩 用户收益:无需账户、无广告、无需互联网连接
|
||||||
- 🗽 自由:免费且开源软件
|
- 🗽 自由:免费且开源软件
|
||||||
@ -39,7 +37,7 @@
|
|||||||
|
|
||||||
> 注意:如果首次无线连接失败,你可能需要无线配对请参阅 [常见问题](#常见问题)
|
> 注意:如果首次无线连接失败,你可能需要无线配对请参阅 [常见问题](#常见问题)
|
||||||
>
|
>
|
||||||
> 注意:需同时开启无线调试功能,并在无线调试页面中获取你的当前设备的无线地址(通常为你连接WIFI时分配的IP地址)及端口号(默认为 5555)
|
> 注意:需同时开启无线调试功能,并在无线调试页面中获取你的当前设备的无线地址(通常为你连接 WIFI 时分配的 IP 地址)及端口号(默认为 5555)
|
||||||
|
|
||||||
1. 同 USB 连接中的 1-2 步骤
|
1. 同 USB 连接中的 1-2 步骤
|
||||||
2. 将获取到的设备 IP 地址及端口号填写到 Escrcpy 中,然后点击连接设备
|
2. 将获取到的设备 IP 地址及端口号填写到 Escrcpy 中,然后点击连接设备
|
||||||
@ -54,29 +52,43 @@
|
|||||||
|
|
||||||
> 持续完善中 目前支持 Scrcpy 中以下常用配置
|
> 持续完善中 目前支持 Scrcpy 中以下常用配置
|
||||||
|
|
||||||
### 显示配置
|
### 视频控制
|
||||||
|
|
||||||
- 分辨率
|
- 分辨率
|
||||||
- 比特率
|
- 比特率
|
||||||
- 刷新率
|
- 刷新率
|
||||||
- 屏幕旋转
|
|
||||||
- 视频解码器
|
- 视频解码器
|
||||||
- 视频编码器
|
- 视频编码器
|
||||||
|
- 屏幕旋转
|
||||||
|
- 屏幕裁剪
|
||||||
|
- 多显示器
|
||||||
|
- 视频缓冲
|
||||||
|
- 音频缓冲
|
||||||
|
- 接收器(v4l2)缓冲
|
||||||
|
- 禁用视频
|
||||||
|
|
||||||
### 设备控制
|
### 设备控制
|
||||||
|
|
||||||
- 保持设备清醒
|
- 展示触摸点
|
||||||
- 连接设备后自动关闭屏幕
|
- 保持清醒
|
||||||
|
- 控制时关闭屏幕
|
||||||
### 音频控制
|
- 控制结束关闭屏幕
|
||||||
|
- 控制时停止充电
|
||||||
- 镜像时禁用音频
|
|
||||||
|
|
||||||
### 窗口控制
|
### 窗口控制
|
||||||
|
|
||||||
- 无边框模式
|
- 无边框模式
|
||||||
- 全屏幕模式
|
- 全屏幕模式
|
||||||
|
|
||||||
|
### 音视频录制
|
||||||
|
|
||||||
|
- 文件保存路径
|
||||||
|
- 录制视频格式
|
||||||
|
|
||||||
|
### 音频控制
|
||||||
|
|
||||||
|
- 禁用音频
|
||||||
|
|
||||||
## 下一步做什么?
|
## 下一步做什么?
|
||||||
|
|
||||||
> 优先级从高到低
|
> 优先级从高到低
|
||||||
@ -85,10 +97,11 @@
|
|||||||
2. 内置的软件更新功能 ✅
|
2. 内置的软件更新功能 ✅
|
||||||
3. 录制和保存音视频 ✅
|
3. 录制和保存音视频 ✅
|
||||||
4. 添加设备快捷交互控制栏 ✅
|
4. 添加设备快捷交互控制栏 ✅
|
||||||
5. 支持自定义 Adb 及 Scrcpy 依赖,并支持生成精简版本和完整版本以满足不同用户需求
|
5. 支持自定义 Adb 及 Scrcpy 依赖,并支持生成精简版本和完整版本以满足不同用户需求 🚧
|
||||||
6. 添加 macOS 及 linux 操作系统的支持 🚧
|
6. 支持自定义设备名称,以及用户配置的导出及导入 🚧
|
||||||
7. 支持语言国际化功能 🚧
|
7. 添加 macOS 及 linux 操作系统的支持 🚧
|
||||||
8. 添加对游戏的增强功能,如游戏键位映射 🚧
|
8. 支持语言国际化功能 🚧
|
||||||
|
9. 添加对游戏的增强功能,如游戏键位映射 🚧
|
||||||
|
|
||||||
## 常见问题
|
## 常见问题
|
||||||
|
|
||||||
@ -102,7 +115,7 @@
|
|||||||
该问题是已知的, Scrcpy 似乎并未直接对中文输入进行测试和支持 需要在手机端安装第三方输入法 以下输入法经测试可以很好支持
|
该问题是已知的, Scrcpy 似乎并未直接对中文输入进行测试和支持 需要在手机端安装第三方输入法 以下输入法经测试可以很好支持
|
||||||
|
|
||||||
- 搜狗输入法
|
- 搜狗输入法
|
||||||
- QQ输入法
|
- QQ 输入法
|
||||||
- 谷歌拼音输入法
|
- 谷歌拼音输入法
|
||||||
- Gboard
|
- Gboard
|
||||||
|
|
||||||
@ -115,7 +128,7 @@
|
|||||||
|
|
||||||
### 无线连接提示: 目标计算机积极拒绝访问
|
### 无线连接提示: 目标计算机积极拒绝访问
|
||||||
|
|
||||||
第一次无线连接可能需要配对 或 插入USB 以保证与电脑建立连接即授权成功后方可使用
|
第一次无线连接可能需要配对 或 插入 USB 以保证与电脑建立连接即授权成功后方可使用
|
||||||
|
|
||||||
### 通过数据线连接后点击无线模式没有反应
|
### 通过数据线连接后点击无线模式没有反应
|
||||||
|
|
||||||
@ -148,6 +161,8 @@
|
|||||||
|
|
||||||
> 如果该项目帮到你的话,可以请我吃包辣条,可以使我更有动力完善该项目
|
> 如果该项目帮到你的话,可以请我吃包辣条,可以使我更有动力完善该项目
|
||||||
|
|
||||||
|
> 注意:非 BUG 或计划外的需求,有偿处理;至于金额,根据问题难易程度,你觉得帮助了多少,看着给吧(维护这些项目已经耗费了大量精力,还要免费花时间解答问题就说不过去了吧...所以白嫖的一律不通过。)
|
||||||
|
|
||||||
<div style="display:flex;">
|
<div style="display:flex;">
|
||||||
<img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/79dcbc40246743e2b6870419e88e0392~tplv-k3u1fbpfcp-watermark.image?" alt="viarotel-wepay" style="width: 36%;">
|
<img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/79dcbc40246743e2b6870419e88e0392~tplv-k3u1fbpfcp-watermark.image?" alt="viarotel-wepay" style="width: 36%;">
|
||||||
<img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1e5e69b83dd746deade95afd4a6864ec~tplv-k3u1fbpfcp-watermark.image?" alt="viarotel-alipay" style="width: 36%;">
|
<img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1e5e69b83dd746deade95afd4a6864ec~tplv-k3u1fbpfcp-watermark.image?" alt="viarotel-alipay" style="width: 36%;">
|
||||||
|
63
electron-builder.json
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://raw.githubusercontent.com/electron-userland/electron-builder/master/packages/app-builder-lib/scheme.json",
|
||||||
|
"appId": "org.viarotel.escrcpy",
|
||||||
|
"asar": true,
|
||||||
|
"productName": "Escrcpy",
|
||||||
|
"directories": {
|
||||||
|
"output": "dist-release/${version}",
|
||||||
|
"buildResources": "electron/resources/build"
|
||||||
|
},
|
||||||
|
"files": ["dist", "dist-electron"],
|
||||||
|
"extraResources": ["electron/resources/extra"],
|
||||||
|
"publish": {
|
||||||
|
"provider": "github",
|
||||||
|
"owner": "viarotel-org",
|
||||||
|
"repo": "escrcpy",
|
||||||
|
"updaterCacheDirName": "escrcpy-updater"
|
||||||
|
},
|
||||||
|
"mac": {
|
||||||
|
"icon": "logo.icns",
|
||||||
|
"target": ["dmg"],
|
||||||
|
"artifactName": "${productName}-${version}-mac-installer.${ext}",
|
||||||
|
"entitlementsInherit": "electron/resources/build/entitlements.mac.plist",
|
||||||
|
"extendInfo": {
|
||||||
|
"NSCameraUsageDescription": "Application requests access to the device's camera.",
|
||||||
|
"NSMicrophoneUsageDescription": "Application requests access to the device's microphone.",
|
||||||
|
"NSDocumentsFolderUsageDescription": "Application requests access to the user's Documents folder.",
|
||||||
|
"NSDownloadsFolderUsageDescription": "Application requests access to the user's Downloads folder."
|
||||||
|
},
|
||||||
|
"notarize": false
|
||||||
|
},
|
||||||
|
"win": {
|
||||||
|
"icon": "logo.ico",
|
||||||
|
"target": [
|
||||||
|
{
|
||||||
|
"target": "nsis",
|
||||||
|
"arch": ["x64"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"target": "zip"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"target": "portable"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nsis": {
|
||||||
|
"artifactName": "${productName}-${version}-win-setup.${ext}",
|
||||||
|
"oneClick": false,
|
||||||
|
"perMachine": false,
|
||||||
|
"allowToChangeInstallationDirectory": true,
|
||||||
|
"deleteAppDataOnUninstall": false
|
||||||
|
},
|
||||||
|
"portable": {
|
||||||
|
"artifactName": "${productName}-${version}-win-portable.${ext}",
|
||||||
|
"requestExecutionLevel": "user"
|
||||||
|
},
|
||||||
|
"linux": {
|
||||||
|
"icon": "logo.png",
|
||||||
|
"target": ["AppImage"],
|
||||||
|
"artifactName": "${productName}-${version}-linux.${ext}"
|
||||||
|
},
|
||||||
|
"npmRebuild": false
|
||||||
|
}
|
@ -1,51 +0,0 @@
|
|||||||
appId: com.electron.app
|
|
||||||
productName: escrcpy
|
|
||||||
directories:
|
|
||||||
buildResources: build
|
|
||||||
files:
|
|
||||||
- '!**/.vscode/*'
|
|
||||||
- '!src/*'
|
|
||||||
- '!electron.vite.config.{js,ts,mjs,cjs}'
|
|
||||||
- '!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}'
|
|
||||||
- '!{.env,.env.*,.npmrc,pnpm-lock.yaml}'
|
|
||||||
asarUnpack:
|
|
||||||
- resources/**
|
|
||||||
win:
|
|
||||||
executableName: escrcpy
|
|
||||||
target:
|
|
||||||
- nsis
|
|
||||||
- zip
|
|
||||||
- portable
|
|
||||||
nsis:
|
|
||||||
artifactName: ${productName}-${version}-setup.${ext}
|
|
||||||
shortcutName: ${productName}
|
|
||||||
uninstallDisplayName: ${productName}
|
|
||||||
createDesktopShortcut: always
|
|
||||||
portable:
|
|
||||||
artifactName: '${productName}-${version}-portable.${ext}'
|
|
||||||
requestExecutionLevel: user
|
|
||||||
mac:
|
|
||||||
entitlementsInherit: build/entitlements.mac.plist
|
|
||||||
extendInfo:
|
|
||||||
- NSCameraUsageDescription: Application requests access to the device's camera.
|
|
||||||
- NSMicrophoneUsageDescription: Application requests access to the device's microphone.
|
|
||||||
- NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder.
|
|
||||||
- NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
|
|
||||||
notarize: false
|
|
||||||
dmg:
|
|
||||||
artifactName: ${name}-${version}.${ext}
|
|
||||||
linux:
|
|
||||||
target:
|
|
||||||
- AppImage
|
|
||||||
- snap
|
|
||||||
- deb
|
|
||||||
maintainer: electronjs.org
|
|
||||||
category: Utility
|
|
||||||
appImage:
|
|
||||||
artifactName: ${name}-${version}.${ext}
|
|
||||||
npmRebuild: false
|
|
||||||
publish:
|
|
||||||
provider: github
|
|
||||||
owner: viarotel-org
|
|
||||||
repo: escrcpy
|
|
||||||
updaterCacheDirName: escrcpy-updater
|
|
@ -1,37 +0,0 @@
|
|||||||
import { resolve } from 'node:path'
|
|
||||||
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
|
|
||||||
import vue from '@vitejs/plugin-vue'
|
|
||||||
import useEslint from 'vite-plugin-eslint'
|
|
||||||
import useUnoCSS from 'unocss/vite'
|
|
||||||
import postcssConfig from '@viarotel-org/postcss-config'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
main: {
|
|
||||||
resolve: {
|
|
||||||
alias: {
|
|
||||||
'@root': resolve('./'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: [externalizeDepsPlugin({ exclude: [] })],
|
|
||||||
},
|
|
||||||
preload: {
|
|
||||||
resolve: {
|
|
||||||
alias: {
|
|
||||||
'@resources': resolve('resources'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: [externalizeDepsPlugin({ exclude: [] })],
|
|
||||||
},
|
|
||||||
renderer: {
|
|
||||||
resolve: {
|
|
||||||
alias: {
|
|
||||||
'@root': resolve('./'),
|
|
||||||
'@renderer': resolve('src/renderer/src'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: [useEslint(), vue(), useUnoCSS()],
|
|
||||||
css: {
|
|
||||||
postcss: postcssConfig(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
@ -1,23 +1,21 @@
|
|||||||
import path from 'node:path'
|
|
||||||
import { app, ipcMain } from 'electron'
|
import { app, ipcMain } from 'electron'
|
||||||
import { is } from '@electron-toolkit/utils'
|
import { is } from '@electron-toolkit/utils'
|
||||||
import { autoUpdater } from 'electron-updater'
|
import { autoUpdater } from 'electron-updater'
|
||||||
|
import devPublishPath from '@root/dev-publish.yml?path'
|
||||||
|
|
||||||
export default (mainWindow) => {
|
export default (mainWindow) => {
|
||||||
// dev-start, 这里是为了在本地做应用升级测试使用,正式环境请务必删除
|
// dev-start, 这里是为了在本地做应用升级测试使用,正式环境请务必删除
|
||||||
if (is.dev && process.env.ELECTRON_RENDERER_URL) {
|
// if (is.dev && process.env.ELECTRON_RENDERER_URL) {
|
||||||
const updateConfigPath = path.join(process.cwd(), './dev-app-update.yml')
|
if (is.dev && process.env.VITE_DEV_SERVER_URL) {
|
||||||
// console.log('updateConfigPath', updateConfigPath)
|
const updateConfigPath = devPublishPath
|
||||||
autoUpdater.updateConfigPath = updateConfigPath
|
autoUpdater.updateConfigPath = updateConfigPath
|
||||||
|
Object.defineProperty(app, 'isPackaged', {
|
||||||
|
get() {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.defineProperty(app, 'isPackaged', {
|
|
||||||
get() {
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
})
|
|
||||||
// dev-end
|
|
||||||
|
|
||||||
// 触发检查更新(此方法用于被渲染线程调用,例如页面点击检查更新按钮来调用此方法)
|
// 触发检查更新(此方法用于被渲染线程调用,例如页面点击检查更新按钮来调用此方法)
|
||||||
ipcMain.on('check-for-update', () => {
|
ipcMain.on('check-for-update', () => {
|
||||||
console.log('ipcMain:check-for-update')
|
console.log('ipcMain:check-for-update')
|
@ -4,7 +4,7 @@ import path from 'node:path'
|
|||||||
import fs from 'node:fs'
|
import fs from 'node:fs'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { Adb } from '@devicefarmer/adbkit'
|
import { Adb } from '@devicefarmer/adbkit'
|
||||||
import adbPath from '@resources/core/adb.exe?asset&asarUnpack'
|
import adbPath from '@resources/extra/core/adb.exe?path'
|
||||||
|
|
||||||
const exec = util.promisify(child_process.exec)
|
const exec = util.promisify(child_process.exec)
|
||||||
|
|
||||||
@ -18,7 +18,8 @@ window.addEventListener('beforeunload', () => {
|
|||||||
|
|
||||||
const shell = async command => exec(`${adbPath} ${command}`)
|
const shell = async command => exec(`${adbPath} ${command}`)
|
||||||
const getDevices = async () => await client.listDevicesWithPaths()
|
const getDevices = async () => await client.listDevicesWithPaths()
|
||||||
const deviceShell = async (id, command) => await client.getDevice(id).shell(command)
|
const deviceShell = async (id, command) =>
|
||||||
|
await client.getDevice(id).shell(command)
|
||||||
const kill = async (...params) => await client.kill(...params)
|
const kill = async (...params) => await client.kill(...params)
|
||||||
const connect = async (...params) => await client.connect(...params)
|
const connect = async (...params) => await client.connect(...params)
|
||||||
const disconnect = async (...params) => await client.disconnect(...params)
|
const disconnect = async (...params) => await client.disconnect(...params)
|
||||||
@ -97,7 +98,7 @@ const watch = async (callback) => {
|
|||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
client = Adb.createClient({ bin: adbPath })
|
client = Adb.createClient({ bin: adbPath })
|
||||||
console.log('client', client)
|
// console.log('client', client)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
shell,
|
shell,
|
@ -4,9 +4,8 @@ import adbkit from './adbkit/index.js'
|
|||||||
import scrcpy from './scrcpy/index.js'
|
import scrcpy from './scrcpy/index.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
install(expose) {
|
init(expose) {
|
||||||
expose('nodePath', path)
|
expose('nodePath', path)
|
||||||
|
|
||||||
expose('electron', electron())
|
expose('electron', electron())
|
||||||
expose('adbkit', adbkit())
|
expose('adbkit', adbkit())
|
||||||
expose('scrcpy', scrcpy())
|
expose('scrcpy', scrcpy())
|
@ -1,7 +1,7 @@
|
|||||||
import util from 'node:util'
|
import util from 'node:util'
|
||||||
import child_process from 'node:child_process'
|
import child_process from 'node:child_process'
|
||||||
import adbPath from '@resources/core/adb.exe?asset&asarUnpack'
|
import adbPath from '@resources/extra/core/adb.exe?path'
|
||||||
import scrcpyPath from '@resources/core/scrcpy.exe?asset&asarUnpack'
|
import scrcpyPath from '@resources/extra/core/scrcpy.exe?path'
|
||||||
|
|
||||||
const exec = util.promisify(child_process.exec)
|
const exec = util.promisify(child_process.exec)
|
||||||
|
|
175
electron/loading/index.js
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
// --------- Preload scripts loading ---------
|
||||||
|
function domReady(condition = ['complete', 'interactive']) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
if (condition.includes(document.readyState)) {
|
||||||
|
resolve(true)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
document.addEventListener('readystatechange', () => {
|
||||||
|
if (condition.includes(document.readyState)) {
|
||||||
|
resolve(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const safeDOM = {
|
||||||
|
append(parent, child) {
|
||||||
|
if (!Array.from(parent.children).find(e => e === child)) {
|
||||||
|
parent.appendChild(child)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
remove(parent, child) {
|
||||||
|
if (Array.from(parent.children).find(e => e === child)) {
|
||||||
|
parent.removeChild(child)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://tobiasahlin.com/spinkit
|
||||||
|
* https://connoratherton.com/loaders
|
||||||
|
* https://projects.lukehaas.me/css-loaders
|
||||||
|
* https://matejkustec.github.io/SpinThatShit
|
||||||
|
*/
|
||||||
|
function useLoading() {
|
||||||
|
const className = 'electron-loading'
|
||||||
|
const loginStyles = `
|
||||||
|
.${className}-core {
|
||||||
|
font-size: 30px;
|
||||||
|
text-indent: -9999em;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin: 72px auto;
|
||||||
|
position: relative;
|
||||||
|
-webkit-transform: translateZ(0);
|
||||||
|
-ms-transform: translateZ(0);
|
||||||
|
transform: translateZ(0);
|
||||||
|
-webkit-animation: electron-loading-dots 1.7s infinite ease, electron-loading-spin 1.7s infinite ease;
|
||||||
|
animation: electron-loading-dots 1.7s infinite ease, electron-loading-spin 1.7s infinite ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes electron-loading-dots {
|
||||||
|
0% {
|
||||||
|
box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em, 0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em;
|
||||||
|
}
|
||||||
|
5%,
|
||||||
|
95% {
|
||||||
|
box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em, 0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em;
|
||||||
|
}
|
||||||
|
10%,
|
||||||
|
59% {
|
||||||
|
box-shadow: 0 -0.83em 0 -0.4em, -0.087em -0.825em 0 -0.42em, -0.173em -0.812em 0 -0.44em, -0.256em -0.789em 0 -0.46em, -0.297em -0.775em 0 -0.477em;
|
||||||
|
}
|
||||||
|
20% {
|
||||||
|
box-shadow: 0 -0.83em 0 -0.4em, -0.338em -0.758em 0 -0.42em, -0.555em -0.617em 0 -0.44em, -0.671em -0.488em 0 -0.46em, -0.749em -0.34em 0 -0.477em;
|
||||||
|
}
|
||||||
|
38% {
|
||||||
|
box-shadow: 0 -0.83em 0 -0.4em, -0.377em -0.74em 0 -0.42em, -0.645em -0.522em 0 -0.44em, -0.775em -0.297em 0 -0.46em, -0.82em -0.09em 0 -0.477em;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em, 0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes electron-loading-dots {
|
||||||
|
0% {
|
||||||
|
box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em, 0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em;
|
||||||
|
}
|
||||||
|
5%,
|
||||||
|
95% {
|
||||||
|
box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em, 0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em;
|
||||||
|
}
|
||||||
|
10%,
|
||||||
|
59% {
|
||||||
|
box-shadow: 0 -0.83em 0 -0.4em, -0.087em -0.825em 0 -0.42em, -0.173em -0.812em 0 -0.44em, -0.256em -0.789em 0 -0.46em, -0.297em -0.775em 0 -0.477em;
|
||||||
|
}
|
||||||
|
20% {
|
||||||
|
box-shadow: 0 -0.83em 0 -0.4em, -0.338em -0.758em 0 -0.42em, -0.555em -0.617em 0 -0.44em, -0.671em -0.488em 0 -0.46em, -0.749em -0.34em 0 -0.477em;
|
||||||
|
}
|
||||||
|
38% {
|
||||||
|
box-shadow: 0 -0.83em 0 -0.4em, -0.377em -0.74em 0 -0.42em, -0.645em -0.522em 0 -0.44em, -0.775em -0.297em 0 -0.46em, -0.82em -0.09em 0 -0.477em;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em, 0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes electron-loading-spin {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: rotate(0deg);
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
-webkit-transform: rotate(360deg);
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes electron-loading-spin {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: rotate(0deg);
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
-webkit-transform: rotate(360deg);
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.${className}-wrap {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 3000;
|
||||||
|
background: white;
|
||||||
|
color: #028D71;
|
||||||
|
}
|
||||||
|
|
||||||
|
.${className}-text {
|
||||||
|
margin-top: -45px;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const styleEl = document.createElement('style')
|
||||||
|
styleEl.id = `${className}-style`
|
||||||
|
styleEl.innerHTML = loginStyles
|
||||||
|
|
||||||
|
const divEl = document.createElement('div')
|
||||||
|
divEl.className = `${className}-wrap`
|
||||||
|
divEl.innerHTML = `
|
||||||
|
<div class="${className}-core"></div>
|
||||||
|
<div class="${className}-text"> 初始化服务中...</div>
|
||||||
|
`
|
||||||
|
|
||||||
|
return {
|
||||||
|
appendLoading() {
|
||||||
|
safeDOM.append(document.head, styleEl)
|
||||||
|
safeDOM.append(document.body, divEl)
|
||||||
|
},
|
||||||
|
removeLoading() {
|
||||||
|
safeDOM.remove(document.head, styleEl)
|
||||||
|
safeDOM.remove(document.body, divEl)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
const { appendLoading, removeLoading } = useLoading()
|
||||||
|
domReady().then(appendLoading)
|
||||||
|
|
||||||
|
window.onmessage = (ev) => {
|
||||||
|
ev.data.payload === 'removeLoading' && removeLoading()
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(removeLoading, 4999)
|
106
electron/main.js
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
import path from 'node:path'
|
||||||
|
import { BrowserWindow, app, shell } from 'electron'
|
||||||
|
import { electronApp, optimizer } from '@electron-toolkit/utils'
|
||||||
|
|
||||||
|
import logoPath from '@resources/build/logo.png?path'
|
||||||
|
import icoLogoPath from '@resources/build/logo.ico?path'
|
||||||
|
import icnsLogoPath from '@resources/build/logo.icns?path'
|
||||||
|
|
||||||
|
import events from './events/index.js'
|
||||||
|
|
||||||
|
// The built directory structure
|
||||||
|
//
|
||||||
|
// ├─┬─┬ dist
|
||||||
|
// │ │ └── index.html
|
||||||
|
// │ │
|
||||||
|
// │ ├─┬ dist-electron
|
||||||
|
// │ │ ├── main.js
|
||||||
|
// │ │ └── preload.js
|
||||||
|
// │
|
||||||
|
|
||||||
|
process.env.DIST = path.join(__dirname, '../dist')
|
||||||
|
|
||||||
|
let mainWindow
|
||||||
|
// 🚧 Use ['ENV_NAME'] avoid vite:define plugin - Vite@2.x
|
||||||
|
const VITE_DEV_SERVER_URL = process.env.VITE_DEV_SERVER_URL
|
||||||
|
|
||||||
|
function createWindow() {
|
||||||
|
let icon = logoPath
|
||||||
|
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
icon = icoLogoPath
|
||||||
|
}
|
||||||
|
else if (process.platform === 'darwin') {
|
||||||
|
icon = icnsLogoPath
|
||||||
|
}
|
||||||
|
|
||||||
|
mainWindow = new BrowserWindow({
|
||||||
|
show: false,
|
||||||
|
icon,
|
||||||
|
minWidth: 1000,
|
||||||
|
minHeight: 700,
|
||||||
|
autoHideMenuBar: true,
|
||||||
|
webPreferences: {
|
||||||
|
// nodeIntegration: true,
|
||||||
|
// contextIsolation: false,
|
||||||
|
preload: path.join(__dirname, './preload.js'),
|
||||||
|
sandbox: false,
|
||||||
|
},
|
||||||
|
backgroundColor: 'white',
|
||||||
|
})
|
||||||
|
|
||||||
|
mainWindow.on('ready-to-show', () => {
|
||||||
|
mainWindow.show()
|
||||||
|
})
|
||||||
|
|
||||||
|
mainWindow.webContents.setWindowOpenHandler((details) => {
|
||||||
|
shell.openExternal(details.url)
|
||||||
|
return { action: 'deny' }
|
||||||
|
})
|
||||||
|
|
||||||
|
// Test active push message to Renderer-process.
|
||||||
|
mainWindow.webContents.on('did-finish-load', () => {
|
||||||
|
mainWindow?.webContents.send(
|
||||||
|
'main-process-message',
|
||||||
|
new Date().toLocaleString(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (VITE_DEV_SERVER_URL) {
|
||||||
|
mainWindow.loadURL(VITE_DEV_SERVER_URL)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// win.loadFile('dist/index.html')
|
||||||
|
mainWindow.loadFile(path.join(process.env.DIST, 'index.html'))
|
||||||
|
}
|
||||||
|
|
||||||
|
events(mainWindow)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quit when all windows are closed, except on macOS. There, it's common
|
||||||
|
// for applications and their menu bar to stay active until the user quits
|
||||||
|
// explicitly with Cmd + Q.
|
||||||
|
app.on('window-all-closed', () => {
|
||||||
|
if (process.platform !== 'darwin') {
|
||||||
|
app.quit()
|
||||||
|
mainWindow = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
app.on('activate', () => {
|
||||||
|
// On OS X it's common to re-create a window in the app when the
|
||||||
|
// dock icon is clicked and there are no other windows open.
|
||||||
|
if (BrowserWindow.getAllWindows().length === 0) {
|
||||||
|
createWindow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
app.whenReady().then(() => {
|
||||||
|
electronApp.setAppUserModelId('com.viarotel.escrcpy')
|
||||||
|
|
||||||
|
app.on('browser-window-created', (_, window) => {
|
||||||
|
optimizer.watchWindowShortcuts(window)
|
||||||
|
})
|
||||||
|
|
||||||
|
createWindow()
|
||||||
|
})
|
7
electron/preload.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import exposes from './exposes/index.js'
|
||||||
|
import { exposeContext } from './helpers/index.js'
|
||||||
|
import './loading/index.js'
|
||||||
|
|
||||||
|
process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = 'true'
|
||||||
|
|
||||||
|
exposes.init(exposeContext)
|
Before Width: | Height: | Size: 170 KiB After Width: | Height: | Size: 170 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
13
index.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" href="/logo.ico" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Escrcpy</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -1,11 +1,12 @@
|
|||||||
{
|
{
|
||||||
"exclude": ["node_modules", "dist", "out"],
|
|
||||||
"include": ["src/**/*"],
|
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@renderer/*": ["src/renderer/src/*"],
|
"@/*": ["src/*"],
|
||||||
"@resources/*": ["resources/*"]
|
"@root/*": ["*"],
|
||||||
|
"@resources/*": ["electron/resources/*"]
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
"exclude": ["node_modules", "dist", "dist-electron", "dist-release"],
|
||||||
|
"include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.vue", "electron"]
|
||||||
|
}
|
56
package.json
@ -1,52 +1,46 @@
|
|||||||
{
|
{
|
||||||
"name": "escrcpy",
|
"name": "escrcpy",
|
||||||
"version": "1.6.2",
|
"version": "1.6.2",
|
||||||
|
"private": true,
|
||||||
"description": "Scrcpy Powered by Electron",
|
"description": "Scrcpy Powered by Electron",
|
||||||
"author": "viarotel",
|
"author": "viarotel",
|
||||||
"homepage": "https://github.com/viarotel-org/escrcpy",
|
"homepage": "https://github.com/viarotel-org/escrcpy",
|
||||||
"main": "./out/main/index.js",
|
"main": "dist-electron/main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"format": "prettier --write .",
|
"dev": "vite",
|
||||||
|
"build": "vite build && electron-builder",
|
||||||
|
"build:win": "vite build && electron-builder --win",
|
||||||
|
"build:mac": "vite build && electron-builder --mac",
|
||||||
|
"build:linux": "vite build && electron-builder --linux",
|
||||||
|
"preview": "vite preview",
|
||||||
"lint": "eslint . --ext .md,.vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --ignore-path .eslintignore --fix",
|
"lint": "eslint . --ext .md,.vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --ignore-path .eslintignore --fix",
|
||||||
"start": "electron-vite preview",
|
"postinstall": "electron-builder install-app-deps"
|
||||||
"dev": "electron-vite dev",
|
|
||||||
"build": "electron-vite build",
|
|
||||||
"postinstall": "electron-builder install-app-deps",
|
|
||||||
"build:win": "npm run build && electron-builder --win --config",
|
|
||||||
"build:mac": "npm run build && electron-builder --mac --config",
|
|
||||||
"build:linux": "npm run build && electron-builder --linux --config"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@devicefarmer/adbkit": "^3.2.5",
|
"vue": "^3.3.4"
|
||||||
"@electron-toolkit/preload": "^2.0.0",
|
|
||||||
"@electron-toolkit/utils": "^2.0.0",
|
|
||||||
"@viarotel-org/design": "^0.7.0",
|
|
||||||
"dayjs": "^1.11.10",
|
|
||||||
"electron-updater": "^6.1.1",
|
|
||||||
"element-plus": "^2.3.14",
|
|
||||||
"fs-extra": "^11.1.1",
|
|
||||||
"lodash-es": "^4.17.21",
|
|
||||||
"pinia": "^2.1.6",
|
|
||||||
"ufo": "^1.3.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@electron-toolkit/eslint-config": "^1.0.1",
|
"@devicefarmer/adbkit": "^3.2.5",
|
||||||
"@rushstack/eslint-patch": "^1.3.3",
|
"@electron-toolkit/preload": "^2.0.0",
|
||||||
|
"@electron-toolkit/utils": "^2.0.1",
|
||||||
|
"@viarotel-org/design": "^0.7.0",
|
||||||
"@viarotel-org/eslint-config": "^0.7.0",
|
"@viarotel-org/eslint-config": "^0.7.0",
|
||||||
"@viarotel-org/postcss-config": "^0.7.0",
|
"@viarotel-org/postcss-config": "^0.7.0",
|
||||||
"@viarotel-org/unocss-config": "^0.7.4",
|
"@viarotel-org/unocss-config": "^0.7.4",
|
||||||
"@vitejs/plugin-vue": "^4.3.1",
|
"@viarotel-org/vite-plugin-path": "^0.8.1",
|
||||||
"@vue/eslint-config-prettier": "^8.0.0",
|
"@vitejs/plugin-vue": "^4.3.4",
|
||||||
"electron": "^25.6.0",
|
"dayjs": "^1.11.10",
|
||||||
|
"electron": "^26.1.0",
|
||||||
"electron-builder": "^24.6.4",
|
"electron-builder": "^24.6.4",
|
||||||
"electron-vite": "^1.0.28",
|
"electron-updater": "^6.1.4",
|
||||||
"eslint": "8.49.0",
|
"element-plus": "^2.4.0",
|
||||||
"eslint-plugin-vue": "^9.17.0",
|
"lodash-es": "^4.17.21",
|
||||||
"less": "^4.2.0",
|
"pinia": "^2.1.7",
|
||||||
"prettier": "^3.0.2",
|
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.2.2",
|
||||||
"vite": "^4.4.9",
|
"vite": "^4.4.9",
|
||||||
|
"vite-plugin-electron": "^0.14.0",
|
||||||
|
"vite-plugin-electron-renderer": "^0.14.5",
|
||||||
"vite-plugin-eslint": "^1.8.1",
|
"vite-plugin-eslint": "^1.8.1",
|
||||||
"vue": "^3.3.4"
|
"vue-tsc": "^1.8.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1745
pnpm-lock.yaml
generated
Before Width: | Height: | Size: 170 KiB After Width: | Height: | Size: 170 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
@ -8,10 +8,7 @@
|
|||||||
:name="item.prop"
|
:name="item.prop"
|
||||||
lazy
|
lazy
|
||||||
>
|
>
|
||||||
<component
|
<component :is="item.prop" :ref="item.prop" />
|
||||||
:is="item.prop"
|
|
||||||
:ref="item.prop"
|
|
||||||
/>
|
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</div>
|
</div>
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col items-center justify-center h-full -mt-8">
|
<div class="flex flex-col items-center justify-center h-full -mt-8">
|
||||||
<div class="">
|
<div class="">
|
||||||
<img src="@renderer/assets/icon.png" class="h-48" alt="" />
|
<img src="@/assets/icon.png" class="h-48" alt="" />
|
||||||
</div>
|
</div>
|
||||||
<div class="pt-4 text-xl text-center italic text-gray-700">
|
<div class="pt-4 text-xl text-center italic text-gray-700">
|
||||||
📱 使用图形化的
|
📱 使用图形化的
|
||||||
@ -22,7 +22,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { version } from '@root/package.json'
|
import { version } from '/package.json'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
@ -108,7 +108,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { debounce } from 'lodash-es'
|
import { debounce } from 'lodash-es'
|
||||||
import { useScrcpyStore } from '@renderer/store/index.js'
|
import { useScrcpyStore } from '@/store/index.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
@ -58,10 +58,10 @@ export default {
|
|||||||
tips: '可以用来开启或关闭屏幕',
|
tips: '可以用来开启或关闭屏幕',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '截屏快照',
|
label: '截取屏幕',
|
||||||
icon: 'Crop',
|
icon: 'Crop',
|
||||||
handle: this.handleScreenCap,
|
handle: this.handleScreenCap,
|
||||||
tips: '不要和切换键搞错啦',
|
tips: '',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="h-full flex flex-col">
|
<div class="h-full flex flex-col">
|
||||||
<div class="flex items-center flex-none space-x-2">
|
<div class="flex items-center flex-none space-x-2">
|
||||||
<el-input v-model="formData.host" placeholder="192.168.0.1" class="w-86 flex-none" clearable>
|
<el-input v-model="formData.host" placeholder="192.168.0.1" class="!w-86 flex-none" clearable>
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
无线连接
|
无线连接
|
||||||
</template>
|
</template>
|
||||||
@ -15,7 +15,7 @@
|
|||||||
placeholder="5555"
|
placeholder="5555"
|
||||||
:min="0"
|
:min="0"
|
||||||
clearable
|
clearable
|
||||||
class="w-32 flex-none"
|
class="!w-32 flex-none"
|
||||||
>
|
>
|
||||||
</el-input>
|
</el-input>
|
||||||
|
|
||||||
@ -69,7 +69,7 @@
|
|||||||
|
|
||||||
{{ row.name }}
|
{{ row.name }}
|
||||||
|
|
||||||
<el-tag v-if="row.$wireless" type="primary" effect="light" class="ml-2">
|
<el-tag v-if="row.$wireless" effect="light" class="ml-2">
|
||||||
WIFI
|
WIFI
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</div>
|
</div>
|
||||||
@ -140,11 +140,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { isIPWithPort, sleep } from '@renderer/utils/index.js'
|
|
||||||
import storage from '@renderer/utils/storages'
|
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import PairDialog from './PairDialog/index.vue'
|
import PairDialog from './PairDialog/index.vue'
|
||||||
import ControlBar from './ControlBar/index.vue'
|
import ControlBar from './ControlBar/index.vue'
|
||||||
|
import storage from '@/utils/storages'
|
||||||
|
import { isIPWithPort, sleep } from '@/utils/index.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@ -155,7 +155,7 @@ export default {
|
|||||||
const adbCache = storage.get('adbCache') || {}
|
const adbCache = storage.get('adbCache') || {}
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
loadingText: '初始化中...',
|
loadingText: '努力加载中...',
|
||||||
connectLoading: false,
|
connectLoading: false,
|
||||||
deviceList: [],
|
deviceList: [],
|
||||||
formData: {
|
formData: {
|
||||||
@ -347,6 +347,7 @@ export default {
|
|||||||
console.log('getDeviceData.data', this.deviceList)
|
console.log('getDeviceData.data', this.deviceList)
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
|
console.log(error)
|
||||||
if (error.message) {
|
if (error.message) {
|
||||||
this.$message.warning(error.message)
|
this.$message.warning(error.message)
|
||||||
}
|
}
|
@ -20,3 +20,8 @@ app.config.globalProperties.$scrcpy = window.scrcpy
|
|||||||
app.config.globalProperties.$path = window.nodePath
|
app.config.globalProperties.$path = window.nodePath
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|
||||||
|
app.$nextTick(() => {
|
||||||
|
// Remove Preload scripts loading
|
||||||
|
postMessage({ payload: 'removeLoading' }, '*')
|
||||||
|
})
|
@ -1,89 +0,0 @@
|
|||||||
import { join } from 'node:path'
|
|
||||||
|
|
||||||
import { BrowserWindow, app, shell } from 'electron'
|
|
||||||
import { electronApp, is, optimizer } from '@electron-toolkit/utils'
|
|
||||||
|
|
||||||
import iconPath from '../../resources/icons/icon.png?asset'
|
|
||||||
import winIconPath from '../../resources/icons/icon.ico?asset'
|
|
||||||
import macIconPath from '../../resources/icons/icon.icns?asset'
|
|
||||||
|
|
||||||
import ipcManage from './ipcManage/index.js'
|
|
||||||
|
|
||||||
function createWindow() {
|
|
||||||
let icon = iconPath
|
|
||||||
|
|
||||||
if (process.platform === 'win32') {
|
|
||||||
icon = winIconPath
|
|
||||||
}
|
|
||||||
else if (process.platform === 'darwin') {
|
|
||||||
icon = macIconPath
|
|
||||||
}
|
|
||||||
// Create the browser window.
|
|
||||||
const mainWindow = new BrowserWindow({
|
|
||||||
icon,
|
|
||||||
minWidth: 1000,
|
|
||||||
minHeight: 700,
|
|
||||||
show: false,
|
|
||||||
autoHideMenuBar: true,
|
|
||||||
webPreferences: {
|
|
||||||
preload: join(__dirname, '../preload/index.js'),
|
|
||||||
sandbox: false,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
mainWindow.on('ready-to-show', () => {
|
|
||||||
mainWindow.show()
|
|
||||||
})
|
|
||||||
|
|
||||||
mainWindow.webContents.setWindowOpenHandler((details) => {
|
|
||||||
shell.openExternal(details.url)
|
|
||||||
return { action: 'deny' }
|
|
||||||
})
|
|
||||||
|
|
||||||
// HMR for renderer base on electron-vite cli.
|
|
||||||
// Load the remote URL for development or the local html file for production.
|
|
||||||
if (is.dev && process.env.ELECTRON_RENDERER_URL) {
|
|
||||||
mainWindow.loadURL(process.env.ELECTRON_RENDERER_URL)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
|
|
||||||
}
|
|
||||||
|
|
||||||
ipcManage(mainWindow)
|
|
||||||
}
|
|
||||||
|
|
||||||
// This method will be called when Electron has finished
|
|
||||||
// initialization and is ready to create browser windows.
|
|
||||||
// Some APIs can only be used after this event occurs.
|
|
||||||
app.whenReady().then(() => {
|
|
||||||
// Set app user model id for windows
|
|
||||||
electronApp.setAppUserModelId('com.electron')
|
|
||||||
|
|
||||||
// Default open or close DevTools by F12 in development
|
|
||||||
// and ignore CommandOrControl + R in production.
|
|
||||||
// see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
|
|
||||||
app.on('browser-window-created', (_, window) => {
|
|
||||||
optimizer.watchWindowShortcuts(window)
|
|
||||||
})
|
|
||||||
|
|
||||||
createWindow()
|
|
||||||
|
|
||||||
app.on('activate', () => {
|
|
||||||
// On macOS it's common to re-create a window in the app when the
|
|
||||||
// dock icon is clicked and there are no other windows open.
|
|
||||||
if (BrowserWindow.getAllWindows().length === 0)
|
|
||||||
createWindow()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// Quit when all windows are closed, except on macOS. There, it's common
|
|
||||||
// for applications and their menu bar to stay active until the user quits
|
|
||||||
// explicitly with Cmd + Q.
|
|
||||||
app.on('window-all-closed', () => {
|
|
||||||
if (process.platform !== 'darwin') {
|
|
||||||
app.quit()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// In this file you can include the rest of your app"s specific main process
|
|
||||||
// code. You can also put them in separate files and require them here.
|
|
@ -1,4 +0,0 @@
|
|||||||
import plugins from './plugins/index.js'
|
|
||||||
import { exposeContext } from './helpers/index.js'
|
|
||||||
|
|
||||||
plugins.install(exposeContext)
|
|
@ -1,18 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<title>Escrcpy</title>
|
|
||||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
|
||||||
<meta
|
|
||||||
http-equiv="Content-Security-Policy"
|
|
||||||
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
|
|
||||||
/>
|
|
||||||
<link rel="icon" href="/src/assets/logo.jpg" />
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div id="app"></div>
|
|
||||||
<script type="module" src="/src/main.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Before Width: | Height: | Size: 18 KiB |
@ -1,7 +1,7 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import storage from '@renderer/utils/storages'
|
|
||||||
import { pickBy } from 'lodash-es'
|
import { pickBy } from 'lodash-es'
|
||||||
import * as scrcpyModel from './model/index.js'
|
import * as scrcpyModel from './model/index.js'
|
||||||
|
import storage from '@/utils/storages/index.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取 Scrcpy 默认配置
|
* 获取 Scrcpy 默认配置
|
@ -3,7 +3,7 @@ export default () => {
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
label: '文件存储地址',
|
label: '文件保存路径',
|
||||||
type: 'input.directory',
|
type: 'input.directory',
|
||||||
field: '--record',
|
field: '--record',
|
||||||
value: $path.resolve('../'),
|
value: $path.resolve('../'),
|
52
vite.config.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { resolve } from 'node:path'
|
||||||
|
import { defineConfig, mergeConfig } from 'vite'
|
||||||
|
import useElectron from 'vite-plugin-electron'
|
||||||
|
import useRenderer from 'vite-plugin-electron-renderer'
|
||||||
|
|
||||||
|
import useVue from '@vitejs/plugin-vue'
|
||||||
|
import useEslint from 'vite-plugin-eslint'
|
||||||
|
import useUnoCSS from 'unocss/vite'
|
||||||
|
import usePath from '@viarotel-org/vite-plugin-path'
|
||||||
|
|
||||||
|
const merge = config =>
|
||||||
|
mergeConfig(
|
||||||
|
{
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@root': resolve('./'),
|
||||||
|
'@resources': resolve('./electron/resources'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [usePath()],
|
||||||
|
},
|
||||||
|
config,
|
||||||
|
)
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
assetsInclude: ['**/*.exe'],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': resolve('./src'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
useEslint(),
|
||||||
|
useUnoCSS(),
|
||||||
|
useVue(),
|
||||||
|
useElectron([
|
||||||
|
{
|
||||||
|
entry: 'electron/main.js',
|
||||||
|
vite: merge({}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
entry: 'electron/preload.js',
|
||||||
|
onstart(args) {
|
||||||
|
args.reload()
|
||||||
|
},
|
||||||
|
vite: merge({}),
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
useRenderer(),
|
||||||
|
],
|
||||||
|
})
|