├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── help_wanted.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── main-build.yml │ └── tag-release.yml ├── .gitignore ├── .npmrc ├── .nvmrc ├── .vscode ├── .debug.script.mjs ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── LICENSE ├── Makefile ├── README-CN.md ├── README.md ├── changelog.md ├── electron-builder.json5 ├── electron ├── config │ ├── common.ts │ ├── contextMenu.ts │ ├── icon.ts │ ├── lang.ts │ ├── menu.ts │ ├── tray.ts │ └── window.ts ├── declarations │ └── svg.d.ts ├── electron-env.d.ts ├── lib │ ├── api.ts │ ├── devtools.ts │ ├── env-main.ts │ ├── env.ts │ ├── hooks.ts │ ├── permission.ts │ ├── pinyin-util.ts │ ├── process.ts │ └── util.ts ├── main │ └── index.ts ├── mapi │ ├── adb │ │ └── render.ts │ ├── app │ │ ├── icons.ts │ │ ├── index.ts │ │ ├── lib │ │ │ └── position.ts │ │ ├── loading.ts │ │ ├── main.ts │ │ ├── render.ts │ │ ├── setup.ts │ │ └── toast.ts │ ├── config │ │ ├── main.ts │ │ └── render.ts │ ├── db │ │ ├── db.ts │ │ ├── main.ts │ │ ├── migration.ts │ │ ├── render.ts │ │ └── type.d.ts │ ├── env.ts │ ├── event │ │ ├── main.ts │ │ └── render.ts │ ├── ffmpeg │ │ └── render.ts │ ├── file │ │ ├── index.ts │ │ ├── main.ts │ │ └── render.ts │ ├── keys │ │ ├── main.ts │ │ └── type.ts │ ├── lang │ │ ├── main.ts │ │ └── render.ts │ ├── log │ │ ├── index.ts │ │ ├── main.ts │ │ └── render.ts │ ├── main.ts │ ├── misc │ │ ├── index.ts │ │ ├── main.ts │ │ └── render.ts │ ├── protocol │ │ └── main.ts │ ├── render.ts │ ├── scrcpy │ │ └── render.ts │ ├── server │ │ ├── api.ts │ │ ├── error.ts │ │ ├── main.ts │ │ ├── render.ts │ │ └── type.d.ts │ ├── statistics │ │ └── render.ts │ ├── storage │ │ ├── main.ts │ │ └── render.ts │ ├── ui │ │ ├── index.ts │ │ └── render.ts │ ├── updater │ │ ├── index.ts │ │ ├── main.ts │ │ └── render.ts │ ├── user │ │ ├── main.ts │ │ └── render.ts │ └── util.ts ├── page │ ├── about.ts │ ├── feedback.ts │ ├── guide.ts │ ├── index.ts │ ├── payment.ts │ ├── setup.ts │ ├── thirdPartyImageBeautifier.ts │ └── user.ts ├── preload │ └── index.ts └── resources │ ├── build │ ├── appx │ │ ├── Square150x150Logo.png │ │ ├── Square44x44Logo.png │ │ ├── StoreLogo.png │ │ └── Wide310x150Logo.png │ ├── entitlements.mac.plist │ ├── logo-gray.png │ ├── logo.icns │ ├── logo.ico │ ├── logo.png │ └── logo_1024x1024.png │ └── extra │ ├── common │ ├── preload │ │ └── pip.js │ └── tray │ │ ├── icon.ico │ │ └── icon.png │ ├── linux │ ├── android-platform-tools-arm64 │ │ └── adb │ └── android-platform-tools │ │ ├── NOTICE.txt │ │ ├── adb │ │ ├── etc1tool │ │ ├── fastboot │ │ ├── hprof-conv │ │ ├── lib64 │ │ └── libc++.so │ │ ├── make_f2fs │ │ ├── make_f2fs_casefold │ │ ├── mke2fs │ │ ├── mke2fs.conf │ │ ├── source.properties │ │ └── sqlite3 │ ├── mac │ ├── android-platform-tools │ │ ├── NOTICE.txt │ │ ├── adb │ │ ├── etc1tool │ │ ├── fastboot │ │ ├── hprof-conv │ │ ├── lib64 │ │ │ └── libc++.dylib │ │ ├── make_f2fs │ │ ├── make_f2fs_casefold │ │ ├── mke2fs │ │ ├── mke2fs.conf │ │ ├── source.properties │ │ └── sqlite3 │ └── tray │ │ ├── iconTemplate.png │ │ ├── iconTemplate@2x.png │ │ └── iconTemplate@4x.png │ └── win │ ├── android-platform-tools │ ├── AdbWinApi.dll │ ├── AdbWinUsbApi.dll │ ├── NOTICE.txt │ ├── adb.exe │ ├── etc1tool.exe │ ├── fastboot.exe │ ├── hprof-conv.exe │ ├── libwinpthread-1.dll │ ├── make_f2fs.exe │ ├── make_f2fs_casefold.exe │ ├── mke2fs.conf │ ├── mke2fs.exe │ ├── source.properties │ └── sqlite3.exe │ └── scrcpy │ ├── AdbWinApi.dll │ ├── AdbWinUsbApi.dll │ ├── SDL2.dll │ ├── adb.exe │ ├── avcodec-61.dll │ ├── avformat-61.dll │ ├── avutil-59.dll │ ├── icon.png │ ├── libusb-1.0.dll │ ├── open_a_terminal_here.bat │ ├── scrcpy-console.bat │ ├── scrcpy-noconsole.vbs │ ├── scrcpy-server │ ├── scrcpy.exe │ └── swresample-5.dll ├── entitlements.mac.plist ├── index.html ├── package.json ├── page ├── about.html ├── feedback.html ├── guide.html ├── payment.html ├── setup.html └── user.html ├── postcss.config.js ├── public ├── iconfont │ ├── iconfont.css │ ├── iconfont.js │ ├── iconfont.json │ ├── iconfont.ttf │ ├── iconfont.woff │ └── iconfont.woff2 ├── logo-white.svg ├── logo.svg ├── splash-bg.svg ├── splash.html └── third-party │ └── image-beautifier │ ├── assets │ ├── demo-CN-KDTod.png │ ├── favicon-Bv5yausS.svg │ ├── imac-pro-BKp3IfOa.png │ ├── index-BcWrJNLv.css │ ├── index-SmyfLWDC.js │ ├── ipadpro-DABKnEyW.png │ ├── iphonepro-DwunDMnR.png │ ├── macbook-air-ByryNZsX.png │ └── macbook-pro-16-Phbq9ZkC.png │ ├── index.html │ └── others │ └── browser-image-compression.js ├── screenshots ├── cn │ ├── appmanage.png │ ├── home.png │ ├── mirror.png │ ├── screenrecord.png │ ├── screenshot.png │ └── shell.png └── en │ ├── appmanage.png │ ├── home.png │ ├── mirror.png │ ├── screenrecord.png │ ├── screenshot.png │ └── shell.png ├── scripts ├── build_optimize.cjs ├── icon_convert.sh └── notarize.cjs ├── src ├── App.vue ├── api │ ├── types │ │ └── base.ts │ └── user.ts ├── assets │ ├── fonts │ │ └── AlibabaPuHuiTi-3-55-Regular.ttf │ └── image │ │ ├── avatar.svg │ │ ├── device-bg.svg │ │ ├── device-empty.svg │ │ ├── gitee.svg │ │ ├── github.svg │ │ ├── logo-white.svg │ │ ├── logo.svg │ │ ├── no-record-dark.svg │ │ └── no-record.svg ├── components │ ├── AppQuitConfirm.vue │ ├── Device │ │ ├── App │ │ │ ├── Icon │ │ │ │ └── android.svg │ │ │ └── NameInfo.json │ │ ├── DeviceActionApp.vue │ │ ├── DeviceActionConnect.vue │ │ ├── DeviceActionDisconnect.vue │ │ ├── DeviceActionMirror.vue │ │ ├── DeviceActionMirrorCamera.vue │ │ ├── DeviceActionMirrorOTG.vue │ │ ├── DeviceActionRecord.vue │ │ ├── DeviceActionScreenshot.vue │ │ ├── DeviceActionWifiOff.vue │ │ ├── DeviceActionWifiOn.vue │ │ ├── DeviceAdbShellDialog.vue │ │ ├── DeviceAppIcon.vue │ │ ├── DeviceAppInstallDialog.vue │ │ ├── DeviceAppManagerDialog.vue │ │ ├── DeviceConnectWifiDialog.vue │ │ ├── DeviceDefaultSettingDialog.vue │ │ ├── DeviceFileManagerDialog.vue │ │ ├── DeviceRecordDialog.vue │ │ ├── DeviceSettingDialog.vue │ │ ├── DeviceShellDialog.vue │ │ ├── DeviceStatus.vue │ │ ├── DeviceType.vue │ │ └── Shell │ │ │ ├── adb.ts │ │ │ ├── basic.ts │ │ │ ├── index.ts │ │ │ └── scrcpy.ts │ ├── PageNav.vue │ ├── Setting │ │ ├── SettingAbout.vue │ │ ├── SettingBasic.vue │ │ └── SettingEnv.vue │ └── common │ │ ├── AudioPlayer.vue │ │ ├── CodeViewerDialog.vue │ │ ├── DragPasteContainer.vue │ │ ├── FeedbackTicketButton.vue │ │ ├── FileExt.vue │ │ ├── FileExtAssets │ │ ├── ai.svg │ │ ├── apk.svg │ │ ├── chm.svg │ │ ├── css.svg │ │ ├── doc.svg │ │ ├── docx.svg │ │ ├── dwg.svg │ │ ├── folder.svg │ │ ├── gif.svg │ │ ├── html.svg │ │ ├── jpeg.svg │ │ ├── jpg.svg │ │ ├── log.svg │ │ ├── mp3.svg │ │ ├── mp4.svg │ │ ├── pdf.svg │ │ ├── png.svg │ │ ├── ppt.svg │ │ ├── pptx.svg │ │ ├── psd.svg │ │ ├── rar.svg │ │ ├── svg.svg │ │ ├── torrent.svg │ │ ├── txt.svg │ │ ├── unknown.svg │ │ ├── xls.svg │ │ ├── xlsx.svg │ │ └── zip.svg │ │ ├── FileLogViewer.vue │ │ ├── HtmlViewer.vue │ │ ├── InputInlineEditor.vue │ │ ├── LogViewer.vue │ │ ├── LogViewerDialog.vue │ │ ├── MEmpty.vue │ │ ├── MLoading.vue │ │ ├── PageWebviewStatus.vue │ │ ├── SettingItemInput.vue │ │ ├── SettingItemYesNo.vue │ │ ├── SettingItemYesNoDefault.vue │ │ ├── TaskBizStatus.vue │ │ ├── UpdaterButton.vue │ │ ├── VideoPlayer.vue │ │ ├── WebFileSelectButton.vue │ │ └── index.ts ├── config.ts ├── declarations │ ├── svg.d.ts │ └── type.d.ts ├── entry │ ├── Page.vue │ ├── about.ts │ ├── feedback.ts │ ├── guide.ts │ ├── payment.ts │ ├── setup.ts │ └── user.ts ├── lang │ ├── en-US.json │ ├── index.ts │ ├── source.json │ └── zh-CN.json ├── layouts │ ├── Main.vue │ └── Raw.vue ├── lib │ ├── api.ts │ ├── audio.ts │ ├── components │ │ └── Prompt.vue │ ├── dialog.ts │ ├── env.ts │ ├── error.ts │ ├── event.ts │ ├── file.ts │ ├── linkandroid.ts │ ├── markdown.ts │ ├── storage.ts │ ├── ui.ts │ └── util.ts ├── main.ts ├── pages │ ├── Device.vue │ ├── Home.vue │ ├── PageAbout.vue │ ├── PageFeedback.vue │ ├── PageGuide.vue │ ├── PagePayment.vue │ ├── PageSetup.vue │ ├── PageUser.vue │ ├── Setting.vue │ └── User │ │ └── hook.ts ├── router.ts ├── store │ ├── index.ts │ └── modules │ │ ├── app.ts │ │ ├── device.ts │ │ ├── setting.ts │ │ ├── task.ts │ │ └── user.ts ├── style.less ├── task │ ├── TestAsync.ts │ ├── TestSync.ts │ └── index.ts ├── types │ ├── Common.ts │ ├── Device.ts │ ├── File.ts │ └── Log.ts └── vite-env.d.ts ├── tailwind.config.js ├── third-party └── image-beautifier.sh ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.flat.txt └── vite.config.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 4 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.{yml, yaml}] 15 | indent_size = 4 16 | 17 | [*.{less, css}] 18 | indent_size = 4 19 | 20 | 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | name: 🐞 Bug report 4 | about: Create a report to help us improve 5 | title: "[Bug] the title of bug report" 6 | labels: bug 7 | assignees: '' 8 | 9 | --- 10 | 11 | #### Describe the bug 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/help_wanted.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🥺 Help wanted 3 | about: Confuse about the use of electron-vue-vite 4 | title: "[Help] the title of help wanted report" 5 | labels: help wanted 6 | assignees: '' 7 | 8 | --- 9 | 10 | #### Describe the problem you confuse 11 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### Description 4 | 5 | 6 | 7 | ### What is the purpose of this pull request? 8 | 9 | - [ ] Bug fix 10 | - [ ] New Feature 11 | - [ ] Documentation update 12 | - [ ] Other 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | dist-electron 14 | dist-release 15 | *.local 16 | 17 | # Editor directories and files 18 | .vscode/.debug.env 19 | .idea 20 | .DS_Store 21 | *.suo 22 | *.ntvs* 23 | *.njsproj 24 | *.sln 25 | *.sw? 26 | 27 | # lockfile 28 | package-lock.json 29 | pnpm-lock.yaml 30 | yarn.lock 31 | database.db 32 | 33 | src/lang/source-use.json 34 | /data 35 | /build/ 36 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | # For electron-builder 2 | # https://github.com/electron-userland/electron-builder/issues/6289#issuecomment-1042620422 3 | shamefully-hoist=true 4 | 5 | # For China 🇨🇳 developers 6 | electron_mirror=https://npmmirror.com/mirrors/electron/ 7 | electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/ 8 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 20 2 | -------------------------------------------------------------------------------- /.vscode/.debug.script.mjs: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs' 2 | import path from 'node:path' 3 | import { fileURLToPath } from 'node:url' 4 | import { createRequire } from 'node:module' 5 | import { spawn } from 'node:child_process' 6 | 7 | const pkg = createRequire(import.meta.url)('../package.json') 8 | const __dirname = path.dirname(fileURLToPath(import.meta.url)) 9 | 10 | // write .debug.env 11 | const envContent = Object.entries(pkg.debug.env).map(([key, val]) => `${key}=${val}`) 12 | fs.writeFileSync(path.join(__dirname, '.debug.env'), envContent.join('\n')) 13 | 14 | // bootstrap 15 | spawn( 16 | // TODO: terminate `npm run dev` when Debug exits. 17 | process.platform === 'win32' ? 'npm.cmd' : 'npm', 18 | ['run', 'dev'], 19 | { 20 | stdio: 'inherit', 21 | env: Object.assign(process.env, { VSCODE_DEBUG: 'true' }), 22 | }, 23 | ) 24 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "Vue.volar", 4 | "Vue.vscode-typescript-vue-plugin" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "compounds": [ 7 | { 8 | "name": "Debug App", 9 | "preLaunchTask": "Before Debug", 10 | "configurations": [ 11 | "Debug Main Process", 12 | "Debug Renderer Process" 13 | ], 14 | "presentation": { 15 | "hidden": false, 16 | "group": "", 17 | "order": 1 18 | }, 19 | "stopAll": true 20 | } 21 | ], 22 | "configurations": [ 23 | { 24 | "name": "Debug Main Process", 25 | "type": "node", 26 | "request": "launch", 27 | "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron", 28 | "windows": { 29 | "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd" 30 | }, 31 | "runtimeArgs": [ 32 | "--remote-debugging-port=9229", 33 | "." 34 | ], 35 | "envFile": "${workspaceFolder}/.vscode/.debug.env", 36 | "console": "integratedTerminal" 37 | }, 38 | { 39 | "name": "Debug Renderer Process", 40 | "port": 9229, 41 | "request": "attach", 42 | "type": "chrome", 43 | "timeout": 60000, 44 | "skipFiles": [ 45 | "/**", 46 | "${workspaceRoot}/node_modules/**", 47 | "${workspaceRoot}/dist-electron/**", 48 | // Skip files in host(VITE_DEV_SERVER_URL) 49 | "http://127.0.0.1:3344/**" 50 | ] 51 | }, 52 | ] 53 | } 54 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib", 3 | "typescript.tsc.autoDetect": "off", 4 | "json.schemas": [ 5 | { 6 | "fileMatch": [ 7 | "/*electron-builder.json5", 8 | "/*electron-builder.json" 9 | ], 10 | "url": "https://json.schemastore.org/electron-builder" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "Before Debug", 8 | "type": "shell", 9 | "command": "node .vscode/.debug.script.mjs", 10 | "isBackground": true, 11 | "problemMatcher": { 12 | "owner": "typescript", 13 | "fileLocation": "relative", 14 | "pattern": { 15 | // TODO: correct "regexp" 16 | "regexp": "^([a-zA-Z]\\:\/?([\\w\\-]\/?)+\\.\\w+):(\\d+):(\\d+): (ERROR|WARNING)\\: (.*)$", 17 | "file": 1, 18 | "line": 3, 19 | "column": 4, 20 | "code": 5, 21 | "message": 6 22 | }, 23 | "background": { 24 | "activeOnStart": true, 25 | "beginsPattern": "^.*VITE v.* ready in \\d* ms.*$", 26 | "endsPattern": "^.*\\[startup\\] Electron App.*$" 27 | } 28 | } 29 | } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | build_and_install: 4 | npm run build; 5 | rm -rfv /Applications/LinkAndroid.app; 6 | cp -a ./dist-release/mac-arm64/LinkAndroid.app /Applications 7 | -------------------------------------------------------------------------------- /electron/config/common.ts: -------------------------------------------------------------------------------- 1 | export const CommonConfig = { 2 | darkModeEnable: true, 3 | } 4 | -------------------------------------------------------------------------------- /electron/config/contextMenu.ts: -------------------------------------------------------------------------------- 1 | import contextMenu from 'electron-context-menu'; 2 | 3 | 4 | const init = () => { 5 | contextMenu({ 6 | showSaveImageAs: false, 7 | showCopyLink: false, 8 | showCopyImage: false, 9 | showSelectAll: false, 10 | showInspectElement: false, 11 | showSearchWithGoogle: false, 12 | showLookUpSelection: false, 13 | }); 14 | } 15 | 16 | 17 | export const ConfigContextMenu = { 18 | init 19 | } 20 | -------------------------------------------------------------------------------- /electron/config/icon.ts: -------------------------------------------------------------------------------- 1 | import {buildResolve, extraResolve} from "../lib/env"; 2 | 3 | export const logoPath = buildResolve('logo.png') 4 | export const icoLogoPath = buildResolve('logo.ico') 5 | export const icnsLogoPath = buildResolve('logo.icns') 6 | 7 | export const trayPath = process.platform === 'darwin' 8 | ? extraResolve('mac/tray/iconTemplate.png') 9 | : extraResolve('common/tray/icon.png') 10 | -------------------------------------------------------------------------------- /electron/config/tray.ts: -------------------------------------------------------------------------------- 1 | import {app, Menu, Tray} from 'electron' 2 | import {trayPath} from "./icon"; 3 | import {AppRuntime} from "../mapi/env"; 4 | import {AppConfig} from "../../src/config"; 5 | import {t} from "./lang"; 6 | import {isMac, isWin} from "../lib/env"; 7 | 8 | let tray = null 9 | 10 | const showApp = () => { 11 | if (isMac) { 12 | app.dock.show() 13 | } 14 | AppRuntime.mainWindow.show() 15 | } 16 | 17 | const hideApp = () => { 18 | if (isMac) { 19 | app.dock.hide() 20 | } 21 | AppRuntime.mainWindow.hide() 22 | } 23 | 24 | const quitApp = () => { 25 | app.quit() 26 | } 27 | 28 | const ready = () => { 29 | tray = new Tray(trayPath) 30 | 31 | tray.setToolTip(AppConfig.name) 32 | 33 | if (isWin) { 34 | tray.on('click', () => { 35 | showApp() 36 | }) 37 | } 38 | 39 | const contextMenu = Menu.buildFromTemplate([ 40 | { 41 | label: t('显示主界面'), 42 | click: () => { 43 | showApp() 44 | }, 45 | }, 46 | { 47 | label: t('重启'), 48 | click: () => { 49 | app.relaunch() 50 | quitApp() 51 | }, 52 | }, 53 | { 54 | label: t('退出'), 55 | click: () => { 56 | quitApp() 57 | }, 58 | }, 59 | ]) 60 | 61 | tray.setContextMenu(contextMenu) 62 | } 63 | 64 | const show = () => { 65 | 66 | if (tray) { 67 | tray.destroy() 68 | tray = null 69 | } 70 | } 71 | 72 | 73 | export const ConfigTray = { 74 | ready 75 | } 76 | -------------------------------------------------------------------------------- /electron/config/window.ts: -------------------------------------------------------------------------------- 1 | export const WindowConfig = { 2 | alwaysOpenDevTools: true, 3 | minWidth: 1100, 4 | minHeight: 680, 5 | initWidth: 1100, 6 | initHeight: 680, 7 | guideWidth:800, 8 | guideHeight:540, 9 | paymentWidth: 500, 10 | paymentHeight: 400, 11 | aboutWidth: 500, 12 | aboutHeight: 400, 13 | feedbackWidth: 600, 14 | feedbackHeight: 600, 15 | setupWidth: 800, 16 | setupHeight: 540, 17 | } 18 | -------------------------------------------------------------------------------- /electron/declarations/svg.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.svg' { 2 | const content: string; 3 | export default content; 4 | } 5 | -------------------------------------------------------------------------------- /electron/electron-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare namespace NodeJS { 4 | interface ProcessEnv { 5 | /** 6 | * The built directory structure 7 | * 8 | * ```tree 9 | * ├─┬ dist-electron 10 | * │ ├─┬ main 11 | * │ │ └── index.js > Electron-Main 12 | * │ └─┬ preload 13 | * │ └── index.mjs > Preload-Scripts 14 | * ├─┬ dist 15 | * │ └── index.html > Electron-Renderer 16 | * ``` 17 | */ 18 | APP_ROOT: string 19 | /** /dist/ or /public/ */ 20 | VITE_PUBLIC: string 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /electron/lib/api.ts: -------------------------------------------------------------------------------- 1 | import Apps from "../mapi/app"; 2 | 3 | export type ResultType = { 4 | code: number, 5 | msg: string, 6 | data?: T 7 | } 8 | 9 | export const post = async (url: string, data: any) => { 10 | data = data || {} 11 | const userAgent = Apps.getUserAgent() 12 | data['AppManagerUserAgent'] = userAgent 13 | return await fetch(url, { 14 | method: 'POST', 15 | headers: { 16 | 'User-Agent': userAgent, 17 | 'Content-Type': 'application/json' 18 | }, 19 | body: JSON.stringify(data) 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /electron/lib/hooks.ts: -------------------------------------------------------------------------------- 1 | import {BrowserView, BrowserWindow} from "electron"; 2 | import {AppsMain} from "../mapi/app/main"; 3 | 4 | type HookType = never 5 | | 'Show' 6 | | 'Hide' 7 | 8 | export const executeHooks = async (win: BrowserWindow, hook: HookType, data?: any) => { 9 | const evalJs = ` 10 | if(window.__page && window.__page.hooks && typeof window.__page.hooks.on${hook} === 'function' ) { 11 | try { 12 | window.__page.hooks.on${hook}(${JSON.stringify(data)}); 13 | } catch(e) { 14 | console.log('executeHooks.on${hook}.error', e); 15 | } 16 | }`; 17 | return win.webContents?.executeJavaScript(evalJs); 18 | } 19 | 20 | export const executeDarkMode = async (view: BrowserWindow | BrowserView, data: { 21 | isSystem: boolean, 22 | }) => { 23 | data = Object.assign({ 24 | isSystem: false 25 | }, data) 26 | if (await AppsMain.shouldDarkMode()) { 27 | // body and html 28 | view.webContents.executeJavaScript(` 29 | document.body.setAttribute('data-theme', 'dark'); 30 | document.documentElement.setAttribute('data-theme', 'dark'); 31 | `); 32 | if (data.isSystem) { 33 | view.webContents.executeJavaScript(`document.body.setAttribute('arco-theme', 'dark');`); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /electron/lib/permission.ts: -------------------------------------------------------------------------------- 1 | import {isMac} from "./env"; 2 | 3 | let nodeMacPermissions = null 4 | if (isMac) { 5 | (async () => { 6 | try { 7 | nodeMacPermissions = await import('node-mac-permissions'); 8 | nodeMacPermissions = nodeMacPermissions.default; 9 | // console.log('nodeMacPermissions',nodeMacPermissions); 10 | } catch (e) { 11 | } 12 | })() 13 | } 14 | 15 | export const Permissions = { 16 | async checkAccessibilityAccess(): Promise { 17 | return new Promise((resolve, reject) => { 18 | if (isMac) { 19 | const status = nodeMacPermissions.getAuthStatus('accessibility'); 20 | resolve(status === 'authorized') 21 | } else { 22 | resolve(true); 23 | } 24 | }) 25 | }, 26 | async askAccessibilityAccess() { 27 | nodeMacPermissions.askForAccessibilityAccess() 28 | }, 29 | async checkScreenCaptureAccess(): Promise { 30 | return new Promise((resolve, reject) => { 31 | if (isMac) { 32 | const status = nodeMacPermissions.getAuthStatus('screen'); 33 | resolve(status === 'authorized') 34 | } else { 35 | resolve(true); 36 | } 37 | }) 38 | }, 39 | async askScreenCaptureAccess() { 40 | nodeMacPermissions.askForScreenCaptureAccess(true) 41 | }, 42 | } 43 | -------------------------------------------------------------------------------- /electron/lib/pinyin-util.ts: -------------------------------------------------------------------------------- 1 | import PinyinMatch from 'pinyin-match'; 2 | 3 | export const PinyinUtil = { 4 | match(input, keywords) { 5 | const index = PinyinMatch.match(input, keywords) 6 | let inputMark = input 7 | let similarity = 0 8 | if (index) { 9 | const indexStart = index[0] 10 | const indexEnd = index[1] 11 | inputMark = input.substring(0, indexStart) + '' + input.substring(indexStart, indexEnd + 1) + '' + input.substring(indexEnd + 1) 12 | similarity = (indexEnd - indexStart + 1) / input.length 13 | } 14 | return { 15 | matched: !!index, 16 | inputMark, 17 | similarity 18 | } 19 | }, 20 | mark(text) { 21 | return `${text}` 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /electron/lib/process.ts: -------------------------------------------------------------------------------- 1 | /** 在主进程中获取关键信息存储到环境变量中,从而在预加载脚本中及渲染进程中使用 */ 2 | import {app} from 'electron' 3 | 4 | /** 注意: app.isPackaged 可能被被某些方法改变所以请将该文件放到 main.js 必须位于非依赖项的顶部 */ 5 | import fixPath from 'fix-path' 6 | 7 | if (process.platform === 'darwin') { 8 | fixPath() 9 | } 10 | 11 | process.env.IS_PACKAGED = String(app.isPackaged) 12 | 13 | process.env.DESKTOP_PATH = app.getPath('desktop') 14 | 15 | process.env.CWD = process.cwd() 16 | 17 | export const isDummy = false 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /electron/mapi/app/setup.ts: -------------------------------------------------------------------------------- 1 | import {Permissions} from "../../lib/permission"; 2 | 3 | export const SetupMain = { 4 | async isOk() { 5 | return true 6 | }, 7 | async list() { 8 | return [ 9 | { 10 | name: 'accessibility', 11 | title: '辅助功能', 12 | status: (await Permissions.checkAccessibilityAccess()) ? 'success' : 'fail', 13 | desc: '系统运行需要依赖辅助功能,请打开设置,找到辅助功能,开启本软件的辅助功能。', 14 | steps: [ 15 | { 16 | title: '打开 设置 → 隐私与安全性 → 辅助功能,开启本软件', 17 | image: '/setup/accessibility.png' 18 | }, 19 | ] 20 | }, 21 | { 22 | name: 'screen', 23 | title: '屏幕录制', 24 | status: (await Permissions.checkScreenCaptureAccess()) ? 'success' : 'fail', 25 | desc: '系统运行需要依赖屏幕录制,请打开设置,找到屏幕录制,开启本软件的屏幕录制权限。', 26 | steps: [ 27 | { 28 | title: '打开 设置 → 隐私与安全性 → 录屏与系统录音,开启本软件', 29 | image: '/setup/screen.png' 30 | }, 31 | ] 32 | } 33 | ] 34 | }, 35 | async open(name: string) { 36 | switch (name) { 37 | case 'accessibility': 38 | Permissions.askAccessibilityAccess().then() 39 | break 40 | case 'screen': 41 | Permissions.askScreenCaptureAccess().then() 42 | break 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /electron/mapi/config/render.ts: -------------------------------------------------------------------------------- 1 | import {ipcRenderer} from "electron"; 2 | 3 | const all = async () => { 4 | return ipcRenderer.invoke('config:all') 5 | } 6 | 7 | const get = async (key: string, defaultValue: any = null) => { 8 | return ipcRenderer.invoke('config:get', key, defaultValue) 9 | } 10 | 11 | const set = async (key: string, value: any) => { 12 | return ipcRenderer.invoke('config:set', key, value) 13 | } 14 | 15 | const allEnv = async () => { 16 | return ipcRenderer.invoke('config:allEnv') 17 | } 18 | 19 | const getEnv = async (key: string, defaultValue: any = null) => { 20 | return ipcRenderer.invoke('config:getEnv', key, defaultValue) 21 | } 22 | 23 | const setEnv = async (key: string, value: any) => { 24 | return ipcRenderer.invoke('config:setEnv', key, value) 25 | } 26 | 27 | export default { 28 | all, 29 | get, 30 | set, 31 | allEnv, 32 | getEnv, 33 | setEnv 34 | } 35 | -------------------------------------------------------------------------------- /electron/mapi/db/db.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/mapi/db/db.ts -------------------------------------------------------------------------------- /electron/mapi/db/migration.ts: -------------------------------------------------------------------------------- 1 | const versions = [ 2 | { 3 | version: 0, 4 | up: async (db: DB) => { 5 | // await db.execute(`CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)`); 6 | // console.log('db.insert', await db.insert(`INSERT INTO users (name, email) VALUES (?, ?)`,['Alice', 'alice@example.com'])); 7 | // console.log('db.select', await db.select(`SELECT * FROM users`)); 8 | // console.log('db.first', await db.first(`SELECT * FROM users`)); 9 | } 10 | } 11 | ] 12 | 13 | export default { 14 | versions, 15 | } 16 | 17 | 18 | -------------------------------------------------------------------------------- /electron/mapi/db/render.ts: -------------------------------------------------------------------------------- 1 | import {ipcRenderer} from "electron"; 2 | 3 | 4 | const init = () => { 5 | 6 | } 7 | 8 | const execute = async (sql: string, params: any = []) => { 9 | return ipcRenderer.invoke('db:execute', sql, params) 10 | } 11 | 12 | const insert = async (sql: string, params: any = []) => { 13 | return ipcRenderer.invoke('db:insert', sql, params) 14 | } 15 | 16 | const first = async (sql: string, params: any = []) => { 17 | return ipcRenderer.invoke('db:first', sql, params) 18 | } 19 | 20 | const select = async (sql: string, params: any = []) => { 21 | return ipcRenderer.invoke('db:select', sql, params) 22 | } 23 | 24 | const update = async (sql: string, params: any = []) => { 25 | return ipcRenderer.invoke('db:update', sql, params) 26 | } 27 | 28 | const deletes = async (sql: string, params: any = []) => { 29 | return ipcRenderer.invoke('db:delete', sql, params) 30 | } 31 | 32 | export default { 33 | init, 34 | execute, 35 | insert, 36 | first, 37 | select, 38 | update, 39 | delete: deletes 40 | } 41 | -------------------------------------------------------------------------------- /electron/mapi/db/type.d.ts: -------------------------------------------------------------------------------- 1 | type DB = { 2 | execute(sql: string, params?: any): Promise; 3 | insert(sql: string, params?: any): Promise; 4 | first(sql: string, params?: any): Promise; 5 | select(sql: string, params?: any): Promise; 6 | update(sql: string, params?: any): Promise; 7 | delete(sql: string, params?: any): Promise; 8 | } 9 | -------------------------------------------------------------------------------- /electron/mapi/env.ts: -------------------------------------------------------------------------------- 1 | import {BrowserWindow} from "electron"; 2 | 3 | export const AppEnv = { 4 | isInit: false, 5 | appRoot: null as string, 6 | appData: null as string, 7 | userData: null as string, 8 | } 9 | 10 | export const AppRuntime = { 11 | splashWindow: null as BrowserWindow, 12 | mainWindow: null as BrowserWindow, 13 | windows: {} as Record, 14 | } 15 | 16 | export const waitAppEnvReady = async () => { 17 | while (!AppEnv.isInit) { 18 | await new Promise(resolve => { 19 | setTimeout(resolve, 1000) 20 | }) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /electron/mapi/event/render.ts: -------------------------------------------------------------------------------- 1 | import {ipcRenderer} from "electron"; 2 | 3 | const init = () => { 4 | 5 | } 6 | 7 | const send = (name: string, type: string, data: any = {}) => { 8 | return ipcRenderer.invoke('event:send', name, type, data).then() 9 | } 10 | 11 | const callPage = async (name: string, type: string, data: any, option: any) => { 12 | return ipcRenderer.invoke('event:callPage', name, type, data, option) 13 | } 14 | 15 | const channelSend = async (channel: string, data: any) => { 16 | return ipcRenderer.invoke('event:channelSend', channel, data) 17 | } 18 | 19 | export default { 20 | init, 21 | send, 22 | callPage, 23 | channelSend, 24 | } 25 | -------------------------------------------------------------------------------- /electron/mapi/ffmpeg/render.ts: -------------------------------------------------------------------------------- 1 | import ffmpegPath from "ffmpeg-static"; 2 | import {Apps} from "../app"; 3 | import {binResolve, isPackaged} from "../../lib/env"; 4 | 5 | const getBinPath = () => { 6 | if (isPackaged) { 7 | return binResolve('ffmpeg/ffmpeg') 8 | } 9 | return ffmpegPath 10 | } 11 | 12 | const version = async () => { 13 | const controller = await Apps.spawnShell(`"${getBinPath()}" -version`) 14 | const text = await controller.result() 15 | const match = text.match(/ffmpeg version ([\d.]+)/) 16 | return match ? match[1] : '' 17 | } 18 | 19 | const run = async (args: string[]) => { 20 | const controller = await Apps.spawnShell(`"${getBinPath()}" ${args.join(' ')}`) 21 | return await controller.result() 22 | } 23 | 24 | export default { 25 | version, 26 | run, 27 | } 28 | -------------------------------------------------------------------------------- /electron/mapi/file/main.ts: -------------------------------------------------------------------------------- 1 | import {dialog, ipcMain, shell} from "electron"; 2 | import fileIndex from "./index"; 3 | 4 | ipcMain.handle('file:openFile', async (_, options) => { 5 | const res = await dialog 6 | .showOpenDialog({ 7 | properties: ['openFile'], 8 | ...options 9 | }) 10 | .catch(e => { 11 | }) 12 | if (!res || res.canceled) { 13 | return null 14 | } 15 | return res.filePaths?.[0] || null 16 | }) 17 | 18 | ipcMain.handle('file:openDirectory', async (_, options) => { 19 | const res = await dialog 20 | .showOpenDialog({ 21 | properties: ['openDirectory'], 22 | ...options 23 | }) 24 | .catch(e => { 25 | }) 26 | if (!res || res.canceled) { 27 | return null 28 | } 29 | return res.filePaths?.[0] || null 30 | }) 31 | 32 | ipcMain.handle('file:openSave', async (_, options) => { 33 | const res = await dialog 34 | .showSaveDialog({ 35 | ...options 36 | }) 37 | .catch(e => { 38 | }) 39 | if (!res || res.canceled) { 40 | return null 41 | } 42 | return res.filePath || null 43 | }) 44 | 45 | ipcMain.handle('file:openPath', async (_, path, options) => { 46 | return shell.openPath(path) 47 | }) 48 | 49 | export default { 50 | ...fileIndex, 51 | } 52 | 53 | export const Files = { 54 | ...fileIndex 55 | } 56 | -------------------------------------------------------------------------------- /electron/mapi/file/render.ts: -------------------------------------------------------------------------------- 1 | import fileIndex from './index' 2 | import {ipcRenderer} from "electron"; 3 | 4 | const openFile = async (options: {} = {}) => { 5 | return ipcRenderer.invoke('file:openFile', options) 6 | } 7 | 8 | const openDirectory = async (options: {} = {}) => { 9 | return ipcRenderer.invoke('file:openDirectory', options) 10 | } 11 | 12 | const openSave = async (options: {} = {}) => { 13 | return ipcRenderer.invoke('file:openSave', options) 14 | } 15 | 16 | const openPath = async (path: string, options: {} = {}) => { 17 | return ipcRenderer.invoke('file:openPath', path, options) 18 | } 19 | 20 | export default { 21 | ...fileIndex, 22 | openFile, 23 | openDirectory, 24 | openSave, 25 | openPath, 26 | } 27 | 28 | -------------------------------------------------------------------------------- /electron/mapi/keys/type.ts: -------------------------------------------------------------------------------- 1 | export enum HotkeyMouseButtonEnum { 2 | LEFT = 1, 3 | RIGHT = 2 4 | } 5 | 6 | export type HotkeyKeyItem = { 7 | key: string 8 | // Alt Option 9 | altKey: boolean 10 | // Ctrl Control 11 | ctrlKey: boolean 12 | // Command Win 13 | metaKey: boolean 14 | // Shift 15 | shiftKey: boolean 16 | times: number 17 | } 18 | 19 | export type HotkeyKeySimpleItem = { 20 | type: 'Ctrl' | 'Alt' | 'Meta', 21 | times: number 22 | } 23 | 24 | export type HotkeyMouseItem = { 25 | button: HotkeyMouseButtonEnum 26 | type: 'click' | 'longPress' 27 | clickTimes?: number 28 | } 29 | -------------------------------------------------------------------------------- /electron/mapi/lang/render.ts: -------------------------------------------------------------------------------- 1 | import {ipcRenderer} from "electron"; 2 | 3 | 4 | const writeSourceKey = async (key: string) => { 5 | return ipcRenderer.invoke('lang:writeSourceKey', key) 6 | } 7 | 8 | const writeSourceKeyUse = async (key: string) => { 9 | return ipcRenderer.invoke('lang:writeSourceKeyUse', key) 10 | } 11 | 12 | 13 | export default { 14 | writeSourceKey, 15 | writeSourceKeyUse 16 | } 17 | -------------------------------------------------------------------------------- /electron/mapi/log/main.ts: -------------------------------------------------------------------------------- 1 | import {ipcMain} from "electron"; 2 | import logIndex from './index' 3 | 4 | ipcMain.handle('log:info', (event, label: string, data: any) => { 5 | logIndex.info(label, data) 6 | }) 7 | ipcMain.handle('log:error', (event, label: string, data: any) => { 8 | logIndex.error(label, data) 9 | }) 10 | 11 | export default { 12 | info: logIndex.info, 13 | error: logIndex.error, 14 | } 15 | 16 | export const Log = { 17 | info: logIndex.info, 18 | error: logIndex.error, 19 | } 20 | -------------------------------------------------------------------------------- /electron/mapi/log/render.ts: -------------------------------------------------------------------------------- 1 | import logIndex from './index' 2 | 3 | export default { 4 | root: logIndex.root, 5 | info: logIndex.infoRenderOrMain, 6 | error: logIndex.errorRenderOrMain, 7 | collect: logIndex.collectRenderOrMain, 8 | } 9 | -------------------------------------------------------------------------------- /electron/mapi/main.ts: -------------------------------------------------------------------------------- 1 | import config from "./config/main"; 2 | import log from "./log/main"; 3 | import app from "./app/main"; 4 | import storage from "./storage/main"; 5 | import db from "./db/main"; 6 | import file from "./file/main"; 7 | import event from "./event/main"; 8 | import ui from "./ui"; 9 | import keys from "./keys/main"; 10 | import user from "./user/main"; 11 | import misc from "./misc/main"; 12 | import {UpdaterMain} from "./updater/main"; 13 | 14 | // import server from "./server/main"; 15 | 16 | const $mapi = { 17 | app, 18 | log, 19 | config, 20 | storage, 21 | db, 22 | file, 23 | event, 24 | ui, 25 | keys, 26 | user, 27 | misc, 28 | // server 29 | } 30 | 31 | export const MAPI = { 32 | init() { 33 | $mapi.user.init() 34 | $mapi.db.init() 35 | $mapi.event.init() 36 | }, 37 | ready() { 38 | $mapi.keys.ready() 39 | setTimeout(() => { 40 | UpdaterMain.checkAndNoticeIfNeed().then() 41 | }, 3000) 42 | }, 43 | destroy() { 44 | $mapi.keys.destroy() 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /electron/mapi/misc/main.ts: -------------------------------------------------------------------------------- 1 | import {ipcMain} from "electron"; 2 | 3 | import index from './index' 4 | 5 | ipcMain.handle('misc:getZipFileContent', async (_, path: string, pathInZip: string) => { 6 | return await index.getZipFileContent(path, pathInZip) 7 | }) 8 | ipcMain.handle('misc:unzip', async (_, zipPath: string, dest: string) => { 9 | return await index.unzip(zipPath, dest) 10 | }) 11 | 12 | export default { 13 | ...index, 14 | } 15 | 16 | export const MiscMain = { 17 | ...index 18 | } 19 | -------------------------------------------------------------------------------- /electron/mapi/misc/render.ts: -------------------------------------------------------------------------------- 1 | import index from './index' 2 | 3 | export default { 4 | ...index 5 | } 6 | -------------------------------------------------------------------------------- /electron/mapi/render.ts: -------------------------------------------------------------------------------- 1 | import {exposeContext} from "./util"; 2 | import {AppEnv} from "./env"; 3 | 4 | import config from "./config/render"; 5 | import log from "./log/render"; 6 | import app from "./app/render"; 7 | import storage from "./storage/render"; 8 | import db from "./db/render"; 9 | import file from "./file/render"; 10 | import event from "./event/render"; 11 | import ui from "./ui/render"; 12 | import updater from "./updater/render"; 13 | import statistics from "./statistics/render"; 14 | import lang from "./lang/render"; 15 | import user from "./user/render"; 16 | import misc from "./misc/render"; 17 | 18 | import adb from "./adb/render"; 19 | import scrcpy from "./scrcpy/render"; 20 | import ffmpeg from "./ffmpeg/render"; 21 | 22 | export const MAPI = { 23 | init(env: typeof AppEnv = null) { 24 | if (!env) { 25 | // expose context 26 | exposeContext('$mapi', { 27 | app, 28 | log, 29 | config, 30 | storage, 31 | db, 32 | file, 33 | event, 34 | ui, 35 | updater, 36 | statistics, 37 | lang, 38 | user, 39 | misc, 40 | 41 | adb, 42 | scrcpy, 43 | ffmpeg, 44 | }) 45 | db.init() 46 | event.init() 47 | ui.init() 48 | } else { 49 | // init context 50 | AppEnv.appRoot = env.appRoot 51 | AppEnv.appData = env.appData 52 | AppEnv.userData = env.userData 53 | AppEnv.isInit = true 54 | } 55 | }, 56 | } 57 | -------------------------------------------------------------------------------- /electron/mapi/server/error.ts: -------------------------------------------------------------------------------- 1 | export const mapError = (e) => { 2 | let msg = e 3 | if (e instanceof Error) { 4 | msg = [ 5 | e.message, 6 | e.stack, 7 | ].join("\n") 8 | } else if (typeof e !== 'string') { 9 | msg = e.toString() 10 | } 11 | const map = { 12 | // 'fetch error': '网络错误', 13 | } 14 | for (let key in map) { 15 | if (msg.includes(key)) { 16 | let error = map[key] 17 | // regex PluginReleaseDocFormatError:-11 18 | const regex = new RegExp(`${key}:(-?\\d+)`) 19 | const match = msg.match(regex) 20 | if (match) { 21 | error += `(${match[1]})` 22 | } 23 | return error 24 | } 25 | } 26 | return msg 27 | } 28 | -------------------------------------------------------------------------------- /electron/mapi/server/render.ts: -------------------------------------------------------------------------------- 1 | import {ipcRenderer} from 'electron' 2 | 3 | const start = async (serverInfo: ServerInfo) => { 4 | return ipcRenderer.invoke('server:start', serverInfo) 5 | } 6 | 7 | const ping = async (serverInfo: ServerInfo) => { 8 | return ipcRenderer.invoke('server:ping', serverInfo) 9 | } 10 | 11 | const stop = async (serverInfo: ServerInfo) => { 12 | return ipcRenderer.invoke('server:stop', serverInfo) 13 | } 14 | 15 | const config = async (serverInfo: ServerInfo) => { 16 | return ipcRenderer.invoke('server:config', serverInfo) 17 | } 18 | 19 | const callFunction = async (serverInfo: ServerInfo, method: string, data: any) => { 20 | return ipcRenderer.invoke('server:callFunction', serverInfo, method, data) 21 | } 22 | 23 | export default { 24 | start, 25 | ping, 26 | stop, 27 | config, 28 | callFunction, 29 | } 30 | -------------------------------------------------------------------------------- /electron/mapi/server/type.d.ts: -------------------------------------------------------------------------------- 1 | interface ServerInfo { 2 | localPath: string, 3 | name: string, 4 | version: string, 5 | setting: { 6 | [key: string]: any, 7 | }, 8 | logFile: string, 9 | } 10 | -------------------------------------------------------------------------------- /electron/mapi/statistics/render.ts: -------------------------------------------------------------------------------- 1 | import {AppConfig} from "../../../src/config"; 2 | import {memoryInfo, platformArch, platformName, platformUUID, platformVersion} from "../../lib/env"; 3 | import {post} from "../../lib/api"; 4 | 5 | let tickDataList = [] 6 | 7 | let tickSendTimer = null 8 | 9 | const tickSendAsync = () => { 10 | if (tickSendTimer) { 11 | clearTimeout(tickSendTimer) 12 | tickSendTimer = null 13 | } 14 | if (!AppConfig.statisticsUrl) { 15 | tickDataList = [] 16 | return 17 | } 18 | tickSendTimer = setTimeout(async () => { 19 | tickSendTimer = null 20 | if (!tickDataList.length) { 21 | return 22 | } 23 | // console.log('tickSend', JSON.stringify(tickDataList)) 24 | post(AppConfig.statisticsUrl, { 25 | data: tickDataList, 26 | version: AppConfig.version, 27 | uuid: platformUUID(), 28 | platform: { 29 | name: platformName(), 30 | version: platformVersion(), 31 | arch: platformArch(), 32 | mem: memoryInfo(), 33 | } 34 | }).then(res => { 35 | // console.log('tickSend', tickDataList, res) 36 | }).catch(err => { 37 | // console.error('tickSend', tickDataList, err) 38 | }) 39 | tickDataList = [] 40 | }, 2000) 41 | } 42 | 43 | const tick = (name: string, data: any) => { 44 | tickDataList.push({ 45 | name, 46 | data, 47 | }) 48 | tickSendAsync() 49 | } 50 | 51 | export default { 52 | tick 53 | } 54 | -------------------------------------------------------------------------------- /electron/mapi/storage/render.ts: -------------------------------------------------------------------------------- 1 | import {ipcRenderer} from "electron"; 2 | 3 | 4 | const all = async (group: string) => { 5 | return ipcRenderer.invoke('storage:all', group) 6 | } 7 | 8 | const get = async (group: string, key: string, defaultValue: any) => { 9 | return ipcRenderer.invoke('storage:get', group, key, defaultValue) 10 | } 11 | 12 | const set = async (group: string, key: string, value: any) => { 13 | return ipcRenderer.invoke('storage:set', group, key, value) 14 | } 15 | 16 | export default { 17 | all, 18 | get, 19 | set 20 | } 21 | -------------------------------------------------------------------------------- /electron/mapi/ui/index.ts: -------------------------------------------------------------------------------- 1 | export default {} 2 | -------------------------------------------------------------------------------- /electron/mapi/updater/index.ts: -------------------------------------------------------------------------------- 1 | import {AppConfig} from "../../../src/config"; 2 | import {platformArch, platformName, platformUUID, platformVersion} from "../../lib/env"; 3 | 4 | const checkForUpdate = async () => { 5 | try { 6 | const res = await fetch(AppConfig.updaterUrl, { 7 | method: 'POST', 8 | headers: { 9 | 'Content-Type': 'application/json' 10 | }, 11 | body: JSON.stringify({ 12 | version: AppConfig.version, 13 | uuid: platformUUID(), 14 | platform: { 15 | name: platformName(), 16 | version: platformVersion(), 17 | arch: platformArch(), 18 | } 19 | }) 20 | }) 21 | return await res.json() 22 | } catch (e) { 23 | return { 24 | code: -1, 25 | msg: `Failed to check update : ${e.message}` 26 | } 27 | } 28 | } 29 | 30 | export default { 31 | checkForUpdate, 32 | } 33 | -------------------------------------------------------------------------------- /electron/mapi/updater/main.ts: -------------------------------------------------------------------------------- 1 | import updaterIndex from './index' 2 | import {Log} from "../log/main"; 3 | import {AppConfig} from "../../../src/config"; 4 | import {VersionUtil} from "../../lib/util"; 5 | import {dialog, ipcMain, shell} from "electron"; 6 | import {t} from "../../config/lang"; 7 | import ConfigMain from "../config/main"; 8 | 9 | 10 | ipcMain.handle('updater:getCheckAtLaunch', async (event) => { 11 | return ConfigMain.get('updaterCheckAtLaunch', 'yes') 12 | }) 13 | 14 | ipcMain.handle('updater:setCheckAtLaunch', async (event, value) => { 15 | return ConfigMain.set('updaterCheckAtLaunch', value) 16 | }) 17 | 18 | const checkAndNotice = async () => { 19 | 20 | const checkAtLaunch = await ConfigMain.get('updaterCheckAtLaunch', 'yes') 21 | if ('yes' !== checkAtLaunch) { 22 | return 23 | } 24 | const res = await updaterIndex.checkForUpdate() 25 | if (res.code) { 26 | Log.info('Updater.checkAndNotice.fail', res.msg) 27 | return 28 | } 29 | // res.data.version = '1.0.0' 30 | if (VersionUtil.le(res.data.version, AppConfig.version)) { 31 | return 32 | } 33 | const ignoreVersion = await ConfigMain.get('updaterIgnoreVersion', '') 34 | if (ignoreVersion === res.data.version) { 35 | return 36 | } 37 | const options = { 38 | type: 'info', 39 | title: t('新版本可用'), 40 | message: t('发现新版本 {version},是否立即升级?', { 41 | version: res.data.version 42 | }), 43 | buttons: [t('下载'), t('忽略')], 44 | defaultId: 0, 45 | cancelId: 1, 46 | }; 47 | dialog.showMessageBox(null, options as any) 48 | .then(result => { 49 | if (result.response === 0) { 50 | shell.openExternal(AppConfig.downloadUrl).then() 51 | } else { 52 | ConfigMain.set('updaterIgnoreVersion', res.data.version).then() 53 | } 54 | }) 55 | } 56 | 57 | const checkAndNoticeIfNeed = async () => { 58 | await checkAndNotice() 59 | } 60 | 61 | export const UpdaterMain = { 62 | checkForUpdate: updaterIndex.checkForUpdate, 63 | checkAndNotice, 64 | checkAndNoticeIfNeed, 65 | } 66 | -------------------------------------------------------------------------------- /electron/mapi/updater/render.ts: -------------------------------------------------------------------------------- 1 | import updaterIndex from './index' 2 | import {ipcRenderer} from "electron"; 3 | 4 | const getCheckAtLaunch = async (): Promise<'yes' | 'no'> => { 5 | return ipcRenderer.invoke('updater:getCheckAtLaunch') 6 | } 7 | 8 | const setCheckAtLaunch = async (value: 'yes' | 'no'): Promise => { 9 | return ipcRenderer.invoke('updater:setCheckAtLaunch', value) 10 | } 11 | 12 | export default { 13 | ...updaterIndex, 14 | getCheckAtLaunch, 15 | setCheckAtLaunch, 16 | } 17 | -------------------------------------------------------------------------------- /electron/mapi/user/render.ts: -------------------------------------------------------------------------------- 1 | import {ipcRenderer} from "electron"; 2 | import AppsRender from "../app/render"; 3 | 4 | const open = async (option: any) => { 5 | await AppsRender.windowOpen('user', option) 6 | } 7 | 8 | const get = async (): Promise => { 9 | return ipcRenderer.invoke('user:get') 10 | } 11 | 12 | const refresh = async () => { 13 | return ipcRenderer.invoke('user:refresh') 14 | } 15 | 16 | const getApiToken = async (): Promise => { 17 | return ipcRenderer.invoke('user:getApiToken') 18 | } 19 | 20 | const getWebEnterUrl = async (url: string) => { 21 | return ipcRenderer.invoke('user:getWebEnterUrl', url) 22 | } 23 | 24 | const openWebUrl = async (url: string) => { 25 | return ipcRenderer.invoke('user:openWebUrl', url) 26 | } 27 | 28 | const apiPost = async ( 29 | url: string, 30 | data: Record, 31 | option?: { 32 | catchException?: boolean, 33 | } 34 | ) => { 35 | return ipcRenderer.invoke('user:apiPost', url, data, option) 36 | } 37 | 38 | export default { 39 | open, 40 | get, 41 | refresh, 42 | getApiToken, 43 | getWebEnterUrl, 44 | openWebUrl, 45 | apiPost, 46 | } 47 | -------------------------------------------------------------------------------- /electron/mapi/util.ts: -------------------------------------------------------------------------------- 1 | import {contextBridge} from 'electron' 2 | 3 | export function exposeContext(key, value) { 4 | if (process.contextIsolated) { 5 | try { 6 | contextBridge.exposeInMainWorld(key, value) 7 | } catch (error) { 8 | console.error(error) 9 | } 10 | } else { 11 | window[key] = value 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /electron/page/about.ts: -------------------------------------------------------------------------------- 1 | import {BrowserWindow} from "electron"; 2 | import {preloadDefault} from "../lib/env-main"; 3 | import {AppRuntime} from "../mapi/env"; 4 | import {t} from "../config/lang"; 5 | import {Page} from "./index"; 6 | import {WindowConfig} from "../config/window"; 7 | 8 | export const PageAbout = { 9 | NAME: 'about', 10 | open: async (option: any) => { 11 | const win = new BrowserWindow({ 12 | title: t('关于'), 13 | parent: AppRuntime.mainWindow, 14 | minWidth: WindowConfig.aboutWidth, 15 | minHeight: WindowConfig.aboutHeight, 16 | width: WindowConfig.aboutWidth, 17 | height: WindowConfig.aboutHeight, 18 | webPreferences: { 19 | preload: preloadDefault, 20 | // Warning: Enable nodeIntegration and disable contextIsolation is not secure in production 21 | nodeIntegration: true, 22 | webSecurity: false, 23 | webviewTag: true, 24 | // Consider using contextBridge.exposeInMainWorld 25 | // Read more on https://www.electronjs.org/docs/latest/tutorial/context-isolation 26 | contextIsolation: false, 27 | }, 28 | show: true, 29 | frame: false, 30 | transparent: false, 31 | }); 32 | return Page.openWindow(PageAbout.NAME, win, "page/about.html"); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /electron/page/feedback.ts: -------------------------------------------------------------------------------- 1 | import {BrowserWindow} from "electron"; 2 | import {preloadDefault} from "../lib/env-main"; 3 | import {AppRuntime} from "../mapi/env"; 4 | import {t} from "../config/lang"; 5 | import {Page} from "./index"; 6 | import {WindowConfig} from "../config/window"; 7 | 8 | export const PageFeedback = { 9 | NAME: 'feedback', 10 | open: async (option: any) => { 11 | const win = new BrowserWindow({ 12 | title: t('工单反馈'), 13 | parent: AppRuntime.mainWindow, 14 | minWidth: WindowConfig.feedbackWidth, 15 | minHeight: WindowConfig.feedbackHeight, 16 | width: WindowConfig.feedbackWidth, 17 | height: WindowConfig.feedbackHeight, 18 | webPreferences: { 19 | preload: preloadDefault, 20 | // Warning: Enable nodeIntegration and disable contextIsolation is not secure in production 21 | nodeIntegration: true, 22 | webSecurity: false, 23 | webviewTag: true, 24 | // Consider using contextBridge.exposeInMainWorld 25 | // Read more on https://www.electronjs.org/docs/latest/tutorial/context-isolation 26 | contextIsolation: false, 27 | }, 28 | show: true, 29 | frame: false, 30 | transparent: false, 31 | }); 32 | return Page.openWindow(PageFeedback.NAME, win, "page/feedback.html"); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /electron/page/thirdPartyImageBeautifier.ts: -------------------------------------------------------------------------------- 1 | import {preloadDefault} from "../lib/env-main"; 2 | import {AppRuntime} from "../mapi/env"; 3 | import {BrowserWindow} from "electron"; 4 | import {Page} from "./index"; 5 | import {t} from "../config/lang"; 6 | 7 | export const PageThirdPartyImageBeautifier = { 8 | NAME: 'thirdPartyImageBeautifier', 9 | open: (option: any) => { 10 | const win = new BrowserWindow({ 11 | title: t('截图编辑'), 12 | parent: AppRuntime.mainWindow, 13 | minWidth: 900, 14 | minHeight: 700, 15 | width: 900, 16 | height: 700, 17 | maximizable: true, 18 | hasShadow: true, 19 | center: true, 20 | autoHideMenuBar: true, 21 | webPreferences: { 22 | nodeIntegration: true, 23 | contextIsolation: false, 24 | webSecurity: false, 25 | preload: preloadDefault 26 | }, 27 | show: true, 28 | frame: true, 29 | }); 30 | // win.maximize(); 31 | return Page.openWindow(PageThirdPartyImageBeautifier.NAME, win, "third-party/image-beautifier/index.html"); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /electron/page/user.ts: -------------------------------------------------------------------------------- 1 | import {BrowserWindow, shell} from "electron"; 2 | import {preloadDefault} from "../lib/env-main"; 3 | import {AppRuntime} from "../mapi/env"; 4 | import {t} from "../config/lang"; 5 | import {Page} from "./index"; 6 | import {AppConfig} from "../../src/config"; 7 | import {User} from "../mapi/user/main"; 8 | 9 | export const PageUser = { 10 | NAME: 'user', 11 | open: async (option: { 12 | parent?: BrowserWindow, 13 | }) => { 14 | option = option || {} 15 | let parent = option.parent || AppRuntime.mainWindow 16 | let alwaysOnTop = !parent 17 | const win = new BrowserWindow({ 18 | title: t('用户中心'), 19 | minWidth: 700, 20 | minHeight: 500, 21 | width: 700, 22 | height: 500, 23 | webPreferences: { 24 | nodeIntegration: true, 25 | contextIsolation: false, 26 | webSecurity: false, 27 | preload: preloadDefault, 28 | webviewTag: true, 29 | }, 30 | show: true, 31 | frame: false, 32 | center: true, 33 | transparent: false, 34 | focusable: true, 35 | parent, 36 | alwaysOnTop, 37 | }); 38 | return Page.openWindow(PageUser.NAME, win, "page/user.html"); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /electron/resources/build/appx/Square150x150Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/build/appx/Square150x150Logo.png -------------------------------------------------------------------------------- /electron/resources/build/appx/Square44x44Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/build/appx/Square44x44Logo.png -------------------------------------------------------------------------------- /electron/resources/build/appx/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/build/appx/StoreLogo.png -------------------------------------------------------------------------------- /electron/resources/build/appx/Wide310x150Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/build/appx/Wide310x150Logo.png -------------------------------------------------------------------------------- /electron/resources/build/entitlements.mac.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.allow-jit 6 | 7 | com.apple.security.cs.allow-unsigned-executable-memory 8 | 9 | com.apple.security.cs.allow-dyld-environment-variables 10 | 11 | 12 | -------------------------------------------------------------------------------- /electron/resources/build/logo-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/build/logo-gray.png -------------------------------------------------------------------------------- /electron/resources/build/logo.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/build/logo.icns -------------------------------------------------------------------------------- /electron/resources/build/logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/build/logo.ico -------------------------------------------------------------------------------- /electron/resources/build/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/build/logo.png -------------------------------------------------------------------------------- /electron/resources/build/logo_1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/build/logo_1024x1024.png -------------------------------------------------------------------------------- /electron/resources/extra/common/preload/pip.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function () { 2 | document.addEventListener('click', function (e) { 3 | e.preventDefault(); 4 | e.stopPropagation(); 5 | return false; 6 | }, true); 7 | }); 8 | -------------------------------------------------------------------------------- /electron/resources/extra/common/tray/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/common/tray/icon.ico -------------------------------------------------------------------------------- /electron/resources/extra/common/tray/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/common/tray/icon.png -------------------------------------------------------------------------------- /electron/resources/extra/linux/android-platform-tools-arm64/adb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/linux/android-platform-tools-arm64/adb -------------------------------------------------------------------------------- /electron/resources/extra/linux/android-platform-tools/NOTICE.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/linux/android-platform-tools/NOTICE.txt -------------------------------------------------------------------------------- /electron/resources/extra/linux/android-platform-tools/adb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/linux/android-platform-tools/adb -------------------------------------------------------------------------------- /electron/resources/extra/linux/android-platform-tools/etc1tool: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/linux/android-platform-tools/etc1tool -------------------------------------------------------------------------------- /electron/resources/extra/linux/android-platform-tools/fastboot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/linux/android-platform-tools/fastboot -------------------------------------------------------------------------------- /electron/resources/extra/linux/android-platform-tools/hprof-conv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/linux/android-platform-tools/hprof-conv -------------------------------------------------------------------------------- /electron/resources/extra/linux/android-platform-tools/lib64/libc++.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/linux/android-platform-tools/lib64/libc++.so -------------------------------------------------------------------------------- /electron/resources/extra/linux/android-platform-tools/make_f2fs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/linux/android-platform-tools/make_f2fs -------------------------------------------------------------------------------- /electron/resources/extra/linux/android-platform-tools/make_f2fs_casefold: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/linux/android-platform-tools/make_f2fs_casefold -------------------------------------------------------------------------------- /electron/resources/extra/linux/android-platform-tools/mke2fs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/linux/android-platform-tools/mke2fs -------------------------------------------------------------------------------- /electron/resources/extra/linux/android-platform-tools/mke2fs.conf: -------------------------------------------------------------------------------- 1 | [defaults] 2 | base_features = sparse_super,large_file,filetype,dir_index,ext_attr 3 | default_mntopts = acl,user_xattr 4 | enable_periodic_fsck = 0 5 | blocksize = 4096 6 | inode_size = 256 7 | inode_ratio = 16384 8 | reserved_ratio = 1.0 9 | 10 | [fs_types] 11 | ext3 = { 12 | features = has_journal 13 | } 14 | ext4 = { 15 | features = has_journal,extent,huge_file,dir_nlink,extra_isize,uninit_bg 16 | inode_size = 256 17 | } 18 | ext4dev = { 19 | features = has_journal,extent,huge_file,flex_bg,inline_data,64bit,dir_nlink,extra_isize 20 | inode_size = 256 21 | options = test_fs=1 22 | } 23 | small = { 24 | blocksize = 1024 25 | inode_size = 128 26 | inode_ratio = 4096 27 | } 28 | floppy = { 29 | blocksize = 1024 30 | inode_size = 128 31 | inode_ratio = 8192 32 | } 33 | big = { 34 | inode_ratio = 32768 35 | } 36 | huge = { 37 | inode_ratio = 65536 38 | } 39 | news = { 40 | inode_ratio = 4096 41 | } 42 | largefile = { 43 | inode_ratio = 1048576 44 | blocksize = -1 45 | } 46 | largefile4 = { 47 | inode_ratio = 4194304 48 | blocksize = -1 49 | } 50 | hurd = { 51 | blocksize = 4096 52 | inode_size = 128 53 | } 54 | -------------------------------------------------------------------------------- /electron/resources/extra/linux/android-platform-tools/source.properties: -------------------------------------------------------------------------------- 1 | Pkg.UserSrc=false 2 | Pkg.Revision=35.0.1 3 | -------------------------------------------------------------------------------- /electron/resources/extra/linux/android-platform-tools/sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/linux/android-platform-tools/sqlite3 -------------------------------------------------------------------------------- /electron/resources/extra/mac/android-platform-tools/NOTICE.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/mac/android-platform-tools/NOTICE.txt -------------------------------------------------------------------------------- /electron/resources/extra/mac/android-platform-tools/adb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/mac/android-platform-tools/adb -------------------------------------------------------------------------------- /electron/resources/extra/mac/android-platform-tools/etc1tool: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/mac/android-platform-tools/etc1tool -------------------------------------------------------------------------------- /electron/resources/extra/mac/android-platform-tools/fastboot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/mac/android-platform-tools/fastboot -------------------------------------------------------------------------------- /electron/resources/extra/mac/android-platform-tools/hprof-conv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/mac/android-platform-tools/hprof-conv -------------------------------------------------------------------------------- /electron/resources/extra/mac/android-platform-tools/lib64/libc++.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/mac/android-platform-tools/lib64/libc++.dylib -------------------------------------------------------------------------------- /electron/resources/extra/mac/android-platform-tools/make_f2fs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/mac/android-platform-tools/make_f2fs -------------------------------------------------------------------------------- /electron/resources/extra/mac/android-platform-tools/make_f2fs_casefold: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/mac/android-platform-tools/make_f2fs_casefold -------------------------------------------------------------------------------- /electron/resources/extra/mac/android-platform-tools/mke2fs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/mac/android-platform-tools/mke2fs -------------------------------------------------------------------------------- /electron/resources/extra/mac/android-platform-tools/mke2fs.conf: -------------------------------------------------------------------------------- 1 | [defaults] 2 | base_features = sparse_super,large_file,filetype,dir_index,ext_attr 3 | default_mntopts = acl,user_xattr 4 | enable_periodic_fsck = 0 5 | blocksize = 4096 6 | inode_size = 256 7 | inode_ratio = 16384 8 | reserved_ratio = 1.0 9 | 10 | [fs_types] 11 | ext3 = { 12 | features = has_journal 13 | } 14 | ext4 = { 15 | features = has_journal,extent,huge_file,dir_nlink,extra_isize,uninit_bg 16 | inode_size = 256 17 | } 18 | ext4dev = { 19 | features = has_journal,extent,huge_file,flex_bg,inline_data,64bit,dir_nlink,extra_isize 20 | inode_size = 256 21 | options = test_fs=1 22 | } 23 | small = { 24 | blocksize = 1024 25 | inode_size = 128 26 | inode_ratio = 4096 27 | } 28 | floppy = { 29 | blocksize = 1024 30 | inode_size = 128 31 | inode_ratio = 8192 32 | } 33 | big = { 34 | inode_ratio = 32768 35 | } 36 | huge = { 37 | inode_ratio = 65536 38 | } 39 | news = { 40 | inode_ratio = 4096 41 | } 42 | largefile = { 43 | inode_ratio = 1048576 44 | blocksize = -1 45 | } 46 | largefile4 = { 47 | inode_ratio = 4194304 48 | blocksize = -1 49 | } 50 | hurd = { 51 | blocksize = 4096 52 | inode_size = 128 53 | } 54 | -------------------------------------------------------------------------------- /electron/resources/extra/mac/android-platform-tools/source.properties: -------------------------------------------------------------------------------- 1 | Pkg.UserSrc=false 2 | Pkg.Revision=35.0.1 3 | -------------------------------------------------------------------------------- /electron/resources/extra/mac/android-platform-tools/sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/mac/android-platform-tools/sqlite3 -------------------------------------------------------------------------------- /electron/resources/extra/mac/tray/iconTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/mac/tray/iconTemplate.png -------------------------------------------------------------------------------- /electron/resources/extra/mac/tray/iconTemplate@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/mac/tray/iconTemplate@2x.png -------------------------------------------------------------------------------- /electron/resources/extra/mac/tray/iconTemplate@4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/mac/tray/iconTemplate@4x.png -------------------------------------------------------------------------------- /electron/resources/extra/win/android-platform-tools/AdbWinApi.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/win/android-platform-tools/AdbWinApi.dll -------------------------------------------------------------------------------- /electron/resources/extra/win/android-platform-tools/AdbWinUsbApi.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/win/android-platform-tools/AdbWinUsbApi.dll -------------------------------------------------------------------------------- /electron/resources/extra/win/android-platform-tools/NOTICE.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/win/android-platform-tools/NOTICE.txt -------------------------------------------------------------------------------- /electron/resources/extra/win/android-platform-tools/adb.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/win/android-platform-tools/adb.exe -------------------------------------------------------------------------------- /electron/resources/extra/win/android-platform-tools/etc1tool.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/win/android-platform-tools/etc1tool.exe -------------------------------------------------------------------------------- /electron/resources/extra/win/android-platform-tools/fastboot.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/win/android-platform-tools/fastboot.exe -------------------------------------------------------------------------------- /electron/resources/extra/win/android-platform-tools/hprof-conv.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/win/android-platform-tools/hprof-conv.exe -------------------------------------------------------------------------------- /electron/resources/extra/win/android-platform-tools/libwinpthread-1.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/win/android-platform-tools/libwinpthread-1.dll -------------------------------------------------------------------------------- /electron/resources/extra/win/android-platform-tools/make_f2fs.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/win/android-platform-tools/make_f2fs.exe -------------------------------------------------------------------------------- /electron/resources/extra/win/android-platform-tools/make_f2fs_casefold.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/win/android-platform-tools/make_f2fs_casefold.exe -------------------------------------------------------------------------------- /electron/resources/extra/win/android-platform-tools/mke2fs.conf: -------------------------------------------------------------------------------- 1 | [defaults] 2 | base_features = sparse_super,large_file,filetype,dir_index,ext_attr 3 | default_mntopts = acl,user_xattr 4 | enable_periodic_fsck = 0 5 | blocksize = 4096 6 | inode_size = 256 7 | inode_ratio = 16384 8 | reserved_ratio = 1.0 9 | 10 | [fs_types] 11 | ext3 = { 12 | features = has_journal 13 | } 14 | ext4 = { 15 | features = has_journal,extent,huge_file,dir_nlink,extra_isize,uninit_bg 16 | inode_size = 256 17 | } 18 | ext4dev = { 19 | features = has_journal,extent,huge_file,flex_bg,inline_data,64bit,dir_nlink,extra_isize 20 | inode_size = 256 21 | options = test_fs=1 22 | } 23 | small = { 24 | blocksize = 1024 25 | inode_size = 128 26 | inode_ratio = 4096 27 | } 28 | floppy = { 29 | blocksize = 1024 30 | inode_size = 128 31 | inode_ratio = 8192 32 | } 33 | big = { 34 | inode_ratio = 32768 35 | } 36 | huge = { 37 | inode_ratio = 65536 38 | } 39 | news = { 40 | inode_ratio = 4096 41 | } 42 | largefile = { 43 | inode_ratio = 1048576 44 | blocksize = -1 45 | } 46 | largefile4 = { 47 | inode_ratio = 4194304 48 | blocksize = -1 49 | } 50 | hurd = { 51 | blocksize = 4096 52 | inode_size = 128 53 | } 54 | -------------------------------------------------------------------------------- /electron/resources/extra/win/android-platform-tools/mke2fs.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/win/android-platform-tools/mke2fs.exe -------------------------------------------------------------------------------- /electron/resources/extra/win/android-platform-tools/source.properties: -------------------------------------------------------------------------------- 1 | Pkg.UserSrc=false 2 | Pkg.Revision=35.0.1 3 | -------------------------------------------------------------------------------- /electron/resources/extra/win/android-platform-tools/sqlite3.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/win/android-platform-tools/sqlite3.exe -------------------------------------------------------------------------------- /electron/resources/extra/win/scrcpy/AdbWinApi.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/win/scrcpy/AdbWinApi.dll -------------------------------------------------------------------------------- /electron/resources/extra/win/scrcpy/AdbWinUsbApi.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/win/scrcpy/AdbWinUsbApi.dll -------------------------------------------------------------------------------- /electron/resources/extra/win/scrcpy/SDL2.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/win/scrcpy/SDL2.dll -------------------------------------------------------------------------------- /electron/resources/extra/win/scrcpy/adb.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/win/scrcpy/adb.exe -------------------------------------------------------------------------------- /electron/resources/extra/win/scrcpy/avcodec-61.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/win/scrcpy/avcodec-61.dll -------------------------------------------------------------------------------- /electron/resources/extra/win/scrcpy/avformat-61.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/win/scrcpy/avformat-61.dll -------------------------------------------------------------------------------- /electron/resources/extra/win/scrcpy/avutil-59.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/win/scrcpy/avutil-59.dll -------------------------------------------------------------------------------- /electron/resources/extra/win/scrcpy/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/win/scrcpy/icon.png -------------------------------------------------------------------------------- /electron/resources/extra/win/scrcpy/libusb-1.0.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/win/scrcpy/libusb-1.0.dll -------------------------------------------------------------------------------- /electron/resources/extra/win/scrcpy/open_a_terminal_here.bat: -------------------------------------------------------------------------------- 1 | @cmd 2 | -------------------------------------------------------------------------------- /electron/resources/extra/win/scrcpy/scrcpy-console.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | scrcpy.exe --pause-on-exit=if-error %* 3 | -------------------------------------------------------------------------------- /electron/resources/extra/win/scrcpy/scrcpy-noconsole.vbs: -------------------------------------------------------------------------------- 1 | strCommand = "cmd /c scrcpy.exe" 2 | 3 | For Each Arg In WScript.Arguments 4 | strCommand = strCommand & " """ & replace(Arg, """", """""""""") & """" 5 | Next 6 | 7 | CreateObject("Wscript.Shell").Run strCommand, 0, false 8 | -------------------------------------------------------------------------------- /electron/resources/extra/win/scrcpy/scrcpy-server: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/win/scrcpy/scrcpy-server -------------------------------------------------------------------------------- /electron/resources/extra/win/scrcpy/scrcpy.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/win/scrcpy/scrcpy.exe -------------------------------------------------------------------------------- /electron/resources/extra/win/scrcpy/swresample-5.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/electron/resources/extra/win/scrcpy/swresample-5.dll -------------------------------------------------------------------------------- /entitlements.mac.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.cs.allow-unsigned-executable-memory 10 | 11 | com.apple.security.cs.allow-dyld-environment-variables 12 | 13 | com.apple.security.cs.disable-library-validation 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %name% 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /page/about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %name% 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /page/feedback.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %name% 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /page/guide.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %name% 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /page/payment.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %name% 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /page/setup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %name% 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /page/user.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %name% 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/iconfont/iconfont.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "iconfont"; /* Project id 4682396 */ 3 | src: url('iconfont.woff2?t=1732330502678') format('woff2'), 4 | url('iconfont.woff?t=1732330502678') format('woff'), 5 | url('iconfont.ttf?t=1732330502678') format('truetype'); 6 | } 7 | 8 | .iconfont { 9 | font-family: "iconfont" !important; 10 | font-size: 16px; 11 | font-style: normal; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | } 15 | 16 | .icon-empty-box:before { 17 | content: "\e618"; 18 | } 19 | 20 | .icon-mic:before { 21 | content: "\e713"; 22 | } 23 | 24 | .icon-refresh-circle:before { 25 | content: "\e61a"; 26 | } 27 | 28 | .icon-cut:before { 29 | content: "\e861"; 30 | } 31 | 32 | .icon-network:before { 33 | content: "\ea7c"; 34 | } 35 | 36 | .icon-usb:before { 37 | content: "\e652"; 38 | } 39 | 40 | .icon-mirror:before { 41 | content: "\e674"; 42 | } 43 | 44 | .icon-stop:before { 45 | content: "\ea89"; 46 | } 47 | 48 | .icon-video:before { 49 | content: "\e612"; 50 | } 51 | 52 | .icon-android:before { 53 | content: "\e703"; 54 | } 55 | 56 | .icon-camera:before { 57 | content: "\e77d"; 58 | } 59 | 60 | .icon-apk:before { 61 | content: "\e611"; 62 | } 63 | 64 | .icon-logo:before { 65 | content: "\e66b"; 66 | } 67 | 68 | .icon-avatar:before { 69 | content: "\e604"; 70 | } 71 | 72 | .icon-github:before { 73 | content: "\e732"; 74 | } 75 | 76 | .icon-gitee:before { 77 | content: "\e601"; 78 | } 79 | 80 | .icon-close:before { 81 | content: "\e61b"; 82 | } 83 | 84 | .icon-min:before { 85 | content: "\e67a"; 86 | } 87 | 88 | .icon-max:before { 89 | content: "\e665"; 90 | } 91 | 92 | -------------------------------------------------------------------------------- /public/iconfont/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/public/iconfont/iconfont.ttf -------------------------------------------------------------------------------- /public/iconfont/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/public/iconfont/iconfont.woff -------------------------------------------------------------------------------- /public/iconfont/iconfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/public/iconfont/iconfont.woff2 -------------------------------------------------------------------------------- /public/logo-white.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/splash.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | %name% 7 | 8 | 9 |
10 |
11 |
12 | 13 |
14 |
%name%
15 |
v%version%
16 |
17 |
18 |
19 |
%slogan%
20 |
高清投屏 · 美化截图 · 文件管理 · 应用安装
21 |
22 |
23 | 正在启动 ... 24 |
25 |
26 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /public/third-party/image-beautifier/assets/demo-CN-KDTod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/public/third-party/image-beautifier/assets/demo-CN-KDTod.png -------------------------------------------------------------------------------- /public/third-party/image-beautifier/assets/favicon-Bv5yausS.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /public/third-party/image-beautifier/assets/imac-pro-BKp3IfOa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/public/third-party/image-beautifier/assets/imac-pro-BKp3IfOa.png -------------------------------------------------------------------------------- /public/third-party/image-beautifier/assets/ipadpro-DABKnEyW.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/public/third-party/image-beautifier/assets/ipadpro-DABKnEyW.png -------------------------------------------------------------------------------- /public/third-party/image-beautifier/assets/iphonepro-DwunDMnR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/public/third-party/image-beautifier/assets/iphonepro-DwunDMnR.png -------------------------------------------------------------------------------- /public/third-party/image-beautifier/assets/macbook-air-ByryNZsX.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/public/third-party/image-beautifier/assets/macbook-air-ByryNZsX.png -------------------------------------------------------------------------------- /public/third-party/image-beautifier/assets/macbook-pro-16-Phbq9ZkC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/public/third-party/image-beautifier/assets/macbook-pro-16-Phbq9ZkC.png -------------------------------------------------------------------------------- /public/third-party/image-beautifier/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /screenshots/cn/appmanage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/screenshots/cn/appmanage.png -------------------------------------------------------------------------------- /screenshots/cn/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/screenshots/cn/home.png -------------------------------------------------------------------------------- /screenshots/cn/mirror.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/screenshots/cn/mirror.png -------------------------------------------------------------------------------- /screenshots/cn/screenrecord.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/screenshots/cn/screenrecord.png -------------------------------------------------------------------------------- /screenshots/cn/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/screenshots/cn/screenshot.png -------------------------------------------------------------------------------- /screenshots/cn/shell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/screenshots/cn/shell.png -------------------------------------------------------------------------------- /screenshots/en/appmanage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/screenshots/en/appmanage.png -------------------------------------------------------------------------------- /screenshots/en/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/screenshots/en/home.png -------------------------------------------------------------------------------- /screenshots/en/mirror.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/screenshots/en/mirror.png -------------------------------------------------------------------------------- /screenshots/en/screenrecord.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/screenshots/en/screenrecord.png -------------------------------------------------------------------------------- /screenshots/en/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/screenshots/en/screenshot.png -------------------------------------------------------------------------------- /screenshots/en/shell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/screenshots/en/shell.png -------------------------------------------------------------------------------- /scripts/notarize.cjs: -------------------------------------------------------------------------------- 1 | const {notarize} = require("@electron/notarize"); 2 | 3 | exports.default = async function notarizing(context) { 4 | const appName = context.packager.appInfo.productFilename; 5 | const {electronPlatformName, appOutDir} = context; 6 | console.log(` • Notarization Start`); 7 | // We skip notarization if the process is not running on MacOS and 8 | // if the enviroment variable SKIP_NOTARIZE is set to `true` 9 | // This is useful for local testing where notarization is useless 10 | if ( 11 | electronPlatformName !== "darwin" || 12 | process.env.SKIP_NOTARIZE === "true" 13 | ) { 14 | console.log(` • Skipping notarization`); 15 | return; 16 | } 17 | 18 | // THIS MUST BE THE SAME AS THE `appId` property 19 | // in your electron builder configuration 20 | const appId = "LinkAndroid"; 21 | 22 | let appPath = `${appOutDir}/${appName}.app`; 23 | let {APPLE_ID, APPLE_ID_PASSWORD, APPLE_TEAM_ID} = process.env; 24 | if (!APPLE_ID) { 25 | console.info(" • Notarization ignore: APPLE_ID is empty"); 26 | return; 27 | } 28 | const notarizeOption = { 29 | tool: "notarytool", 30 | appBundleId: appId, 31 | appPath, 32 | appleId: APPLE_ID, 33 | appleIdPassword: APPLE_ID_PASSWORD, 34 | teamId: APPLE_TEAM_ID, 35 | verbose: true, 36 | } 37 | console.log(` • Notarizing`, `appPath:${appPath} notarizeOption:${JSON.stringify(notarizeOption)}`); 38 | try { 39 | const result = await notarize(notarizeOption); 40 | console.log(" • Notarization successful!"); 41 | return result; 42 | } catch (error) { 43 | console.error(" • Notarization failed:", error.message); 44 | console.error(" • Stack trace:", error.stack); 45 | } 46 | 47 | }; 48 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 25 | -------------------------------------------------------------------------------- /src/api/types/base.ts: -------------------------------------------------------------------------------- 1 | interface ApiResult { 2 | code: number 3 | msg: string 4 | data: T 5 | } 6 | -------------------------------------------------------------------------------- /src/api/user.ts: -------------------------------------------------------------------------------- 1 | import {request,} from "../lib/api"; 2 | 3 | export function userInfoApi(): Promise> { 9 | return request({ 10 | url: "app_manager/user_info", 11 | method: "post" 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /src/assets/fonts/AlibabaPuHuiTi-3-55-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modstart-lib/linkandroid/b6ecd26998a22b4cfb74366b19350cdb19e16c7a/src/assets/fonts/AlibabaPuHuiTi-3-55-Regular.ttf -------------------------------------------------------------------------------- /src/assets/image/gitee.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/image/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/image/logo-white.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/image/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Device/App/Icon/android.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Device/App/NameInfo.json: -------------------------------------------------------------------------------- 1 | { 2 | "cn.xuexi.android": "学习强国", 3 | "com.xingin.xhs": "小红书", 4 | "com.taobao.taobao": "手机淘宝", 5 | "com.eg.android.AlipayGphone": "支付宝", 6 | "com.cainiao.wireless": "菜鸟裹裹", 7 | "com.alibaba.android.rimet": "钉钉", 8 | "com.tencent.mobileqq": "QQ", 9 | "com.tencent.mm": "微信", 10 | "com.tencent.tmgp.sgame": "王者荣耀", 11 | "com.tencent.karaoke": "全民K歌", 12 | "com.hpbr.bosszhipin": "BOSS直聘", 13 | "com.qiyi.video": "爱奇艺", 14 | "com.sankuai.meituan": "美团", 15 | "com.sankuai.meituan.takeoutnew": "美团外卖", 16 | "com.MobileTicket": "铁路12306", 17 | "com.anjuke.android.app": "安居客", 18 | "com.tencent.edu": "腾讯课堂", 19 | "com.xunmeng.pinduoduo": "拼多多", 20 | "com.suning.mobile.ebuy": "苏宁易购", 21 | "com.jingdong.app.mall": "京东", 22 | "ctrip.android.view": "携程旅行", 23 | "com.achievo.vipshop": "唯品会", 24 | "com.netease.newsreader.activity": "网易新闻", 25 | "com.ss.android.article.lite": "今日头条极速版", 26 | "com.ss.android.article.news": "今日头条", 27 | "com.UCMobile": "UC浏览器", 28 | "com.ss.android.ugc.aweme": "抖音短视频", 29 | "com.sina.weibo": "微博", 30 | "com.greenpoint.android.mc10086.activity": "中国移动", 31 | "com.sinovatech.unicom.ui": "手机营业厅(联通)", 32 | "com.baidu.tieba": "百度贴吧", 33 | "com.baidu.input_huawei": "百度输入法华为版", 34 | "com.qihoo.appstore": "360手机助手", 35 | "com.android.chrome": "谷歌浏览器", 36 | "com.ddsy.songyao": "叮当快药" 37 | } 38 | -------------------------------------------------------------------------------- /src/components/Device/DeviceActionApp.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 37 | 38 | 41 | -------------------------------------------------------------------------------- /src/components/Device/DeviceActionConnect.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 33 | 34 | 37 | -------------------------------------------------------------------------------- /src/components/Device/DeviceActionDisconnect.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 33 | 34 | 37 | -------------------------------------------------------------------------------- /src/components/Device/DeviceActionMirror.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 37 | 38 | 41 | -------------------------------------------------------------------------------- /src/components/Device/DeviceActionMirrorCamera.vue: -------------------------------------------------------------------------------- 1 | 42 | 43 | 48 | 49 | 52 | -------------------------------------------------------------------------------- /src/components/Device/DeviceActionMirrorOTG.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 45 | 46 | 49 | -------------------------------------------------------------------------------- /src/components/Device/DeviceActionRecord.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 22 | 23 | 26 | -------------------------------------------------------------------------------- /src/components/Device/DeviceActionScreenshot.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 37 | 38 | 41 | -------------------------------------------------------------------------------- /src/components/Device/DeviceActionWifiOff.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 35 | 36 | 39 | -------------------------------------------------------------------------------- /src/components/Device/DeviceActionWifiOn.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 39 | 40 | 43 | -------------------------------------------------------------------------------- /src/components/Device/DeviceAppIcon.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 32 | 33 | 47 | -------------------------------------------------------------------------------- /src/components/Device/DeviceAppInstallDialog.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 53 | 54 | -------------------------------------------------------------------------------- /src/components/Device/DeviceConnectWifiDialog.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 64 | 65 | -------------------------------------------------------------------------------- /src/components/Device/DeviceShellDialog.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 57 | -------------------------------------------------------------------------------- /src/components/Device/DeviceStatus.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 26 | 27 | -------------------------------------------------------------------------------- /src/components/Device/DeviceType.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 26 | 27 | -------------------------------------------------------------------------------- /src/components/Device/Shell/adb.ts: -------------------------------------------------------------------------------- 1 | import {debounce} from 'lodash-es' 2 | import {createStderr, createStdout, textFormatter} from 'vue-command' 3 | import {useFixCursor} from "./index"; 4 | import {t} from "../../../lang"; 5 | 6 | export const useAdbCommand = ({loading, vueCommand, history}) => { 7 | const adbCommand = async (args) => { 8 | loading.value = true 9 | const command = args.slice(1).join(' ') 10 | const appendToHistory = debounce(vueCommand.value.appendToHistory, 500) 11 | let stdoutText = '' 12 | let stderrText = '' 13 | window.$mapi.adb.adbSpawnShell(command, { 14 | stdout(text) { 15 | loading.value = false 16 | stdoutText += text 17 | useFixCursor(history) 18 | appendToHistory(createStdout(stdoutText)) 19 | }, 20 | stderr(text) { 21 | loading.value = false 22 | stderrText += text 23 | useFixCursor(history) 24 | appendToHistory(createStderr(stderrText)) 25 | }, 26 | success() { 27 | if (!stdoutText) { 28 | stdoutText += t('执行成功') 29 | } 30 | useFixCursor(history) 31 | appendToHistory(createStderr(stdoutText)) 32 | loading.value = false 33 | }, 34 | error() { 35 | if (!stdoutText) { 36 | stdoutText += t('执行失败') 37 | } 38 | useFixCursor(history) 39 | appendToHistory(createStderr(stdoutText)) 40 | loading.value = false 41 | } 42 | }) 43 | return textFormatter('Waiting...') 44 | } 45 | 46 | return { 47 | adbCommand 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/components/Device/Shell/basic.ts: -------------------------------------------------------------------------------- 1 | import {createQuery, createStdout, listFormatter} from "vue-command"; 2 | import {t} from "../../../lang"; 3 | 4 | export const useBasicCommand = ({loading, vueCommand, history, commands}) => { 5 | const clearCommand = () => { 6 | history.value = [] 7 | return createQuery() 8 | } 9 | 10 | const helpCommand = () => { 11 | const commandList = Object.keys(commands.value) 12 | return createStdout(listFormatter(t('支持的命令:'), ...commandList)) 13 | } 14 | 15 | return { 16 | clearCommand, 17 | helpCommand 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/components/Device/Shell/index.ts: -------------------------------------------------------------------------------- 1 | import {textFormatter} from 'vue-command' 2 | 3 | export function useFixCursor(history) { 4 | const length = history.value.length 5 | if (history.value[length - 1]?.__name === 'VueCommandQuery') { 6 | history.value.splice(length - 1, 1, textFormatter('Waiting...')) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/components/Device/Shell/scrcpy.ts: -------------------------------------------------------------------------------- 1 | import {debounce} from 'lodash-es' 2 | import {createStderr, createStdout, textFormatter} from 'vue-command' 3 | import {useFixCursor} from "./index"; 4 | import {t} from "../../../lang"; 5 | 6 | export const useScrcpyCommand = ({loading, vueCommand, history}) => { 7 | const scrcpyCommand = async (args) => { 8 | loading.value = true 9 | const command = args.slice(1).join(' ') 10 | const appendToHistory = debounce(vueCommand.value.appendToHistory, 500) 11 | let stdoutText = '' 12 | let stderrText = '' 13 | window.$mapi.scrcpy.spawnShell(command, { 14 | stdout(text) { 15 | loading.value = false 16 | stdoutText += text 17 | useFixCursor(history) 18 | appendToHistory(createStdout(stdoutText)) 19 | }, 20 | stderr(text) { 21 | loading.value = false 22 | stderrText += text 23 | useFixCursor(history) 24 | appendToHistory(createStderr(stderrText)) 25 | }, 26 | success() { 27 | if (!stdoutText) { 28 | stdoutText += t('执行成功') 29 | } 30 | useFixCursor(history) 31 | appendToHistory(createStderr(stdoutText)) 32 | loading.value = false 33 | }, 34 | error() { 35 | if (!stdoutText) { 36 | stdoutText += t('执行失败') 37 | } 38 | useFixCursor(history) 39 | appendToHistory(createStderr(stdoutText)) 40 | loading.value = false 41 | } 42 | }) 43 | return textFormatter('Waiting...') 44 | } 45 | 46 | return { 47 | scrcpyCommand 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/components/Setting/SettingBasic.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 50 | 51 | 54 | -------------------------------------------------------------------------------- /src/components/common/CodeViewerDialog.vue: -------------------------------------------------------------------------------- 1 | 60 | 61 | 74 | 75 | 81 | 82 | -------------------------------------------------------------------------------- /src/components/common/FeedbackTicketButton.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 19 | -------------------------------------------------------------------------------- /src/components/common/FileExtAssets/ai.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/components/common/FileExtAssets/chm.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/components/common/FileExtAssets/doc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/components/common/FileExtAssets/docx.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/components/common/FileExtAssets/dwg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | dwg 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/components/common/FileExtAssets/folder.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/common/FileExtAssets/html.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/components/common/FileExtAssets/log.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/components/common/FileExtAssets/mp4.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | 13 | -------------------------------------------------------------------------------- /src/components/common/FileExtAssets/ppt.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 21 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/components/common/FileExtAssets/pptx.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 21 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/components/common/FileExtAssets/psd.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/components/common/FileExtAssets/rar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | 9 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/components/common/FileExtAssets/torrent.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | 14 | 15 | -------------------------------------------------------------------------------- /src/components/common/FileExtAssets/txt.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | 14 | 15 | -------------------------------------------------------------------------------- /src/components/common/FileExtAssets/unknown.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | 13 | -------------------------------------------------------------------------------- /src/components/common/FileExtAssets/xls.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 21 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/components/common/FileExtAssets/zip.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | 9 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/components/common/FileLogViewer.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 48 | 49 | 52 | -------------------------------------------------------------------------------- /src/components/common/HtmlViewer.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | 11 | 16 | -------------------------------------------------------------------------------- /src/components/common/InputInlineEditor.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 49 | -------------------------------------------------------------------------------- /src/components/common/LogViewer.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 36 | 37 | 40 | -------------------------------------------------------------------------------- /src/components/common/LogViewerDialog.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 50 | 51 | -------------------------------------------------------------------------------- /src/components/common/MEmpty.vue: -------------------------------------------------------------------------------- 1 | 16 | 18 | -------------------------------------------------------------------------------- /src/components/common/MLoading.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /src/components/common/PageWebviewStatus.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 49 | 50 | 53 | -------------------------------------------------------------------------------- /src/components/common/SettingItemInput.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /src/components/common/SettingItemYesNo.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 11 | 12 | 15 | -------------------------------------------------------------------------------- /src/components/common/SettingItemYesNoDefault.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | 13 | 16 | -------------------------------------------------------------------------------- /src/components/common/TaskBizStatus.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 59 | 60 | -------------------------------------------------------------------------------- /src/components/common/UpdaterButton.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 41 | -------------------------------------------------------------------------------- /src/components/common/VideoPlayer.vue: -------------------------------------------------------------------------------- 1 | 48 | 49 | 53 | 54 | 57 | -------------------------------------------------------------------------------- /src/components/common/WebFileSelectButton.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 30 | 31 | 34 | -------------------------------------------------------------------------------- /src/components/common/index.ts: -------------------------------------------------------------------------------- 1 | import MLoading from "./MLoading.vue"; 2 | import MEmpty from "./MEmpty.vue"; 3 | 4 | export const CommonComponents = { 5 | install(Vue: any) { 6 | Vue.component('m-loading', MLoading); 7 | Vue.component('m-empty', MEmpty); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | import packageJson from '../package.json'; 2 | import {TimeUtil} from "../electron/lib/util"; 3 | 4 | const BASE_URL = 'https://linkandroid.com'; 5 | 6 | export const AppConfig = { 7 | name: 'LinkAndroid', 8 | slogan: 'Link android to PC easily', 9 | version: packageJson.version, 10 | website: `${BASE_URL}`, 11 | websiteGithub: 'https://github.com/modstart-lib/linkandroid', 12 | websiteGitee: 'https://gitee.com/modstart-lib/linkandroid', 13 | apiBaseUrl: `${BASE_URL}/api`, 14 | updaterUrl: `${BASE_URL}/app_manager/updater`, 15 | downloadUrl: `${BASE_URL}/app_manager/download`, 16 | feedbackUrl: `${BASE_URL}/feedback_ticket`, 17 | statisticsUrl: `${BASE_URL}/app_manager/collect`, 18 | guideUrl: `${BASE_URL}/app_manager/guide`, 19 | helpUrl: `${BASE_URL}/app_manager/help`, 20 | basic: { 21 | userEnable: false, 22 | }, 23 | } 24 | 25 | -------------------------------------------------------------------------------- /src/declarations/svg.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.svg' { 2 | const content: string; 3 | export default content; 4 | } 5 | -------------------------------------------------------------------------------- /src/entry/Page.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 55 | -------------------------------------------------------------------------------- /src/entry/about.ts: -------------------------------------------------------------------------------- 1 | import {createApp} from 'vue' 2 | import store from "../store"; 3 | 4 | import ArcoVue, {Message} from '@arco-design/web-vue' 5 | import ArcoVueIcon from '@arco-design/web-vue/es/icon' 6 | import '@arco-design/web-vue/dist/arco.css' 7 | 8 | import {i18n, t} from "../lang"; 9 | 10 | import '../style.less' 11 | import {Dialog} from "../lib/dialog"; 12 | 13 | import {CommonComponents} from "../components/common"; 14 | import Page from "./Page.vue"; 15 | import PageAbout from "../pages/PageAbout.vue"; 16 | 17 | const app = createApp(Page, { 18 | name: 'about', 19 | title: t('关于'), 20 | page: PageAbout 21 | }) 22 | app.use(ArcoVue) 23 | app.use(ArcoVueIcon) 24 | app.use(CommonComponents) 25 | app.use(i18n) 26 | app.use(store) 27 | Message._context = app._context 28 | app.config.globalProperties.$mapi = window.$mapi 29 | app.config.globalProperties.$dialog = Dialog 30 | app.config.globalProperties.$t = t as any 31 | app.mount('#app') 32 | .$nextTick(() => { 33 | postMessage({payload: 'removeLoading'}, '*') 34 | }) 35 | -------------------------------------------------------------------------------- /src/entry/feedback.ts: -------------------------------------------------------------------------------- 1 | import {createApp} from 'vue' 2 | import store from "../store"; 3 | 4 | import ArcoVue, {Message} from '@arco-design/web-vue' 5 | import ArcoVueIcon from '@arco-design/web-vue/es/icon' 6 | import '@arco-design/web-vue/dist/arco.css' 7 | 8 | import {i18n, t} from "../lang"; 9 | 10 | import '../style.less' 11 | import {Dialog} from "../lib/dialog"; 12 | 13 | import {CommonComponents} from "../components/common"; 14 | import Page from "./Page.vue"; 15 | import PageFeedback from "../pages/PageFeedback.vue"; 16 | 17 | const app = createApp(Page, { 18 | name: 'feedback', 19 | title: t('工单反馈'), 20 | page: PageFeedback 21 | }) 22 | app.use(ArcoVue) 23 | app.use(ArcoVueIcon) 24 | app.use(CommonComponents) 25 | app.use(i18n) 26 | app.use(store) 27 | Message._context = app._context 28 | app.config.globalProperties.$mapi = window.$mapi 29 | app.config.globalProperties.$dialog = Dialog 30 | app.config.globalProperties.$t = t as any 31 | app.mount('#app') 32 | .$nextTick(() => { 33 | postMessage({payload: 'removeLoading'}, '*') 34 | }) 35 | -------------------------------------------------------------------------------- /src/entry/guide.ts: -------------------------------------------------------------------------------- 1 | import {createApp} from 'vue' 2 | import store from "../store"; 3 | 4 | import ArcoVue, {Message} from '@arco-design/web-vue' 5 | import ArcoVueIcon from '@arco-design/web-vue/es/icon' 6 | import '@arco-design/web-vue/dist/arco.css' 7 | 8 | import {i18n, t} from "../lang"; 9 | 10 | import '../style.less' 11 | import {Dialog} from "../lib/dialog"; 12 | 13 | import {CommonComponents} from "../components/common"; 14 | import Page from "./Page.vue"; 15 | import PageGuide from "../pages/PageGuide.vue"; 16 | 17 | const app = createApp(Page, { 18 | name: 'guide', 19 | title: t('新手指引'), 20 | page: PageGuide 21 | }) 22 | app.use(ArcoVue) 23 | app.use(ArcoVueIcon) 24 | app.use(CommonComponents) 25 | app.use(i18n) 26 | app.use(store) 27 | Message._context = app._context 28 | app.config.globalProperties.$mapi = window.$mapi 29 | app.config.globalProperties.$dialog = Dialog 30 | app.config.globalProperties.$t = t as any 31 | app.mount('#app') 32 | .$nextTick(() => { 33 | postMessage({payload: 'removeLoading'}, '*') 34 | }) 35 | -------------------------------------------------------------------------------- /src/entry/payment.ts: -------------------------------------------------------------------------------- 1 | import {createApp} from 'vue' 2 | import store from "../store"; 3 | 4 | import ArcoVue, {Message} from '@arco-design/web-vue' 5 | import ArcoVueIcon from '@arco-design/web-vue/es/icon' 6 | import '@arco-design/web-vue/dist/arco.css' 7 | 8 | import {i18n, t} from "../lang"; 9 | 10 | import '../style.less' 11 | import {Dialog} from "../lib/dialog"; 12 | 13 | import {CommonComponents} from "../components/common"; 14 | import Page from "./Page.vue"; 15 | import PagePayment from "../pages/PagePayment.vue"; 16 | 17 | const app = createApp(Page, { 18 | name: 'payment', 19 | title: '扫码支付', 20 | page: PagePayment 21 | }) 22 | app.use(ArcoVue) 23 | app.use(ArcoVueIcon) 24 | app.use(CommonComponents) 25 | app.use(i18n) 26 | app.use(store) 27 | Message._context = app._context 28 | app.config.globalProperties.$mapi = window.$mapi 29 | app.config.globalProperties.$dialog = Dialog 30 | app.config.globalProperties.$t = t as any 31 | app.mount('#app') 32 | .$nextTick(() => { 33 | postMessage({payload: 'removeLoading'}, '*') 34 | }) 35 | -------------------------------------------------------------------------------- /src/entry/setup.ts: -------------------------------------------------------------------------------- 1 | import {createApp} from 'vue' 2 | import store from "../store"; 3 | 4 | import ArcoVue, {Message} from '@arco-design/web-vue' 5 | import ArcoVueIcon from '@arco-design/web-vue/es/icon' 6 | import '@arco-design/web-vue/dist/arco.css' 7 | 8 | import {i18n, t} from "../lang"; 9 | 10 | import '../style.less' 11 | import {Dialog} from "../lib/dialog"; 12 | 13 | import {CommonComponents} from "../components/common"; 14 | import Page from "./Page.vue"; 15 | import PageSetup from "../pages/PageSetup.vue"; 16 | 17 | const app = createApp(Page, { 18 | name: 'setup', 19 | title: '请按照说明完成软件配置', 20 | page: PageSetup 21 | }) 22 | app.use(ArcoVue) 23 | app.use(ArcoVueIcon) 24 | app.use(CommonComponents) 25 | app.use(i18n) 26 | app.use(store) 27 | Message._context = app._context 28 | app.config.globalProperties.$mapi = window.$mapi 29 | app.config.globalProperties.$dialog = Dialog 30 | app.config.globalProperties.$t = t as any 31 | app.mount('#app') 32 | .$nextTick(() => { 33 | postMessage({payload: 'removeLoading'}, '*') 34 | }) 35 | -------------------------------------------------------------------------------- /src/entry/user.ts: -------------------------------------------------------------------------------- 1 | import {createApp} from 'vue' 2 | import store from "../store"; 3 | 4 | import ArcoVue, {Message} from '@arco-design/web-vue' 5 | import ArcoVueIcon from '@arco-design/web-vue/es/icon' 6 | import '@arco-design/web-vue/dist/arco.css' 7 | 8 | import {i18n, t} from "../lang"; 9 | 10 | import '../style.less' 11 | import {Dialog} from "../lib/dialog"; 12 | 13 | import {CommonComponents} from "../components/common"; 14 | import Page from "./Page.vue"; 15 | import PageUser from "../pages/PageUser.vue"; 16 | 17 | const app = createApp(Page, { 18 | name: 'user', 19 | title: t('用户中心'), 20 | page: PageUser 21 | }) 22 | app.use(ArcoVue) 23 | app.use(ArcoVueIcon) 24 | app.use(CommonComponents) 25 | app.use(i18n) 26 | app.use(store) 27 | Message._context = app._context 28 | app.config.globalProperties.$mapi = window.$mapi 29 | app.config.globalProperties.$dialog = Dialog 30 | app.config.globalProperties.$t = t as any 31 | app.mount('#app') 32 | .$nextTick(() => { 33 | postMessage({payload: 'removeLoading'}, '*') 34 | }) 35 | -------------------------------------------------------------------------------- /src/layouts/Raw.vue: -------------------------------------------------------------------------------- 1 | 17 | 35 | -------------------------------------------------------------------------------- /src/lib/components/Prompt.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 21 | 22 | -------------------------------------------------------------------------------- /src/lib/env.ts: -------------------------------------------------------------------------------- 1 | export const isDev = process.env.NODE_ENV === "development"; 2 | -------------------------------------------------------------------------------- /src/lib/error.ts: -------------------------------------------------------------------------------- 1 | import {t} from "../lang"; 2 | 3 | export function mapError(msg: any) { 4 | if (typeof msg !== 'string') { 5 | msg = msg.toString() 6 | } 7 | const map = { 8 | 'FileAlreadyExists': t('文件已存在'), 9 | 'FileNotFound': t('文件未找到'), 10 | 'ProcessTimeout': t('处理超时'), 11 | 'RequestError': t('请求错误'), 12 | 'Could not find any ADB device': t('找不到设备'), 13 | 'DeviceNotConnected': t('设备未连接'), 14 | } 15 | for (let key in map) { 16 | if (msg.includes(key)) { 17 | let error = map[key] 18 | // regex PluginReleaseDocFormatError:-11 19 | const regex = new RegExp(`${key}:(-?\\d+)`) 20 | const match = msg.match(regex) 21 | if (match) { 22 | error += `(${match[1]})` 23 | } 24 | return error 25 | } 26 | } 27 | return msg 28 | } 29 | -------------------------------------------------------------------------------- /src/lib/event.ts: -------------------------------------------------------------------------------- 1 | import {EventType} from "../types/Event.js"; 2 | import {TinyEmitter} from "tiny-emitter"; 3 | 4 | const emitter = new TinyEmitter(); 5 | 6 | export const GlobalEvent = { 7 | on: function (event: EventType, callback: Function) { 8 | emitter.on(event, callback) 9 | }, 10 | once: function (event: EventType, callback: Function) { 11 | emitter.once(event, callback) 12 | }, 13 | off: function (event: EventType, callback: Function) { 14 | emitter.off(event, callback) 15 | }, 16 | emit: function (event: EventType, ...args: any[]) { 17 | emitter.emit(event, ...args) 18 | }, 19 | } 20 | 21 | -------------------------------------------------------------------------------- /src/lib/file.ts: -------------------------------------------------------------------------------- 1 | export const FileUtil = { 2 | extensionToType(extension: string) { 3 | const mime = { 4 | 'mp3': 'audio/mpeg', 5 | 'wav': 'audio/wav', 6 | } 7 | return mime[extension] || '' 8 | }, 9 | bufferToBlob(buffer: ArrayBuffer, type: string) { 10 | if (!type.indexOf('/')) { 11 | type = this.extensionToType(type) 12 | } 13 | return new Blob([buffer], {type: type}) 14 | }, 15 | blobToFile(blob: Blob, name: string) { 16 | return new File([blob], name) 17 | }, 18 | getExt(path: string) { 19 | const ext = path.lastIndexOf('.') 20 | if (ext >= 0) { 21 | return path.substring(ext + 1) 22 | } 23 | return '' 24 | }, 25 | getBaseName(path: string, withExt: boolean = false) { 26 | // windows 27 | if (path.includes('\\')) { 28 | path = path.replace(/\\/g, '/') 29 | } 30 | const last = path.lastIndexOf('/') 31 | if (last >= 0) { 32 | path = path.substring(last + 1) 33 | } 34 | if (!withExt) { 35 | const ext = path.lastIndexOf('.') 36 | if (ext >= 0) { 37 | path = path.substring(0, ext) 38 | } 39 | return path 40 | } 41 | return path 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/lib/linkandroid.ts: -------------------------------------------------------------------------------- 1 | import {t} from "../lang"; 2 | 3 | export function replaceIP(value: string) { 4 | return value.replaceAll('.', '_').replaceAll(':', '-') 5 | } 6 | 7 | export function restoreIP(value: string) { 8 | return value.replaceAll('_', '.').replaceAll('-', ':') 9 | } 10 | 11 | export function isIPWithPort(ip: string) { 12 | const regex 13 | = /^((25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d):(1\d{0,4}|[1-9]\d{0,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$/ 14 | return regex.test(ip) 15 | } 16 | 17 | export function parseIPPort(ip: string) { 18 | const [ip_, port] = ip.split(':') 19 | return { 20 | ip: restoreIP(ip_), 21 | port: parseInt(port) 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /src/lib/markdown.ts: -------------------------------------------------------------------------------- 1 | import Showdown from "showdown" 2 | 3 | 4 | const converter = new Showdown.Converter() 5 | 6 | export const MarkdownUtil = { 7 | toHtml(markdown: string): string { 8 | return converter.makeHtml(markdown) 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import {createApp} from 'vue' 2 | import App from './App.vue' 3 | import router from './router' 4 | import store from "./store"; 5 | 6 | import ArcoVue, {Message} from '@arco-design/web-vue' 7 | import ArcoVueIcon from '@arco-design/web-vue/es/icon' 8 | import '@arco-design/web-vue/dist/arco.css' 9 | 10 | import {i18n, t} from "./lang"; 11 | 12 | import './style.less' 13 | import {Dialog} from "./lib/dialog"; 14 | 15 | import {CommonComponents} from "./components/common"; 16 | import {TaskManager} from "./task"; 17 | import {useSettingStore} from "./store/modules/setting"; 18 | 19 | const settingStore = useSettingStore() 20 | 21 | const app = createApp(App) 22 | app.use(ArcoVue) 23 | app.use(ArcoVueIcon) 24 | app.use(CommonComponents) 25 | app.use(i18n) 26 | app.use(store) 27 | app.use(router) 28 | Message._context = app._context 29 | app.config.globalProperties.$mapi = window.$mapi 30 | app.config.globalProperties.$dialog = Dialog 31 | app.config.globalProperties.$t = t as any 32 | TaskManager.init() 33 | 34 | app.mount('#app') 35 | .$nextTick(() => { 36 | postMessage({payload: 'removeLoading'}, '*') 37 | }) 38 | -------------------------------------------------------------------------------- /src/pages/Home.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 28 | 29 | -------------------------------------------------------------------------------- /src/pages/PageGuide.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 43 | 44 | 50 | -------------------------------------------------------------------------------- /src/pages/PageUser.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 48 | 49 | 55 | -------------------------------------------------------------------------------- /src/router.ts: -------------------------------------------------------------------------------- 1 | import {createRouter, createWebHashHistory} from 'vue-router' 2 | 3 | 4 | const routes = [ 5 | { 6 | path: '/', 7 | component: () => import('./layouts/Main.vue'), 8 | children: [ 9 | {path: '', component: () => import('./pages/Home.vue')}, 10 | {path: 'home', component: () => import('./pages/Home.vue')}, 11 | {path: 'device', component: () => import('./pages/Device.vue')}, 12 | {path: 'setting', component: () => import('./pages/Setting.vue')}, 13 | ] 14 | }, 15 | { 16 | path: '/', 17 | component: () => import('./layouts/Raw.vue'), 18 | children: [ 19 | ] 20 | }, 21 | ] 22 | 23 | const router = createRouter({ 24 | history: createWebHashHistory(), 25 | routes 26 | }) 27 | 28 | // watch router change 29 | router.beforeEach((to, from, next) => { 30 | window.$mapi?.statistics?.tick('visit', { 31 | path: to.path 32 | }) 33 | next() 34 | }) 35 | 36 | export default router 37 | -------------------------------------------------------------------------------- /src/store/index.ts: -------------------------------------------------------------------------------- 1 | import { createPinia } from "pinia" 2 | 3 | const store = createPinia() 4 | export default store 5 | -------------------------------------------------------------------------------- /src/store/modules/app.ts: -------------------------------------------------------------------------------- 1 | import {defineStore} from "pinia" 2 | import store from "../index"; 3 | 4 | export const appStore = defineStore("app", { 5 | state() { 6 | return {} 7 | }, 8 | actions: { 9 | async init() { 10 | 11 | }, 12 | } 13 | }) 14 | 15 | export const app = appStore(store) 16 | app.init().then(() => { 17 | }) 18 | 19 | export const useAppStore = () => { 20 | return app 21 | } 22 | -------------------------------------------------------------------------------- /src/task/TestAsync.ts: -------------------------------------------------------------------------------- 1 | import {TaskBiz} from "../store/modules/task"; 2 | 3 | export const TestAsync: TaskBiz = { 4 | runFunc: async (bizId, bizParam) => { 5 | console.log('TestAsync.runFunc', {bizId, bizParam}) 6 | return 'success' 7 | }, 8 | queryFunc(bizId, bizParam) { 9 | return new Promise((resolve) => { 10 | console.log('TestAsync.queryFunc', {bizId, bizParam}) 11 | setTimeout(() => { 12 | resolve(Math.random() > 0.7 ? 'success' : 'running') 13 | }, 1000) 14 | }) 15 | }, 16 | successFunc: async (bizId, bizParam) => { 17 | console.log('TestAsync.successFunc', {bizId, bizParam}) 18 | }, 19 | failFunc: async (bizId, msg, bizParam) => { 20 | console.log('TestAsync.failFunc', {bizId, bizParam, msg}) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/task/TestSync.ts: -------------------------------------------------------------------------------- 1 | import {TaskBiz} from "../store/modules/task"; 2 | 3 | export const TestSync: TaskBiz = { 4 | runFunc: async (bizId, bizParam) => { 5 | console.log('TestSync.runFunc', {bizId, bizParam}) 6 | return 'success' 7 | }, 8 | successFunc: async (bizId, bizParam) => { 9 | console.log('TestSync.successFunc', {bizId, bizParam}) 10 | }, 11 | failFunc: async (bizId, msg, bizParam) => { 12 | console.log('TestSync.failFunc', {bizId, bizParam, msg}) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/task/index.ts: -------------------------------------------------------------------------------- 1 | import {useTaskStore} from "../store/modules/task"; 2 | import {StringUtil} from "../lib/util"; 3 | import {TestAsync} from "./TestAsync"; 4 | import {TestSync} from "./TestSync"; 5 | 6 | const taskStore = useTaskStore() 7 | 8 | export const TaskManager = { 9 | init() { 10 | // taskStore.register('TestSync', TestSync) 11 | // taskStore.register('TestAsync', TestAsync) 12 | // setInterval(async () => { 13 | // // await taskStore.dispatch('TestSync', StringUtil.random()) 14 | // await taskStore.dispatch('TestAsync', StringUtil.random(), { 15 | // 'a': 1, 16 | // }, { 17 | // timeout: 3 * 1000, 18 | // }) 19 | // }, 10 * 1000) 20 | }, 21 | count() { 22 | return taskStore.records.length 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/types/Common.ts: -------------------------------------------------------------------------------- 1 | export type ResultType = { 2 | code: number 3 | msg: string 4 | data: any 5 | } 6 | -------------------------------------------------------------------------------- /src/types/Device.ts: -------------------------------------------------------------------------------- 1 | import {ComputedRef} from "vue"; 2 | 3 | export enum EnumDeviceStatus { 4 | WAIT_CONNECTING = 'waitConnecting', 5 | CONNECTED = 'connected', 6 | DISCONNECTED = 'disconnected', 7 | } 8 | 9 | export enum EnumDeviceType { 10 | USB = 'usb', 11 | WIFI = 'wifi', 12 | } 13 | 14 | export type DeviceRecord = { 15 | id: string, 16 | type: EnumDeviceType, 17 | name: string | null, 18 | raw: any, 19 | 20 | status?: any, 21 | runtime?: any, 22 | screenshot?: string | null, 23 | setting?: { 24 | dimWhenMirror?: any, 25 | alwaysTop?: any, 26 | mirrorSound?: any, 27 | previewImage?: any, 28 | videoBitRate?: any, 29 | maxFps?: any, 30 | scrcpyArgs?: any, 31 | }, 32 | } 33 | 34 | export type DeviceRuntime = { 35 | status: EnumDeviceStatus, 36 | mirrorController: any, 37 | screenBrightness?: number, 38 | previewImage: any, 39 | } 40 | -------------------------------------------------------------------------------- /src/types/File.ts: -------------------------------------------------------------------------------- 1 | export type FileItem = { 2 | name: string 3 | isDirectory: boolean 4 | size: number 5 | lastModified: number 6 | path: string 7 | fullPath: string 8 | content: string 9 | contentBase64: string 10 | mode: number 11 | } 12 | -------------------------------------------------------------------------------- /src/types/Log.ts: -------------------------------------------------------------------------------- 1 | export enum EnumLogType { 2 | INFO = 'info', 3 | ERROR = 'error', 4 | WARN = 'warn', 5 | } 6 | 7 | export type LogRecord = { 8 | projectId: string | null; 9 | level: EnumLogType; 10 | time: number; 11 | msg: string; 12 | data: any | null; 13 | } 14 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import {Dialog} from "./lib/dialog"; 3 | 4 | declare module '*.vue' { 5 | import type {DefineComponent} from 'vue' 6 | const component: DefineComponent<{}, {}, any> 7 | export default component 8 | } 9 | 10 | declare module '@vue/runtime-core' { 11 | interface ComponentCustomProperties { 12 | $mapi: typeof window.$mapi, 13 | $dialog: typeof Dialog, 14 | $t: typeof import('vue-i18n').GlobalTranslate 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: [ 4 | "./index.html", 5 | "./src/**/*.{js,ts,jsx,tsx,vue}", 6 | ], 7 | darkMode: ['selector', '[data-theme="dark"]'], 8 | theme: { 9 | extend: {}, 10 | }, 11 | plugins: [], 12 | } 13 | 14 | -------------------------------------------------------------------------------- /third-party/image-beautifier.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Build third-party" 4 | 5 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 6 | PROJECT_ROOT=$(realpath "${DIR}/..") 7 | echo "PROJECT_ROOT: ${PROJECT_ROOT}" 8 | 9 | THIRD_PARTY_ROOT="${PROJECT_ROOT}/third-party" 10 | THIRD_PARTY_PUBLIC_ROOT="${PROJECT_ROOT}/public/third-party" 11 | echo "THIRD_PARTY_ROOT: ${THIRD_PARTY_ROOT}" 12 | 13 | echo "Build image beautifier" 14 | cd "${THIRD_PARTY_ROOT}/image-beautifier" 15 | npm run build 16 | cp -av others dist/ 17 | rm -rfv "${THIRD_PARTY_PUBLIC_ROOT}/image-beautifier" 18 | mv "${THIRD_PARTY_ROOT}/image-beautifier/dist" "${THIRD_PARTY_PUBLIC_ROOT}/image-beautifier" 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "moduleResolution": "Node", 7 | "strict": true, 8 | "jsx": "preserve", 9 | "resolveJsonModule": true, 10 | "isolatedModules": true, 11 | "esModuleInterop": true, 12 | "lib": [ 13 | "ESNext", 14 | "DOM" 15 | ], 16 | "skipLibCheck": true, 17 | "noEmit": true, 18 | "noImplicitAny": false, 19 | "allowJs": true 20 | }, 21 | "include": [ 22 | "src", 23 | ], 24 | "references": [ 25 | { 26 | "path": "./tsconfig.node.json" 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "resolveJsonModule": true, 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": [ 10 | "vite.config.ts", 11 | "package.json", 12 | "electron", 13 | "src/config.ts", 14 | ], 15 | } 16 | --------------------------------------------------------------------------------