From 79a78d4322465dd4a39931b199c287fe83d767c6 Mon Sep 17 00:00:00 2001 From: Nexmoe <16796652+nexmoe@users.noreply.github.com> Date: Sat, 20 Dec 2025 19:03:13 +0800 Subject: [PATCH] feat: initialize VidBee download extension with core functionality * Add .gitignore files for main and monkey directories. * Create initial project structure for VidBee download extension. * Implement main functionality in src/main/index.ts to handle deep links. * Develop UI components including DownloadDialog and VidBee download button. * Set up TypeScript configuration and Vite for build process. * Add styles for download button and tooltips. * Integrate localization for user interface elements. * Establish deep link handling in the renderer for improved user experience. --- .gitignore | 2 +- monkey/.gitignore | 24 + monkey/package.json | 16 + monkey/pnpm-lock.yaml | 943 ++++++++++++++++++ monkey/src/main.ts | 146 +++ monkey/src/style.css | 169 ++++ monkey/src/vite-env.d.ts | 4 + monkey/tsconfig.json | 24 + monkey/tsconfig.node.json | 13 + monkey/vite.config.ts | 67 ++ src/main/index.ts | 32 +- src/renderer/src/App.tsx | 20 +- .../components/download/DownloadDialog.tsx | 75 ++ .../download/PlaylistDownloadGroup.tsx | 35 +- .../download/UnifiedDownloadHistory.tsx | 32 +- src/renderer/src/locales/en.json | 1 + 16 files changed, 1564 insertions(+), 39 deletions(-) create mode 100644 monkey/.gitignore create mode 100644 monkey/package.json create mode 100644 monkey/pnpm-lock.yaml create mode 100644 monkey/src/main.ts create mode 100644 monkey/src/style.css create mode 100644 monkey/src/vite-env.d.ts create mode 100644 monkey/tsconfig.json create mode 100644 monkey/tsconfig.node.json create mode 100644 monkey/vite.config.ts diff --git a/.gitignore b/.gitignore index ad6f01d..5f89311 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ node_modules -dist +/dist out .conductor/ .DS_Store diff --git a/monkey/.gitignore b/monkey/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/monkey/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/monkey/package.json b/monkey/package.json new file mode 100644 index 0000000..e574284 --- /dev/null +++ b/monkey/package.json @@ -0,0 +1,16 @@ +{ + "name": "vidbee-quick-download", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview" + }, + "devDependencies": { + "typescript": "^5.9.2", + "vite": "^7.1.3", + "vite-plugin-monkey": "^7.1.1" + } +} diff --git a/monkey/pnpm-lock.yaml b/monkey/pnpm-lock.yaml new file mode 100644 index 0000000..4028bcd --- /dev/null +++ b/monkey/pnpm-lock.yaml @@ -0,0 +1,943 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + typescript: + specifier: ^5.9.2 + version: 5.9.3 + vite: + specifier: ^7.1.3 + version: 7.3.0 + vite-plugin-monkey: + specifier: ^7.1.1 + version: 7.1.8(postcss@8.5.6)(vite@7.3.0) + +packages: + + '@esbuild/aix-ppc64@0.27.2': + resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.2': + resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.2': + resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.2': + resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.2': + resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.2': + resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.2': + resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.2': + resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.2': + resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.2': + resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.2': + resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.2': + resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.2': + resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.2': + resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.2': + resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.2': + resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.2': + resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.2': + resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.2': + resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.2': + resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.2': + resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.2': + resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.2': + resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.2': + resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.2': + resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.2': + resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@rollup/rollup-android-arm-eabi@4.54.0': + resolution: {integrity: sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.54.0': + resolution: {integrity: sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.54.0': + resolution: {integrity: sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.54.0': + resolution: {integrity: sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.54.0': + resolution: {integrity: sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.54.0': + resolution: {integrity: sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.54.0': + resolution: {integrity: sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.54.0': + resolution: {integrity: sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.54.0': + resolution: {integrity: sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.54.0': + resolution: {integrity: sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.54.0': + resolution: {integrity: sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.54.0': + resolution: {integrity: sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.54.0': + resolution: {integrity: sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.54.0': + resolution: {integrity: sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.54.0': + resolution: {integrity: sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.54.0': + resolution: {integrity: sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.54.0': + resolution: {integrity: sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openharmony-arm64@4.54.0': + resolution: {integrity: sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.54.0': + resolution: {integrity: sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.54.0': + resolution: {integrity: sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.54.0': + resolution: {integrity: sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.54.0': + resolution: {integrity: sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==} + cpu: [x64] + os: [win32] + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + acorn-walk@8.3.4: + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} + engines: {node: '>=0.4.0'} + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + bundle-name@4.1.0: + resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} + engines: {node: '>=18'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + cuint@0.2.2: + resolution: {integrity: sha512-d4ZVpCW31eWwCMe1YT3ur7mUDnTXbgwyzaL320DrcRT45rfjYxkt5QWLrmOJ+/UEAI2+fQgKe/fCjR8l4TpRgw==} + + default-browser-id@5.0.1: + resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==} + engines: {node: '>=18'} + + default-browser@5.4.0: + resolution: {integrity: sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg==} + engines: {node: '>=18'} + + define-lazy-prop@3.0.0: + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} + engines: {node: '>=12'} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + + esbuild@0.27.2: + resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} + engines: {node: '>=18'} + hasBin: true + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + htmlparser2@10.0.0: + resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==} + + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + + is-wsl@3.1.0: + resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} + engines: {node: '>=16'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + make-dir@3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + + mime@2.5.2: + resolution: {integrity: sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==} + engines: {node: '>=4.0.0'} + hasBin: true + + minimatch@3.0.8: + resolution: {integrity: sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==} + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + open@10.2.0: + resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} + engines: {node: '>=18'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + postcss-url@10.1.3: + resolution: {integrity: sha512-FUzyxfI5l2tKmXdYc6VTu3TWZsInayEKPbiyW+P6vmmIrrb4I6CGX0BFoewgYHLK+oIL5FECEK02REYRpBvUCw==} + engines: {node: '>=10'} + peerDependencies: + postcss: ^8.0.0 + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + rollup@4.54.0: + resolution: {integrity: sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-applescript@7.1.0: + resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} + engines: {node: '>=18'} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + systemjs@6.15.1: + resolution: {integrity: sha512-Nk8c4lXvMB98MtbmjX7JwJRgJOL8fluecYCfCeYBznwmpOs8Bf15hLM6z4z71EDAhQVrQrI+wt1aLWSXZq+hXA==} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + vite-plugin-monkey@7.1.8: + resolution: {integrity: sha512-FGz1jlHcodt+L120UcHLiN0jXqQZdZHxLuHcGQg8Zjas6BulxLM1oiGUg3M95HbjYVFH6ygrOJoZ5YCYu1purw==} + peerDependencies: + vite: ^6.0.0 || ^7.0.0 + peerDependenciesMeta: + vite: + optional: true + + vite@7.3.0: + resolution: {integrity: sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + wsl-utils@0.1.0: + resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} + engines: {node: '>=18'} + + xxhashjs@0.2.2: + resolution: {integrity: sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==} + +snapshots: + + '@esbuild/aix-ppc64@0.27.2': + optional: true + + '@esbuild/android-arm64@0.27.2': + optional: true + + '@esbuild/android-arm@0.27.2': + optional: true + + '@esbuild/android-x64@0.27.2': + optional: true + + '@esbuild/darwin-arm64@0.27.2': + optional: true + + '@esbuild/darwin-x64@0.27.2': + optional: true + + '@esbuild/freebsd-arm64@0.27.2': + optional: true + + '@esbuild/freebsd-x64@0.27.2': + optional: true + + '@esbuild/linux-arm64@0.27.2': + optional: true + + '@esbuild/linux-arm@0.27.2': + optional: true + + '@esbuild/linux-ia32@0.27.2': + optional: true + + '@esbuild/linux-loong64@0.27.2': + optional: true + + '@esbuild/linux-mips64el@0.27.2': + optional: true + + '@esbuild/linux-ppc64@0.27.2': + optional: true + + '@esbuild/linux-riscv64@0.27.2': + optional: true + + '@esbuild/linux-s390x@0.27.2': + optional: true + + '@esbuild/linux-x64@0.27.2': + optional: true + + '@esbuild/netbsd-arm64@0.27.2': + optional: true + + '@esbuild/netbsd-x64@0.27.2': + optional: true + + '@esbuild/openbsd-arm64@0.27.2': + optional: true + + '@esbuild/openbsd-x64@0.27.2': + optional: true + + '@esbuild/openharmony-arm64@0.27.2': + optional: true + + '@esbuild/sunos-x64@0.27.2': + optional: true + + '@esbuild/win32-arm64@0.27.2': + optional: true + + '@esbuild/win32-ia32@0.27.2': + optional: true + + '@esbuild/win32-x64@0.27.2': + optional: true + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@rollup/rollup-android-arm-eabi@4.54.0': + optional: true + + '@rollup/rollup-android-arm64@4.54.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.54.0': + optional: true + + '@rollup/rollup-darwin-x64@4.54.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.54.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.54.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.54.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.54.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.54.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.54.0': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.54.0': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.54.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.54.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.54.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.54.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.54.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.54.0': + optional: true + + '@rollup/rollup-openharmony-arm64@4.54.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.54.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.54.0': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.54.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.54.0': + optional: true + + '@types/estree@1.0.8': {} + + acorn-walk@8.3.4: + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + balanced-match@1.0.2: {} + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + bundle-name@4.1.0: + dependencies: + run-applescript: 7.1.0 + + concat-map@0.0.1: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + cuint@0.2.2: {} + + default-browser-id@5.0.1: {} + + default-browser@5.4.0: + dependencies: + bundle-name: 4.1.0 + default-browser-id: 5.0.1 + + define-lazy-prop@3.0.0: {} + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + entities@4.5.0: {} + + entities@6.0.1: {} + + esbuild@0.27.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.2 + '@esbuild/android-arm': 0.27.2 + '@esbuild/android-arm64': 0.27.2 + '@esbuild/android-x64': 0.27.2 + '@esbuild/darwin-arm64': 0.27.2 + '@esbuild/darwin-x64': 0.27.2 + '@esbuild/freebsd-arm64': 0.27.2 + '@esbuild/freebsd-x64': 0.27.2 + '@esbuild/linux-arm': 0.27.2 + '@esbuild/linux-arm64': 0.27.2 + '@esbuild/linux-ia32': 0.27.2 + '@esbuild/linux-loong64': 0.27.2 + '@esbuild/linux-mips64el': 0.27.2 + '@esbuild/linux-ppc64': 0.27.2 + '@esbuild/linux-riscv64': 0.27.2 + '@esbuild/linux-s390x': 0.27.2 + '@esbuild/linux-x64': 0.27.2 + '@esbuild/netbsd-arm64': 0.27.2 + '@esbuild/netbsd-x64': 0.27.2 + '@esbuild/openbsd-arm64': 0.27.2 + '@esbuild/openbsd-x64': 0.27.2 + '@esbuild/openharmony-arm64': 0.27.2 + '@esbuild/sunos-x64': 0.27.2 + '@esbuild/win32-arm64': 0.27.2 + '@esbuild/win32-ia32': 0.27.2 + '@esbuild/win32-x64': 0.27.2 + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + fsevents@2.3.3: + optional: true + + htmlparser2@10.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 6.0.1 + + import-meta-resolve@4.2.0: {} + + is-docker@3.0.0: {} + + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + + is-wsl@3.1.0: + dependencies: + is-inside-container: 1.0.0 + + isexe@2.0.0: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + make-dir@3.1.0: + dependencies: + semver: 6.3.1 + + mime@2.5.2: {} + + minimatch@3.0.8: + dependencies: + brace-expansion: 1.1.12 + + mrmime@2.0.1: {} + + nanoid@3.3.11: {} + + open@10.2.0: + dependencies: + default-browser: 5.4.0 + define-lazy-prop: 3.0.0 + is-inside-container: 1.0.0 + wsl-utils: 0.1.0 + + path-key@3.1.1: {} + + picocolors@1.1.1: {} + + picomatch@4.0.3: {} + + postcss-url@10.1.3(postcss@8.5.6): + dependencies: + make-dir: 3.1.0 + mime: 2.5.2 + minimatch: 3.0.8 + postcss: 8.5.6 + xxhashjs: 0.2.2 + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + rollup@4.54.0: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.54.0 + '@rollup/rollup-android-arm64': 4.54.0 + '@rollup/rollup-darwin-arm64': 4.54.0 + '@rollup/rollup-darwin-x64': 4.54.0 + '@rollup/rollup-freebsd-arm64': 4.54.0 + '@rollup/rollup-freebsd-x64': 4.54.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.54.0 + '@rollup/rollup-linux-arm-musleabihf': 4.54.0 + '@rollup/rollup-linux-arm64-gnu': 4.54.0 + '@rollup/rollup-linux-arm64-musl': 4.54.0 + '@rollup/rollup-linux-loong64-gnu': 4.54.0 + '@rollup/rollup-linux-ppc64-gnu': 4.54.0 + '@rollup/rollup-linux-riscv64-gnu': 4.54.0 + '@rollup/rollup-linux-riscv64-musl': 4.54.0 + '@rollup/rollup-linux-s390x-gnu': 4.54.0 + '@rollup/rollup-linux-x64-gnu': 4.54.0 + '@rollup/rollup-linux-x64-musl': 4.54.0 + '@rollup/rollup-openharmony-arm64': 4.54.0 + '@rollup/rollup-win32-arm64-msvc': 4.54.0 + '@rollup/rollup-win32-ia32-msvc': 4.54.0 + '@rollup/rollup-win32-x64-gnu': 4.54.0 + '@rollup/rollup-win32-x64-msvc': 4.54.0 + fsevents: 2.3.3 + + run-applescript@7.1.0: {} + + semver@6.3.1: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + source-map-js@1.2.1: {} + + systemjs@6.15.1: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + typescript@5.9.3: {} + + vite-plugin-monkey@7.1.8(postcss@8.5.6)(vite@7.3.0): + dependencies: + acorn: 8.15.0 + acorn-walk: 8.3.4 + cross-spawn: 7.0.6 + htmlparser2: 10.0.0 + import-meta-resolve: 4.2.0 + magic-string: 0.30.21 + mrmime: 2.0.1 + open: 10.2.0 + picocolors: 1.1.1 + postcss-url: 10.1.3(postcss@8.5.6) + systemjs: 6.15.1 + optionalDependencies: + vite: 7.3.0 + transitivePeerDependencies: + - postcss + + vite@7.3.0: + dependencies: + esbuild: 0.27.2 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.54.0 + tinyglobby: 0.2.15 + optionalDependencies: + fsevents: 2.3.3 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wsl-utils@0.1.0: + dependencies: + is-wsl: 3.1.0 + + xxhashjs@0.2.2: + dependencies: + cuint: 0.2.2 diff --git a/monkey/src/main.ts b/monkey/src/main.ts new file mode 100644 index 0000000..24219c4 --- /dev/null +++ b/monkey/src/main.ts @@ -0,0 +1,146 @@ +import './style.css' + +// Get current video URL +// yt-dlp can handle all URLs directly, so we just return the current URL +function getVideoUrl(): string | null { + return window.location.href +} + +// Temporarily hide button +function hideButtonTemporarily(): void { + const container = document.getElementById('vidbee-download-btn') + if (container) { + container.classList.add('vidbee-hidden') + // Auto restore after 5 seconds + setTimeout(() => { + if (container) { + container.classList.remove('vidbee-hidden') + } + }, 5000) + } +} + +// Create VidBee download button +function createVidBeeButton(): void { + // Check if button already exists + if (document.getElementById('vidbee-download-btn')) { + return + } + + const videoUrl = getVideoUrl() + if (!videoUrl) { + return + } + + // Create button container + const container = document.createElement('div') + container.id = 'vidbee-download-btn' + container.className = 'vidbee-download-container' + + // Create main download button + const button = document.createElement('button') + button.className = 'vidbee-download-button' + button.setAttribute('aria-label', 'Download with VidBee') + button.innerHTML = ` + + + + + + Download with VidBee + ` + + // Create close button + const closeButton = document.createElement('button') + closeButton.className = 'vidbee-close-button' + closeButton.setAttribute('aria-label', 'Hide button') + closeButton.innerHTML = ` + + + + + Hide + ` + + // Handle close button click - temporarily hide + closeButton.addEventListener('click', (e) => { + e.stopPropagation() + hideButtonTemporarily() + }) + + let clickTimer: number | null = null + let clickCount = 0 + + // Handle main button click event - single click for download + button.addEventListener('click', () => { + clickCount++ + + if (clickCount === 1) { + clickTimer = window.setTimeout(() => { + // Single click - trigger download + const vidbeeUrl = `vidbee://download?url=${encodeURIComponent(videoUrl)}` + window.location.href = vidbeeUrl + clickCount = 0 + }, 300) + } else if (clickCount === 2) { + // Double click - temporarily hide + if (clickTimer !== null) { + clearTimeout(clickTimer) + } + clickCount = 0 + hideButtonTemporarily() + } + }) + + // Assemble container + container.appendChild(button) + container.appendChild(closeButton) + + // Insert container directly to body (fixed position) + document.body.appendChild(container) +} + +// Initialize when page loads +function init(): void { + // Wait for page to fully load + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', createVidBeeButton) + } else { + createVidBeeButton() + } + + // Handle SPA navigation (when navigating between videos on sites like YouTube, Bilibili, etc.) + let lastUrl = location.href + let urlCheckTimer: number | null = null + + const checkUrlChange = () => { + const currentUrl = location.href + if (currentUrl !== lastUrl) { + lastUrl = currentUrl + // Remove old button and create new one + const oldButton = document.getElementById('vidbee-download-btn') + if (oldButton) { + oldButton.remove() + } + // Wait a bit for the page to update (different sites have different update speeds) + const hostname = window.location.hostname + const delay = hostname.includes('bilibili.com') ? 800 : 500 + setTimeout(createVidBeeButton, delay) + } + } + + // Use MutationObserver for DOM changes (works for most SPA sites) + new MutationObserver(() => { + if (urlCheckTimer !== null) { + clearTimeout(urlCheckTimer) + } + urlCheckTimer = window.setTimeout(checkUrlChange, 100) + }).observe(document.body, { childList: true, subtree: true }) + + // Also listen to popstate for browser navigation + window.addEventListener('popstate', () => { + setTimeout(checkUrlChange, 300) + }) +} + +init() diff --git a/monkey/src/style.css b/monkey/src/style.css new file mode 100644 index 0000000..191f9e7 --- /dev/null +++ b/monkey/src/style.css @@ -0,0 +1,169 @@ +/* VidBee Download Button Container */ +.vidbee-download-container { + position: fixed; + bottom: 16px; + right: 16px; + z-index: 9999; + transition: all 0.2s ease; + overflow: visible; +} + +/* Hidden state */ +.vidbee-download-container.vidbee-hidden { + opacity: 0; + pointer-events: none; + transform: scale(0); +} + +/* Main Download Button */ +.vidbee-download-button { + position: relative; + display: flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + padding: 0; + background: rgba(0, 0, 0, 0.5); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + color: rgba(255, 255, 255, 0.8); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 50%; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); + cursor: pointer; + font-size: 0; + transition: all 0.2s ease; + opacity: 0.6; + overflow: visible; +} + +.vidbee-download-button:hover { + opacity: 1; + background: rgba(0, 0, 0, 0.7); + border-color: rgba(255, 255, 255, 0.2); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + transform: scale(1.1); +} + +.vidbee-download-button:active { + transform: scale(0.95); +} + +.vidbee-download-button svg { + flex-shrink: 0; + stroke: currentColor; + width: 16px; + height: 16px; +} + +/* Tooltip */ +.vidbee-tooltip { + position: absolute; + right: calc(100% + 8px); + top: 50%; + padding: 6px 10px; + background: rgba(0, 0, 0, 0.9); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + color: white; + font-size: 12px; + font-weight: 500; + white-space: nowrap; + border-radius: 4px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); + opacity: 0; + pointer-events: none; + transition: opacity 0.2s ease, transform 0.2s ease; + transform: translateY(-50%) translateX(4px); + z-index: 10000; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + line-height: 1; +} + +.vidbee-tooltip::after { + content: ''; + position: absolute; + left: 100%; + top: 50%; + transform: translateY(-50%); + border: 4px solid transparent; + border-left-color: rgba(0, 0, 0, 0.9); +} + +.vidbee-download-button:hover .vidbee-tooltip { + opacity: 1; + transform: translateY(-50%) translateX(0); +} + +/* Close Button - Hidden by default */ +.vidbee-close-button { + position: absolute; + top: -6px; + right: -6px; + display: flex; + align-items: center; + justify-content: center; + width: 18px; + height: 18px; + padding: 0; + background: rgba(255, 77, 77, 0.9); + backdrop-filter: blur(4px); + -webkit-backdrop-filter: blur(4px); + color: white; + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 50%; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); + cursor: pointer; + font-size: 0; + transition: all 0.2s ease; + z-index: 1; + opacity: 0; + pointer-events: none; + overflow: visible; +} + +/* Show close button on container hover */ +.vidbee-download-container:hover .vidbee-close-button { + opacity: 1; + pointer-events: auto; +} + +.vidbee-close-button:hover { + background: rgba(255, 77, 77, 1); + transform: scale(1.15); + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.4); +} + +.vidbee-close-button:active { + transform: scale(0.9); +} + +.vidbee-close-button svg { + flex-shrink: 0; + stroke: currentColor; + width: 10px; + height: 10px; +} + +/* Close button tooltip positioning */ +.vidbee-close-button .vidbee-tooltip { + right: calc(100% + 6px); + top: 50%; + left: auto; + transform: translateY(-50%) translateX(4px); +} + +.vidbee-close-button .vidbee-tooltip::after { + left: 100%; + top: 50%; + right: auto; + transform: translateY(-50%); + border-left-color: rgba(0, 0, 0, 0.9); + border-top-color: transparent; +} + +.vidbee-download-container:hover .vidbee-close-button:hover .vidbee-tooltip { + opacity: 1; + transform: translateY(-50%) translateX(0); +} diff --git a/monkey/src/vite-env.d.ts b/monkey/src/vite-env.d.ts new file mode 100644 index 0000000..57b9e37 --- /dev/null +++ b/monkey/src/vite-env.d.ts @@ -0,0 +1,4 @@ +/// +/// +//// +/// diff --git a/monkey/tsconfig.json b/monkey/tsconfig.json new file mode 100644 index 0000000..a4883f2 --- /dev/null +++ b/monkey/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/monkey/tsconfig.node.json b/monkey/tsconfig.node.json new file mode 100644 index 0000000..46d1fa3 --- /dev/null +++ b/monkey/tsconfig.node.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "noEmit": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/monkey/vite.config.ts b/monkey/vite.config.ts new file mode 100644 index 0000000..f29dbd5 --- /dev/null +++ b/monkey/vite.config.ts @@ -0,0 +1,67 @@ +import { defineConfig } from 'vite' +import monkey from 'vite-plugin-monkey' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [ + monkey({ + entry: 'src/main.ts', + userscript: { + icon: 'https://vidbee.org/favicon.svg', + namespace: 'vidbee', + match: [ + // YouTube + 'https://www.youtube.com/*', + 'https://youtube.com/*', + 'https://music.youtube.com/*', + // Bilibili (哔哩哔哩) + 'https://www.bilibili.com/*', + 'https://bilibili.com/*', + // TikTok + 'https://www.tiktok.com/*', + 'https://tiktok.com/*', + // Vimeo + 'https://vimeo.com/*', + 'https://www.vimeo.com/*', + // Dailymotion + 'https://www.dailymotion.com/*', + 'https://dailymotion.com/*', + // Twitch + 'https://www.twitch.tv/*', + 'https://twitch.tv/*', + // Twitter/X + 'https://twitter.com/*', + 'https://www.twitter.com/*', + 'https://x.com/*', + 'https://www.x.com/*', + // Instagram + 'https://www.instagram.com/*', + 'https://instagram.com/*', + // Facebook + 'https://www.facebook.com/*', + 'https://facebook.com/*', + 'https://fb.com/*', + 'https://www.fb.com/*', + // Reddit + 'https://www.reddit.com/*', + 'https://reddit.com/*', + // SoundCloud + 'https://soundcloud.com/*', + 'https://www.soundcloud.com/*', + // NicoNico + 'https://www.nicovideo.jp/*', + 'https://nicovideo.jp/*', + // Kick + 'https://kick.com/*', + 'https://www.kick.com/*', + // Bandcamp + 'https://bandcamp.com/*', + 'https://*.bandcamp.com/*', + // Mixcloud + 'https://www.mixcloud.com/*', + 'https://mixcloud.com/*' + ] + } + }) + ] +}) diff --git a/src/main/index.ts b/src/main/index.ts index b2b3b98..c1bfc2b 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -39,10 +39,14 @@ protocol.registerSchemesAsPrivileged([ let mainWindow: BrowserWindow | null = null let isQuitting = false -const pendingDeepLinkUrls: string[] = [] +interface DeepLinkData { + url: string + type: 'single' | 'playlist' +} +const pendingDeepLinkUrls: DeepLinkData[] = [] let isRendererReady = false -const parseDownloadDeepLink = (rawUrl: string): string | null => { +const parseDownloadDeepLink = (rawUrl: string): DeepLinkData | null => { try { const parsed = new URL(rawUrl) if (parsed.protocol !== `${APP_PROTOCOL}:`) { @@ -61,16 +65,22 @@ const parseDownloadDeepLink = (rawUrl: string): string | null => { return null } - return targetUrl.trim() + const typeParam = parsed.searchParams.get('type') + const type = typeParam === 'playlist' ? 'playlist' : 'single' + + return { + url: targetUrl.trim(), + type + } } catch (error) { log.warn('Failed to parse deep link:', error) return null } } -const deliverDeepLink = (videoUrl: string): void => { +const deliverDeepLink = (data: DeepLinkData): void => { if (!mainWindow || !isRendererReady) { - pendingDeepLinkUrls.push(videoUrl) + pendingDeepLinkUrls.push(data) return } @@ -81,7 +91,7 @@ const deliverDeepLink = (videoUrl: string): void => { mainWindow.show() } mainWindow.focus() - mainWindow.webContents.send('download:deeplink', videoUrl) + mainWindow.webContents.send('download:deeplink', data) } const flushPendingDeepLinks = (): void => { @@ -90,18 +100,18 @@ const flushPendingDeepLinks = (): void => { } const pending = pendingDeepLinkUrls.splice(0, pendingDeepLinkUrls.length) - for (const url of pending) { - mainWindow.webContents.send('download:deeplink', url) + for (const data of pending) { + mainWindow.webContents.send('download:deeplink', data) } } const handleDeepLinkUrl = (rawUrl: string): void => { - const videoUrl = parseDownloadDeepLink(rawUrl) - if (!videoUrl) { + const data = parseDownloadDeepLink(rawUrl) + if (!data) { log.warn('Ignored unsupported deep link:', rawUrl) return } - deliverDeepLink(videoUrl) + deliverDeepLink(data) } const handleDeepLinkArgv = (argv: string[]): void => { diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index 668f1a3..faee053 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -5,7 +5,7 @@ import { TitleBar } from '@renderer/components/ui/title-bar' import type { SubscriptionRule } from '@shared/types' import { useAtom, useSetAtom } from 'jotai' import { ThemeProvider } from 'next-themes' -import { useEffect, useRef, useState } from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { HashRouter, Navigate, Route, Routes, useLocation, useNavigate } from 'react-router' import { toast } from 'sonner' @@ -59,12 +59,15 @@ function AppContent() { const currentPage = pathToPage(location.pathname) const supportedSitesUrl = 'https://vidbee.org/supported-sites/' - const handlePageChange = (page: Page) => { - const targetPath = pageToPath[page] ?? '/' - if (normalizePathname(location.pathname) !== targetPath) { - navigate(targetPath) - } - } + const handlePageChange = useCallback( + (page: Page) => { + const targetPath = pageToPath[page] ?? '/' + if (normalizePathname(location.pathname) !== targetPath) { + navigate(targetPath) + } + }, + [location.pathname, navigate] + ) const handleOpenSupportedSites = () => { window.open(supportedSitesUrl, '_blank') @@ -80,8 +83,9 @@ function AppContent() { if (!url) { return } + // Switch to home page to show download dialog handlePageChange('home') - // TODO: Handle deep link URL in download dialog + // The DownloadDialog component will handle opening the dialog and parsing the video } ipcEvents.on('download:deeplink', handleDeepLink) diff --git a/src/renderer/src/components/download/DownloadDialog.tsx b/src/renderer/src/components/download/DownloadDialog.tsx index 1aeb7c0..271440c 100644 --- a/src/renderer/src/components/download/DownloadDialog.tsx +++ b/src/renderer/src/components/download/DownloadDialog.tsx @@ -229,6 +229,81 @@ export function DownloadDialog({ onOpenSupportedSites, onOpenSettings }: Downloa [addHistoryRecord, removeDownload] ) + // Listen for deep link events + useEffect(() => { + const handleDeepLink = async (data: unknown) => { + // Support both old format (string) and new format (object with url and type) + let url: string + let type: 'single' | 'playlist' = 'single' + + if (typeof data === 'string') { + // Legacy format: just URL string + url = data.trim() + } else if (data && typeof data === 'object' && 'url' in data) { + // New format: object with url and type + url = typeof data.url === 'string' ? data.url.trim() : '' + if ('type' in data && data.type === 'playlist') { + type = 'playlist' + } + } else { + return + } + + if (!url) { + return + } + + // Open dialog and set URL + setOpen(true) + setActiveTab(type) + + if (type === 'playlist') { + // Handle playlist + setPlaylistUrl(url) + setPlaylistInfo(null) + setPlaylistPreviewError(null) + setPlaylistCustomDownloadPath('') + + // Wait for dialog to open, then fetch playlist info + setTimeout(async () => { + setPlaylistPreviewError(null) + setPlaylistPreviewLoading(true) + try { + const info = await ipcServices.download.getPlaylistInfo(url) + setPlaylistInfo(info) + if (info.entryCount === 0) { + toast.error(t('playlist.noEntries')) + return + } + toast.success(t('playlist.foundVideos', { count: info.entryCount })) + } catch (error) { + console.error('Failed to fetch playlist info:', error) + const message = + error instanceof Error && error.message ? error.message : t('playlist.previewFailed') + setPlaylistPreviewError(message) + setPlaylistInfo(null) + toast.error(t('playlist.previewFailed')) + } finally { + setPlaylistPreviewLoading(false) + } + }, 100) + } else { + // Handle single video + setUrl(url) + + // Wait for dialog to open and settings to load, then fetch video info + setTimeout(async () => { + await fetchVideoInfo(url) + }, 100) + } + } + + ipcEvents.on('download:deeplink', handleDeepLink) + return () => { + ipcEvents.removeListener('download:deeplink', handleDeepLink) + } + }, [fetchVideoInfo, t]) + useEffect(() => { if (!open) return diff --git a/src/renderer/src/components/download/PlaylistDownloadGroup.tsx b/src/renderer/src/components/download/PlaylistDownloadGroup.tsx index 4fd3d44..b83fb65 100644 --- a/src/renderer/src/components/download/PlaylistDownloadGroup.tsx +++ b/src/renderer/src/components/download/PlaylistDownloadGroup.tsx @@ -78,24 +78,24 @@ export function PlaylistDownloadGroup({ const aggregatePercent = totalCount > 0 ? Math.min((totalProgress / totalCount) * 100, 100) : 0 return ( -
+
-
- -
+
+

{displayTitle}

@@ -117,7 +117,7 @@ export function PlaylistDownloadGroup({ )}
-
+
{canDeletePlaylist && (